diff options
Diffstat (limited to 'bgpd')
-rw-r--r-- | bgpd/bgp_advertise.c | 10 | ||||
-rw-r--r-- | bgpd/bgp_aspath.c | 18 | ||||
-rw-r--r-- | bgpd/bgp_aspath.h | 4 | ||||
-rw-r--r-- | bgpd/bgp_attr.c | 671 | ||||
-rw-r--r-- | bgpd/bgp_attr.h | 23 | ||||
-rw-r--r-- | bgpd/bgp_clist.c | 4 | ||||
-rw-r--r-- | bgpd/bgp_common.c | 2 | ||||
-rw-r--r-- | bgpd/bgp_common.h | 8 | ||||
-rw-r--r-- | bgpd/bgp_community.c | 13 | ||||
-rw-r--r-- | bgpd/bgp_community.h | 2 | ||||
-rw-r--r-- | bgpd/bgp_connection.c | 56 | ||||
-rw-r--r-- | bgpd/bgp_connection.h | 75 | ||||
-rw-r--r-- | bgpd/bgp_ecommunity.c | 33 | ||||
-rw-r--r-- | bgpd/bgp_ecommunity.h | 4 | ||||
-rw-r--r-- | bgpd/bgp_fsm.c | 11 | ||||
-rw-r--r-- | bgpd/bgp_msg_read.c | 4 | ||||
-rw-r--r-- | bgpd/bgp_network.c | 232 | ||||
-rw-r--r-- | bgpd/bgp_network.h | 23 | ||||
-rw-r--r-- | bgpd/bgp_nexthop.c | 3 | ||||
-rw-r--r-- | bgpd/bgp_notification.c | 7 | ||||
-rw-r--r-- | bgpd/bgp_packet.c | 159 | ||||
-rw-r--r-- | bgpd/bgp_peer.c | 24 | ||||
-rw-r--r-- | bgpd/bgp_peer.h | 53 | ||||
-rw-r--r-- | bgpd/bgp_route.c | 160 | ||||
-rw-r--r-- | bgpd/bgp_routemap.c | 27 | ||||
-rw-r--r-- | bgpd/bgp_session.c | 39 | ||||
-rw-r--r-- | bgpd/bgp_session.h | 6 | ||||
-rw-r--r-- | bgpd/bgp_vty.c | 82 | ||||
-rw-r--r-- | bgpd/bgp_zebra.c | 16 | ||||
-rw-r--r-- | bgpd/bgpd.c | 566 | ||||
-rw-r--r-- | bgpd/bgpd.h | 18 |
31 files changed, 1372 insertions, 981 deletions
diff --git a/bgpd/bgp_advertise.c b/bgpd/bgp_advertise.c index 495d0fdc..c03546d7 100644 --- a/bgpd/bgp_advertise.c +++ b/bgpd/bgp_advertise.c @@ -140,13 +140,13 @@ bgp_advertise_unintern (struct hash *hash, struct bgp_advertise_attr *baa) baa->refcnt--; if (baa->refcnt && baa->attr) - bgp_attr_unintern (baa->attr); + bgp_attr_unintern (&baa->attr); else { if (baa->attr) { hash_release (hash, baa); - bgp_attr_unintern (baa->attr); + bgp_attr_unintern (&baa->attr); } baa_free (baa); } @@ -323,7 +323,7 @@ bgp_adj_out_remove (struct bgp_node *rn, struct bgp_adj_out *adj, assert((rn == adj->rn) && (peer == adj->peer)) ; if (adj->attr) - bgp_attr_unintern (adj->attr); + bgp_attr_unintern (&adj->attr); if (adj->adv) bgp_advertise_clean (peer, adj, afi, safi); @@ -364,7 +364,7 @@ bgp_adj_in_set (struct bgp_node *rn, struct peer *peer, struct attr *attr) { if (adj->attr != attr) { - bgp_attr_unintern (adj->attr); + bgp_attr_unintern (&adj->attr); adj->attr = bgp_attr_intern (attr); } return; @@ -410,7 +410,7 @@ bgp_adj_in_remove (struct bgp_node *rn, struct bgp_adj_in *bai) assert(rn == bai->rn) ; /* Done with this copy of attributes */ - bgp_attr_unintern (bai->attr); + bgp_attr_unintern (&bai->attr); /* Unhook from peer */ if (bai->route_next != NULL) diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c index 9857501b..dc6ed166 100644 --- a/bgpd/bgp_aspath.c +++ b/bgpd/bgp_aspath.c @@ -341,19 +341,21 @@ aspath_free (struct aspath *aspath) /* Unintern aspath from AS path bucket. */ void -aspath_unintern (struct aspath *aspath) +aspath_unintern (struct aspath **aspath) { struct aspath *ret; + struct aspath *asp = *aspath; - if (aspath->refcnt) - aspath->refcnt--; + if (asp->refcnt) + asp->refcnt--; - if (aspath->refcnt == 0) + if (asp->refcnt == 0) { /* This aspath must exist in aspath hash table. */ - ret = hash_release (ashash, aspath); + ret = hash_release (ashash, asp); assert (ret != NULL); - aspath_free (aspath); + aspath_free (asp); + *aspath = NULL; } } @@ -790,7 +792,7 @@ assegments_parse (struct stream *s, size_t length, int use32bit, int as4_path) * have segments == NULL and str == zero length string (unique). */ struct aspath * -aspath_parse (struct stream *s, size_t length, int use32bit, int as4_path) +aspath_parse (struct stream *s, size_t length, bool use32bit, bool as4_path) { struct aspath as; struct aspath *find; @@ -1643,7 +1645,7 @@ aspath_segment_add (struct aspath *as, int type) struct aspath * aspath_empty (void) { - return aspath_parse (NULL, 0, 1, 0); /* 32Bit ;-) not AS4_PATH */ + return aspath_parse (NULL, 0, true, false); /* 32Bit ;-) not AS4_PATH */ } struct aspath * diff --git a/bgpd/bgp_aspath.h b/bgpd/bgp_aspath.h index c02a84aa..49a3a5be 100644 --- a/bgpd/bgp_aspath.h +++ b/bgpd/bgp_aspath.h @@ -67,7 +67,7 @@ struct aspath /* Prototypes. */ extern void aspath_init (void); extern void aspath_finish (void); -extern struct aspath *aspath_parse (struct stream *, size_t, int, int); +extern struct aspath *aspath_parse (struct stream *, size_t, bool, bool); extern struct aspath *aspath_dup (struct aspath *); extern struct aspath *aspath_aggregate (struct aspath *, struct aspath *); extern struct aspath *aspath_prepend (struct aspath *, struct aspath *); @@ -82,7 +82,7 @@ extern struct aspath *aspath_empty_get (void); extern struct aspath *aspath_str2aspath (const char *); extern void aspath_free (struct aspath *); extern struct aspath *aspath_intern (struct aspath *); -extern void aspath_unintern (struct aspath *); +extern void aspath_unintern (struct aspath **); extern const char *aspath_print (struct aspath *); extern void aspath_print_vty (struct vty *, const char *, struct aspath *, const char *); extern void aspath_print_all_vty (struct vty *); diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index b90fbce8..c5a710c8 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -365,15 +365,9 @@ attrhash_key_make (void *p) MIX(transit_hash_key_make (attr->extra->transit)); #ifdef HAVE_IPV6 - { - int i; - - MIX(attr->extra->mp_nexthop_len); - for (i = 0; i < 16; i++) - key = jhash(&attr->extra->mp_nexthop_global.s6_addr, 16, key); - for (i = 0; i < 16; i++) - key = jhash(&attr->extra->mp_nexthop_local.s6_addr, 16, key); - } + MIX(attr->extra->mp_nexthop_len); + key = jhash(attr->extra->mp_nexthop_global.s6_addr, 16, key); + key = jhash(attr->extra->mp_nexthop_local.s6_addr, 16, key); #endif /* HAVE_IPV6 */ } @@ -530,6 +524,7 @@ bgp_attr_intern (struct attr *attr) attre->ecommunity = ecommunity_intern (attre->ecommunity); else attre->ecommunity->refcnt++; + } if (attre->cluster) { @@ -593,7 +588,7 @@ bgp_attr_default_intern (u_char origin) new = bgp_attr_intern (&attr); bgp_attr_extra_free (&attr); - aspath_unintern (new->aspath); + aspath_unintern (&new->aspath); return new; } @@ -645,10 +640,41 @@ bgp_attr_aggregate_intern (struct bgp *bgp, u_char origin, new = bgp_attr_intern (&attr); bgp_attr_extra_free (&attr); - aspath_unintern (new->aspath); + aspath_unintern (&new->aspath); return new; } +/* Unintern just the sub-components of the attr, but not the attr */ +extern void +bgp_attr_unintern_sub (struct attr *attr, bool free_extra) +{ + /* aspath refcount shoud be decrement. */ + if (attr->aspath) + aspath_unintern (&attr->aspath); + UNSET_FLAG(attr->flag, BGP_ATTR_AS_PATH); + + if (attr->community) + community_unintern (&attr->community); + UNSET_FLAG(attr->flag, BGP_ATTR_COMMUNITIES); + + if (attr->extra) + { + if (attr->extra->ecommunity) + ecommunity_unintern (&attr->extra->ecommunity); + UNSET_FLAG(attr->flag, BGP_ATTR_EXT_COMMUNITIES); + + if (attr->extra->cluster) + cluster_unintern (attr->extra->cluster); + UNSET_FLAG(attr->flag, BGP_ATTR_CLUSTER_LIST); + + if (attr->extra->transit) + transit_unintern (attr->extra->transit); + + if (free_extra) + bgp_attr_extra_free (attr) ; + } +} + /*------------------------------------------------------------------------------ * Free bgp attribute and aspath. * @@ -671,49 +697,36 @@ bgp_attr_aggregate_intern (struct bgp *bgp, u_char origin, * reference count SHOULD be zero. */ void -bgp_attr_unintern (struct attr *attr) +bgp_attr_unintern (struct attr **attr) { - struct attr *ret; - struct aspath *aspath; - struct community *community; - struct ecommunity *ecommunity = NULL; - struct cluster_list *cluster = NULL; - struct transit *transit = NULL; + struct attr tmp ; + struct attr_extra tmp_extra ; - /* Decrement attribute reference. */ - aspath = attr->aspath; - community = attr->community; - if (attr->extra) + /* Take copy of attributes so that can unintern sub-objects */ + tmp = *(*attr); + + if ((*attr)->extra) { - ecommunity = attr->extra->ecommunity; - cluster = attr->extra->cluster; - transit = attr->extra->transit; + tmp.extra = &tmp_extra ; + memcpy (tmp.extra, (*attr)->extra, sizeof (struct attr_extra)); } /* If reference becomes zero then free attribute object. */ - if (attr->refcnt != 0) + if ((*attr)->refcnt != 0) { - --attr->refcnt ; - if (attr->refcnt == 0) + --(*attr)->refcnt ; + if ((*attr)->refcnt == 0) { - ret = hash_release (attrhash, attr); + struct attr *ret; + ret = hash_release (attrhash, *attr); assert (ret != NULL); - bgp_attr_extra_free (attr); - XFREE (MTYPE_ATTR, attr); + bgp_attr_extra_free (*attr); + XFREE (MTYPE_ATTR, *attr); /* sets *attr = NULL */ } ; } ; - /* aspath refcount should be decremented. */ - if (aspath) - aspath_unintern (aspath); - if (community) - community_unintern (community); - if (ecommunity) - ecommunity_unintern (ecommunity); - if (cluster) - cluster_unintern (cluster); - if (transit) - transit_unintern (transit); + /* Now the sub-objects */ + bgp_attr_unintern_sub (&tmp, false) ; /* false => don't free extra */ } /*------------------------------------------------------------------------------ @@ -733,7 +746,7 @@ bgp_attr_flush (struct attr *attr) { struct attr_extra *attre = attr->extra; if (attre->ecommunity && (attre->ecommunity->refcnt == 0)) - ecommunity_free (attre->ecommunity); + ecommunity_free (&attre->ecommunity); if (attre->cluster && (attre->cluster->refcnt == 0)) cluster_free (attre->cluster); if (attre->transit && (attre->transit->refcnt == 0)) @@ -741,8 +754,67 @@ bgp_attr_flush (struct attr *attr) } } +/* Implement draft-ietf-idr-optional-transitive behaviour and + * avoid resetting sessions for malformed attributes which are + * are partial/optional and hence where the error likely was not + * introduced by the sending neighbour. + */ +static bgp_attr_parse_ret_t +bgp_attr_malformed (struct peer *peer, u_char attr_type, u_char flag, + u_char subcode, u_char *startp, bgp_size_t length) +{ + /* Only relax error handling for eBGP peers */ + if (peer_sort (peer) == BGP_PEER_EBGP) + { + switch (attr_type) + { + /* where an optional attribute is inconsequential, e.g. it does not + * affect route selection, and can be safely ignored then any such + * attributes which are malformed should just be ignored and the + * route processed as normal. + */ + case BGP_ATTR_AS4_AGGREGATOR: + case BGP_ATTR_AGGREGATOR: + case BGP_ATTR_ATOMIC_AGGREGATE: + return BGP_ATTR_PARSE_PROCEED; + + /* Core attributes, particularly ones which may influence route + * selection should always cause session resets + */ + case BGP_ATTR_ORIGIN: + case BGP_ATTR_AS_PATH: + case BGP_ATTR_NEXT_HOP: + case BGP_ATTR_MULTI_EXIT_DISC: + case BGP_ATTR_LOCAL_PREF: + case BGP_ATTR_COMMUNITIES: + case BGP_ATTR_ORIGINATOR_ID: + case BGP_ATTR_CLUSTER_LIST: + case BGP_ATTR_MP_REACH_NLRI: + case BGP_ATTR_MP_UNREACH_NLRI: + case BGP_ATTR_EXT_COMMUNITIES: + break ; + + /* Partial optional attributes that are malformed should not cause + * the whole session to be reset. Instead treat it as a withdrawal + * of the routes, if possible. + */ + default: + if (CHECK_FLAG (flag, BGP_ATTR_FLAG_TRANS) && + CHECK_FLAG (flag, BGP_ATTR_FLAG_OPTIONAL) && + CHECK_FLAG (flag, BGP_ATTR_FLAG_PARTIAL)) + return BGP_ATTR_PARSE_WITHDRAW; + break ; + } ; + } ; + + /* default to reset */ + bgp_peer_down_error_with_data(peer, BGP_NOTIFY_UPDATE_ERR, subcode, + startp, length) ; + return BGP_ATTR_PARSE_ERROR; +} ; + /* Get origin attribute of the update message. */ -static int +static bgp_attr_parse_ret_t bgp_attr_origin (struct peer *peer, bgp_size_t length, struct attr *attr, u_char flag, u_char *startp) { @@ -760,11 +832,9 @@ bgp_attr_origin (struct peer *peer, bgp_size_t length, { zlog (peer->log, LOG_ERR, "Origin attribute flag isn't transitive %d", flag); - bgp_peer_down_error_with_data (peer, - BGP_NOTIFY_UPDATE_ERR, + return bgp_attr_malformed (peer, BGP_ATTR_ORIGIN, flag, BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR, startp, total); - return -1; } /* If any recognized attribute has Attribute Length that conflicts @@ -776,10 +846,9 @@ bgp_attr_origin (struct peer *peer, bgp_size_t length, { zlog (peer->log, LOG_ERR, "Origin attribute length is not one %d", length); - bgp_peer_down_error_with_data (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, - startp, total); - return -1; + return bgp_attr_malformed (peer, BGP_ATTR_ORIGIN, flag, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + startp, total); } /* Fetch origin attribute. */ @@ -794,64 +863,63 @@ bgp_attr_origin (struct peer *peer, bgp_size_t length, { zlog (peer->log, LOG_ERR, "Origin attribute value is invalid %d", attr->origin); - - bgp_peer_down_error_with_data (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_INVAL_ORIGIN, - startp, total); - return -1; + return bgp_attr_malformed (peer, BGP_ATTR_ORIGIN, flag, + BGP_NOTIFY_UPDATE_INVAL_ORIGIN, + startp, total); } /* Set oring attribute flag. */ attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_ORIGIN); - return 0; + return BGP_ATTR_PARSE_PROCEED ; } -/* Parse AS path information. This function is wrapper of aspath_parse. +/*------------------------------------------------------------------------------ + * Parse AS path information. This function is wrapper of aspath_parse. * * Parses AS_PATH or AS4_PATH. * - * Returns: if valid: address of struct aspath in the hash of known aspaths, - * with reference count incremented. - * else: NULL + * Returns: if valid: BGP_ATTR_PARSE_PROCEED + * and sets *p_asp = address of struct aspath in the hash of + * known aspaths, with reference count incremented. + * + * else: whatever bgp_attr_malformed() decides. * * NB: empty AS path (length == 0) is valid. The returned struct aspath will * have segments == NULL and str == zero length string (unique). */ -static struct aspath * -bgp_attr_aspath (struct peer *peer, bgp_size_t length, - struct attr *attr, u_char flag, u_char *startp, int as4_path) +static bgp_attr_parse_ret_t +bgp_attr_aspath (struct peer *peer, struct aspath** p_asp, bgp_size_t length, + struct attr *attr, u_char flag, u_char *startp, + u_char attr_type) { u_char require ; - struct aspath *asp ; + bool as4_path = (attr_type == BGP_ATTR_AS4_PATH) ; /* Check the attribute flags */ - require = as4_path ? BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS - : BGP_ATTR_FLAG_TRANS ; + require = as4_path ? BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL + : BGP_ATTR_FLAG_TRANS ; if ((flag & (BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS)) != require) { - const char* path_type ; bgp_size_t total; - path_type = as4_path ? "AS4_PATH" : "AS_PATH" ; + *p_asp = NULL ; if (!CHECK_FLAG(flag, BGP_ATTR_FLAG_TRANS)) - zlog (peer->log, LOG_ERR, - "%s attribute flag isn't transitive %d", path_type, flag) ; + zlog (peer->log, LOG_ERR, "%s attribute flag isn't transitive 0x%02X", + as4_path ? "AS4_PATH" : "AS_PATH", flag) ; if ((flag & BGP_ATTR_FLAG_OPTIONAL) != (require & BGP_ATTR_FLAG_OPTIONAL)) - zlog (peer->log, LOG_ERR, - "%s attribute flag must %sbe optional %d", path_type, - (flag & BGP_ATTR_FLAG_OPTIONAL) ? "not " : "", flag) ; + zlog (peer->log, LOG_ERR, "%s attribute flag must %sbe optional 0x%02X", + as4_path ? "AS4_PATH" : "AS_PATH", + (flag & BGP_ATTR_FLAG_OPTIONAL) ? "not " : "", flag) ; total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3); - bgp_peer_down_error_with_data (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR, - startp, total) ; - - return NULL ; + return bgp_attr_malformed (peer, attr_type, flag, + BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR, + startp, total); } ; /* Parse the AS_PATH/AS4_PATH body. @@ -859,28 +927,27 @@ bgp_attr_aspath (struct peer *peer, bgp_size_t length, * For AS_PATH peer with AS4 => 4Byte ASN otherwise 2Byte ASN * AS4_PATH 4Byte ASN */ - asp = aspath_parse (peer->ibuf, length, - as4_path || PEER_CAP_AS4_USE(peer), as4_path) ; - - if (asp != NULL) + *p_asp = aspath_parse (peer->ibuf, length, + PEER_CAP_AS4_USE(peer) || as4_path, as4_path) ; + if (*p_asp == NULL) { - attr->flag |= ATTR_FLAG_BIT (as4_path ? BGP_ATTR_AS4_PATH - : BGP_ATTR_AS_PATH) ; - } - else - { - zlog (peer->log, LOG_ERR, "Malformed AS path length is %d", length); + zlog (peer->log, LOG_ERR, "Malformed %s from %s, length is %d", + as4_path ? "AS4_PATH" : "AS4_PATH", peer->host, length); - /* TODO: should BGP_NOTIFY_UPDATE_MAL_AS_PATH be sent for AS4_PATH ?? */ - bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_MAL_AS_PATH) ; + return bgp_attr_malformed (peer, attr_type, flag, + BGP_NOTIFY_UPDATE_MAL_AS_PATH, + NULL, 0); } ; - return asp ; + /* Success ! + */ + attr->flag |= ATTR_FLAG_BIT (attr_type) ; + + return BGP_ATTR_PARSE_PROCEED; } ; -static int bgp_attr_aspath_check( struct peer *peer, - struct attr *attr) +static bgp_attr_parse_ret_t +bgp_attr_aspath_check (struct peer *peer, struct attr *attr, u_char flag) { /* These checks were part of bgp_attr_aspath, but with * as4 we should to check aspath things when @@ -891,30 +958,33 @@ static int bgp_attr_aspath_check( struct peer *peer, */ struct bgp *bgp = peer->bgp; struct aspath *aspath; + bgp_peer_sort_t sort = peer_sort(peer) ; bgp = peer->bgp; /* Confederation sanity check. */ - if ((peer_sort (peer) == BGP_PEER_CONFED && ! aspath_left_confed_check (attr->aspath)) || - (peer_sort (peer) == BGP_PEER_EBGP && aspath_confed_check (attr->aspath))) + if ( ((sort == BGP_PEER_CONFED) && ! aspath_left_confed_check (attr->aspath)) + || ((sort == BGP_PEER_EBGP) && aspath_confed_check (attr->aspath)) ) { zlog (peer->log, LOG_ERR, "Malformed AS path from %s", peer->host); - bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_MAL_AS_PATH); - return -1; + + return bgp_attr_malformed (peer, BGP_ATTR_AS_PATH, flag, + BGP_NOTIFY_UPDATE_MAL_AS_PATH, + NULL, 0); } /* First AS check for EBGP. */ if (bgp != NULL && bgp_flag_check (bgp, BGP_FLAG_ENFORCE_FIRST_AS)) { - if (peer_sort (peer) == BGP_PEER_EBGP + if (sort == BGP_PEER_EBGP && ! aspath_firstas_check (attr->aspath, peer->as)) { zlog (peer->log, LOG_ERR, "%s incorrect first AS (must be %u)", peer->host, peer->as); - bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_MAL_AS_PATH); - return -1; + + return bgp_attr_malformed (peer, BGP_ATTR_AS_PATH, flag, + BGP_NOTIFY_UPDATE_MAL_AS_PATH, + NULL, 0); } } @@ -924,16 +994,15 @@ static int bgp_attr_aspath_check( struct peer *peer, { aspath = aspath_dup (attr->aspath); aspath = aspath_add_seq (aspath, peer->change_local_as); - aspath_unintern (attr->aspath); + aspath_unintern (&attr->aspath); attr->aspath = aspath_intern (aspath); } - return 0; - + return BGP_ATTR_PARSE_PROCEED; } /* Nexthop attribute. */ -static int +static bgp_attr_parse_ret_t bgp_attr_nexthop (struct peer *peer, bgp_size_t length, struct attr *attr, u_char flag, u_char *startp) { @@ -947,11 +1016,10 @@ bgp_attr_nexthop (struct peer *peer, bgp_size_t length, { zlog (peer->log, LOG_ERR, "Origin attribute flag isn't transitive %d", flag); - bgp_peer_down_error_with_data (peer, - BGP_NOTIFY_UPDATE_ERR, + + return bgp_attr_malformed (peer, BGP_ATTR_NEXT_HOP, flag, BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR, startp, total); - return -1; } /* Check nexthop attribute length. */ @@ -960,21 +1028,19 @@ bgp_attr_nexthop (struct peer *peer, bgp_size_t length, zlog (peer->log, LOG_ERR, "Nexthop attribute length isn't four [%d]", length); - bgp_peer_down_error_with_data (peer, - BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, - startp, total); - return -1; + return bgp_attr_malformed (peer, BGP_ATTR_NEXT_HOP, flag, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + startp, total); } attr->nexthop.s_addr = stream_get_ipv4 (peer->ibuf); attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP); - return 0; + return BGP_ATTR_PARSE_PROCEED; } /* MED atrribute. */ -static int +static bgp_attr_parse_ret_t bgp_attr_med (struct peer *peer, bgp_size_t length, struct attr *attr, u_char flag, u_char *startp) { @@ -988,22 +1054,20 @@ bgp_attr_med (struct peer *peer, bgp_size_t length, zlog (peer->log, LOG_ERR, "MED attribute length isn't four [%d]", length); - bgp_peer_down_error_with_data (peer, - BGP_NOTIFY_UPDATE_ERR, + return bgp_attr_malformed (peer, BGP_ATTR_MULTI_EXIT_DISC, flag, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, startp, total); - return -1; } attr->med = stream_getl (peer->ibuf); attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC); - return 0; + return BGP_ATTR_PARSE_PROCEED; } /* Local preference attribute. */ -static int +static bgp_attr_parse_ret_t bgp_attr_local_pref (struct peer *peer, bgp_size_t length, struct attr *attr, u_char flag) { @@ -1013,7 +1077,7 @@ bgp_attr_local_pref (struct peer *peer, bgp_size_t length, if (peer_sort (peer) == BGP_PEER_EBGP) { stream_forward_getp (peer->ibuf, length); - return 0; + return BGP_ATTR_PARSE_PROCEED; } if (length == 4) @@ -1024,7 +1088,7 @@ bgp_attr_local_pref (struct peer *peer, bgp_size_t length, /* Set atomic aggregate flag. */ attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF); - return 0; + return BGP_ATTR_PARSE_PROCEED; } /* Atomic aggregate. */ @@ -1036,16 +1100,15 @@ bgp_attr_atomic (struct peer *peer, bgp_size_t length, { zlog (peer->log, LOG_ERR, "Bad atomic aggregate length %d", length); - bgp_peer_down_error (peer, - BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); - return -1; + return bgp_attr_malformed (peer, BGP_ATTR_ATOMIC_AGGREGATE, flag, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + NULL, 0); } /* Set atomic aggregate flag. */ attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_ATOMIC_AGGREGATE); - return 0; + return BGP_ATTR_PARSE_PROCEED; } /* Aggregator attribute */ @@ -1065,9 +1128,9 @@ bgp_attr_aggregator (struct peer *peer, bgp_size_t length, zlog (peer->log, LOG_ERR, "Aggregator length is not %d [%d]", wantedlen, length); - bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); - return -1; + return bgp_attr_malformed (peer, BGP_ATTR_AGGREGATOR, flag, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + NULL, 0); } if ( PEER_CAP_AS4_USE(peer) ) @@ -1079,35 +1142,35 @@ bgp_attr_aggregator (struct peer *peer, bgp_size_t length, /* Set atomic aggregate flag. */ attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR); - return 0; + return BGP_ATTR_PARSE_PROCEED; } /* New Aggregator attribute */ -static int +static bgp_attr_parse_ret_t bgp_attr_as4_aggregator (struct peer *peer, bgp_size_t length, - struct attr *attr, as_t *as4_aggregator_as, + struct attr *attr, u_char flag, + as_t *as4_aggregator_as, struct in_addr *as4_aggregator_addr) { if (length != 8) { zlog (peer->log, LOG_ERR, "New Aggregator length is not 8 [%d]", length); - - bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); - return -1; + return bgp_attr_malformed (peer, BGP_ATTR_AS4_AGGREGATOR, flag, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + NULL, 0); } *as4_aggregator_as = stream_getl (peer->ibuf); as4_aggregator_addr->s_addr = stream_get_ipv4 (peer->ibuf); attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AS4_AGGREGATOR); - return 0; + return BGP_ATTR_PARSE_PROCEED; } /* Munge Aggregator and New-Aggregator, AS_PATH and NEW_AS_PATH. */ -static int -bgp_attr_munge_as4_attrs (struct peer *peer, struct attr *attr, +static bgp_attr_parse_ret_t +bgp_attr_munge_as4_attrs (struct peer *peer, struct attr *attr, u_char flag, struct aspath *as4_path, as_t as4_aggregator, struct in_addr *as4_aggregator_addr) { @@ -1133,11 +1196,11 @@ bgp_attr_munge_as4_attrs (struct peer *peer, struct attr *attr, peer->host, "AS4 capable peer, yet it sent"); } - return 0; + return BGP_ATTR_PARSE_PROCEED; } - if (attr->flag & ( ATTR_FLAG_BIT( BGP_ATTR_AS4_PATH)) - && !(attr->flag & ( ATTR_FLAG_BIT( BGP_ATTR_AS_PATH)))) + if (attr->flag & (ATTR_FLAG_BIT (BGP_ATTR_AS4_PATH)) + && !(attr->flag & (ATTR_FLAG_BIT (BGP_ATTR_AS_PATH)))) { /* Hu? This is not supposed to happen at all! * got as4_path and no aspath, @@ -1149,9 +1212,10 @@ bgp_attr_munge_as4_attrs (struct peer *peer, struct attr *attr, zlog (peer->log, LOG_ERR, "%s BGP not AS4 capable peer sent AS4_PATH but" " no AS_PATH, cant do anything here", peer->host); - bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_MAL_ATTR); - return -1; + + return bgp_attr_malformed (peer, BGP_ATTR_AS_PATH, flag, + BGP_NOTIFY_UPDATE_MAL_ATTR, + NULL, 0); } /* We have a asn16 peer. First, look for AS4_AGGREGATOR @@ -1159,7 +1223,7 @@ bgp_attr_munge_as4_attrs (struct peer *peer, struct attr *attr, */ if (attr->flag & (ATTR_FLAG_BIT (BGP_ATTR_AS4_AGGREGATOR) ) ) { - if ( attr->flag & (ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR) ) ) + if (attr->flag & (ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR) ) ) { assert (attre); @@ -1175,7 +1239,7 @@ bgp_attr_munge_as4_attrs (struct peer *peer, struct attr *attr, * Aggregating node and the AS_PATH is to be * constructed "as in all other cases" */ - if ( attre->aggregator_as != BGP_AS_TRANS ) + if (attre->aggregator_as != BGP_AS_TRANS) { /* ignore */ if ( BGP_DEBUG(as4, AS4)) @@ -1202,49 +1266,57 @@ bgp_attr_munge_as4_attrs (struct peer *peer, struct attr *attr, if ( BGP_DEBUG(as4, AS4)) zlog_debug ("[AS4] %s BGP not AS4 capable peer send" " AS4_AGGREGATOR but no AGGREGATOR, will take" - " it as if AGGREGATOR with AS_TRANS had been there", peer->host); - (attre = bgp_attr_extra_get (attr))->aggregator_as = as4_aggregator; + " it as if AGGREGATOR with AS_TRANS had been there", + peer->host); + attre = bgp_attr_extra_get (attr) ; + attre->aggregator_as = as4_aggregator; /* sweep it under the carpet and simulate a "good" AGGREGATOR */ attr->flag |= (ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR)); } } /* need to reconcile NEW_AS_PATH and AS_PATH */ - if ( !ignore_as4_path && (attr->flag & ( ATTR_FLAG_BIT( BGP_ATTR_AS4_PATH))) ) + if (!ignore_as4_path && (attr->flag & (ATTR_FLAG_BIT( BGP_ATTR_AS4_PATH)))) { newpath = aspath_reconcile_as4 (attr->aspath, as4_path); - aspath_unintern (attr->aspath); + aspath_unintern (&attr->aspath); attr->aspath = aspath_intern (newpath); } - return 0; + return BGP_ATTR_PARSE_PROCEED; } /* Community attribute. */ -static int +static bgp_attr_parse_ret_t bgp_attr_community (struct peer *peer, bgp_size_t length, - struct attr *attr, u_char flag) + struct attr *attr, u_char flag, u_char *startp) { + bgp_size_t total + = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3); + if (length == 0) { attr->community = NULL; - return 0; + return BGP_ATTR_PARSE_PROCEED; } attr->community = - community_parse ((u_int32_t *)stream_pnt (peer->ibuf), length); + community_parse ((u_int32_t *)stream_pnt (peer->ibuf), length); + /* XXX: fix community_parse to use stream API and remove this */ stream_forward_getp (peer->ibuf, length); if (!attr->community) - return -1 ; + return bgp_attr_malformed (peer, BGP_ATTR_COMMUNITIES, flag, + BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, + startp, total); attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES); - return 0; + return BGP_ATTR_PARSE_PROCEED; } /* Originator ID attribute. */ -static int +static bgp_attr_parse_ret_t bgp_attr_originator_id (struct peer *peer, bgp_size_t length, struct attr *attr, u_char flag) { @@ -1252,9 +1324,9 @@ bgp_attr_originator_id (struct peer *peer, bgp_size_t length, { zlog (peer->log, LOG_ERR, "Bad originator ID length %d", length); - bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); - return -1; + return bgp_attr_malformed (peer, BGP_ATTR_ORIGINATOR_ID, flag, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + NULL, 0); } (bgp_attr_extra_get (attr))->originator_id.s_addr @@ -1262,11 +1334,11 @@ bgp_attr_originator_id (struct peer *peer, bgp_size_t length, attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_ORIGINATOR_ID); - return 0; + return BGP_ATTR_PARSE_PROCEED; } /* Cluster list attribute. */ -static int +static bgp_attr_parse_ret_t bgp_attr_cluster_list (struct peer *peer, bgp_size_t length, struct attr *attr, u_char flag) { @@ -1275,23 +1347,24 @@ bgp_attr_cluster_list (struct peer *peer, bgp_size_t length, { zlog (peer->log, LOG_ERR, "Bad cluster list length %d", length); - bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); - return -1; + return bgp_attr_malformed (peer, BGP_ATTR_CLUSTER_LIST, flag, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + NULL, 0); } (bgp_attr_extra_get (attr))->cluster = cluster_parse ((struct in_addr *)stream_pnt (peer->ibuf), length); - stream_forward_getp (peer->ibuf, length);; + /* XXX: Fix cluster_parse to use stream API and then remove this */ + stream_forward_getp (peer->ibuf, length); attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_CLUSTER_LIST); - return 0; + return BGP_ATTR_PARSE_PROCEED; } /* Multiprotocol reachability information parse. */ -int +extern bgp_attr_parse_ret_t bgp_mp_reach_parse (struct peer *peer, bgp_size_t length, struct attr *attr, struct bgp_nlri *mp_update) { @@ -1314,7 +1387,7 @@ bgp_mp_reach_parse (struct peer *peer, bgp_size_t length, struct attr *attr, { zlog_info ("%s: %s sent invalid length, %lu", __func__, peer->host, (unsigned long)length); - return -1; + return BGP_ATTR_PARSE_ERROR; } /* Load AFI, SAFI. */ @@ -1328,7 +1401,7 @@ bgp_mp_reach_parse (struct peer *peer, bgp_size_t length, struct attr *attr, { zlog_info ("%s: %s, MP nexthop length, %u, goes past end of attribute", __func__, peer->host, attre->mp_nexthop_len); - return -1; + return BGP_ATTR_PARSE_ERROR; } /* Nexthop length check. */ @@ -1363,7 +1436,9 @@ bgp_mp_reach_parse (struct peer *peer, bgp_size_t length, struct attr *attr, char buf2[INET6_ADDRSTRLEN]; if (BGP_DEBUG (update, UPDATE_IN)) - zlog_debug ("%s got two nexthop %s %s but second one is not a link-local nexthop", peer->host, + zlog_debug ("%s got two nexthop %s %s " + "but second one is not a link-local nexthop", + peer->host, inet_ntop (AF_INET6, &attre->mp_nexthop_global, buf1, INET6_ADDRSTRLEN), inet_ntop (AF_INET6, &attre->mp_nexthop_local, @@ -1376,14 +1451,14 @@ bgp_mp_reach_parse (struct peer *peer, bgp_size_t length, struct attr *attr, default: zlog_info ("%s: (%s) Wrong multiprotocol next hop length: %d", __func__, peer->host, attre->mp_nexthop_len); - return -1; + return BGP_ATTR_PARSE_ERROR; } if (!LEN_LEFT) { zlog_info ("%s: (%s) Failed to read SNPA and NLRI(s)", __func__, peer->host); - return -1; + return BGP_ATTR_PARSE_ERROR; } { @@ -1399,7 +1474,7 @@ bgp_mp_reach_parse (struct peer *peer, bgp_size_t length, struct attr *attr, { zlog_info ("%s: (%s) Failed to read NLRI", __func__, peer->host); - return -1; + return BGP_ATTR_PARSE_ERROR; } if (safi != BGP_SAFI_VPNV4) @@ -1409,7 +1484,7 @@ bgp_mp_reach_parse (struct peer *peer, bgp_size_t length, struct attr *attr, { zlog_info ("%s: (%s) NLRI doesn't pass sanity check", __func__, peer->host); - return -1; + return BGP_ATTR_PARSE_ERROR; } } @@ -1420,12 +1495,12 @@ bgp_mp_reach_parse (struct peer *peer, bgp_size_t length, struct attr *attr, stream_forward_getp (s, nlri_len); - return 0; + return BGP_ATTR_PARSE_PROCEED; #undef LEN_LEFT } /* Multiprotocol unreachable parse */ -int +extern bgp_attr_parse_ret_t bgp_mp_unreach_parse (struct peer *peer, bgp_size_t length, struct bgp_nlri *mp_withdraw) { @@ -1439,7 +1514,7 @@ bgp_mp_unreach_parse (struct peer *peer, bgp_size_t length, #define BGP_MP_UNREACH_MIN_SIZE 3 if ((length > STREAM_READABLE(s)) || (length < BGP_MP_UNREACH_MIN_SIZE)) - return -1; + return BGP_ATTR_PARSE_ERROR; afi = stream_getw (s); safi = stream_getc (s); @@ -1450,7 +1525,7 @@ bgp_mp_unreach_parse (struct peer *peer, bgp_size_t length, { ret = bgp_nlri_sanity_check (peer, afi, stream_pnt (s), withdraw_len); if (ret < 0) - return -1; + return BGP_ATTR_PARSE_ERROR; } mp_withdraw->afi = afi; @@ -1460,38 +1535,43 @@ bgp_mp_unreach_parse (struct peer *peer, bgp_size_t length, stream_forward_getp (s, withdraw_len); - return 0; + return BGP_ATTR_PARSE_PROCEED; } /* Extended Community attribute. */ -static int +static bgp_attr_parse_ret_t bgp_attr_ext_communities (struct peer *peer, bgp_size_t length, - struct attr *attr, u_char flag) + struct attr *attr, u_char flag, u_char *startp) { + bgp_size_t total + = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3); + if (length == 0) { if (attr->extra) attr->extra->ecommunity = NULL; - /* Empty extcomm doesn't seem to be invalid per se */ - return 0; + /* Empty extcomm doesn't seem to be invalid per se */ + return BGP_ATTR_PARSE_PROCEED; } (bgp_attr_extra_get (attr))->ecommunity = - ecommunity_parse ((u_int8_t *)stream_pnt (peer->ibuf), length); + ecommunity_parse ((u_int8_t *)stream_pnt (peer->ibuf), length); /* XXX: fix ecommunity_parse to use stream API */ stream_forward_getp (peer->ibuf, length); if (!attr->extra->ecommunity) - return -1; + return bgp_attr_malformed (peer, BGP_ATTR_EXT_COMMUNITIES, flag, + BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, + startp, total); attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES); - return 0; + return BGP_ATTR_PARSE_PROCEED; } /* BGP unknown attribute treatment. */ -static int +static bgp_attr_parse_ret_t bgp_attr_unknown (struct peer *peer, struct attr *attr, u_char flag, u_char type, bgp_size_t length, u_char *startp) { @@ -1517,20 +1597,17 @@ bgp_attr_unknown (struct peer *peer, struct attr *attr, u_char flag, then the Error Subcode is set to Unrecognized Well-known Attribute. The Data field contains the unrecognized attribute (type, length and value). */ - if (! CHECK_FLAG (flag, BGP_ATTR_FLAG_OPTIONAL)) + if (!CHECK_FLAG (flag, BGP_ATTR_FLAG_OPTIONAL)) { - /* Adjust startp to do not include flag value. */ - bgp_peer_down_error_with_data (peer, - BGP_NOTIFY_UPDATE_ERR, + return bgp_attr_malformed (peer, type, flag, BGP_NOTIFY_UPDATE_UNREC_ATTR, startp, total); - return -1; } /* Unrecognized non-transitive optional attributes must be quietly ignored and not passed along to other BGP peers. */ if (! CHECK_FLAG (flag, BGP_ATTR_FLAG_TRANS)) - return 0; + return BGP_ATTR_PARSE_PROCEED; /* If a path with recognized transitive optional attribute is accepted and passed along to other BGP peers and the Partial bit @@ -1553,7 +1630,7 @@ bgp_attr_unknown (struct peer *peer, struct attr *attr, u_char flag, memcpy (transit->val + transit->length, startp, total); transit->length += total; - return 0; + return BGP_ATTR_PARSE_PROCEED; } /*------------------------------------------------------------------------------ @@ -1575,12 +1652,12 @@ bgp_attr_unknown (struct peer *peer, struct attr *attr, u_char flag, * However, the attr object itself is NOT internalised. * (So its reference count will be zero.) */ -int +bgp_attr_parse_ret_t bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, struct bgp_nlri *mp_update, struct bgp_nlri *mp_withdraw) { int ret; - u_char flag; + u_char flag = 0; u_char type = 0; bgp_size_t length; u_char *startp, *endp; @@ -1611,8 +1688,10 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, (unsigned long) (endp - STREAM_PNT (BGP_INPUT (peer)))); bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); - return -1; + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); + if (as4_path != NULL) + aspath_unintern (&as4_path); + return BGP_ATTR_PARSE_ERROR; } /* Fetch attribute flag and type. */ @@ -1630,20 +1709,22 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, (unsigned long) (endp - STREAM_PNT (BGP_INPUT (peer)))); bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); - return -1; + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); + if (as4_path != NULL) + aspath_unintern (&as4_path); + return BGP_ATTR_PARSE_ERROR; } - /* Check extended attribue length bit. */ + /* Check extended attribute length bit. */ if (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN)) length = stream_getw (BGP_INPUT (peer)); else length = stream_getc (BGP_INPUT (peer)); /* If any attribute appears more than once in the UPDATE - message, then the Error Subcode is set to Malformed Attribute - List. */ - + * message, then the Error Subcode is set to Malformed Attribute + * List. + */ if (CHECK_BITMAP (seen, type)) { zlog (peer->log, LOG_WARNING, @@ -1651,13 +1732,15 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, peer->host, type); bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_MAL_ATTR); - return -1; + BGP_NOTIFY_UPDATE_MAL_ATTR); + if (as4_path != NULL) + aspath_unintern (&as4_path); + return BGP_ATTR_PARSE_ERROR; } /* Set type to bitmap to check duplicate attribute. `type' is - unsigned char so it never overflow bitmap range. */ - + * unsigned char so it never overflow bitmap range. + */ SET_BITMAP (seen, type); /* Overflow check. */ @@ -1666,10 +1749,15 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, if (attr_endp > endp) { zlog (peer->log, LOG_WARNING, - "%s BGP type %d length %d is too large, attribute total length is %d. attr_endp is %p. endp is %p", peer->host, type, length, size, attr_endp, endp); + "%s BGP type %d length %d is too large, " + "attribute total length is %d. attr_endp is %p. endp is %p", + peer->host, type, length, size, attr_endp, endp); + bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); - return -1; + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); + if (as4_path != NULL) + aspath_unintern (&as4_path); + return BGP_ATTR_PARSE_ERROR; } /* OK check attribute and store it's value. */ @@ -1679,13 +1767,13 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, ret = bgp_attr_origin (peer, length, attr, flag, startp); break; case BGP_ATTR_AS_PATH: - attr->aspath = bgp_attr_aspath (peer, length, attr, flag, startp, 0); - ret = attr->aspath ? 0 : -1 ; + ret = bgp_attr_aspath (peer, &attr->aspath, length, attr, flag, + startp, BGP_ATTR_AS_PATH); break; case BGP_ATTR_AS4_PATH: - as4_path = bgp_attr_aspath (peer, length, attr, flag, startp, 1); - ret = as4_path ? 0 : -1 ; - break; + ret = bgp_attr_aspath (peer, &as4_path, length, attr, flag, + startp, BGP_ATTR_AS4_PATH); + break; case BGP_ATTR_NEXT_HOP: ret = bgp_attr_nexthop (peer, length, attr, flag, startp); break; @@ -1702,10 +1790,12 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, ret = bgp_attr_aggregator (peer, length, attr, flag); break; case BGP_ATTR_AS4_AGGREGATOR: - ret = bgp_attr_as4_aggregator (peer, length, attr, &as4_aggregator, &as4_aggregator_addr); + ret = bgp_attr_as4_aggregator (peer, length, attr, flag, + &as4_aggregator, + &as4_aggregator_addr); break; case BGP_ATTR_COMMUNITIES: - ret = bgp_attr_community (peer, length, attr, flag); + ret = bgp_attr_community (peer, length, attr, flag, startp); break; case BGP_ATTR_ORIGINATOR_ID: ret = bgp_attr_originator_id (peer, length, attr, flag); @@ -1720,23 +1810,38 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, ret = bgp_mp_unreach_parse (peer, length, mp_withdraw); break; case BGP_ATTR_EXT_COMMUNITIES: - ret = bgp_attr_ext_communities (peer, length, attr, flag); + ret = bgp_attr_ext_communities (peer, length, attr, flag, startp); break; default: ret = bgp_attr_unknown (peer, attr, flag, type, length, startp); break; } - /* If error occured immediately return to the caller. */ - if (ret < 0) + /* If hard error occured immediately return to the caller. */ + if (ret == BGP_ATTR_PARSE_ERROR) { zlog (peer->log, LOG_WARNING, "%s: Attribute %s, parse error", peer->host, LOOKUP (attr_str, type)); + bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_MAL_ATTR); - return ret; + BGP_NOTIFY_UPDATE_MAL_ATTR); + if (as4_path != NULL) + aspath_unintern (&as4_path); + return ret; + } + + if (ret == BGP_ATTR_PARSE_WITHDRAW) + { + zlog (peer->log, LOG_WARNING, + "%s: Attribute %s, parse error - treating as withdrawal", + peer->host, + LOOKUP (attr_str, type)); + + if (as4_path != NULL) + aspath_unintern (&as4_path); + return ret; } /* Check the fetched length. */ @@ -1755,8 +1860,10 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, } ; bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); - return -1; + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); + if (as4_path != NULL) + aspath_unintern (&as4_path); + return BGP_ATTR_PARSE_ERROR; } } @@ -1766,9 +1873,12 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, zlog (peer->log, LOG_WARNING, "%s BGP attribute %s, length mismatch", peer->host, LOOKUP (attr_str, type)); + bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); - return -1; + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); + if (as4_path != NULL) + aspath_unintern (&as4_path); + return BGP_ATTR_PARSE_ERROR; } /* @@ -1778,41 +1888,43 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, * the as4 handling does not say whether AS4_PATH has to be sent * after AS_PATH or not - and when AS4_AGGREGATOR will be send * in relationship to AGGREGATOR. + * * So, to be defensive, we are not relying on any order and read * all attributes first, including these 32bit ones, and now, * afterwards, we look what and if something is to be done for as4. */ - if (bgp_attr_munge_as4_attrs (peer, attr, as4_path, - as4_aggregator, &as4_aggregator_addr)) - return -1; + ret = bgp_attr_munge_as4_attrs (peer, attr, flag, as4_path, + as4_aggregator, &as4_aggregator_addr) ; /* At this stage, we have done all fiddling with as4, and the * resulting info is in attr->aggregator resp. attr->aspath * so we can chuck as4_aggregator and as4_path alltogether in * order to save memory - */ - if ( as4_path ) - { - aspath_unintern( as4_path ); /* unintern - it is in the hash */ - as4_path = NULL; - /* The flag that we got this is still there, but that does not - * do any trouble - */ - } - /* + * * The "rest" of the code does nothing with as4_aggregator. * there is no memory attached specifically which is not part * of the attr. * so ignoring just means do nothing. */ - /* - * Finally do the checks on the aspath we did not do yet + if (as4_path != NULL) + { + /* The flag that we got this is still there, but that does not + * do any trouble + */ + aspath_unintern (&as4_path); + as4_path = NULL ; + } ; + + if (ret != BGP_ATTR_PARSE_PROCEED) + return ret; + + /* Finally do the checks on the aspath we did not do yet * because we waited for a potentially synthesized aspath. */ - if ( attr->flag & ( ATTR_FLAG_BIT( BGP_ATTR_AS_PATH))) + if (attr->flag & (ATTR_FLAG_BIT(BGP_ATTR_AS_PATH))) { - ret = bgp_attr_aspath_check( peer, attr ); - if ( ret < 0 ) + ret = bgp_attr_aspath_check (peer, attr, flag); + if (ret != BGP_ATTR_PARSE_PROCEED) return ret; } @@ -1820,7 +1932,7 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, if (attr->extra && attr->extra->transit) attr->extra->transit = transit_intern (attr->extra->transit); - return 0; + return BGP_ATTR_PARSE_PROCEED; } /* Well-known attribute check. */ @@ -1847,13 +1959,13 @@ bgp_attr_check (struct peer *peer, struct attr *attr) zlog (peer->log, LOG_WARNING, "%s Missing well-known attribute %d.", peer->host, type); - bgp_peer_down_error_with_data (peer, - BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_MISS_ATTR, - &type, 1); - return -1; + + bgp_peer_down_error_with_data (peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MISS_ATTR, + &type, 1); + return BGP_ATTR_PARSE_ERROR; } - return 0; + return BGP_ATTR_PARSE_PROCEED; } int stream_put_prefix (struct stream *, struct prefix *); @@ -1871,6 +1983,7 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, int send_as4_path = 0; int send_as4_aggregator = 0; int use32bit = PEER_CAP_AS4_USE(peer) ; + bgp_peer_sort_t sort ; if (! bgp) bgp = bgp_get_default (); @@ -1887,7 +2000,9 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, /* AS path attribute. */ /* If remote-peer is EBGP */ - if (peer_sort (peer) == BGP_PEER_EBGP + sort = peer_sort(peer) ; + + if (sort == BGP_PEER_EBGP && (! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_AS_PATH_UNCHANGED) || attr->aspath->segments == NULL) && (! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT))) @@ -1908,7 +2023,7 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, aspath = aspath_add_seq (aspath, peer->change_local_as); } } - else if (peer_sort (peer) == BGP_PEER_CONFED) + else if (sort == BGP_PEER_CONFED) { /* A confed member, so we need to do the AS_CONFED_SEQUENCE thing */ aspath = aspath_dup (attr->aspath); @@ -1965,8 +2080,7 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, } /* Local preference. */ - if (peer_sort (peer) == BGP_PEER_IBGP || - peer_sort (peer) == BGP_PEER_CONFED) + if ((sort == BGP_PEER_IBGP) || (sort == BGP_PEER_CONFED)) { stream_putc (s, BGP_ATTR_FLAG_TRANS); stream_putc (s, BGP_ATTR_LOCAL_PREF); @@ -2025,13 +2139,14 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, { if (attr->community->size * 4 > 255) { - stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN); + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS + | BGP_ATTR_FLAG_EXTLEN); stream_putc (s, BGP_ATTR_COMMUNITIES); stream_putw (s, attr->community->size * 4); } else { - stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS); + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS); stream_putc (s, BGP_ATTR_COMMUNITIES); stream_putc (s, attr->community->size * 4); } @@ -2039,9 +2154,8 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, } /* Route Reflector. */ - if (peer_sort (peer) == BGP_PEER_IBGP - && from - && peer_sort (from) == BGP_PEER_IBGP) + if ( (sort == BGP_PEER_IBGP) && (from != NULL) + && (peer_sort (from) == BGP_PEER_IBGP) ) { /* Originator ID. */ stream_putc (s, BGP_ATTR_FLAG_OPTIONAL); @@ -2177,18 +2291,18 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, assert (attre); - if (peer_sort (peer) == BGP_PEER_IBGP - || peer_sort (peer) == BGP_PEER_CONFED) + if ((sort == BGP_PEER_IBGP) || (sort == BGP_PEER_CONFED)) { if (attre->ecommunity->size * 8 > 255) { - stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN); + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS + | BGP_ATTR_FLAG_EXTLEN); stream_putc (s, BGP_ATTR_EXT_COMMUNITIES); stream_putw (s, attre->ecommunity->size * 8); } else { - stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS); + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS); stream_putc (s, BGP_ATTR_EXT_COMMUNITIES); stream_putc (s, attre->ecommunity->size * 8); } @@ -2216,13 +2330,14 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, { if (ecom_tr_size * 8 > 255) { - stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN); + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS + | BGP_ATTR_FLAG_EXTLEN); stream_putc (s, BGP_ATTR_EXT_COMMUNITIES); stream_putw (s, ecom_tr_size * 8); } else { - stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS); + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS); stream_putc (s, BGP_ATTR_EXT_COMMUNITIES); stream_putc (s, ecom_tr_size * 8); } @@ -2257,7 +2372,8 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, */ aspath = aspath_delete_confed_seq (aspath); - stream_putc (s, BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_EXTLEN); + stream_putc (s, BGP_ATTR_FLAG_TRANS |BGP_ATTR_FLAG_OPTIONAL + | BGP_ATTR_FLAG_EXTLEN); stream_putc (s, BGP_ATTR_AS4_PATH); aspath_sizep = stream_get_endp (s); stream_putw (s, 0); @@ -2434,7 +2550,7 @@ bgp_dump_routes_attr (struct stream *s, struct attr *attr, if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR)) { assert (attr->extra); - stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS); + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS); stream_putc (s, BGP_ATTR_AGGREGATOR); stream_putc (s, 8); stream_putl (s, attr->extra->aggregator_as); @@ -2446,7 +2562,8 @@ bgp_dump_routes_attr (struct stream *s, struct attr *attr, { if (attr->community->size * 4 > 255) { - stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN); + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS + | BGP_ATTR_FLAG_EXTLEN); stream_putc (s, BGP_ATTR_COMMUNITIES); stream_putw (s, attr->community->size * 4); } diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index 1017a035..8cca316d 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -21,6 +21,8 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #ifndef _QUAGGA_BGP_ATTR_H #define _QUAGGA_BGP_ATTR_H +#include <stdbool.h> + #include "bgpd/bgp_common.h" #include "bgpd/bgpd.h" @@ -135,17 +137,25 @@ struct transit #define ATTR_FLAG_BIT(X) (1 << ((X) - 1)) +typedef enum { + BGP_ATTR_PARSE_PROCEED = 0, + BGP_ATTR_PARSE_ERROR = -1, + BGP_ATTR_PARSE_WITHDRAW = -2, +} bgp_attr_parse_ret_t; + /* Prototypes. */ extern void bgp_attr_init (void); extern void bgp_attr_finish (void); -extern int bgp_attr_parse (struct peer *, struct attr *, bgp_size_t, - struct bgp_nlri *, struct bgp_nlri *); +extern bgp_attr_parse_ret_t bgp_attr_parse (struct peer *, struct attr *, + bgp_size_t, struct bgp_nlri *, + struct bgp_nlri *); extern int bgp_attr_check (struct peer *, struct attr *); extern struct attr_extra *bgp_attr_extra_get (struct attr *); extern void bgp_attr_extra_free (struct attr *); extern void bgp_attr_dup (struct attr *, struct attr *); extern struct attr *bgp_attr_intern (struct attr *attr); -extern void bgp_attr_unintern (struct attr *); +extern void bgp_attr_unintern_sub (struct attr *attr, bool free_extra) ; +extern void bgp_attr_unintern (struct attr **); extern void bgp_attr_flush (struct attr *); extern struct attr *bgp_attr_default_set (struct attr *attr, u_char); extern struct attr *bgp_attr_default_intern (u_char); @@ -175,8 +185,9 @@ extern void cluster_unintern (struct cluster_list *); void transit_unintern (struct transit *); /* Exported for unit-test purposes only */ -extern int bgp_mp_reach_parse (struct peer *, bgp_size_t, struct attr *, - struct bgp_nlri *); -extern int bgp_mp_unreach_parse (struct peer *, bgp_size_t, struct bgp_nlri *); +extern bgp_attr_parse_ret_t bgp_mp_reach_parse (struct peer *, + bgp_size_t, struct attr *, struct bgp_nlri *); +extern bgp_attr_parse_ret_t bgp_mp_unreach_parse (struct peer *, bgp_size_t, + struct bgp_nlri *); #endif /* _QUAGGA_BGP_ATTR_H */ diff --git a/bgpd/bgp_clist.c b/bgpd/bgp_clist.c index 0df34bb0..ecf1a526 100644 --- a/bgpd/bgp_clist.c +++ b/bgpd/bgp_clist.c @@ -87,7 +87,7 @@ community_entry_free (struct community_entry *entry) if (entry->config) XFREE (MTYPE_ECOMMUNITY_STR, entry->config); if (entry->u.ecom) - ecommunity_free (entry->u.ecom); + ecommunity_free (&entry->u.ecom); break; case COMMUNITY_LIST_EXPANDED: case EXTCOMMUNITY_LIST_EXPANDED: @@ -732,7 +732,7 @@ extcommunity_list_unset (struct community_list_handler *ch, entry = community_list_entry_lookup (list, str, direct); if (ecom) - ecommunity_free (ecom); + ecommunity_free (&ecom); if (regex) bgp_regex_free (regex); diff --git a/bgpd/bgp_common.c b/bgpd/bgp_common.c index c1ac7120..8a3a57ff 100644 --- a/bgpd/bgp_common.c +++ b/bgpd/bgp_common.c @@ -110,7 +110,7 @@ const iSAFI_t iSAFI_map[] = [qafx_num_other] = iSAFI_Reserved, } ; -const pAF_t pAF_map[] = +const sa_family_t sa_family_map[] = { [qafx_ipv4_unicast] = AF_INET, [qafx_ipv4_multicast] = AF_INET, diff --git a/bgpd/bgp_common.h b/bgpd/bgp_common.h index fb695125..e115bfd5 100644 --- a/bgpd/bgp_common.h +++ b/bgpd/bgp_common.h @@ -329,14 +329,14 @@ get_iSAFI(qafx_num_t num) * NB: it is a mistake to try to map qafx_num_undef (FATAL unless NDEBUG). */ -extern const pAF_t pAF_map[] ; +extern const sa_family_t sa_family_map[] ; -Inline pAF_t -get_pAF(qafx_num_t num) +Inline sa_family_t +get_sa_family(qafx_num_t num) { dassert((num >= qafx_num_min) && (num <= qafx_num_max)) ; - return pAF_map[num] ; + return sa_family_map[num] ; } ; /*============================================================================== diff --git a/bgpd/bgp_community.c b/bgpd/bgp_community.c index 68383adf..9cbf5f4a 100644 --- a/bgpd/bgp_community.c +++ b/bgpd/bgp_community.c @@ -328,21 +328,22 @@ community_intern (struct community *com) /* Free community attribute. */ void -community_unintern (struct community *com) +community_unintern (struct community **com) { struct community *ret; - if (com->refcnt) - com->refcnt--; + if ((*com)->refcnt) + (*com)->refcnt--; /* Pull off from hash. */ - if (com->refcnt == 0) + if ((*com)->refcnt == 0) { /* Community value com must exist in hash. */ - ret = (struct community *) hash_release (comhash, com); + ret = (struct community *) hash_release (comhash, *com); assert (ret != NULL); - community_free (com); + community_free (*com); + *com = NULL; } } diff --git a/bgpd/bgp_community.h b/bgpd/bgp_community.h index 78cbfe2b..5643e285 100644 --- a/bgpd/bgp_community.h +++ b/bgpd/bgp_community.h @@ -57,7 +57,7 @@ extern void community_free (struct community *); extern struct community *community_uniq_sort (struct community *); extern struct community *community_parse (u_int32_t *, u_short); extern struct community *community_intern (struct community *); -extern void community_unintern (struct community *); +extern void community_unintern (struct community **); extern char *community_str (struct community *); extern unsigned int community_hash_make (struct community *); extern struct community *community_str2com (const char *); diff --git a/bgpd/bgp_connection.c b/bgpd/bgp_connection.c index cdd37848..24c86230 100644 --- a/bgpd/bgp_connection.c +++ b/bgpd/bgp_connection.c @@ -18,6 +18,7 @@ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ +#include <zebra.h> #include "misc.h" @@ -130,6 +131,7 @@ bgp_connection_init_new(bgp_connection connection, bgp_session session, * * notification NULL -- none received or sent * * err no error, so far * * cap_suppress do not suppress capabilities + * * gtsm false -- no minttl set, yet * * su_local NULL -- no address, yet * * su_remote NULL -- no address, yet * * hold_timer_interval none -- set when connection is opened @@ -156,8 +158,6 @@ bgp_connection_init_new(bgp_connection connection, bgp_session session, connection->p_mutex = session->mutex ; connection->lock_count = 0 ; /* no question about it */ - connection->paf = AF_UNSPEC ; - connection->ordinal = ordinal ; connection->accepted = (ordinal == bgp_connection_secondary) ; @@ -317,7 +317,7 @@ bgp_connection_make_primary(bgp_connection connection) extern void bgp_connection_exit(bgp_connection connection) { - bgp_connection_close_down(connection) ; /* make sure */ + bgp_connection_close(connection, false) ; /* false => not keep timers */ assert(connection->state == bgp_fsm_sStopping) ; @@ -343,7 +343,7 @@ bgp_connection_free(bgp_connection connection) /* Make sure is closed, so no active file, no timers, pending queue is empty, * not on the connection queue, etc. */ - bgp_connection_close_down(connection) ; + bgp_connection_close(connection, false) ; /* false => not keep timers */ /* Free any components which still exist */ connection->qf = qps_file_free(connection->qf) ; @@ -586,7 +586,7 @@ bgp_connection_add_pending(bgp_connection connection, mqueue_block mqb, * Sets: * * * if secondary connection, turn off accept() - * * sets the qfile and fd ready for use -- disabled in all modes + * * sets the qfile and sock_fd ready for use -- disabled in all modes * * clears err -- must be OK so far * * discards any open_state * * copies hold_timer_interval and keep_alive_timer_interval from session @@ -609,28 +609,23 @@ bgp_connection_add_pending(bgp_connection connection, mqueue_block mqb, * NB: requires the session to be LOCKED. */ extern void -bgp_connection_open(bgp_connection connection, int fd, int family) +bgp_connection_open(bgp_connection connection, int sock_fd) { bgp_session session = connection->session ; - /* Make sure that there is no file and that buffers are clear, etc. */ - /* If this is the secondary connection, do not accept any more. */ - bgp_connection_close(connection) ; /* FSM deals with timers */ + /* Make sure that there is no file and that buffers are clear, etc. + * If this is the secondary connection, do not accept any more. + * The FSM deals with the timers. + */ + bgp_connection_close(connection, true) ; /* true => keep timers */ /* Set the file going */ - qps_add_file(bgp_nexus->selection, connection->qf, fd, connection) ; + qps_add_file(bgp_nexus->selection, connection->qf, sock_fd, connection) ; connection->err = 0 ; /* so far, so good */ bgp_open_state_unset(&connection->open_recv) ; - /* Note the address family for the socket. - * - * This is the real family -- so is IPv6 independent of whether one or - * both addresses are actually mapped IPv4. - */ - connection->paf = family ; - /* Copy the original hold_timer_interval and keepalive_timer_interval * Assume these have sensible initial values. * @@ -758,7 +753,7 @@ bgp_connection_query_accept(bgp_session session) /*------------------------------------------------------------------------------ * Close connection. * - * * if there is an fd, close it + * * if there is an sock_fd, close it * * if qfile is active, remove it * * forget any addresses * * reset all stream buffers to empty @@ -774,7 +769,7 @@ bgp_connection_query_accept(bgp_session session) * * * state of the connection * * links to and from the session - * * the timers remain initialised (but may have been unset) + * * the timers remain initialised -- and remain on or are unset * * the buffers remain (but reset) * * logging and host string * * any open_state that has been received @@ -792,19 +787,19 @@ bgp_connection_query_accept(bgp_session session) * NB: requires the session to be LOCKED. */ extern void -bgp_connection_full_close(bgp_connection connection, int unset_timers) +bgp_connection_close(bgp_connection connection, bool keep_timers) { - int fd ; + int sock_fd ; /* Close connection's file, if any. */ qps_remove_file(connection->qf) ; - fd = qps_file_unset_fd(connection->qf) ; - if (fd != fd_undef) - close(fd) ; + sock_fd = qps_file_unset_fd(connection->qf) ; + if (sock_fd != fd_undef) + close(sock_fd) ; /* If required, unset the timers. */ - if (unset_timers) + if (!keep_timers) { qtimer_unset(connection->hold_timer) ; qtimer_unset(connection->keepalive_timer) ; @@ -830,7 +825,8 @@ bgp_connection_full_close(bgp_connection connection, int unset_timers) * This is done when the connection is about to be fully closed, but need to * send a NOTIFICATION message before finally closing. * - * * if there is an fd, shutdown(, SHUT_RD) and disable the qfile for reading + * * if there is an sock_fd, shutdown(, SHUT_RD) and disable the qfile for + * reading * * reset all read buffering to empty * * discard all output except any partially written message * * empty the pending queue @@ -851,18 +847,18 @@ extern bool bgp_connection_part_close(bgp_connection connection) { bgp_wbuffer wb = &connection->wbuff ; - int fd ; + int sock_fd ; uint8_t* p ; bgp_size_t mlen ; /* Check that have a usable file descriptor */ - fd = qps_file_fd(connection->qf) ; + sock_fd = qps_file_fd(connection->qf) ; - if (fd == fd_undef) + if (sock_fd == fd_undef) return false ; /* Shutdown the read side of this connection */ - shutdown(fd, SHUT_RD) ; + shutdown(sock_fd, SHUT_RD) ; qps_disable_modes(connection->qf, qps_read_mbit) ; /* Stop all buffering activity, except for write buffer. */ diff --git a/bgpd/bgp_connection.h b/bgpd/bgp_connection.h index 8c5e00d4..4df1fbca 100644 --- a/bgpd/bgp_connection.h +++ b/bgpd/bgp_connection.h @@ -166,7 +166,7 @@ struct bgp_connection bgp_open_state open_recv ; /* the open received. */ qps_file qf ; /* qpselect file structure */ - pAF_t paf ; /* address family */ + bool gtsm ; /* minttl has been set */ union sockunion* su_local ; /* address of the near end */ union sockunion* su_remote ; /* address of the far end */ @@ -206,56 +206,24 @@ struct bgp_connection * The functions */ -extern bgp_connection -bgp_connection_init_new(bgp_connection connection, bgp_session session, - bgp_connection_ord_t ordinal) ; -extern void -bgp_connection_open(bgp_connection connection, int fd, int family) ; - -extern void -bgp_connection_start(bgp_connection connection, union sockunion* su_local, - union sockunion* su_remote) ; -extern void -bgp_connection_enable_accept(bgp_connection connection) ; - -extern void -bgp_connection_disable_accept(bgp_connection connection) ; - -extern bgp_connection -bgp_connection_query_accept(bgp_session session) ; - -extern bgp_connection -bgp_connection_get_sibling(bgp_connection connection) ; - -extern void -bgp_connection_make_primary(bgp_connection connection) ; - -extern void -bgp_connection_full_close(bgp_connection connection, int unset_timers) ; - -#define bgp_connection_close(conn) bgp_connection_full_close(conn, false) -#define bgp_connection_close_down(conn) bgp_connection_full_close(conn, true) - -extern bool -bgp_connection_part_close(bgp_connection connection) ; - -extern void -bgp_connection_exit(bgp_connection connection) ; - -extern void -bgp_connection_read_enable(bgp_connection connection) ; - -extern int -bgp_connection_write(bgp_connection connection, struct stream* s) ; - -extern void -bgp_connection_queue_add(bgp_connection connection) ; - -extern void -bgp_connection_queue_del(bgp_connection connection) ; - -extern int -bgp_connection_queue_process(void) ; +extern bgp_connection bgp_connection_init_new(bgp_connection connection, + bgp_session session, bgp_connection_ord_t ordinal) ; +extern void bgp_connection_open(bgp_connection connection, int sock_fd) ; +extern void bgp_connection_start(bgp_connection connection, sockunion su_local, + sockunion su_remote) ; +extern void bgp_connection_enable_accept(bgp_connection connection) ; +extern void bgp_connection_disable_accept(bgp_connection connection) ; +extern bgp_connection bgp_connection_query_accept(bgp_session session) ; +extern bgp_connection bgp_connection_get_sibling(bgp_connection connection) ; +extern void bgp_connection_make_primary(bgp_connection connection) ; +extern void bgp_connection_close(bgp_connection connection, bool keep_timers) ; +extern bool bgp_connection_part_close(bgp_connection connection) ; +extern void bgp_connection_exit(bgp_connection connection) ; +extern void bgp_connection_read_enable(bgp_connection connection) ; +extern int bgp_connection_write(bgp_connection connection, struct stream* s) ; +extern void bgp_connection_queue_add(bgp_connection connection) ; +extern void bgp_connection_queue_del(bgp_connection connection) ; +extern int bgp_connection_queue_process(void) ; Inline bool bgp_connection_no_pending(bgp_connection connection, bgp_connection* is_pending) @@ -264,9 +232,8 @@ bgp_connection_no_pending(bgp_connection connection, bgp_connection* is_pending) || (*is_pending != NULL) ) ; } ; -extern void -bgp_connection_add_pending(bgp_connection connection, mqueue_block mqb, - bgp_connection* is_pending) ; +extern void bgp_connection_add_pending(bgp_connection connection, + mqueue_block mqb, bgp_connection* is_pending) ; /*------------------------------------------------------------------------------ * Set buffer *unwritable* (buffer appears full, but nothing pending). diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index 605875a9..c107b031 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -42,13 +42,14 @@ ecommunity_new (void) /* Allocate ecommunities. */ void -ecommunity_free (struct ecommunity *ecom) +ecommunity_free (struct ecommunity **ecom) { - if (ecom->val) - XFREE (MTYPE_ECOMMUNITY_VAL, ecom->val); - if (ecom->str) - XFREE (MTYPE_ECOMMUNITY_STR, ecom->str); - XFREE (MTYPE_ECOMMUNITY, ecom); + if ((*ecom)->val) + XFREE (MTYPE_ECOMMUNITY_VAL, (*ecom)->val); + if ((*ecom)->str) + XFREE (MTYPE_ECOMMUNITY_STR, (*ecom)->str); + XFREE (MTYPE_ECOMMUNITY, *ecom); + ecom = NULL; } /* Add a new Extended Communities value to Extended Communities @@ -197,7 +198,7 @@ ecommunity_intern (struct ecommunity *ecom) find = (struct ecommunity *) hash_get (ecomhash, ecom, hash_alloc_intern); if (find != ecom) - ecommunity_free (ecom); + ecommunity_free (&ecom); find->refcnt++; @@ -209,18 +210,18 @@ ecommunity_intern (struct ecommunity *ecom) /* Unintern Extended Communities Attribute. */ void -ecommunity_unintern (struct ecommunity *ecom) +ecommunity_unintern (struct ecommunity **ecom) { struct ecommunity *ret; - if (ecom->refcnt) - ecom->refcnt--; - + if ((*ecom)->refcnt) + (*ecom)->refcnt--; + /* Pull off from hash. */ - if (ecom->refcnt == 0) + if ((*ecom)->refcnt == 0) { /* Extended community must be in the hash. */ - ret = (struct ecommunity *) hash_release (ecomhash, ecom); + ret = (struct ecommunity *) hash_release (ecomhash, *ecom); assert (ret != NULL); ecommunity_free (ecom); @@ -516,7 +517,7 @@ ecommunity_str2com (const char *str, int type, int keyword_included) if (! keyword_included || keyword) { if (ecom) - ecommunity_free (ecom); + ecommunity_free (&ecom); return NULL; } keyword = 1; @@ -536,7 +537,7 @@ ecommunity_str2com (const char *str, int type, int keyword_included) if (! keyword) { if (ecom) - ecommunity_free (ecom); + ecommunity_free (&ecom); return NULL; } keyword = 0; @@ -549,7 +550,7 @@ ecommunity_str2com (const char *str, int type, int keyword_included) case ecommunity_token_unknown: default: if (ecom) - ecommunity_free (ecom); + ecommunity_free (&ecom); return NULL; } } diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h index 942fdc73..2f59dc40 100644 --- a/bgpd/bgp_ecommunity.h +++ b/bgpd/bgp_ecommunity.h @@ -67,13 +67,13 @@ struct ecommunity_val extern void ecommunity_init (void); extern void ecommunity_finish (void); -extern void ecommunity_free (struct ecommunity *); +extern void ecommunity_free (struct ecommunity **); extern struct ecommunity *ecommunity_parse (u_int8_t *, u_short); extern struct ecommunity *ecommunity_dup (struct ecommunity *); extern struct ecommunity *ecommunity_merge (struct ecommunity *, struct ecommunity *); extern struct ecommunity *ecommunity_intern (struct ecommunity *); extern int ecommunity_cmp (const void *, const void *); -extern void ecommunity_unintern (struct ecommunity *); +extern void ecommunity_unintern (struct ecommunity **); extern unsigned int ecommunity_hash_make (void *); extern struct ecommunity *ecommunity_str2com (const char *, int, int); extern char *ecommunity_ecom2str (struct ecommunity *, int); diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index 3ce8cc19..a03f1b2c 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -1795,7 +1795,8 @@ static bgp_fsm_action(bgp_fsm_fatal) */ static bgp_fsm_action(bgp_fsm_retry) { - bgp_connection_close(connection) ; /* FSM does timers */ + bgp_connection_close(connection, true) ; /* true => keep timers, the + * FSM handles them. */ bgp_fsm_throw(connection, bgp_session_eRetry, NULL, 0, bgp_fsm_eBGP_Start) ; @@ -1830,8 +1831,8 @@ static bgp_fsm_action(bgp_fsm_expire) /* The process of sending a NOTIFICATION comes to an end here. */ if (connection->notification_pending) { - bgp_connection_close(connection) ; /* FSM deals with timers */ - + bgp_connection_close(connection, true) ; /* true => keep timers, the + * FSM handles them. */ return next_state ; } ; @@ -2176,8 +2177,8 @@ bgp_fsm_catch(bgp_connection connection, bgp_fsm_state_t next_state) } else { - bgp_connection_close(connection) ; /* FSM deals with timers */ - + bgp_connection_close(connection, true) ; /* true => keep timers, the + * FSM handles them. */ if (next_state == bgp_fsm_sStopping) /* can exit if sStopping */ bgp_fsm_event(connection, bgp_fsm_eBGP_Stop) ; } ; diff --git a/bgpd/bgp_msg_read.c b/bgpd/bgp_msg_read.c index 5eef4d1f..ed49f535 100644 --- a/bgpd/bgp_msg_read.c +++ b/bgpd/bgp_msg_read.c @@ -1716,11 +1716,11 @@ bgp_msg_orf_recv(bgp_connection connection, bgp_route_refresh rr, static int bgp_msg_orf_prefix_recv(orf_prefix orfpe, qafx_bit_t qb, sucker sr) { - pAF_t paf ; + sa_family_t paf ; int left ; assert(qb != 0) ; - paf = get_pAF(qafx_num(qb)) ; + paf = get_sa_family(qafx_num(qb)) ; /* Must have the minimum Prefix ORF entry, less the common byte, left */ left = suck_left(sr) - (BGP_ORF_E_P_MIN_L - BGP_ORF_E_COM_L) ; diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index 87f4f953..405fc62d 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -49,21 +49,15 @@ extern struct zebra_privs_t bgpd_privs; */ /* Forward references. */ -static void -bgp_connect_action(qps_file qf, void* file_info) ; - -static void -bgp_accept_action(qps_file qf, void* file_info) ; - -static int -bgp_get_names(int sock_fd, union sockunion* su_local, +static void bgp_connect_action(qps_file qf, void* file_info) ; +static void bgp_accept_action(qps_file qf, void* file_info) ; +static int bgp_get_names(int sock_fd, union sockunion* su_local, union sockunion* su_remote) ; - -static int -bgp_socket_set_common_options(int sock_fd, union sockunion* su, int ttl, - const char* password) ; -static int -bgp_md5_set_listeners(union sockunion* su, const char* password) ; +static int bgp_socket_set_common_options(int sock_fd, union sockunion* su, + bgp_connection connection) ; +static int bgp_set_ttl(int sock_fd, bgp_connection connnection, + int ttl, bool gtsm) ; +static int bgp_md5_set_listeners(union sockunion* su, const char* password) ; /*============================================================================== * Open and close the listeners. @@ -333,7 +327,7 @@ bgp_open_listener(sockunion su, unsigned short port, int sock_fd ; /* Construct socket and set the common options. */ - sock_fd = socket (sockunion_family(su), sock_type, sock_protocol) ; + sock_fd = sockunion_socket(su, sock_type, sock_protocol) ; if (sock_fd < 0) { err = errno ; @@ -342,7 +336,7 @@ bgp_open_listener(sockunion su, unsigned short port, return errno = err ; } - err = bgp_socket_set_common_options(sock_fd, su, 0, NULL) ; + err = bgp_socket_set_common_options(sock_fd, su, NULL) ; /* Want only IPV6 on ipv6 socket (not mapped addresses) * @@ -352,18 +346,10 @@ bgp_open_listener(sockunion su, unsigned short port, * Also, for all the apparent utility of IPv4-mapped addresses, the semantics * are simpler if IPv6 sockets speak IPv6 and IPv4 sockets speak IPv4. */ -#if defined(HAVE_IPV6) && defined(IPV6_V6ONLY) +#ifdef HAVE_IPV6 if ((err == 0) && (sockunion_family(su) == AF_INET6)) - { - int on = 1; - ret = setsockopt (sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); - if (ret < 0) - { - err = errno ; - zlog_err("%s: could not set IPV6_V6ONLY: %s", __func__, - errtoa(err, 0).str) ; - } - } ; + if (setsockopt_ipv6_v6only(sock_fd) < 0) + err = errno ; #endif /* Bind to port and address (if any) */ @@ -575,9 +561,17 @@ bgp_accept_action(qps_file qf, void* file_info) bool exists ; int sock_fd ; int err ; - int family ; - /* Accept client connection. */ + /* Accept client connection. + * + * We arrange for an IPv4 listener *and* an IPv6 one (assuming have IPv6), + * and we arrange for AF_INET6 listener to be IPV6_V6ONLY. This means that + * should NOT get an IPv4 mapped address. However, should we get such an + * address, the su_remote will be set to the actual IPv4 address. + * + * This means: the address family of su_remote is the address family of the + * underlying connection, NOT NECESSARILY the socket -- should that matter. + */ sock_fd = sockunion_accept(qps_file_fd(qf), &su_remote) ; if (sock_fd < 0) { @@ -620,19 +614,11 @@ bgp_accept_action(qps_file qf, void* file_info) /* Set the common socket options. * Does not set password -- that is inherited from the listener. * - * At this point, su_remote is the value returned by accept(), so is the - * actual address (which may be IPv6 mapped IPv4). + * At this point, su_remote is the value returned by sockunion_accept(), so + * if we have an AF_INET6 socket with an IPv4 mapped address, then su_remote + * is an AF_INET. */ - err = bgp_socket_set_common_options(sock_fd, &su_remote, - connection->session->ttl, NULL) ; - - /* Get the actual socket family. */ - if (err == 0) - { - family = sockunion_getsockfamily(sock_fd) ; - if (family < 0) - err = errno ; - } ; + err = bgp_socket_set_common_options(sock_fd, &su_remote, connection) ; /* Get the local and remote addresses -- noting that IPv6 mapped IPv4 * addresses are rendered as IPv4 addresses. @@ -644,7 +630,7 @@ bgp_accept_action(qps_file qf, void* file_info) * to go. Set session not to accept further inbound connections. */ if (err == 0) - bgp_connection_open(connection, sock_fd, family) ; + bgp_connection_open(connection, sock_fd) ; else close(sock_fd) ; @@ -658,6 +644,7 @@ bgp_accept_action(qps_file qf, void* file_info) * Open BGP Connection -- connect() to the other end */ +static int bgp_md5_set_socket(int sock_fd, sockunion su, const char *password) ; static int bgp_bind_ifname(bgp_connection connection, int sock_fd) ; static int bgp_bind_ifaddress(bgp_connection connection, int sock_fd) ; @@ -676,23 +663,31 @@ static int bgp_bind_ifaddress(bgp_connection connection, int sock_fd) ; extern void bgp_open_connect(bgp_connection connection) { - int sock_fd ; - int err ; - int family ; - union sockunion* su = connection->session->su_peer ; + int sock_fd ; + int err ; + + sockunion su = connection->session->su_peer ; err = 0 ; /* Make socket for the connect connection. */ - family = sockunion_family(su) ; - sock_fd = sockunion_socket(family, SOCK_STREAM, 0) ; + sock_fd = sockunion_socket(su, SOCK_STREAM, 0) ; if (sock_fd < 0) err = errno ; + if (BGP_DEBUG(events, EVENTS)) + plog_debug(connection->log, "%s [Event] Connect start to %s socket %d%s", + connection->host, connection->host, sock_fd, + (sock_fd < 0) ? " -- failed" : "" ) ; + /* Set the common options. */ if (err == 0) - err = bgp_socket_set_common_options(sock_fd, su, connection->session->ttl, - connection->session->password) ; + err = bgp_socket_set_common_options(sock_fd, su, connection) ; + + /* Set the TCP MD5 "password", if required. */ + if (err== 0) + if (connection->session->password != NULL) + err = bgp_md5_set_socket(sock_fd, su, connection->session->password) ; /* Bind socket. */ if (err == 0) @@ -702,10 +697,6 @@ bgp_open_connect(bgp_connection connection) if (err == 0) err = bgp_bind_ifaddress(connection, sock_fd) ; - if (BGP_DEBUG(events, EVENTS)) - plog_debug(connection->log, "%s [Event] Connect start to %s socket %d", - connection->host, connection->host, sock_fd); - /* Connect to the remote peer. */ if (err == 0) { @@ -742,7 +733,7 @@ bgp_open_connect(bgp_connection connection) * in any case, this will be handled by the qpselect action. */ - bgp_connection_open(connection, sock_fd, family) ; + bgp_connection_open(connection, sock_fd) ; qps_enable_mode(connection->qf, qps_read_mnum, bgp_connect_action) ; qps_enable_mode(connection->qf, qps_write_mnum, bgp_connect_action) ; @@ -830,7 +821,7 @@ bgp_connect_action(qps_file qf, void* file_info) * Set the TTL for the given connection (if any), if there is an sock_fd. */ extern void -bgp_set_ttl(bgp_connection connection, int ttl) +bgp_set_new_ttl(bgp_connection connection, int ttl, bool gtsm) { int sock_fd ; @@ -841,8 +832,50 @@ bgp_set_ttl(bgp_connection connection, int ttl) if (sock_fd < 0) return ; - if (ttl != 0) - sockopt_ttl(sock_fd, ttl) ; + bgp_set_ttl(sock_fd, connection, ttl, gtsm) ; +} ; + +/*------------------------------------------------------------------------------ + * BGP set minttl (GTSM) and/or ttl. + * + * A ttl of <= 0 is treated as "turn off" -- effectively MAXTTL, forcing gtsm + * *off*. + * + * If GTSM is not supported, then sets ttl. + * + * Returns: 0 : OK (so far so good) + * != 0 : error number (from errno or otherwise) + */ +static int +bgp_set_ttl(int sock_fd, bgp_connection connection, int ttl, bool gtsm) +{ + int ret ; + + if (gtsm && (ttl > 0)) + { + ret = setsockopt_minttl(sock_fd, ttl) ; + + if (ret >= 0) + { + ttl = MAXTTL ; + connection->gtsm = true ; + } + else if (errno != EOPNOTSUPP) + return errno ; + } + else if (connection->gtsm) + { + ret = setsockopt_minttl(sock_fd, 0) ; /* turn off */ + + if (ret < 0) /* must have turned it on, so should not fail */ + return errno ; + + connection->gtsm = false ; + } ; + + ret = setsockopt_ttl(sock_fd, ttl) ; + + return (ret >= 0) ? 0 : errno ; } ; /*============================================================================== @@ -927,7 +960,9 @@ bgp_bind_ifname(bgp_connection connection, int sock_fd) } ; /*------------------------------------------------------------------------------ - * Update source selection. + * Update source selection -- if connection specifies an IP address. + * + * If required, tries to bind the given socket to the given address. * * Returns: 0 : OK (so far so good) * != 0 : error number (from errno or otherwise) @@ -937,27 +972,11 @@ bgp_bind_ifaddress(bgp_connection connection, int sock_fd) { if (connection->session->ifaddress != NULL) { - union sockunion su ; + union sockunion su[1] ; int ret ; - int family ; - sockunion_new_sockaddr(&su, &connection->session->ifaddress->sa) ; - - family = sockunion_getsockfamily(sock_fd) ; - if (family < 0) - return errno ; - -#ifdef HAVE_IPV6 - if (family != sockunion_family(&su)) - { - if (family == AF_INET) - sockunion_unmap_ipv4(&su) ; - if (family == AF_INET6) - sockunion_map_ipv4(&su) ; - } ; -#endif - - ret = sockunion_bind (sock_fd, &su, 0, &su) ; + sockunion_new_sockaddr(su, &connection->session->ifaddress->sa) ; + ret = sockunion_bind (sock_fd, su, 0, false) ; if (ret < 0) return errno ; @@ -969,29 +988,28 @@ bgp_bind_ifaddress(bgp_connection connection, int sock_fd) * BGP Socket Option handling */ -static int -bgp_md5_set_socket(int sock_fd, union sockunion *su, const char *password) ; - /*------------------------------------------------------------------------------ * Common socket options: * * * non-blocking -- at all times * * reuseaddr * * reuseport - * * set TTL if given ttl != 0 - * * set password if given password != NULL + * * set security ttl (GTSM) and/or ttl -- if connection given. * * for IPv4, set TOS if required * - * These options are set on all sockets: connect/listen/accept + * These options are set on all sockets: listen/connect/accept + * (except either form of ttl, which is not set on listen). + * + * Note that the family of the given sockunion is the *protocol*, not the + * *socket* family. * * Returns: 0 => OK * != 0 == errno -- not that we really expect any errors here */ static int -bgp_socket_set_common_options(int sock_fd, union sockunion* su, int ttl, - const char* password) +bgp_socket_set_common_options(int sock_fd, union sockunion* su, + bgp_connection connection) { - int err ; int val ; /* Make socket non-blocking */ @@ -1002,23 +1020,27 @@ bgp_socket_set_common_options(int sock_fd, union sockunion* su, int ttl, return errno ; /* Reuse addr and port */ - if (sockopt_reuseaddr(sock_fd) < 0) + if (setsockopt_reuseaddr(sock_fd) < 0) return errno ; - if (sockopt_reuseport(sock_fd) < 0) + if (setsockopt_reuseport(sock_fd) < 0) return errno ; /* Adjust ttl if required */ - if (ttl != 0) - if (sockopt_ttl(sock_fd, ttl) != 0) - return errno ; - - /* Set the TCP MD5 "password", if required. */ - if (password != NULL) - if ((err = bgp_md5_set_socket(sock_fd, su, password)) != 0) - return err ; + if (connection != NULL) + { + int err ; + err = bgp_set_ttl(sock_fd, connection, connection->session->ttl, + connection->session->gtsm) ; + if (err != 0) + return err ; + } ; #ifdef IPTOS_PREC_INTERNETCONTROL - /* set IPPROTO_IP/IP_TOS -- if is AF_INET */ + /* set IPPROTO_IP/IP_TOS -- if is AF_INET + * + * We assume that if the socket is an AF_INET6 with an IPv4 mapped address, + * then can still set IP_PROTOCOL/IP_TOS. + */ if (sockunion_family(su) == AF_INET) if (setsockopt_ipv4_tos(sock_fd, IPTOS_PREC_INTERNETCONTROL) < 0) return errno ; @@ -1035,13 +1057,13 @@ bgp_socket_set_common_options(int sock_fd, union sockunion* su, int ttl, * Returns: 0 => OK * otherwise: errno * - * NB: if MD5 is not supported, returns ENOSYS error (but it should not come - * to this !). + * NB: if MD5 is not supported, returns EOPNOTSUPP error (but it should not + * come to this !). * * NB: has to change up privileges, which can fail (if things are badly set up) */ static int -bgp_md5_set_socket(int sock_fd, union sockunion *su, const char *password) +bgp_md5_set_socket(int sock_fd, sockunion su, const char *password) { int err, ret ; @@ -1055,9 +1077,9 @@ bgp_md5_set_socket(int sock_fd, union sockunion *su, const char *password) zlog_err("%s: could not raise privs: %s", __func__, errtoa(errno, 0).str); } ; - ret = sockopt_tcp_signature(sock_fd, su, password) ; + ret = setsockopt_tcp_signature(sock_fd, su, password) ; - if (ret != 0) /* TODO: error already logged as zlog_err() */ + if (ret != 0) err = errno ; if (bgpd_privs.change(ZPRIVS_LOWER)) @@ -1066,10 +1088,6 @@ bgp_md5_set_socket(int sock_fd, union sockunion *su, const char *password) err = errno ; zlog_err("%s: could not lower privs: %s", __func__, errtoa(errno, 0).str); } ; - - if (err != 0) - zlog (NULL, LOG_WARNING, "cannot set TCP_MD5SIG option on socket %d: %s", - sock_fd, errtoa(err, 0).str) ; return err ; } ; diff --git a/bgpd/bgp_network.h b/bgpd/bgp_network.h index 4aa62aac..d05fa3a0 100644 --- a/bgpd/bgp_network.h +++ b/bgpd/bgp_network.h @@ -24,22 +24,11 @@ #include "bgpd/bgp_connection.h" -extern int -bgp_open_listeners(const char *address, unsigned short port) ; - -extern void -bgp_close_listeners(void) ; - -extern void -bgp_open_connect(bgp_connection connection) ; - -extern void -bgp_prepare_to_accept(bgp_connection connection) ; - -extern void -bgp_not_prepared_to_accept(bgp_connection connection) ; - -extern void -bgp_set_ttl(bgp_connection connection, int ttl) ; +extern int bgp_open_listeners(const char *address, unsigned short port) ; +extern void bgp_close_listeners(void) ; +extern void bgp_open_connect(bgp_connection connection) ; +extern void bgp_prepare_to_accept(bgp_connection connection) ; +extern void bgp_not_prepared_to_accept(bgp_connection connection) ; +extern void bgp_set_new_ttl(bgp_connection connection, int ttl, bool gtsm) ; #endif /* _QUAGGA_BGP_NETWORK_H */ diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index de745007..6c21c247 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -461,7 +461,8 @@ bgp_scan (afi_t afi, safi_t safi) changed = 0; metricchanged = 0; - if (peer_sort (bi->peer) == BGP_PEER_EBGP && bi->peer->ttl == 1) + if ((peer_sort (bi->peer) == BGP_PEER_EBGP) + && (bi->peer->ttl == 1)) valid = bgp_nexthop_check_ebgp (afi, bi->attr); else valid = bgp_nexthop_lookup (afi, bi->peer, bi, diff --git a/bgpd/bgp_notification.c b/bgpd/bgp_notification.c index 64228ddb..d70491c9 100644 --- a/bgpd/bgp_notification.c +++ b/bgpd/bgp_notification.c @@ -106,7 +106,8 @@ bgp_notify_new_expect(bgp_nom_code_t code, bgp_nom_subcode_t subcode, /*------------------------------------------------------------------------------ * Allocate and initialise new notification, complete with data * - * Can specify an expected amount of data. + * Can specify an expected amount of data -- copes with len == 0 (and data may + * be NULL iff len == 0). * * NB: returns a 'NOT received' notification. */ @@ -260,7 +261,7 @@ bgp_notify_reset(bgp_notify notification, bgp_nom_code_t code, /*============================================================================== * Append data to given notification * - * Copes with zero length append. + * Copes with zero length append (and data may be NULL if len == 0). * * NB: returns possibly NEW ADDRESS of the notification. */ @@ -268,7 +269,7 @@ extern void bgp_notify_append_data(bgp_notify notification, const void* data, bgp_size_t len) { - bgp_size_t new_length = notification->length + len ; + bgp_size_t new_length = notification->length + len ; /* unsigned */ if (new_length > notification->size) { diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index ec2c0ade..3052eeba 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -175,9 +175,9 @@ bgp_update_packet (struct peer *peer, afi_t afi, safi_t safi) prd = (struct prefix_rd *) &rn->prn->p; if (binfo) { + from = binfo->peer; if (binfo->extra) tag = binfo->extra->tag; - from = binfo->peer; } bgp_packet_set_marker (s, BGP_MSG_UPDATE); @@ -202,7 +202,7 @@ bgp_update_packet (struct peer *peer, afi_t afi, safi_t safi) /* Synchnorize attribute. */ if (adj->attr) - bgp_attr_unintern (adj->attr); + bgp_attr_unintern (&adj->attr); else peer->scount[afi][safi]++; @@ -637,7 +637,7 @@ bgp_write (bgp_peer peer, struct stream* s) static int bgp_write_notify (struct peer *peer) { - int ret; + int ret, val; u_char type; struct stream *s; @@ -647,7 +647,10 @@ bgp_write_notify (struct peer *peer) return 0; assert (stream_get_endp (s) >= BGP_HEADER_SIZE); - /* I'm not sure fd is writable. */ + /* Put socket in blocking mode. */ + val = fcntl (peer->fd, F_GETFL, 0); + fcntl (peer->fd, F_SETFL, val & ~O_NONBLOCK); + ret = writen (peer->fd, STREAM_DATA (s), stream_get_endp (s)); if (ret <= 0) { @@ -1419,6 +1422,7 @@ bgp_update_receive (struct peer *peer, bgp_size_t size) struct bgp_nlri mp_update; struct bgp_nlri mp_withdraw; char attrstr[BUFSIZ] = ""; + bgp_attr_parse_ret_t ap_ret ; /* Status must be Established. */ if (peer->state != bgp_peer_pEstablished) @@ -1509,22 +1513,42 @@ bgp_update_receive (struct peer *peer, bgp_size_t size) return -1; } + /* Certain attribute parsing errors should not be considered bad enough + * to reset the session for, most particularly any partial/optional + * attributes that have 'tunneled' over speakers that don't understand + * them. Instead we withdraw only the prefix concerned. + * + * Complicates the flow a little though.. + */ + ap_ret = BGP_ATTR_PARSE_PROCEED; + + /* This define morphs the update case into a withdraw when lower levels + * have signalled an error condition where this is best. + */ +#define NLRI_ATTR_ARG (ap_ret != BGP_ATTR_PARSE_WITHDRAW ? &attr : NULL) + /* Parse attribute when it exists. */ if (attribute_len) { - ret = bgp_attr_parse (peer, &attr, attribute_len, - &mp_update, &mp_withdraw); - if (ret < 0) + ap_ret = bgp_attr_parse (peer, &attr, attribute_len, + &mp_update, &mp_withdraw); + if (ap_ret == BGP_ATTR_PARSE_ERROR) return -1; } /* Logging the attribute. */ - if (BGP_DEBUG (update, UPDATE_IN)) + if ((ap_ret == BGP_ATTR_PARSE_WITHDRAW) || BGP_DEBUG (update, UPDATE_IN)) { ret= bgp_dump_attr (peer, &attr, attrstr, BUFSIZ); + int lvl = (ap_ret == BGP_ATTR_PARSE_WITHDRAW) ? LOG_ERR : LOG_DEBUG ; + + if (ap_ret == BGP_ATTR_PARSE_WITHDRAW) + zlog (peer->log, LOG_ERR, + "%s rcvd UPDATE with errors in attr(s)!! Withdrawing route.", + peer->host); if (ret) - zlog (peer->log, LOG_DEBUG, "%s rcvd UPDATE w/ attr: %s", + zlog (peer->log, lvl, "%s rcvd UPDATE w/ attr: %s", peer->host, attrstr); } @@ -1536,7 +1560,10 @@ bgp_update_receive (struct peer *peer, bgp_size_t size) /* Check NLRI packet format and prefix length. */ ret = bgp_nlri_sanity_check (peer, AFI_IP, stream_pnt (s), update_len); if (ret < 0) - return -1; + { + bgp_attr_unintern_sub (&attr, true) ; /* true => free extra */ + return -1; + } /* Set NLRI portion to structure. */ update.afi = AFI_IP; @@ -1559,15 +1586,18 @@ bgp_update_receive (struct peer *peer, bgp_size_t size) update. */ ret = bgp_attr_check (peer, &attr); if (ret < 0) - return -1; + { + bgp_attr_unintern_sub (&attr, true) ; /* true => free extra */ + return -1; + } - bgp_nlri_parse (peer, &attr, &update); + bgp_nlri_parse (peer, NLRI_ATTR_ARG, &update); } if (mp_update.length && mp_update.afi == AFI_IP && mp_update.safi == SAFI_UNICAST) - bgp_nlri_parse (peer, &attr, &mp_update); + bgp_nlri_parse (peer, NLRI_ATTR_ARG, &mp_update); if (mp_withdraw.length && mp_withdraw.afi == AFI_IP @@ -1595,7 +1625,7 @@ bgp_update_receive (struct peer *peer, bgp_size_t size) if (mp_update.length && mp_update.afi == AFI_IP && mp_update.safi == SAFI_MULTICAST) - bgp_nlri_parse (peer, &attr, &mp_update); + bgp_nlri_parse (peer, NLRI_ATTR_ARG, &mp_update); if (mp_withdraw.length && mp_withdraw.afi == AFI_IP @@ -1626,7 +1656,7 @@ bgp_update_receive (struct peer *peer, bgp_size_t size) if (mp_update.length && mp_update.afi == AFI_IP6 && mp_update.safi == SAFI_UNICAST) - bgp_nlri_parse (peer, &attr, &mp_update); + bgp_nlri_parse (peer, NLRI_ATTR_ARG, &mp_update); if (mp_withdraw.length && mp_withdraw.afi == AFI_IP6 @@ -1657,7 +1687,7 @@ bgp_update_receive (struct peer *peer, bgp_size_t size) if (mp_update.length && mp_update.afi == AFI_IP6 && mp_update.safi == SAFI_MULTICAST) - bgp_nlri_parse (peer, &attr, &mp_update); + bgp_nlri_parse (peer, NLRI_ATTR_ARG, &mp_update); if (mp_withdraw.length && mp_withdraw.afi == AFI_IP6 @@ -1686,7 +1716,7 @@ bgp_update_receive (struct peer *peer, bgp_size_t size) if (mp_update.length && mp_update.afi == AFI_IP && mp_update.safi == BGP_SAFI_VPNV4) - bgp_nlri_parse_vpnv4 (peer, &attr, &mp_update); + bgp_nlri_parse_vpnv4 (peer, NLRI_ATTR_ARG, &mp_update); if (mp_withdraw.length && mp_withdraw.afi == AFI_IP @@ -1709,31 +1739,7 @@ bgp_update_receive (struct peer *peer, bgp_size_t size) /* Everything is done. We unintern temporary structures which interned in bgp_attr_parse(). */ - if (attr.aspath) - aspath_unintern (attr.aspath); - if (attr.community) - community_unintern (attr.community); - if (attr.extra) - { - if (attr.extra->ecommunity) - ecommunity_unintern (attr.extra->ecommunity); - if (attr.extra->cluster) - cluster_unintern (attr.extra->cluster); - if (attr.extra->transit) - transit_unintern (attr.extra->transit); - bgp_attr_extra_free (&attr); - } - - /* If peering is stopped due to some reason, do not generate BGP - event. */ - if (peer->state != bgp_peer_pEstablished) - return 0; - - /* Generate BGP event. */ - /* TODO: is this needed? */ -#if 0 - BGP_EVENT_ADD (peer, Receive_UPDATE_message); -#endif + bgp_attr_unintern_sub (&attr, true) ; /* true => free extra */ return 0; } @@ -2013,8 +2019,14 @@ bgp_route_refresh_receive (struct peer *peer, bgp_size_t size) while (p_pnt < p_end) { + /* If the ORF entry is malformed, want to read as much of it + * as possible without going beyond the bounds of the entry, + * to maximise debug information. + */ + int ok; memset (&orfp, 0, sizeof (struct orf_prefix)); common = *p_pnt++; + /* after ++: p_pnt <= p_end */ if (common & ORF_COMMON_PART_REMOVE_ALL) { if (BGP_DEBUG (normal, NORMAL)) @@ -2022,34 +2034,60 @@ bgp_route_refresh_receive (struct peer *peer, bgp_size_t size) prefix_bgp_orf_remove_all (name); break; } - memcpy (&seq, p_pnt, sizeof (u_int32_t)); - p_pnt += sizeof (u_int32_t); - orfp.seq = ntohl (seq); - orfp.ge = *p_pnt++; - orfp.le = *p_pnt++; - orfp.p.prefixlen = *p_pnt++; - orfp.p.family = afi2family (afi); - psize = PSIZE (orfp.p.prefixlen); - memcpy (&orfp.p.u.prefix, p_pnt, psize); + ok = ((p_end - p_pnt) >= sizeof(u_int32_t)) ; + if (!ok) + { + memcpy (&seq, p_pnt, sizeof (u_int32_t)); + p_pnt += sizeof (u_int32_t); + orfp.seq = ntohl (seq); + } + else + p_pnt = p_end ; + + if ((ok = (p_pnt < p_end))) + orfp.ge = *p_pnt++ ; /* value checked in prefix_bgp_orf_set() */ + if ((ok = (p_pnt < p_end))) + orfp.le = *p_pnt++ ; /* value checked in prefix_bgp_orf_set() */ + if ((ok = (p_pnt < p_end))) + orfp.p.prefixlen = *p_pnt++ ; + orfp.p.family = afi2family (afi); /* afi checked already */ + + psize = PSIZE (orfp.p.prefixlen); /* 0 if not ok */ + if (psize > prefix_blen(&orfp.p)) /* valid for family ? */ + { + ok = 0 ; + psize = prefix_blen(&orfp.p) ; + } + if (psize > (p_end - p_pnt)) /* valid for packet ? */ + { + ok = 0 ; + psize = p_end - p_pnt ; + } + + if (psize > 0) + memcpy (&orfp.p.u.prefix, p_pnt, psize); p_pnt += psize; if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s rcvd %s %s seq %u %s/%d ge %d le %d", + zlog_debug ("%s rcvd %s %s seq %u %s/%d ge %d le %d%s", peer->host, (common & ORF_COMMON_PART_REMOVE ? "Remove" : "Add"), (common & ORF_COMMON_PART_DENY ? "deny" : "permit"), orfp.seq, inet_ntop (orfp.p.family, &orfp.p.u.prefix, buf, BUFSIZ), - orfp.p.prefixlen, orfp.ge, orfp.le); + orfp.p.prefixlen, orfp.ge, orfp.le, + ok ? "" : " MALFORMED"); - ret = prefix_bgp_orf_set (name, afi, &orfp, - (common & ORF_COMMON_PART_DENY ? 0 : 1 ), - (common & ORF_COMMON_PART_REMOVE ? 0 : 1)); + if (ok) + ret = prefix_bgp_orf_set (name, afi, &orfp, + (common & ORF_COMMON_PART_DENY ? 0 : 1 ), + (common & ORF_COMMON_PART_REMOVE ? 0 : 1)); - if (ret != CMD_SUCCESS) + if (!ok || (ret != CMD_SUCCESS)) { if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s Received misformatted prefixlist ORF. Remove All pfxlist", peer->host); + zlog_debug ("%s Received misformatted prefixlist ORF." + " Remove All pfxlist", peer->host); prefix_bgp_orf_remove_all (name); break; } @@ -2248,12 +2286,13 @@ bgp_read_packet (struct peer *peer) return 0; /* Read packet from fd. */ - nbytes = stream_read_unblock (peer->ibuf, peer->fd, readsize); + nbytes = stream_read_try (peer->ibuf, peer->fd, readsize); /* If read byte is smaller than zero then error occured. */ if (nbytes < 0) { - if (errno == EAGAIN) + /* Transient error should retry */ + if (nbytes == -2) return -1; plog_err (peer->log, "%s [Error] bgp_read_packet error: %s", diff --git a/bgpd/bgp_peer.c b/bgpd/bgp_peer.c index 8af38876..196f0d54 100644 --- a/bgpd/bgp_peer.c +++ b/bgpd/bgp_peer.c @@ -772,10 +772,19 @@ bgp_peer_create (union sockunion *su, struct bgp *bgp, as_t local_as, peer->local_id = bgp->router_id; peer->v_holdtime = bgp->default_holdtime; peer->v_keepalive = bgp->default_keepalive; + if (peer_sort (peer) == BGP_PEER_IBGP) - peer->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV; - else - peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV; + { + peer->ttl = MAXTTL ; + peer->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV; + + } + else /* BGP_PEER_EBGP or BGP_PEER_CONFED */ + { + peer->ttl = 1 ; + peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV; + } ; + peer->gtsm = false ; SET_FLAG(peer->sflags, PEER_STATUS_REAL_PEER) ; peer = bgp_peer_lock (peer); /* bgp peer list reference */ @@ -791,9 +800,6 @@ bgp_peer_create (union sockunion *su, struct bgp *bgp, as_t local_as, /* Last reset time set */ peer->resettime = bgp_clock (); - /* Default TTL set. */ - peer->ttl = (peer_sort (peer) == BGP_PEER_IBGP ? 255 : 1); - /* Make peer's address string. */ peer->host = sockunion_su2str (su, MTYPE_BGP_PEER_HOST) ; @@ -1352,7 +1358,7 @@ bgp_peer_down_error(struct peer* peer, * session. * * Same as above, except that this accepts a data part for the notification - * message. + * message -- but len may be 0 (and data may be null iff len == 0). */ extern void bgp_peer_down_error_with_data (struct peer* peer, @@ -1888,7 +1894,7 @@ bgp_stop_with_error (struct peer *peer) * If has a choice, uses address that best matches the peer's address. */ extern sockunion -bgp_peer_get_ifaddress(bgp_peer peer, const char* ifname, pAF_t paf) +bgp_peer_get_ifaddress(bgp_peer peer, const char* ifname, sa_family_t af) { struct interface* ifp ; struct connected* connected; @@ -1913,7 +1919,7 @@ bgp_peer_get_ifaddress(bgp_peer peer, const char* ifname, pAF_t paf) for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, connected)) { - if (connected->address->family != paf) + if (connected->address->family != af) continue ; this = prefix_common_bits (connected->address, peer_prefix) ; diff --git a/bgpd/bgp_peer.h b/bgpd/bgp_peer.h index 37738a63..89708661 100644 --- a/bgpd/bgp_peer.h +++ b/bgpd/bgp_peer.h @@ -236,7 +236,9 @@ struct peer bgp_peer_index_entry index_entry ; bgp_session session ; /* Current session */ - int ttl; /* TTL of TCP connection to the peer. */ + int ttl ; /* TTL of TCP connection to the peer. */ + bool gtsm ; /* ttl set by neighbor xxx ttl_security */ + char *desc; /* Description of the peer. */ unsigned short port; /* Destination port for peer */ char *host; /* Printable address of the peer. */ @@ -471,44 +473,23 @@ extern const char *peer_down_str[]; * */ -extern void -bgp_session_do_event(mqueue_block mqb, mqb_flag_t flag); - -extern void -bgp_peer_enable(bgp_peer peer); - -extern void -bgp_peer_down(bgp_peer peer, peer_down_t why_down) ; - -extern void -bgp_peer_down_error(struct peer* peer, - bgp_nom_code_t code, bgp_nom_subcode_t subcode) ; -extern void -bgp_peer_down_error_with_data (struct peer* peer, +extern void bgp_session_do_event(mqueue_block mqb, mqb_flag_t flag); +extern void bgp_peer_enable(bgp_peer peer); +extern void bgp_peer_down(bgp_peer peer, peer_down_t why_down) ; +extern void bgp_peer_down_error(bgp_peer peer, + bgp_nom_code_t code, bgp_nom_subcode_t subcode) ; +extern void bgp_peer_down_error_with_data (bgp_peer peer, bgp_nom_code_t code, bgp_nom_subcode_t subcode, const u_int8_t* data, size_t datalen) ; - -extern void -bgp_peer_clearing_completed(struct peer *peer) ; - -extern struct peer * -bgp_peer_new (struct bgp *bgp); - -extern struct peer * -bgp_peer_create (union sockunion *su, struct bgp *bgp, as_t local_as, +extern void bgp_peer_clearing_completed(bgp_peer peer) ; +extern bgp_peer bgp_peer_new (struct bgp *bgp); +extern bgp_peer bgp_peer_create (sockunion su, struct bgp *bgp, as_t local_as, as_t remote_as, afi_t afi, safi_t safi); - -extern struct -peer *bgp_peer_lock (struct peer *) ; - -extern struct -peer *bgp_peer_unlock (struct peer *) ; - -extern int -bgp_peer_delete (struct peer *peer); - -extern sockunion -bgp_peer_get_ifaddress(bgp_peer peer, const char* ifname, pAF_t paf) ; +extern bgp_peer bgp_peer_lock (bgp_peer peer) ; +extern bgp_peer bgp_peer_unlock (bgp_peer peer) ; +extern int bgp_peer_delete (bgp_peer peer); +extern sockunion bgp_peer_get_ifaddress(bgp_peer peer, const char* ifname, + sa_family_t af) ; #endif /* _QUAGGA_BGP_PEER_H */ diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index b4f57419..34d580b8 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -140,7 +140,7 @@ static void bgp_info_free (struct bgp_info *binfo) { if (binfo->attr) - bgp_attr_unintern (binfo->attr); + bgp_attr_unintern (&binfo->attr); bgp_info_extra_free (&binfo->extra); @@ -357,6 +357,7 @@ bgp_info_cmp (struct bgp *bgp, struct bgp_info *new, struct bgp_info *exist) int internal_as_route = 0; int confed_as_route = 0; int ret; + bgp_peer_sort_t new_sort, exist_sort ; /* 0. Null check. */ if (new == NULL) @@ -465,18 +466,18 @@ bgp_info_cmp (struct bgp *bgp, struct bgp_info *new, struct bgp_info *exist) return 0; } - /* 7. Peer type check. */ - if (peer_sort (new->peer) == BGP_PEER_EBGP - && peer_sort (exist->peer) == BGP_PEER_IBGP) + /* 7. Peer type check. CONFED and iBGP rank equal, "internal" (RFC5065) + */ + new_sort = peer_sort(new->peer) ; + exist_sort = peer_sort(exist->peer) ; + + if ((new_sort == BGP_PEER_EBGP) && (exist_sort == BGP_PEER_IBGP)) return 1; - if (peer_sort (new->peer) == BGP_PEER_EBGP - && peer_sort (exist->peer) == BGP_PEER_CONFED) + if ((new_sort == BGP_PEER_EBGP) && (exist_sort == BGP_PEER_CONFED)) return 1; - if (peer_sort (new->peer) == BGP_PEER_IBGP - && peer_sort (exist->peer) == BGP_PEER_EBGP) + if ((new_sort == BGP_PEER_IBGP) && (exist_sort == BGP_PEER_EBGP)) return 0; - if (peer_sort (new->peer) == BGP_PEER_CONFED - && peer_sort (exist->peer) == BGP_PEER_EBGP) + if ((new_sort == BGP_PEER_CONFED) && (exist_sort == BGP_PEER_EBGP)) return 0; /* 8. IGP metric check. */ @@ -498,8 +499,8 @@ bgp_info_cmp (struct bgp *bgp, struct bgp_info *new, struct bgp_info *exist) newer path won't displace an older one, even if it was the preferred route based on the additional decision criteria below. */ if (! bgp_flag_check (bgp, BGP_FLAG_COMPARE_ROUTER_ID) - && peer_sort (new->peer) == BGP_PEER_EBGP - && peer_sort (exist->peer) == BGP_PEER_EBGP) + && (new_sort == BGP_PEER_EBGP) + && (exist_sort == BGP_PEER_EBGP) ) { if (CHECK_FLAG (new->flags, BGP_INFO_SELECTED)) return 1; @@ -633,18 +634,19 @@ bgp_community_filter (struct peer *peer, struct attr *attr) { if (attr->community) { + bgp_peer_sort_t sort = peer_sort(peer) ; + /* NO_ADVERTISE check. */ if (community_include (attr->community, COMMUNITY_NO_ADVERTISE)) return 1; /* NO_EXPORT check. */ - if (peer_sort (peer) == BGP_PEER_EBGP && + if ((sort == BGP_PEER_EBGP) && community_include (attr->community, COMMUNITY_NO_EXPORT)) return 1; /* NO_EXPORT_SUBCONFED check. */ - if (peer_sort (peer) == BGP_PEER_EBGP - || peer_sort (peer) == BGP_PEER_CONFED) + if ((sort == BGP_PEER_EBGP) || (sort == BGP_PEER_CONFED)) if (community_include (attr->community, COMMUNITY_NO_EXPORT_SUBCONFED)) return 1; } @@ -839,7 +841,7 @@ bgp_rs_route_reset(struct rs_route* rt) { if (rt->rs_in_attr != NULL) { - bgp_attr_unintern(rt->rs_in_attr) ; + bgp_attr_unintern(&rt->rs_in_attr) ; rt->rs_in_attr = NULL ; } ; @@ -972,7 +974,7 @@ bgp_export_modifier (struct peer *rsclient, struct rs_route* rt, */ bgp_attr_flush (rmap_attr); - bgp_attr_unintern(client_attr) ; + bgp_attr_unintern(&client_attr) ; client_attr = NULL ; } @@ -988,7 +990,7 @@ bgp_export_modifier (struct peer *rsclient, struct rs_route* rt, old_attr = client_attr ; client_attr = bgp_attr_intern(rmap_attr) ; - bgp_attr_unintern(old_attr) ; + bgp_attr_unintern(&old_attr) ; } ; /* Discard any "extra" part of the duplicated attributes. */ @@ -1061,7 +1063,7 @@ bgp_import_modifier (struct peer *rsclient, struct rs_route* rt, bgp_attr_flush (rmap_attr); bgp_attr_extra_free (rmap_attr); - bgp_attr_unintern(client_attr) ; + bgp_attr_unintern(&client_attr) ; return NULL ; } @@ -1080,7 +1082,7 @@ bgp_import_modifier (struct peer *rsclient, struct rs_route* rt, old_attr = client_attr ; client_attr = bgp_attr_intern(rmap_attr) ; - bgp_attr_unintern(old_attr) ; + bgp_attr_unintern(&old_attr) ; bgp_attr_extra_free (rmap_attr) ; } ; @@ -1098,6 +1100,7 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p, struct bgp *bgp; int transparent; int reflect; + bgp_peer_sort_t sort, from_sort ; from = ri->peer; filter = &peer->filter[afi][safi]; @@ -1216,7 +1219,10 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p, } /* Route-Reflect check. */ - if (peer_sort (from) == BGP_PEER_IBGP && peer_sort (peer) == BGP_PEER_IBGP) + from_sort = peer_sort (from) ; + sort = peer_sort (peer) ; + + if ((from_sort == BGP_PEER_IBGP) && (sort == BGP_PEER_IBGP)) reflect = 1; else reflect = 0; @@ -1248,8 +1254,7 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p, bgp_attr_dup (attr, ri->attr); /* If local-preference is not set. */ - if ((peer_sort (peer) == BGP_PEER_IBGP - || peer_sort (peer) == BGP_PEER_CONFED) + if (((sort == BGP_PEER_IBGP) || (sort == BGP_PEER_CONFED)) && (! (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF)))) { attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF); @@ -1257,7 +1262,7 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p, } /* Remove MED if its an EBGP peer - will get overwritten by route-maps */ - if (peer_sort (peer) == BGP_PEER_EBGP + if ((sort == BGP_PEER_EBGP) && attr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC)) { if (ri->peer != bgp->peer_self && ! transparent @@ -1283,7 +1288,7 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p, || (p->family == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED(&attr->extra->mp_nexthop_global)) #endif /* HAVE_IPV6 */ - || (peer_sort (peer) == BGP_PEER_EBGP + || ((sort == BGP_PEER_EBGP) && bgp_multiaccess_check_v4 (attr->nexthop, peer->host) == 0)) { /* Set IPv4 nexthop. */ @@ -1349,7 +1354,7 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p, #endif /* HAVE_IPV6 */ /* If this is EBGP peer and remove-private-AS is set. */ - if (peer_sort (peer) == BGP_PEER_EBGP + if ((sort == BGP_PEER_EBGP) && peer_af_flag_check (peer, afi, safi, PEER_FLAG_REMOVE_PRIVATE_AS) && aspath_private_as_check (attr->aspath)) attr->aspath = aspath_empty_get (); @@ -1367,8 +1372,7 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p, /* The route reflector is not allowed to modify the attributes of the reflected IBGP routes. */ - if (peer_sort (from) == BGP_PEER_IBGP - && peer_sort (peer) == BGP_PEER_IBGP) + if ((from_sort == BGP_PEER_IBGP) && (sort == BGP_PEER_IBGP)) { dummy_attr = &dummy_attr_s ; bgp_attr_dup (dummy_attr, attr); @@ -2371,7 +2375,7 @@ bgp_update_rsclient (struct peer *rsclient, struct rs_route* rt) rt->p->prefixlen, rsclient->host); /* Discard the duplicate interned attributes */ - bgp_attr_unintern (client_attr); + bgp_attr_unintern (&client_attr); /* Unlock node -- locked in bgp_afi_node_get() */ bgp_unlock_node (rn); @@ -2386,7 +2390,7 @@ bgp_update_rsclient (struct peer *rsclient, struct rs_route* rt) bgp_info_set_flag (rn, ri, BGP_INFO_ATTR_CHANGED); /* Discard the old attribute */ - bgp_attr_unintern (ri->attr); + bgp_attr_unintern (&ri->attr); } else { @@ -2396,6 +2400,11 @@ bgp_update_rsclient (struct peer *rsclient, struct rs_route* rt) ri->sub_type = rt->sub_type; ri->peer = rt->peer; ri->uptime = bgp_clock (); +#if 0 /* TODO: do we need this ?? */ + /* Update MPLS tag. */ + if (safi == SAFI_MPLS_VPN) + memcpy ((bgp_info_extra_get (ri))->tag, tag, 3); +#endif /* Register new BGP information. */ bgp_info_add (rn, ri); @@ -2449,7 +2458,7 @@ bgp_update_rsclient (struct peer *rsclient, struct rs_route* rt) bgp_rib_remove (rn, ri, rt->peer, rt->afi, rt->safi); if (client_attr != NULL) - bgp_attr_unintern (client_attr); + bgp_attr_unintern (&client_attr); bgp_unlock_node (rn); return; @@ -2500,12 +2509,15 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr, struct bgp_info *new; const char *reason; char buf[SU_ADDRSTRLEN]; + bgp_peer_sort_t sort ; use_attr = NULL ; /* nothing to use, yet */ bgp = peer->bgp; rn = bgp_afi_node_get (bgp->rib[afi][safi], afi, safi, p, prd); + sort = peer_sort(peer) ; + /* When peer's soft reconfiguration enabled. Record input packet in Adj-RIBs-In. */ if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG) @@ -2575,7 +2587,7 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr, { /* If the peer is EBGP and nexthop is not on connected route, discard it. */ - if ((peer_sort (peer) == BGP_PEER_EBGP) && (peer->ttl == 1) + if ((sort == BGP_PEER_EBGP) && (peer->ttl == 1) && ! bgp_nexthop_check_ebgp (afi, use_attr) && ! CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)) { @@ -2606,7 +2618,7 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr, bgp_info_unset_flag (rn, ri, BGP_INFO_ATTR_CHANGED); if (CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING) - && peer_sort (peer) == BGP_PEER_EBGP + && (sort == BGP_PEER_EBGP) && CHECK_FLAG (ri->flags, BGP_INFO_HISTORY)) { if (BGP_DEBUG (update, UPDATE_IN)) @@ -2638,7 +2650,7 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr, } } - bgp_attr_unintern (use_attr); + bgp_attr_unintern (&use_attr); bgp_unlock_node (rn); return 0; @@ -2677,7 +2689,7 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr, /* Update bgp route dampening information. */ if (CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING) - && peer_sort (peer) == BGP_PEER_EBGP) + && (sort == BGP_PEER_EBGP)) { /* This is implicit withdraw so we should update dampening information. */ @@ -2686,7 +2698,7 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr, } /* Update to new attribute. */ - bgp_attr_unintern (ri->attr); + bgp_attr_unintern (&ri->attr); ri->attr = use_attr ; /* Update MPLS tag. */ @@ -2695,7 +2707,7 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr, /* Update bgp route dampening information. */ if (CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING) - && peer_sort (peer) == BGP_PEER_EBGP) + && (sort == BGP_PEER_EBGP)) { int ret ; /* Now we do normal update dampening. */ @@ -2710,9 +2722,9 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr, /* Nexthop reachability check. */ if ((afi == AFI_IP || afi == AFI_IP6) && safi == SAFI_UNICAST - && (peer_sort (peer) == BGP_PEER_IBGP - || peer_sort (peer) == BGP_PEER_CONFED - || (peer_sort (peer) == BGP_PEER_EBGP && peer->ttl != 1) + && ( (sort == BGP_PEER_IBGP) + || (sort == BGP_PEER_CONFED) + || ((sort == BGP_PEER_EBGP) && (peer->ttl != 1)) || CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK))) { if (bgp_nexthop_lookup (afi, peer, ri, NULL, NULL)) @@ -2756,9 +2768,9 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr, /* Nexthop reachability check. */ if ((afi == AFI_IP || afi == AFI_IP6) && safi == SAFI_UNICAST - && (peer_sort (peer) == BGP_PEER_IBGP - || peer_sort (peer) == BGP_PEER_CONFED - || (peer_sort (peer) == BGP_PEER_EBGP && peer->ttl != 1) + && ( (sort == BGP_PEER_IBGP) + || (sort == BGP_PEER_CONFED) + || ((sort == BGP_PEER_EBGP) && (peer->ttl != 1)) || CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK))) { if (bgp_nexthop_lookup (afi, peer, new, NULL, NULL)) @@ -2805,7 +2817,7 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr, bgp_rib_remove (rn, ri, peer, afi, safi); if (use_attr != NULL) - bgp_attr_unintern (use_attr); + bgp_attr_unintern (&use_attr); bgp_unlock_node (rn); return 0; @@ -2995,7 +3007,7 @@ bgp_default_originate (struct peer *peer, afi_t afi, safi_t safi, int withdraw) } bgp_attr_extra_free (&attr); - aspath_unintern (aspath); + aspath_unintern (&aspath); } static void @@ -3983,7 +3995,7 @@ bgp_static_update_rsclient (struct peer *rsclient, struct prefix *p, bgp_attr_extra_free (rmap_attr); /* Unintern original. */ - aspath_unintern (static_attr->aspath); + aspath_unintern (&static_attr->aspath); bgp_attr_extra_free (static_attr); bgp_static_withdraw_rsclient (bgp, rsclient, p, afi, safi); @@ -3998,7 +4010,7 @@ bgp_static_update_rsclient (struct peer *rsclient, struct prefix *p, client_attr = bgp_attr_intern (static_attr) ; /* Have now finished with the static_attr */ - aspath_unintern (static_attr->aspath); + aspath_unintern (&static_attr->aspath); bgp_attr_extra_free (static_attr); /* run the import route-map for the rsclient. */ @@ -4041,7 +4053,7 @@ bgp_static_update_rsclient (struct peer *rsclient, struct prefix *p, !CHECK_FLAG(ri->flags, BGP_INFO_REMOVED)) { /* No point duplicating */ - bgp_attr_unintern (client_attr); + bgp_attr_unintern (&client_attr); } else { @@ -4052,8 +4064,8 @@ bgp_static_update_rsclient (struct peer *rsclient, struct prefix *p, if (CHECK_FLAG(ri->flags, BGP_INFO_REMOVED)) bgp_info_restore(rn, ri); - bgp_attr_unintern (ri->attr); - ri->attr = client_attr ; + bgp_attr_unintern (&ri->attr); + ri->attr = client_attr ; ri->uptime = bgp_clock (); /* Process change. */ @@ -4131,7 +4143,7 @@ bgp_static_update_main (struct bgp *bgp, struct prefix *p, bgp_attr_flush (&attr_tmp); /* Unintern original. */ - aspath_unintern (attr.aspath); + aspath_unintern (&attr.aspath); bgp_attr_extra_free (&attr); bgp_static_withdraw (bgp, p, afi, safi); return; @@ -4152,8 +4164,8 @@ bgp_static_update_main (struct bgp *bgp, struct prefix *p, !CHECK_FLAG(ri->flags, BGP_INFO_REMOVED)) { bgp_unlock_node (rn); - bgp_attr_unintern (attr_new); - aspath_unintern (attr.aspath); + bgp_attr_unintern (&attr_new); + aspath_unintern (&attr.aspath); bgp_attr_extra_free (&attr); return; } @@ -4167,7 +4179,7 @@ bgp_static_update_main (struct bgp *bgp, struct prefix *p, bgp_info_restore(rn, ri); else bgp_aggregate_decrement (bgp, p, ri, afi, safi); - bgp_attr_unintern (ri->attr); + bgp_attr_unintern (&ri->attr); ri->attr = attr_new; ri->uptime = bgp_clock (); @@ -4175,7 +4187,7 @@ bgp_static_update_main (struct bgp *bgp, struct prefix *p, bgp_aggregate_increment (bgp, p, ri, afi, safi); bgp_process (bgp, rn, afi, safi); bgp_unlock_node (rn); - aspath_unintern (attr.aspath); + aspath_unintern (&attr.aspath); bgp_attr_extra_free (&attr); return; } @@ -4203,7 +4215,7 @@ bgp_static_update_main (struct bgp *bgp, struct prefix *p, bgp_process (bgp, rn, afi, safi); /* Unintern original. */ - aspath_unintern (attr.aspath); + aspath_unintern (&attr.aspath); bgp_attr_extra_free (&attr); } @@ -4224,8 +4236,8 @@ bgp_static_update (struct bgp *bgp, struct prefix *p, } static void -bgp_static_update_vpnv4 (struct bgp *bgp, struct prefix *p, u_int16_t afi, - u_char safi, struct prefix_rd *prd, u_char *tag) +bgp_static_update_vpnv4 (struct bgp *bgp, struct prefix *p, afi_t afi, + safi_t safi, struct prefix_rd *prd, u_char *tag) { struct bgp_node *rn; struct bgp_info *new; @@ -4661,7 +4673,7 @@ DEFUN (bgp_network, "Specify a network to announce via BGP\n" "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n") { - return bgp_static_set (vty, vty->index, argv[0], + return bgp_static_set (vty, vty->index, argv[0], AFI_IP, bgp_node_safi (vty), NULL, 0); } @@ -6081,7 +6093,7 @@ bgp_redistribute_add (struct prefix *p, struct in_addr *nexthop, bgp_attr_extra_free (&attr_new); /* Unintern original. */ - aspath_unintern (attr.aspath); + aspath_unintern (&attr.aspath); bgp_attr_extra_free (&attr); bgp_redistribute_delete (p, type); return; @@ -6104,8 +6116,8 @@ bgp_redistribute_add (struct prefix *p, struct in_addr *nexthop, if (attrhash_cmp (bi->attr, new_attr) && !CHECK_FLAG(bi->flags, BGP_INFO_REMOVED)) { - bgp_attr_unintern (new_attr); - aspath_unintern (attr.aspath); + bgp_attr_unintern (&new_attr); + aspath_unintern (&attr.aspath); bgp_attr_extra_free (&attr); bgp_unlock_node (bn); return; @@ -6120,7 +6132,7 @@ bgp_redistribute_add (struct prefix *p, struct in_addr *nexthop, bgp_info_restore(bn, bi); else bgp_aggregate_decrement (bgp, p, bi, afi, SAFI_UNICAST); - bgp_attr_unintern (bi->attr); + bgp_attr_unintern (&bi->attr); bi->attr = new_attr; bi->uptime = bgp_clock (); @@ -6128,7 +6140,7 @@ bgp_redistribute_add (struct prefix *p, struct in_addr *nexthop, bgp_aggregate_increment (bgp, p, bi, afi, SAFI_UNICAST); bgp_process (bgp, bn, afi, SAFI_UNICAST); bgp_unlock_node (bn); - aspath_unintern (attr.aspath); + aspath_unintern (&attr.aspath); bgp_attr_extra_free (&attr); return; } @@ -6150,7 +6162,7 @@ bgp_redistribute_add (struct prefix *p, struct in_addr *nexthop, } /* Unintern original. */ - aspath_unintern (attr.aspath); + aspath_unintern (&attr.aspath); bgp_attr_extra_free (&attr); } @@ -7176,6 +7188,7 @@ bgp_show_route_in_table (struct vty *vty, struct bgp *bgp, SAFI_MPLS_VPN); } } + bgp_unlock_node (rm); } } @@ -7200,6 +7213,7 @@ bgp_show_route_in_table (struct vty *vty, struct bgp *bgp, route_vty_out_detail (vty, bgp, &rn->p, ri, afi, safi); } } + bgp_unlock_node (rn); } } @@ -8324,19 +8338,19 @@ bgp_show_community (struct vty *vty, const char *view_name, { bgp = bgp_lookup_by_name (view_name); if (bgp == NULL) - { - vty_out (vty, "Can't find BGP view %s%s", view_name, VTY_NEWLINE); - return CMD_WARNING; - } + { + vty_out (vty, "Can't find BGP view %s%s", view_name, VTY_NEWLINE); + return CMD_WARNING; + } } else { bgp = bgp_get_default (); if (bgp == NULL) - { - vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); - return CMD_WARNING; - } + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } } b = buffer_new (1024); diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 042620cf..f9bf2844 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -1549,10 +1549,10 @@ route_set_ecommunity_rt (void *rule, struct prefix *prefix, else new_ecom = ecommunity_dup (ecom); - bgp_info->attr->extra->ecommunity = new_ecom; + bgp_info->attr->extra->ecommunity = ecommunity_intern (new_ecom); if (old_ecom) - ecommunity_free (old_ecom); + ecommunity_unintern (&old_ecom); bgp_info->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES); } @@ -1568,7 +1568,7 @@ route_set_ecommunity_rt_compile (const char *arg) ecom = ecommunity_str2com (arg, ECOMMUNITY_ROUTE_TARGET, 0); if (! ecom) return NULL; - return ecom; + return ecommunity_intern (ecom); } /* Free function for set community. */ @@ -1576,7 +1576,7 @@ static void route_set_ecommunity_rt_free (void *rule) { struct ecommunity *ecom = rule; - ecommunity_free (ecom); + ecommunity_unintern (&ecom); } /* Set community rule structure. */ @@ -1595,7 +1595,7 @@ static route_map_result_t route_set_ecommunity_soo (void *rule, struct prefix *prefix, route_map_object_t type, void *object) { - struct ecommunity *ecom; + struct ecommunity *ecom, *old_ecom, *new_ecom; struct bgp_info *bgp_info; if (type == RMAP_BGP) @@ -1606,8 +1606,19 @@ route_set_ecommunity_soo (void *rule, struct prefix *prefix, if (! ecom) return RMAP_OKAY; + old_ecom = (bgp_attr_extra_get (bgp_info->attr))->ecommunity; + + if (old_ecom) + new_ecom = ecommunity_merge (ecommunity_dup (old_ecom), ecom); + else + new_ecom = ecommunity_dup (ecom); + + bgp_info->attr->extra->ecommunity = ecommunity_intern (new_ecom); + + if (old_ecom) + ecommunity_unintern (&old_ecom); + bgp_info->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES); - (bgp_attr_extra_get (bgp_info->attr))->ecommunity = ecommunity_dup (ecom); } return RMAP_OKAY; } @@ -1622,7 +1633,7 @@ route_set_ecommunity_soo_compile (const char *arg) if (! ecom) return NULL; - return ecom; + return ecommunity_intern (ecom); } /* Free function for set community. */ @@ -1630,7 +1641,7 @@ static void route_set_ecommunity_soo_free (void *rule) { struct ecommunity *ecom = rule; - ecommunity_free (ecom); + ecommunity_unintern (&ecom); } /* Set community rule structure. */ diff --git a/bgpd/bgp_session.c b/bgpd/bgp_session.c index d09fe217..7e04e216 100644 --- a/bgpd/bgp_session.c +++ b/bgpd/bgp_session.c @@ -324,11 +324,12 @@ bgp_session_enable(bgp_peer peer) session->open_send = bgp_peer_open_state_init_new(session->open_send, peer) ; bgp_open_state_unset(&session->open_recv) ; - session->connect = (peer->flags & PEER_FLAG_PASSIVE) == 0 ; - session->listen = 1 ; + session->connect = (peer->flags & PEER_FLAG_PASSIVE) == 0 ; + session->listen = true ; - session->ttl = peer->ttl ; - session->port = peer->port ; + session->ttl = peer->ttl ; + session->gtsm = peer->gtsm ; + session->port = peer->port ; if (session->ifname != NULL) free(session->ifname) ; @@ -952,23 +953,26 @@ bgp_session_do_XON(mqueue_block mqb, mqb_flag_t flag) } /*============================================================================== - * Routing Engine: send set ttl message to BGP Engine - * - */ + * Routing Engine: send set ttl message to BGP Engine, if session is active. + */ void -bgp_session_set_ttl(bgp_session session, int ttl) +bgp_session_set_ttl(bgp_session session, int ttl, bool gtsm) { mqueue_block mqb ; struct bgp_session_ttl_args *args; - mqb = mqb_init_new(NULL, bgp_session_do_set_ttl, session) ; + if (bgp_session_is_active(session)) + { + mqb = mqb_init_new(NULL, bgp_session_do_set_ttl, session) ; - args = mqb_get_args(mqb) ; - args->ttl = ttl ; + args = mqb_get_args(mqb) ; + args->ttl = ttl ; + args->gtsm = gtsm ; - ++bgp_engine_queue_stats.event ; + ++bgp_engine_queue_stats.event ; - bgp_to_bgp_engine(mqb, mqb_ordinary) ; + bgp_to_bgp_engine(mqb, mqb_ordinary) ; + } ; } /*------------------------------------------------------------------------------ @@ -985,10 +989,13 @@ bgp_session_do_set_ttl(mqueue_block mqb, mqb_flag_t flag) BGP_SESSION_LOCK(session) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - session->ttl = args->ttl ; + session->ttl = args->ttl ; + session->gtsm = args->gtsm ; - bgp_set_ttl(session->connections[bgp_connection_primary], session->ttl); - bgp_set_ttl(session->connections[bgp_connection_secondary], session->ttl); + bgp_set_new_ttl(session->connections[bgp_connection_primary], + session->ttl, session->gtsm) ; + bgp_set_new_ttl(session->connections[bgp_connection_secondary], + session->ttl, session->gtsm) ; BGP_SESSION_UNLOCK(session) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ } diff --git a/bgpd/bgp_session.h b/bgpd/bgp_session.h index cc8acc19..f8cfa547 100644 --- a/bgpd/bgp_session.h +++ b/bgpd/bgp_session.h @@ -157,6 +157,7 @@ struct bgp_session match */ int ttl ; /* TTL to set, if not zero */ + bool gtsm ; /* ttl set by ttl-security */ unsigned short port ; /* destination port for peer */ /* TODO: ifindex and ifaddress should be rebound if the peer hears any @@ -294,7 +295,8 @@ enum { BGP_XON_REFRESH = 40, struct bgp_session_ttl_args /* to bgp Engine */ { - int ttl ; /* time to live */ + int ttl ; + bool gtsm ; } ; MQB_ARGS_SIZE_OK(bgp_session_ttl_args) ; @@ -357,7 +359,7 @@ extern int bgp_session_dec_flow_count(bgp_peer peer) ; extern void -bgp_session_set_ttl(bgp_session session, int ttl); +bgp_session_set_ttl(bgp_session session, int ttl, bool gtsm); extern void bgp_session_get_stats(bgp_session session, struct bgp_session_stats *stats); diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 0015e043..40efd6b8 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -217,6 +217,11 @@ bgp_vty_return (struct vty *vty, int ret) break; case BGP_ERR_PEER_EXISTS: str = "Cannot have the same neighbor in different bgp views"; + case BGP_ERR_NO_EBGP_MULTIHOP_WITH_GTSM: + str = "ebgp-multihop and ttl-security cannot be configured together"; + break; + case BGP_ERR_NO_IBGP_WITH_TTLHACK: + str = "ttl-security only allowed for EBGP peers"; break; } if (str) @@ -2683,13 +2688,11 @@ peer_ebgp_multihop_set_vty (struct vty *vty, const char *ip_str, return CMD_WARNING; if (! ttl_str) - ttl = TTL_MAX; + ttl = MAXTTL; else - VTY_GET_INTEGER_RANGE ("TTL", ttl, ttl_str, 1, 255); - - peer_ebgp_multihop_set (peer, ttl); + VTY_GET_INTEGER_RANGE ("TTL", ttl, ttl_str, 1, MAXTTL); - return CMD_SUCCESS; + return bgp_vty_return (vty, peer_ebgp_multihop_set (peer, ttl)); } static int @@ -2701,9 +2704,7 @@ peer_ebgp_multihop_unset_vty (struct vty *vty, const char *ip_str) if (! peer) return CMD_WARNING; - peer_ebgp_multihop_unset (peer); - - return CMD_SUCCESS; + return bgp_vty_return (vty, peer_ebgp_multihop_unset (peer)); } /* neighbor ebgp-multihop. */ @@ -4011,6 +4012,43 @@ DEFUN (no_neighbor_allowas_in, return bgp_vty_return (vty, ret); } + +DEFUN (neighbor_ttl_security, + neighbor_ttl_security_cmd, + NEIGHBOR_CMD2 "ttl-security hops <1-254>", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Specify the maximum number of hops to the BGP peer\n") +{ + struct peer *peer; + int gtsm_hops; + + peer = peer_and_group_lookup_vty (vty, argv[0]); + if (! peer) + return CMD_WARNING; + + VTY_GET_INTEGER_RANGE ("", gtsm_hops, argv[1], 1, 254); + + return bgp_vty_return (vty, peer_ttl_security_hops_set (peer, gtsm_hops)); +} + +DEFUN (no_neighbor_ttl_security, + no_neighbor_ttl_security_cmd, + NO_NEIGHBOR_CMD2 "ttl-security hops <1-254>", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Specify the maximum number of hops to the BGP peer\n") +{ + struct peer *peer; + + peer = peer_and_group_lookup_vty (vty, argv[0]); + if (! peer) + return CMD_WARNING; + + return bgp_vty_return (vty, peer_ttl_security_hops_unset (peer)); +} + /* Address family configuration. */ DEFUN_ATTR (address_family_ipv4, address_family_ipv4_cmd, @@ -4126,14 +4164,14 @@ DEFUN_ATTR (exit_address_family, } ; /* BGP clear sort. */ -enum clear_sort +typedef enum { clear_all, clear_peer, clear_group, clear_external, clear_as -}; +} clear_sort_t ; static void bgp_clear_vty_error (struct vty *vty, struct peer *peer, afi_t afi, @@ -4159,7 +4197,7 @@ bgp_clear_vty_error (struct vty *vty, struct peer *peer, afi_t afi, /* `clear ip bgp' functions. */ static int bgp_clear (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, - enum clear_sort sort,enum bgp_clear_type stype, const char *arg) + clear_sort_t sort, bgp_clear_type_t stype, const char *arg) { int ret; struct peer *peer; @@ -4301,8 +4339,7 @@ bgp_clear (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, static int bgp_clear_vty (struct vty *vty, const char *name, afi_t afi, safi_t safi, - enum clear_sort sort, enum bgp_clear_type stype, - const char *arg) + clear_sort_t sort, bgp_clear_type_t stype, const char *arg) { struct bgp *bgp; @@ -7713,10 +7750,17 @@ bgp_show_peer (struct vty *vty, struct peer *p) p->host, VTY_NEWLINE); } - /* EBGP Multihop */ - if (peer_sort (p) != BGP_PEER_IBGP && p->ttl > 1) - vty_out (vty, " External BGP neighbor may be up to %d hops away.%s", - p->ttl, VTY_NEWLINE); + /* EBGP Multihop and GTSM */ + if (peer_sort (p) != BGP_PEER_IBGP) + { + if (p->gtsm) + vty_out (vty, " External BGP neighbor may be up to %d hops away" + " -- using GTSM.%s", + p->ttl, VTY_NEWLINE); + else if (p->ttl > 1) + vty_out (vty, " External BGP neighbor may be up to %d hops away.%s", + p->ttl, VTY_NEWLINE); + } /* Local address. */ if (p->su_local) @@ -10185,6 +10229,10 @@ bgp_vty_init (void) install_element (BGP_IPV6_NODE, &no_bgp_redistribute_ipv6_metric_rmap_cmd); #endif /* HAVE_IPV6 */ + /* ttl_security commands */ + install_element (BGP_NODE, &neighbor_ttl_security_cmd); + install_element (BGP_NODE, &no_neighbor_ttl_security_cmd); + /* "show bgp memory" commands. */ install_element (VIEW_NODE, &show_bgp_memory_cmd); install_element (RESTRICTED_NODE, &show_bgp_memory_cmd); diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 195ccaef..fd7cd39e 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -646,6 +646,7 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp) int flags; u_char distance; struct peer *peer; + bgp_peer_sort_t sort ; if (zclient->sock < 0) return; @@ -654,15 +655,16 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp) return; flags = 0; - peer = info->peer; + peer = info->peer; + sort = peer_sort(peer) ; - if (peer_sort (peer) == BGP_PEER_IBGP || peer_sort (peer) == BGP_PEER_CONFED) + if ((sort == BGP_PEER_IBGP) || (sort == BGP_PEER_CONFED)) { SET_FLAG (flags, ZEBRA_FLAG_IBGP); SET_FLAG (flags, ZEBRA_FLAG_INTERNAL); } - if ((peer_sort (peer) == BGP_PEER_EBGP && peer->ttl != 1) + if (((sort == BGP_PEER_EBGP) && (peer->ttl != 1)) || CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)) SET_FLAG (flags, ZEBRA_FLAG_INTERNAL); @@ -780,6 +782,7 @@ bgp_zebra_withdraw (struct prefix *p, struct bgp_info *info) { int flags; struct peer *peer; + bgp_peer_sort_t sort ; if (zclient->sock < 0) return; @@ -787,16 +790,17 @@ bgp_zebra_withdraw (struct prefix *p, struct bgp_info *info) if (! zclient->redist[ZEBRA_ROUTE_BGP]) return; - peer = info->peer; flags = 0; + peer = info->peer; + sort = peer_sort(peer) ; - if (peer_sort (peer) == BGP_PEER_IBGP) + if (sort == BGP_PEER_IBGP) { SET_FLAG (flags, ZEBRA_FLAG_INTERNAL); SET_FLAG (flags, ZEBRA_FLAG_IBGP); } - if ((peer_sort (peer) == BGP_PEER_EBGP && peer->ttl != 1) + if (((sort == BGP_PEER_EBGP) && (peer->ttl != 1)) || CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)) SET_FLAG (flags, ZEBRA_FLAG_INTERNAL); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 2eee6cae..5a5eab83 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -275,11 +275,12 @@ bgp_confederation_id_set (struct bgp *bgp, as_t as) were not doing confederation before, reset all EBGP sessions. */ for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) { + bgp_peer_sort_t sort = peer_sort (peer) ; /* We're looking for peers who's AS is not local or part of our confederation. */ if (already_confed) { - if (peer_sort (peer) == BGP_PEER_EBGP) + if (sort == BGP_PEER_EBGP) { peer->local_as = as; bgp_peer_down(peer, PEER_DOWN_CONFED_ID_CHANGE) ; @@ -289,10 +290,10 @@ bgp_confederation_id_set (struct bgp *bgp, as_t as) { /* Not doing confederation before, so reset every non-local session */ - if (peer_sort (peer) != BGP_PEER_IBGP) + if (sort != BGP_PEER_IBGP) { /* Reset the local_as to be our EBGP one */ - if (peer_sort (peer) == BGP_PEER_EBGP) + if (sort == BGP_PEER_EBGP) peer->local_as = as; bgp_peer_down(peer, PEER_DOWN_CONFED_ID_CHANGE) ; @@ -561,7 +562,19 @@ peer_global_config_reset (struct peer *peer) { peer->weight = 0; peer->change_local_as = 0; - peer->ttl = (peer_sort (peer) == BGP_PEER_IBGP ? 255 : 1); + + if (peer_sort (peer) == BGP_PEER_IBGP) + { + peer->ttl = MAXTTL ; + peer->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV; + } + else /* BGP_PEER_EBGP & BGP_PEER_CONFED */ + { + peer->ttl = 1 ; + peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV; + } ; + peer->gtsm = false ; + if (peer->update_source) { sockunion_free (peer->update_source); @@ -573,11 +586,6 @@ peer_global_config_reset (struct peer *peer) peer->update_if = NULL; } - if (peer_sort (peer) == BGP_PEER_IBGP) - peer->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV; - else - peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV; - peer->flags = 0; peer->config = 0; peer->holdtime = 0; @@ -586,7 +594,23 @@ peer_global_config_reset (struct peer *peer) peer->v_connect = BGP_DEFAULT_CONNECT_RETRY; } -/* Check peer's AS number and determin is this peer IBGP or EBGP */ +/*------------------------------------------------------------------------------ + * Check peer's AS number and determine is this peer IBGP or EBGP + * + * peer->as is the ASN heard from the peer at OPEN time (remote ASN) + * + * peer->local_as is the ASN sent to the peer at OPEN time + * + * Generally this will be the bgp->as, but for confederations + * the bgp->as is the member ASN, so for peers outside the + * confederation the local_as is set to the confed_id. + * + * Returns: BGP_PEER_EBGP + * BGP_PEER_IBGP -- including within a confederation member-AS + * BGP_PEER_CONFED -- between different confederation member-ASes + * BGP_PEER_INTERNAL -- this is group, and remote ASN is not set + * or is not group amd local ASN is not set + */ int peer_sort (struct peer *peer) { @@ -594,35 +618,58 @@ peer_sort (struct peer *peer) bgp = peer->bgp; - /* Peer-group */ + /* Peer-group + * + * If the group's ASN (remote ASN) is set, return iBGP or eBGP depending on + * the bgp ASN (local ASN). + * + * Otherwise: look at first peer in group (if any) and return iBGP or eBGP, + * depending on that peer's local and remote ASNs. + * + * Note that all peers in a group which does not specify an ASN, + * must all be of the same sort. + * + * Does not check for peer1->local_as being unset, because that + * is not realy possible... TODO ??? + * + * Otherwise: return BGP_PEER_INTERNAL. + * + * NB: does not trouble itself about CONFEDERATION TODO ???? + */ if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) { if (peer->as) - return (bgp->as == peer->as ? BGP_PEER_IBGP : BGP_PEER_EBGP); + return (bgp->as == peer->as) ? BGP_PEER_IBGP : BGP_PEER_EBGP ; else { struct peer *peer1; peer1 = listnode_head (peer->group->peer); if (peer1) - return (peer1->local_as == peer1->as - ? BGP_PEER_IBGP : BGP_PEER_EBGP); + return (peer1->local_as == peer1->as) ? BGP_PEER_IBGP + : BGP_PEER_EBGP ; } return BGP_PEER_INTERNAL; } - /* Normal peer */ + /* Normal peer + * + * If the peer's local ASN is not set, return BGP_PEER_INTERNAL. + * + * Otherwise: if peer's remote and local ASN are the same, return iBGP unless + * it's the confed_id, in which case return eBGP TODO ???? + * + * Otherwise: if this is a confederation peer, return BGP_PEER_CONFED. + * + * Otherwise: return eBGP + */ + if (peer->local_as == 0) /* not really possible... TODO ??? */ + return BGP_PEER_INTERNAL; + if (bgp && CHECK_FLAG (bgp->config, BGP_CONFIG_CONFEDERATION)) { - if (peer->local_as == 0) - return BGP_PEER_INTERNAL; - if (peer->local_as == peer->as) - { - if (peer->local_as == bgp->confed_id) - return BGP_PEER_EBGP; - else - return BGP_PEER_IBGP; - } + return (peer->local_as != bgp->confed_id) ? BGP_PEER_IBGP + : BGP_PEER_EBGP ; if (bgp_confederation_peers_check (bgp, peer->as)) return BGP_PEER_CONFED; @@ -631,9 +678,7 @@ peer_sort (struct peer *peer) } else { - return (peer->local_as == 0 - ? BGP_PEER_INTERNAL : peer->local_as == peer->as - ? BGP_PEER_IBGP : BGP_PEER_EBGP); + return (peer->local_as == peer->as) ? BGP_PEER_IBGP : BGP_PEER_EBGP ; } } @@ -655,10 +700,11 @@ peer_create_accept (struct bgp *bgp) static void peer_as_change (struct peer *peer, as_t as) { - int type; + bgp_peer_sort_t old_sort, new_sort ; - type = peer_sort (peer); + old_sort = peer_sort (peer); peer->as = as; + new_sort = peer_sort (peer); if (bgp_config_check (peer->bgp, BGP_CONFIG_CONFEDERATION) && ! bgp_confederation_peers_check (peer->bgp, as) @@ -668,16 +714,33 @@ peer_as_change (struct peer *peer, as_t as) peer->local_as = peer->bgp->as; /* Advertisement-interval reset */ - if (peer_sort (peer) == BGP_PEER_IBGP) + if (new_sort == BGP_PEER_IBGP) peer->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV; else peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV; - /* TTL reset */ - if (peer_sort (peer) == BGP_PEER_IBGP) - peer->ttl = 255; - else if (type == BGP_PEER_IBGP) - peer->ttl = 1; + /* TTL reset + * + * -------|----------- new -----------| + * old | iBGP | eBGP | Confed | + * -------|---------|--------|--------| + * iBGP | iBGP | eBGP | eBGP | -- sans GTSM + * -------|---------|--------|--------| + * eBGP | iBGP | stet | stet | + * -------|---------|--------|--------| + * Confed | iBGP | stet | stet | + * -------|---------|--------|--------| +* */ + if (new_sort == BGP_PEER_IBGP) + { + peer->ttl = MAXTTL ; + peer->gtsm = false ; + } + else if (old_sort == BGP_PEER_IBGP) + { + peer->ttl = 1; + peer->gtsm = false ; /* should already be the case ! */ + } ; /* reflector-client reset */ if (peer_sort (peer) != BGP_PEER_IBGP) @@ -988,6 +1051,7 @@ peer_group_get (struct bgp *bgp, const char *name) group->conf->group = group; group->conf->as = 0; group->conf->ttl = 1; + group->conf->gtsm = false ; group->conf->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV; UNSET_FLAG (group->conf->config, PEER_CONFIG_TIMER); UNSET_FLAG (group->conf->config, PEER_CONFIG_CONNECT); @@ -1023,8 +1087,9 @@ peer_group2peer_config_copy (struct peer_group *group, struct peer *peer, if (conf->change_local_as) peer->change_local_as = conf->change_local_as; - /* TTL */ - peer->ttl = conf->ttl; + /* TTL & GTSM */ + peer->ttl = conf->ttl; + peer->gtsm = conf->gtsm; /* Weight */ peer->weight = conf->weight; @@ -1446,7 +1511,10 @@ peer_group_bind (struct bgp *bgp, union sockunion *su, /* ebgp-multihop reset */ if (peer_sort (group->conf) == BGP_PEER_IBGP) - group->conf->ttl = 255; + { + group->conf->ttl = MAXTTL; + group->conf->gtsm = false ; + } ; /* local-as reset */ if (peer_sort (group->conf) != BGP_PEER_EBGP) @@ -2283,81 +2351,183 @@ peer_af_flag_unset (struct peer *peer, afi_t afi, safi_t safi, u_int32_t flag) return peer_af_flag_modify (peer, afi, safi, flag, false); } -/* EBGP multihop configuration. */ +/*------------------------------------------------------------------------------ + * eBGP multihop configuration set -- Confed is eBGP for this purpose. + * + * This is simply ignored if iBGP. For iBGP peer->ttl is set to MAXTTL, and + * peer->gtsm is always false. + * + * For eBGP and for Confed, peer->ttl is set to 1, and peer->gtsm is also + * set false -- until either ebgp-multihop or ttl-security is seen. + * + * NB: cannot set ebgp-multihop if ttl-security (GTSM) is set. + * + * cannot set ebgp-multihop on a group if ttl-security (GTSM) is set on any + * group member. + * + * NB: setting ebgp-multihop of 1 is the same as unsetting it. + * + * setting any value < 1 also unsets ebgp-multihop (sets ttl = 1) + */ int peer_ebgp_multihop_set (struct peer *peer, int ttl) { struct peer_group *group; struct listnode *node, *nnode; - bgp_session session = peer->session; if (peer_sort (peer) == BGP_PEER_IBGP) return 0; - peer->ttl = ttl; + if (peer->gtsm) + return BGP_ERR_NO_EBGP_MULTIHOP_WITH_GTSM; + + if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + struct peer *pgm; + group = peer->group; + + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, pgm)) + { + if (peer_sort (pgm) == BGP_PEER_IBGP) + continue; + + if (pgm->gtsm) + return BGP_ERR_NO_EBGP_MULTIHOP_WITH_GTSM; + } + } ; + + if (ttl <= 0) + ttl = 1 ; + + peer->ttl = ttl ; +//qassert(!peer->gtsm) ; if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) { - if (bgp_session_is_active(session) && peer_sort (peer) != BGP_PEER_IBGP) - bgp_session_set_ttl (session, peer->ttl); + bgp_session_set_ttl (peer->session, peer->ttl, peer->gtsm); } else { + struct peer *pgm; group = peer->group; - for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, pgm)) { - if (peer_sort (peer) == BGP_PEER_IBGP) - continue; - - peer->ttl = group->conf->ttl; - session = peer->session; + if (peer_sort (pgm) == BGP_PEER_IBGP) + continue; - if (bgp_session_is_active(session)) - bgp_session_set_ttl (session, peer->ttl); + pgm->ttl = ttl; +// qassert(!pgm->gtsm) ; + bgp_session_set_ttl (pgm->session, pgm->ttl, pgm->gtsm); } } return 0; } +/*------------------------------------------------------------------------------ + * eBGP multihop configuration unset -- Confed is eBGP for this purpose. + * + * Implemented by setting the ttl to 0 ! + */ int peer_ebgp_multihop_unset (struct peer *peer) { + return peer_ebgp_multihop_set (peer, 0) ; +} + +/*------------------------------------------------------------------------------ + * eBGP ttl-security hops configuration set -- Confed is eBGP for this purpose. + * + * Setting ttl-security hops is equivalent to setting eBGP multi-hop, except + * that it also enables the GTSM -- if available. + * + * This is simply ignored if iBGP. For iBGP peer->ttl is set to MAXTTL, and + * peer->gtsm is always false. + * + * For eBGP and for Confed, peer->ttl is set to 1, and peer->gtsm is also + * set false -- until either ebgp-multihop or ttl-security is seen. + * + * NB: cannot set ttl-security (GTSM) if eBGP multi-hop is set. + * + * cannot set ttl-security (GTSM) on a group if eBGP multi-hop is set on any + * group member. + * + * NB: setting ebgp-multihop of < 1 is unsets it (sets ttl = 1, and gtsm false) + */ +int +peer_ttl_security_hops_set (struct peer *peer, int ttl) +{ struct peer_group *group; struct listnode *node, *nnode; - bgp_session session = peer->session; + bool gtsm ; + + zlog_debug ("peer_ttl_security_hops_set: set gtsm_hops to %d for %s", + ttl, peer->host) ; if (peer_sort (peer) == BGP_PEER_IBGP) - return 0; + return BGP_ERR_NO_IBGP_WITH_TTLHACK ; - if (peer_group_active (peer)) - peer->ttl = peer->group->conf->ttl; + if (!peer->gtsm && (peer->ttl > 1)) + return BGP_ERR_NO_EBGP_MULTIHOP_WITH_GTSM; + + if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + struct peer *pgm; + group = peer->group; + + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, pgm)) + { + if (peer_sort (pgm) == BGP_PEER_IBGP) + continue; + + if (!pgm->gtsm && (pgm->ttl > 1)) + return BGP_ERR_NO_EBGP_MULTIHOP_WITH_GTSM; + } + } ; + + if (ttl > 0) + gtsm = true ; else - peer->ttl = 1; + { + gtsm = false ; + ttl = 1 ; + } ; + + peer->ttl = ttl ; + peer->gtsm = gtsm ; if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) { - if (bgp_session_is_active(session) && peer_sort (peer) != BGP_PEER_IBGP) - bgp_session_set_ttl (session, peer->ttl); + bgp_session_set_ttl (peer->session, peer->ttl, peer->gtsm); } else { + struct peer *pgm; group = peer->group; - for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) - { - if (peer_sort (peer) == BGP_PEER_IBGP) - continue; - - peer->ttl = 1; - session = peer->session; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, pgm)) + { + if (peer_sort (pgm) == BGP_PEER_IBGP) + continue; - if (bgp_session_is_active(session)) - bgp_session_set_ttl (session, peer->ttl); - } + pgm->ttl = ttl; + pgm->gtsm = gtsm ; + bgp_session_set_ttl (pgm->session, pgm->ttl, pgm->gtsm); + } ; } - return 0; } +/*------------------------------------------------------------------------------ + * eBGP ttl-security hops configuration unset -- Confed is eBGP for this purpose. + * + * Implemented by setting the ttl to 0 ! + */ +int +peer_ttl_security_hops_unset (struct peer *peer) +{ + return peer_ttl_security_hops_set(peer, 0) ; +} + + /* Neighbor description. */ int peer_description_set (struct peer *peer, char *desc) @@ -3835,7 +4005,7 @@ peer_clear (struct peer *peer) int peer_clear_soft (struct peer *peer, afi_t afi, safi_t safi, - enum bgp_clear_type stype) + bgp_clear_type_t stype) { if (peer->state != bgp_peer_pEstablished) return 0; @@ -4043,160 +4213,168 @@ bgp_config_write_peer (struct vty *vty, struct bgp *bgp, struct peer *peer, afi_t afi, safi_t safi) { struct bgp_filter *filter; - struct peer *g_peer = NULL; + struct peer *g_conf ; + bool pgm, agm ; char *addr; + bgp_peer_sort_t sort ; - filter = &peer->filter[afi][safi]; addr = peer->host; + sort = peer_sort(peer) ; if (peer_group_active (peer)) - g_peer = peer->group->conf; + g_conf = peer->group->conf ; + else + g_conf = NULL ; + pgm = (g_conf != NULL) ; /* group member for >= 1 address families */ - /************************************ - ****** Global to the neighbor ****** - ************************************/ - if (afi == AFI_IP && safi == SAFI_UNICAST) + filter = &peer->filter[afi][safi]; + agm = (peer->af_group[afi][safi] != 0) ; + + if ((afi == AFI_IP) && (safi == SAFI_UNICAST)) { - /* remote-as. */ - if (! peer_group_active (peer)) + /* First: stuff which is global to the neighbor */ + + /* peer group and/or remote-as. */ + if (!pgm) { if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) - vty_out (vty, " neighbor %s peer-group%s", addr, - VTY_NEWLINE); + vty_out (vty, " neighbor %s peer-group%s", addr, VTY_NEWLINE); if (peer->as) vty_out (vty, " neighbor %s remote-as %u%s", addr, peer->as, - VTY_NEWLINE); + VTY_NEWLINE); } else { - if (! g_peer->as) + if (g_conf->as == 0) vty_out (vty, " neighbor %s remote-as %u%s", addr, peer->as, - VTY_NEWLINE); - if (peer->af_group[AFI_IP][SAFI_UNICAST]) + VTY_NEWLINE); + if (agm) vty_out (vty, " neighbor %s peer-group %s%s", addr, - peer->group->name, VTY_NEWLINE); + peer->group->name, VTY_NEWLINE); } - /* local-as. */ + /* local-as. */ if (peer->change_local_as) - if (! peer_group_active (peer)) + if (!pgm) vty_out (vty, " neighbor %s local-as %u%s%s", addr, - peer->change_local_as, - CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND) ? - " no-prepend" : "", VTY_NEWLINE); + peer->change_local_as, + CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND) + ? " no-prepend" : "", VTY_NEWLINE); - /* Description. */ + /* Description. */ if (peer->desc) vty_out (vty, " neighbor %s description %s%s", addr, peer->desc, - VTY_NEWLINE); + VTY_NEWLINE); - /* Shutdown. */ + /* Shutdown. */ if (CHECK_FLAG (peer->flags, PEER_FLAG_SHUTDOWN)) - if (! peer_group_active (peer) || - ! CHECK_FLAG (g_peer->flags, PEER_FLAG_SHUTDOWN)) + if (!pgm || ! CHECK_FLAG (g_conf->flags, PEER_FLAG_SHUTDOWN)) vty_out (vty, " neighbor %s shutdown%s", addr, VTY_NEWLINE); - /* Password. */ + /* Password. */ if (peer->password) - if (!peer_group_active (peer) - || ! g_peer->password - || strcmp (peer->password, g_peer->password) != 0) + if (!pgm || (g_conf->password == NULL) + || (strcmp (peer->password, g_conf->password) != 0)) vty_out (vty, " neighbor %s password %s%s", addr, peer->password, - VTY_NEWLINE); + VTY_NEWLINE); - /* BGP port. */ + /* BGP port. */ if (peer->port != BGP_PORT_DEFAULT) - vty_out (vty, " neighbor %s port %d%s", addr, peer->port, - VTY_NEWLINE); + vty_out (vty, " neighbor %s port %d%s", addr, peer->port, VTY_NEWLINE); - /* Local interface name. */ + /* Local interface name. */ if (peer->ifname) vty_out (vty, " neighbor %s interface %s%s", addr, peer->ifname, - VTY_NEWLINE); + VTY_NEWLINE); - /* Passive. */ + /* Passive. */ if (CHECK_FLAG (peer->flags, PEER_FLAG_PASSIVE)) - if (! peer_group_active (peer) || - ! CHECK_FLAG (g_peer->flags, PEER_FLAG_PASSIVE)) + if (!pgm || ! CHECK_FLAG (g_conf->flags, PEER_FLAG_PASSIVE)) vty_out (vty, " neighbor %s passive%s", addr, VTY_NEWLINE); - /* EBGP multihop. */ - if (peer_sort (peer) != BGP_PEER_IBGP && peer->ttl != 1) - if (! peer_group_active (peer) || - g_peer->ttl != peer->ttl) - vty_out (vty, " neighbor %s ebgp-multihop %d%s", addr, peer->ttl, - VTY_NEWLINE); + /* EBGP multihop and ttl-security hops. */ + if (sort != BGP_PEER_IBGP) + { + if (peer->gtsm) + { + /* ttl-security hops */ + if (!pgm || ! g_conf->gtsm) + vty_out (vty, " neighbor %s ttl-security hops %d%s", addr, + peer->ttl, VTY_NEWLINE) ; + } + else if (peer->ttl != 1) + { + /* eBGP multihop */ + if (!pgm || (g_conf->ttl != peer->ttl)) + vty_out (vty, " neighbor %s ebgp-multihop %d%s", addr, + peer->ttl, VTY_NEWLINE) ; + } ; + } ; - /* disable-connected-check. */ + /* disable-connected-check. */ if (CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)) - if (! peer_group_active (peer) || - ! CHECK_FLAG (g_peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)) - vty_out (vty, " neighbor %s disable-connected-check%s", addr, VTY_NEWLINE); + if (!pgm || + ! CHECK_FLAG (g_conf->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)) + vty_out (vty, " neighbor %s disable-connected-check%s", addr, + VTY_NEWLINE); - /* Update-source. */ + /* Update-source. */ if (peer->update_if) - if (! peer_group_active (peer) || ! g_peer->update_if - || strcmp (g_peer->update_if, peer->update_if) != 0) + if (!pgm || ! g_conf->update_if + || (strcmp (g_conf->update_if, peer->update_if) != 0)) vty_out (vty, " neighbor %s update-source %s%s", addr, - peer->update_if, VTY_NEWLINE); + peer->update_if, VTY_NEWLINE); if (peer->update_source) - if (! peer_group_active (peer) || ! g_peer->update_source - || sockunion_cmp (g_peer->update_source, - peer->update_source) != 0) + if (!pgm || ! g_conf->update_source + || (sockunion_cmp (g_conf->update_source, peer->update_source) != 0)) vty_out (vty, " neighbor %s update-source %s%s", addr, sutoa(peer->update_source).str, VTY_NEWLINE); - /* advertisement-interval */ + /* advertisement-interval */ if (CHECK_FLAG (peer->config, PEER_CONFIG_ROUTEADV)) vty_out (vty, " neighbor %s advertisement-interval %d%s", addr, peer->v_routeadv, VTY_NEWLINE); - /* timers. */ - if (CHECK_FLAG (peer->config, PEER_CONFIG_TIMER) - && ! peer_group_active (peer)) - vty_out (vty, " neighbor %s timers %d %d%s", addr, - peer->keepalive, peer->holdtime, VTY_NEWLINE); + /* timers. */ + if (!pgm && CHECK_FLAG (peer->config, PEER_CONFIG_TIMER)) + vty_out (vty, " neighbor %s timers %d %d%s", addr, + peer->keepalive, peer->holdtime, VTY_NEWLINE); if (CHECK_FLAG (peer->config, PEER_CONFIG_CONNECT)) - vty_out (vty, " neighbor %s timers connect %d%s", addr, - peer->connect, VTY_NEWLINE); + vty_out (vty, " neighbor %s timers connect %d%s", addr, + peer->connect, VTY_NEWLINE); - /* Default weight. */ + /* Default weight. */ if (CHECK_FLAG (peer->config, PEER_CONFIG_WEIGHT)) - if (! peer_group_active (peer) || - g_peer->weight != peer->weight) + if (!pgm || g_conf->weight != peer->weight) vty_out (vty, " neighbor %s weight %d%s", addr, peer->weight, - VTY_NEWLINE); + VTY_NEWLINE); - /* Dynamic capability. */ + /* Dynamic capability. */ if (CHECK_FLAG (peer->flags, PEER_FLAG_DYNAMIC_CAPABILITY)) - if (! peer_group_active (peer) || - ! CHECK_FLAG (g_peer->flags, PEER_FLAG_DYNAMIC_CAPABILITY)) + if (!pgm || ! CHECK_FLAG (g_conf->flags, PEER_FLAG_DYNAMIC_CAPABILITY)) vty_out (vty, " neighbor %s capability dynamic%s", addr, - VTY_NEWLINE); + VTY_NEWLINE); - /* dont capability negotiation. */ + /* dont capability negotiation. */ if (CHECK_FLAG (peer->flags, PEER_FLAG_DONT_CAPABILITY)) - if (! peer_group_active (peer) || - ! CHECK_FLAG (g_peer->flags, PEER_FLAG_DONT_CAPABILITY)) - vty_out (vty, " neighbor %s dont-capability-negotiate%s", addr, - VTY_NEWLINE); + if (!pgm || ! CHECK_FLAG (g_conf->flags, PEER_FLAG_DONT_CAPABILITY)) + vty_out (vty, " neighbor %s dont-capability-negotiate%s", addr, + VTY_NEWLINE); - /* override capability negotiation. */ + /* override capability negotiation. */ if (CHECK_FLAG (peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY)) - if (! peer_group_active (peer) || - ! CHECK_FLAG (g_peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY)) - vty_out (vty, " neighbor %s override-capability%s", addr, - VTY_NEWLINE); + if (!pgm || ! CHECK_FLAG (g_conf->flags, PEER_FLAG_OVERRIDE_CAPABILITY)) + vty_out (vty, " neighbor %s override-capability%s", addr, + VTY_NEWLINE); - /* strict capability negotiation. */ + /* strict capability negotiation. */ if (CHECK_FLAG (peer->flags, PEER_FLAG_STRICT_CAP_MATCH)) - if (! peer_group_active (peer) || - ! CHECK_FLAG (g_peer->flags, PEER_FLAG_STRICT_CAP_MATCH)) - vty_out (vty, " neighbor %s strict-capability-match%s", addr, - VTY_NEWLINE); + if (!pgm || ! CHECK_FLAG (g_conf->flags, PEER_FLAG_STRICT_CAP_MATCH)) + vty_out (vty, " neighbor %s strict-capability-match%s", addr, + VTY_NEWLINE); - if (! peer_group_active (peer)) + if (!pgm) { if (bgp_flag_check (bgp, BGP_FLAG_NO_DEFAULT_IPV4)) { @@ -4210,25 +4388,25 @@ bgp_config_write_peer (struct vty *vty, struct bgp *bgp, } } } - - - /************************************ - ****** Per AF to the neighbor ****** - ************************************/ - - if (! (afi == AFI_IP && safi == SAFI_UNICAST)) + else { - if (peer->af_group[afi][safi]) + /* Per AF for the neighbor */ + + if (agm) vty_out (vty, " neighbor %s peer-group %s%s", addr, - peer->group->name, VTY_NEWLINE); + peer->group->name, VTY_NEWLINE); else vty_out (vty, " neighbor %s activate%s", addr, VTY_NEWLINE); } + /*-------------------------------------------------------------------- + * From now on we are dealing with the particular address family. + */ + /* ORF capability. */ if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_SM) || CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_RM)) - if (! peer->af_group[afi][safi]) + if (!agm) { vty_out (vty, " neighbor %s capability orf prefix-list", addr); @@ -4243,24 +4421,20 @@ bgp_config_write_peer (struct vty *vty, struct bgp *bgp, } /* Route reflector client. */ - if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_REFLECTOR_CLIENT) - && ! peer->af_group[afi][safi]) + if (!agm && peer_af_flag_check (peer, afi, safi, PEER_FLAG_REFLECTOR_CLIENT)) vty_out (vty, " neighbor %s route-reflector-client%s", addr, VTY_NEWLINE); /* Nexthop self. */ - if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_NEXTHOP_SELF) - && ! peer->af_group[afi][safi]) + if (!agm && peer_af_flag_check (peer, afi, safi, PEER_FLAG_NEXTHOP_SELF)) vty_out (vty, " neighbor %s next-hop-self%s", addr, VTY_NEWLINE); /* Remove private AS. */ - if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_REMOVE_PRIVATE_AS) - && ! peer->af_group[afi][safi]) - vty_out (vty, " neighbor %s remove-private-AS%s", - addr, VTY_NEWLINE); + if (!agm && peer_af_flag_check (peer, afi, safi, PEER_FLAG_REMOVE_PRIVATE_AS)) + vty_out (vty, " neighbor %s remove-private-AS%s", addr, VTY_NEWLINE); /* send-community print. */ - if (! peer->af_group[afi][safi]) + if (!agm) { if (bgp_option_check (BGP_OPT_CONFIG_CISCO)) { @@ -4288,9 +4462,8 @@ bgp_config_write_peer (struct vty *vty, struct bgp *bgp, } } - /* Default information */ - if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_DEFAULT_ORIGINATE) - && ! peer->af_group[afi][safi]) + /* Default information */ + if (!agm && peer_af_flag_check (peer, afi, safi, PEER_FLAG_DEFAULT_ORIGINATE)) { vty_out (vty, " neighbor %s default-originate", addr); if (peer->default_rmap[afi][safi].name) @@ -4298,19 +4471,19 @@ bgp_config_write_peer (struct vty *vty, struct bgp *bgp, vty_out (vty, "%s", VTY_NEWLINE); } - /* Soft reconfiguration inbound. */ + /* Soft reconfiguration inbound. */ if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG)) - if (! peer->af_group[afi][safi] || - ! CHECK_FLAG (g_peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG)) + if (!agm || + ! CHECK_FLAG (g_conf->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG)) vty_out (vty, " neighbor %s soft-reconfiguration inbound%s", addr, - VTY_NEWLINE); + VTY_NEWLINE); - /* maximum-prefix. */ + /* maximum-prefix. */ if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX)) - if (! peer->af_group[afi][safi] - || g_peer->pmax[afi][safi] != peer->pmax[afi][safi] - || g_peer->pmax_threshold[afi][safi] != peer->pmax_threshold[afi][safi] - || CHECK_FLAG (g_peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING) + if (!agm + || g_conf->pmax[afi][safi] != peer->pmax[afi][safi] + || g_conf->pmax_threshold[afi][safi] != peer->pmax_threshold[afi][safi] + || CHECK_FLAG (g_conf->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING) != CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING)) { vty_out (vty, " neighbor %s maximum-prefix %ld", addr, peer->pmax[afi][safi]); @@ -4324,15 +4497,14 @@ bgp_config_write_peer (struct vty *vty, struct bgp *bgp, } /* Route server client. */ - if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT) - && ! peer->af_group[afi][safi]) + if (!agm && CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT)) vty_out (vty, " neighbor %s route-server-client%s", addr, VTY_NEWLINE); /* Allow AS in. */ if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_ALLOWAS_IN)) - if (! peer_group_active (peer) - || ! peer_af_flag_check (g_peer, afi, safi, PEER_FLAG_ALLOWAS_IN) - || peer->allowas_in[afi][safi] != g_peer->allowas_in[afi][safi]) + if (!pgm + || ! peer_af_flag_check (g_conf, afi, safi, PEER_FLAG_ALLOWAS_IN) + || peer->allowas_in[afi][safi] != g_conf->allowas_in[afi][safi]) { if (peer->allowas_in[afi][safi] == 3) vty_out (vty, " neighbor %s allowas-in%s", addr, VTY_NEWLINE); @@ -4345,10 +4517,10 @@ bgp_config_write_peer (struct vty *vty, struct bgp *bgp, bgp_config_write_filter (vty, peer, afi, safi); /* atribute-unchanged. */ - if ((CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_AS_PATH_UNCHANGED) + if (!agm && + ( CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_AS_PATH_UNCHANGED) || CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_UNCHANGED) - || CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MED_UNCHANGED)) - && ! peer->af_group[afi][safi]) + || CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MED_UNCHANGED) )) { if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_AS_PATH_UNCHANGED) && CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_UNCHANGED) diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 3a657456..f20d8315 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -195,8 +195,6 @@ struct bgp_rd - - #define PEER_PASSWORD_MINLEN (1) #define PEER_PASSWORD_MAXLEN (80) @@ -342,16 +340,16 @@ struct bgp_nlri /* IBGP/EBGP identifier. We also have a CONFED peer, which is to say, a peer who's AS is part of our Confederation. */ -enum +typedef enum { BGP_PEER_IBGP, BGP_PEER_EBGP, BGP_PEER_INTERNAL, BGP_PEER_CONFED -}; +} bgp_peer_sort_t ; /* Flag for peer_clear_soft(). */ -enum bgp_clear_type +typedef enum { BGP_CLEAR_SOFT_NONE, BGP_CLEAR_SOFT_OUT, @@ -359,7 +357,7 @@ enum bgp_clear_type BGP_CLEAR_SOFT_BOTH, BGP_CLEAR_SOFT_IN_ORF_PREFIX, BGP_CLEAR_SOFT_RSCLIENT -}; +} bgp_clear_type_t ; /* Macros. */ #define BGP_INPUT(P) ((P)->ibuf) @@ -400,7 +398,9 @@ enum bgp_clear_type #define BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS -28 #define BGP_ERR_TCPSIG_FAILED -29 #define BGP_ERR_PEER_EXISTS -30 -#define BGP_ERR_MAX -31 +#define BGP_ERR_NO_EBGP_MULTIHOP_WITH_GTSM -31 +#define BGP_ERR_NO_IBGP_WITH_TTLHACK -32 +#define BGP_ERR_MAX -33 /*------------------------------------------------------------------------------ * Globals. @@ -600,8 +600,10 @@ extern int peer_maximum_prefix_set (struct peer *, afi_t, safi_t, u_int32_t, u_c extern int peer_maximum_prefix_unset (struct peer *, afi_t, safi_t); extern int peer_clear (struct peer *); -extern int peer_clear_soft (struct peer *, afi_t, safi_t, enum bgp_clear_type); +extern int peer_clear_soft (struct peer *, afi_t, safi_t, bgp_clear_type_t); extern void program_terminate_if_all_disabled(void); +extern int peer_ttl_security_hops_set (struct peer *, int); +extern int peer_ttl_security_hops_unset (struct peer *); #endif /* _QUAGGA_BGPD_H */ |