diff options
author | Chris Hall <chris.hall@highwayman.com> | 2011-08-12 15:06:06 +0100 |
---|---|---|
committer | Chris Hall <chris.hall@highwayman.com> | 2011-08-12 15:06:06 +0100 |
commit | cec1fae79110dffa900c0c5f38c3d3b48f5b0db6 (patch) | |
tree | 408055322e19098b98766168624f1b96865ac73b | |
parent | 228e06bad624a33090da4a09f32f8fed84a7e15c (diff) | |
parent | 7bd8653ef788a6395b07583d6766be8950598342 (diff) | |
download | quagga-ex18p.tar.bz2 quagga-ex18p.tar.xz |
Merge branch 'euro_ix' of /git/quagga.euro-ix into pipeworkex18p
Merge with euro_ix branch v0.99.18ex17.
Update version to: 0.99.18ex18p
Of particular note:
* includes support for GTSM:
neighbor ... ttl-security hops X
no neighbor ... ttl-security hops X
where X is 1-254. For usual case of immediately connected
peer, X == 1.
Cannot set ttl-security while ebgp-multihop is set, and
vice-versa.
If underlying O/S does not support GTSM, then will set ttl
as per ebgp-multihop.
In passing, have fixed various bugs in the main Quagga branch.
* initial support for draft-ietf-idr-optional-transitive
Does not yet support "neighbor-complete" flag.
* main Quagga now uses TCP_CORK and permanent non-blocking
Do not beleive TCP_CORK to be necessary for euro_ix code...
which has a different buffering strategy.
The euro_ix code already runs sockets permanently non-blocking.
* various fixes to attribute intern/unintern
Trying to remove memory leaks. Nobody seems convinced that
this has been perfected, yet.
* fixes for ospfd and ospf6d issues.
Up to date with master branch up to:
commit 538cb284864c17de66152a5236db4cd80e3e7639
Merge: 036a6e6 8ced4e8
Author: Paul Jakma <paul@quagga.net>
Date: Fri Jul 29 18:21:50 2011 +0100
67 files changed, 3347 insertions, 1911 deletions
diff --git a/HACKING.pending b/HACKING.pending index 25ea0bd4..80c8cb45 100644 --- a/HACKING.pending +++ b/HACKING.pending @@ -28,7 +28,11 @@ the list have been stored. Tom Henderson of Boeing has created a repository to work on multi-topology routing support for OSPF. Work on this repository +<<<<<<< HEAD takes place on the branch mtr, which has a branch poing of 0.99.17 +======= +takes place on the branch mtr, which has a branch point of 0.99.17 +>>>>>>> 538cb284864c17de66152a5236db4cd80e3e7639 * posted patches diff --git a/README.NetBSD b/README.NetBSD index 9aac4c35..6bbc680b 100755 --- a/README.NetBSD +++ b/README.NetBSD @@ -20,13 +20,15 @@ PREFIX=/usr/pkg case $1 in build) + # Omitted because it is now default: + # --enable-opaque-lsa ./bootstrap.sh LDFLAGS="-L/usr/pkg/lib -R/usr/pkg/lib" CPPFLAGS="-I/usr/pkg/include" \ ./configure --prefix=${PREFIX} \ --sysconfdir=/etc/zebra --localstatedir=/var/run/zebra \ --enable-exampledir=${PREFIX}/share/examples/zebra \ --enable-pkgsrcrcdir=${PREFIX}/etc/rc.d \ - --enable-opaque-lsa --enable-vtysh + --enable-vtysh ${MAKE} ;; 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 */ diff --git a/configure.ac b/configure.ac index c2670fb7..01dc36bb 100755 --- a/configure.ac +++ b/configure.ac @@ -8,7 +8,7 @@ ## $Id$ AC_PREREQ(2.53) -AC_INIT(Quagga, 0.99.18ex16p, [http://bugzilla.quagga.net]) +AC_INIT(Quagga, 0.99.18ex18p, [http://bugzilla.quagga.net]) AC_CONFIG_SRCDIR(lib/zebra.h) AC_CONFIG_MACRO_DIR([m4]) diff --git a/doc/Makefile.am b/doc/Makefile.am index c3016b2b..cd4d71bd 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -43,7 +43,7 @@ info_TEXINFOS = quagga.texi # us to have a generic automatic .pdf rule to build the figure sources # because it cant just work from the png's directly it seems - contrary # to the documentation... -quagga.pdf: $(info_TEXINFOS) $(figures_pdf) +quagga.pdf: $(info_TEXINFOS) $(figures_pdf) $(quagga_TEXINFOS) $(TEXI2PDF) -o "$@" $< quagga_TEXINFOS = appendix.texi basic.texi bgpd.texi filter.texi install.texi \ diff --git a/doc/texinfo.tex b/doc/texinfo.tex deleted file mode 120000 index 19501744..00000000 --- a/doc/texinfo.tex +++ /dev/null @@ -1 +0,0 @@ -/usr/share/automake-1.11/texinfo.tex
\ No newline at end of file diff --git a/lib/prefix.h b/lib/prefix.h index bc44244c..7944fd69 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -40,8 +40,8 @@ typedef const union sockunion* const_sockunion ; /* IPv4 and IPv6 unified prefix structure. */ struct prefix { - u_char family; - u_char prefixlen; + sa_family_t family; + u_char prefixlen; union { u_char prefix; @@ -61,36 +61,66 @@ struct prefix /* IPv4 prefix structure. */ struct prefix_ipv4 { - u_char family; - u_char prefixlen; + sa_family_t family; + u_char prefixlen; struct in_addr prefix __attribute__ ((aligned (8))); }; +CONFIRM(offsetof(struct prefix_ipv4, family) + == offsetof(struct prefix, family)) ; +CONFIRM(offsetof(struct prefix_ipv4, prefixlen) + == offsetof(struct prefix, prefixlen)) ; +CONFIRM(offsetof(struct prefix_ipv4, prefix) + == offsetof(struct prefix, u.prefix4)) ; +CONFIRM(sizeof(struct prefix_ipv4) <= sizeof(struct prefix)) ; /* IPv6 prefix structure. */ #ifdef HAVE_IPV6 struct prefix_ipv6 { - u_char family; - u_char prefixlen; + sa_family_t family; + u_char prefixlen; struct in6_addr prefix __attribute__ ((aligned (8))); }; +CONFIRM(offsetof(struct prefix_ipv6, family) + == offsetof(struct prefix, family)) ; +CONFIRM(offsetof(struct prefix_ipv6, prefixlen) + == offsetof(struct prefix, prefixlen)) ; +CONFIRM(offsetof(struct prefix_ipv6, prefix) + == offsetof(struct prefix, u.prefix6)) ; +CONFIRM(sizeof(struct prefix_ipv6) <= sizeof(struct prefix)) ; #endif /* HAVE_IPV6 */ struct prefix_ls { - u_char family; - u_char prefixlen; + sa_family_t family; + u_char prefixlen; struct in_addr id __attribute__ ((aligned (8))); struct in_addr adv_router; }; +CONFIRM(offsetof(struct prefix_ls, family) + == offsetof(struct prefix, family)) ; +CONFIRM(offsetof(struct prefix_ls, prefixlen) + == offsetof(struct prefix, prefixlen)) ; +CONFIRM(offsetof(struct prefix_ls, id) + == offsetof(struct prefix, u.lp.id)) ; +CONFIRM(offsetof(struct prefix_ls, adv_router) + == offsetof(struct prefix, u.lp.adv_router)) ; +CONFIRM(sizeof(struct prefix_ls) <= sizeof(struct prefix)) ; /* Prefix for routing distinguisher. */ struct prefix_rd { - u_char family; - u_char prefixlen; - u_char val[8] __attribute__ ((aligned (8))); + sa_family_t family; + u_char prefixlen; + u_char val[8] __attribute__ ((aligned (8))); }; +CONFIRM(offsetof(struct prefix_rd, family) + == offsetof(struct prefix, family)) ; +CONFIRM(offsetof(struct prefix_rd, prefixlen) + == offsetof(struct prefix, prefixlen)) ; +CONFIRM(offsetof(struct prefix_rd, val) + == offsetof(struct prefix, u.val)) ; +CONFIRM(sizeof(struct prefix_rd) <= sizeof(struct prefix)) ; #ifndef INET_ADDRSTRLEN #define INET_ADDRSTRLEN 16 diff --git a/lib/qafi_safi.h b/lib/qafi_safi.h index c93b741e..5559097d 100644 --- a/lib/qafi_safi.h +++ b/lib/qafi_safi.h @@ -139,11 +139,6 @@ typedef struct qAFI_SAFI qAFI_SAFI_t ; typedef struct qAFI_SAFI* qAFI_SAFI ; /*============================================================================== - * POSIX address family type - */ -typedef int pAF_t ; - -/*============================================================================== * Quagga AFI/SAFI values -- original macro definitions */ diff --git a/lib/qfstring.c b/lib/qfstring.c index 9f2e2e9c..eda2aa9a 100644 --- a/lib/qfstring.c +++ b/lib/qfstring.c @@ -20,6 +20,8 @@ */ #include "misc.h" +#include <stdio.h> + #include "qfstring.h" /*============================================================================== @@ -397,7 +399,9 @@ qfs_pointer(qf_str qfs, void* p_val, enum pf_flags flags, * pf_plus -- requires '+' or '-' * pf_space -- requires space or '-' * pf_zeros -- zero fill to width - * pf_alt -- add '0x' or '0X' if hex (no effect on decimal) + * pf_alt -- add '0x' or '0X' if hex -- depending on pf_uc + * add '0' if octal and not zero. + * no effect otherwise * * pf_precision -- explicit precision (needed if precision == 0) * @@ -430,7 +434,11 @@ qfs_pointer(qf_str qfs, void* p_val, enum pf_flags flags, * * * pf_unsigned or sign == 0 takes precedence over pf_plus and pf_space. * - * For hex output, pf_commas groups digits in 4's, separated by '_'. + * For decimal output, pf_commas groups digits in 3's, separated by ','. + * For hex output, pf_commas groups digits in 4's, separated by '_'. + * For oct output, pf_commas is ignored. + * + * Note that pf_commas is a glibc extension, which does not apply to hex ! * * For hex output if precision is: * @@ -521,6 +529,9 @@ qfs_number(qf_str qfs, uintmax_t val, int sign, enum pf_flags flags, if ((flags & pf_precision) || (width <= 0)) flags &= ~pf_zeros ; /* turn off zero fill */ + if (flags & pf_oct) + flags &= ~pf_commas ; /* turn off commas */ + /* Set up any required sign and radix prefix */ if ((flags & pf_unsigned) || (sign == 0)) { @@ -548,15 +559,22 @@ qfs_number(qf_str qfs, uintmax_t val, int sign, enum pf_flags flags, sign_len = 0 ; } ; - if ((flags & (pf_hex | pf_alt)) == (pf_hex | pf_alt)) - { - radix_str = (flags & pf_uc) ? "0X" : "0x" ; - radix_len = 2 ; - } - else + radix_str = "" ; + radix_len = 0 ; + + if (flags & pf_alt) { - radix_str = "" ; - radix_len = 0 ; + if (flags & pf_hex) + { + confirm(pf_uc != 0) ; + radix_str = (flags & pf_uc) ? "0X" : "0x" ; + radix_len = 2 ; + } + else if ((flags & pf_oct) && (val != 0)) + { + radix_str = "0" ; + radix_len = 1 ; + } ; } ; /* Turn off zero fill if left justify (width < 0) */ @@ -574,8 +592,14 @@ qfs_number(qf_str qfs, uintmax_t val, int sign, enum pf_flags flags, } ; /* Start with the basic digit conversion. */ - base = (flags & pf_hex) ? 16 : 10 ; + base = 10 ; + if (flags & pf_hex) + base = 16 ; + else if (flags & pf_oct) + base = 8 ; + digits = (flags & pf_uc) ? uc : lc ; + confirm(pf_uc != 0) ; e = p = num + sizeof(num) ; v = val ; @@ -734,12 +758,15 @@ enum pf_phase pfp_flags, pfp_width, pfp_precision, - pfp_num_type, + pfp_int_type, + pfp_float_type, pfp_done, pfp_failed } ; +CONFIRM(pfp_float_type > pfp_int_type) ; + /* Number types for printing */ enum arg_num_type { @@ -750,7 +777,8 @@ enum arg_num_type ant_long_long, /* ll */ ant_intmax_t, /* j */ ant_size_t, /* z */ - ant_ptr_t, /* %p */ + ant_ptr_t, /* void* */ + ant_long_double, /* L for float */ ant_default = ant_int, }; @@ -759,14 +787,16 @@ static enum pf_phase qfs_arg_string(qf_str qfs, const char* src, enum pf_flags flags, int width, int precision) ; static enum pf_phase qfs_arg_char(qf_str qfs, char ch, enum pf_flags flags, int width, int precision) ; -static enum pf_phase qfs_arg_number(qf_str qfs, va_list* p_va, +static enum pf_phase qfs_arg_integer(qf_str qfs, va_list* p_va, enum pf_flags flags, int width, int precision, enum arg_num_type ant) ; +static enum pf_phase qfs_arg_float(qf_str qfs, va_list* p_va, + const char* start, size_t flen, enum arg_num_type ant) ; /*------------------------------------------------------------------------------ * Formatted print to qf_str -- cf printf() -- appends to the qf_str. * - * This operation is async-signal-safe. Takes into account the offset, and - * adds up any overflow. + * This operation is async-signal-safe -- EXCEPT for floating point values. + * Takes into account the offset, and adds up any overflow. * * Returns: the resulting length of the qf_str. */ @@ -786,8 +816,8 @@ qfs_printf(qf_str qfs, const char* format, ...) /*------------------------------------------------------------------------------ * Formatted print to qf_str -- cf vprintf() -- appends to the qf_str. * - * This operation is async-signal-safe. Takes into account the offset, and - * adds up any overflow + * This operation is async-signal-safe -- EXCEPT for floating point values. + * Takes into account the offset, and adds up any overflow. * * Operates on a copy of the va_list -- so the original is *unchanged*. * @@ -851,6 +881,11 @@ qfs_vprintf(qf_str qfs, const char *format, va_list va) phase = (phase <= pfp_flags) ? pfp_flags : pfp_failed ; break ; + case '#': + flags |= pf_alt ; + phase = (phase <= pfp_flags) ? pfp_flags : pfp_failed ; + break ; + case ' ': flags |= pf_space ; phase = (phase <= pfp_flags) ? pfp_flags : pfp_failed ; @@ -909,13 +944,13 @@ qfs_vprintf(qf_str qfs, const char *format, va_list va) break ; case '.': - phase = (phase <= pfp_precision) ? pfp_precision : pfp_failed; + phase = (phase < pfp_precision) ? pfp_precision : pfp_failed ; flags |= pf_precision ; precision = 0 ; break ; case 'l': /* 1 or 2 'l', not 'h', 'j' or 'z' */ - phase = pfp_num_type ; + phase = (phase <= pfp_int_type) ? pfp_int_type : pfp_failed ; if (ant == ant_default) ant = ant_long ; else if (ant == ant_long) @@ -925,7 +960,7 @@ qfs_vprintf(qf_str qfs, const char *format, va_list va) break ; case 'h': /* 1 or 2 'h', not 'l', 'j' or 'z' */ - phase = pfp_num_type ; + phase = (phase <= pfp_int_type) ? pfp_int_type : pfp_failed ; if (ant == ant_default) ant = ant_short ; else if (ant == ant_short) @@ -935,17 +970,22 @@ qfs_vprintf(qf_str qfs, const char *format, va_list va) break ; case 'j': /* 1 'j', not 'h', 'l' or 'z' */ - phase = (phase <= pfp_num_type) ? pfp_num_type : pfp_failed ; + phase = (phase <= pfp_int_type) ? pfp_int_type : pfp_failed ; ant = ant_intmax_t ; break ; case 'z': /* 1 'z', not 'h', 'l' or 'j' */ - phase = (phase <= pfp_num_type) ? pfp_num_type : pfp_failed ; + phase = (phase <= pfp_int_type) ? pfp_int_type : pfp_failed ; ant = ant_size_t ; break ; + case 'L': /* 1 'L', not for integers ! */ + phase = (phase < pfp_int_type) ? pfp_float_type : pfp_failed ; + ant = ant_long_double ; + break ; + case 's': - if (phase == pfp_num_type) + if (phase == pfp_int_type) phase = pfp_failed ; /* don't do 'l' etc. */ else phase = qfs_arg_string(qfs, va_arg(vac, char*), @@ -953,7 +993,7 @@ qfs_vprintf(qf_str qfs, const char *format, va_list va) break ; case 'c': - if (phase == pfp_num_type) + if (phase == pfp_int_type) phase = pfp_failed ; /* don't do 'l' etc. */ else phase = qfs_arg_char(qfs, (char)va_arg(vac, int), @@ -962,33 +1002,53 @@ qfs_vprintf(qf_str qfs, const char *format, va_list va) case 'd': case 'i': - phase = qfs_arg_number(qfs, &vac, flags, width, precision, + phase = qfs_arg_integer(qfs, &vac, flags, width, precision, ant) ; break ; case 'u': - phase = qfs_arg_number(qfs, &vac, flags | pf_unsigned, width, + phase = qfs_arg_integer(qfs, &vac, flags | pf_unsigned, width, + precision, ant) ; + break ; + + case 'o': + phase = qfs_arg_integer(qfs, &vac, flags | pf_oct, width, precision, ant) ; break ; case 'x': - phase = qfs_arg_number(qfs, &vac, flags | pf_hex_x, width, + phase = qfs_arg_integer(qfs, &vac, flags | pf_hex_x, width, precision, ant) ; break ; case 'X': - phase = qfs_arg_number(qfs, &vac, flags | pf_hex_X, width, + phase = qfs_arg_integer(qfs, &vac, flags | pf_hex_X, width, precision, ant) ; break ; case 'p': - if (phase == pfp_num_type) + if (phase == pfp_int_type) phase = pfp_failed ; else - phase = qfs_arg_number(qfs, &vac, flags | pf_void_p, width, + phase = qfs_arg_integer(qfs, &vac, flags | pf_void_p, width, precision, ant_ptr_t) ; break ; + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + case 'a': + case 'A': + if (phase == pfp_int_type) + phase = pfp_failed ; + else + phase = qfs_arg_float(qfs, &vac, start, format - start, + ant) ; + break ; + default: /* unrecognised format */ phase = pfp_failed ; break ; @@ -1089,14 +1149,16 @@ qfs_arg_char(qf_str qfs, char ch, enum pf_flags flags, int width, int precision) } ; /*------------------------------------------------------------------------------ - * %d, %i, %u, %x, %X and %p handler + * %d, %i, %u, %o, %x, %X and %p handler * - * Accepts: pf_commas -- format with commas + * Accepts: pf_commas -- format with commas or '_' for hex (non-standard) + * ignored for octal. * pf_minus -- left justify (any width will be -ve) * pf_plus -- requires sign * pf_space -- requires space or '-' * pf_zeros -- zero fill to width * pf_alt -- '0x' or '0X' for hex + * '0' for octal * * pf_precision -- precision specified * @@ -1107,16 +1169,24 @@ qfs_arg_char(qf_str qfs, char ch, enum pf_flags flags, int width, int precision) * * and: all the number argument types. * + * Rejects: ant == ant_long_double -- which is how the parser spots an + * erroneous %Ld for example. + * * This operation is async-signal-safe. Takes into account the offset, and * adds up any overflow */ static enum pf_phase -qfs_arg_number(qf_str qfs, va_list* p_va, enum pf_flags flags, +qfs_arg_integer(qf_str qfs, va_list* p_va, enum pf_flags flags, int width, int precision, enum arg_num_type ant) { uintmax_t u_val ; intmax_t s_val ; + /* Reject if seen an 'L' + */ + if (ant == ant_long_double) + return pfp_failed ; + /* Special for hex with '0... if no explicit precision, set -1 for byte * and -2 for everything else -- see qfs_number(). */ @@ -1208,3 +1278,99 @@ qfs_arg_number(qf_str qfs, va_list* p_va, enum pf_flags flags, return pfp_done ; } ; +/*------------------------------------------------------------------------------ + * %e, %E, %f, %F, %g, %G, %a and %A handler + * + * This uses the standard library sprintf() to do the business, so this is + * NOT async-signal-safe. This means that we get the full precision supported + * by the system ! Attempting to construct async-signal-safe conversion is + * doomed to failure, because any floating point operation may affect flags + * and other state in the processor :-( + * + * This operation is *NOT* async-signal-safe. Takes into account the offset, + * and adds up any overflow + */ + +union float_value +{ + double d ; + long double ld ; +} ; + +static int +qfs_arg_float_snprintf(void* buf, int have, const char* format, + union float_value* p_val, enum arg_num_type ant) +{ + if (ant == ant_default) + return snprintf(buf, have, format, p_val->d) ; + else + return snprintf(buf, have, format, p_val->ld) ; +} ; + +static enum pf_phase +qfs_arg_float(qf_str qfs, va_list* p_va, const char* start, size_t flen, + enum arg_num_type ant) +{ + union float_value val ; + char format[flen + 1] ; + int want ; + + if (ant == ant_default) + val.d = va_arg(*p_va, double) ; + else + val.ld = va_arg(*p_va, long double) ; + + memcpy(format, start, flen) ; + format[flen + 1] = '\0' ; + + if (qfs->offset == 0) + { + /* No offset, so can use the qfs directly. + */ + int have ; + + have = qfs_left(qfs) ; + want = qfs_arg_float_snprintf(qfs->ptr, have + 1, format, &val, ant) ; + + if (want > 0) + { + if (want <= have) + qfs->ptr += want ; + else + { + qfs->ptr = qfs->end ; + qfs->overflow += (want - have) ; + } ; + } ; + } + else + { + /* Because the offset is not zero, need to use an intermediate + * buffer and then copy part after the offset. + * + * First, discover full extent of the formatted value, then if that + * exceeds the offset, construct buffer and copy what we can to the + * qps; otherwise, reduce the offset. + */ + want = qfs_arg_float_snprintf(NULL, 0, format, &val, ant) ; + + if (want > 0) + { + int take ; + + take = qfs->offset + qfs_left(qfs) ; + if (take > want) + take = want ; + + { + char tmp[take + 1] ; + want = qfs_arg_float_snprintf(tmp, take + 1, format, &val, ant) ; + + if (want > 0) + qfs_append_n(qfs, tmp, want) ; + } ; + } ; + } ; + + return (want >= 0) ? pfp_done : pfp_failed ; +} ; diff --git a/lib/qfstring.h b/lib/qfstring.h index 03204c3d..be858899 100644 --- a/lib/qfstring.h +++ b/lib/qfstring.h @@ -26,11 +26,15 @@ #include "vargs.h" /*============================================================================== - * These "qfstrings" address the issues of dealing with *fixed* length + * These "qfstring" address the issues of dealing with *fixed* length * strings, particularly where the string handling must be async-signal-safe. * * Are also used to support snprintf() style printing, but to one or more * fixed length buffers. + * + * All operations that can possibly be async-signal-safe, are. Notable + * exception is anything involving floating point values -- because of the + * state contain in floating point status/option registers ! */ /* When initialised a qf_string is set: @@ -75,8 +79,9 @@ enum pf_flags pf_precision = BIT( 7), /* '.' seen */ /* The following signal how to render the value */ - pf_hex = BIT( 8), /* hex */ - pf_uc = BIT( 9), /* upper-case */ + pf_oct = BIT( 8), /* octal */ + pf_hex = BIT( 9), /* hex */ + pf_uc = BIT(10), /* upper-case */ /* The following signal the type of value */ pf_ptr = BIT(14), /* is a pointer */ @@ -38,6 +38,7 @@ #include <lib/version.h> #include "memory.h" #include "sockunion.h" +#include "sockopt.h" #include "smux.h" #define min(A,B) ((A) < (B) ? (A) : (B)) @@ -215,8 +216,8 @@ smux_socket (void) sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sock < 0) continue; - sockopt_reuseaddr (sock); - sockopt_reuseport (sock); + setsockopt_reuseaddr (sock); + setsockopt_reuseport (sock); ret = connect (sock, res->ai_addr, res->ai_addrlen); if (ret < 0) { @@ -251,8 +252,8 @@ smux_socket (void) serv.sin_addr.s_addr = htonl (INADDR_LOOPBACK); - sockopt_reuseaddr (sock); - sockopt_reuseport (sock); + setsockopt_reuseaddr (sock); + setsockopt_reuseport (sock); ret = connect (sock, (struct sockaddr *) &serv, sizeof (struct sockaddr_in)); if (ret < 0) diff --git a/lib/sockopt.c b/lib/sockopt.c index aa747429..083cafc2 100644 --- a/lib/sockopt.c +++ b/lib/sockopt.c @@ -1,4 +1,4 @@ -/* setsockopt functions +/* Setting and getting socket options -- utility functions. * Copyright (C) 1999 Kunihiro Ishiguro * * This file is part of GNU Zebra. @@ -20,218 +20,585 @@ */ #include <zebra.h> + #include "log.h" #include "sockopt.h" #include "sockunion.h" #include "pthread_safe.h" -int -setsockopt_so_recvbuf (int sock_fd, int size) +/*------------------------------------------------------------------------------ + * Set socket SO_REUSEADDR option + * + * Returns: >= 0 => OK + * < 0 => failed -- see errno + * + * Logs a LOG_WARNING message if fails. + */ +extern int +setsockopt_reuseaddr (int sock_fd) { int ret; + int on = 1; - ret = setsockopt (sock_fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) ; + ret = setsockopt (sock_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); if (ret < 0) { int err = errno ; - zlog_err ("socket %d: cannot setsockopt SO_RCVBUF to %d: %s", - sock_fd, size, errtoa(err, 0).str) ; + zlog_warn ("cannot set sockopt SO_REUSEADDR on socket %d: %s", sock_fd, + errtoa(err, 0).str) ; errno = err ; } ; - return ret; + return ret ; } -int -setsockopt_so_sendbuf (const int sock_fd, int size) +/*------------------------------------------------------------------------------ + * Set socket SO_REUSEPORT option -- if it is locally supported. + * + * Returns: >= 0 => OK -- or not supported + * < 0 => failed -- see errno + * + * Logs a LOG_WARNING message if fails. + */ +extern int +setsockopt_reuseport (int sock_fd) { - int ret ; + int ret; - ret = setsockopt (sock_fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)); +#ifdef SO_REUSEPORT + int on = 1; + ret = setsockopt (sock_fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)); +#else + ret = 0 ; +#endif if (ret < 0) { int err = errno ; - zlog_err ("socket %d: cannot setsockopt SO_SNDBUF to %d: %s", - sock_fd, size, errtoa(err, 0).str) ; + zlog_warn ("cannot set sockopt SO_REUSEPORT on socket %d: %s", sock_fd, + errtoa(err, 0).str) ; errno = err ; } ; - return ret; + return ret ; +} ; + +/*------------------------------------------------------------------------------ + * Set socket SO_BROADCAST option + * + * Returns: >= 0 => OK + * < 0 => failed -- see errno + * + * Logs a LOG_WARNING message if fails. + */ +extern int +setsockopt_broadcast (int sock_fd) +{ + int ret; + int on = 1; + + ret = setsockopt (sock_fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)); + if (ret < 0) + { + int err = errno ; + zlog_warn ("cannot set sockopt SO_BROADCAST on socket %d: %s", sock_fd, + errtoa(err, 0).str) ; + errno = err ; + } + return ret ; } -int -getsockopt_so_sendbuf (const int sock_fd) +/*------------------------------------------------------------------------------ + * Set TCP_CORK, if available. + * + * Returns: >= 0 => OK -- or not supported + * < 0 => failed -- see errno + * + * Logs a LOG_WARNING message if fails. + */ +extern int +setsockopt_cork (int sock_fd, int onoff) { - u_int32_t optval; - socklen_t optlen = sizeof (optval); +#ifdef TCP_CORK + int ret; - int ret = getsockopt (sock_fd, SOL_SOCKET, SO_SNDBUF, &optval, &optlen); + ret = setsockopt (sock_fd, IPPROTO_TCP, TCP_CORK, &onoff, sizeof(onoff)); if (ret < 0) - { - int err = errno ; - zlog_err ("socket %d: cannot getsockopt SO_SNDBUF: %s", + { + int err = errno ; + zlog_warn ("cannot set sockopt TCP_CORK to %d on socket %d: %s", onoff, sock_fd, errtoa(err, 0).str) ; - errno = err ; - return ret; - } - - return optval; + errno = err ; + } + return ret ; +#else + return 0; +#endif } -static void * -getsockopt_cmsg_data (struct msghdr *msgh, int level, int type) +/*------------------------------------------------------------------------------ + * Set IP_TTL/IPV6_UNICAST_HOPS for socket, if available + * + * The ttl given is the maximum number of hops to allow -- so will generally + * be 1 or 255 or some small number. + * + * NB: This code treats any ttl outside the range 1..MAXTTL as MAXTTL. + * + * Returns: >= 0 => OK -- or not supported + * < 0 => failed, see errno. + * + * Logs a LOG_WARNING message if fails. + * + * NB: for AF_INET6 where have: IN6_IS_ADDR_V4MAPPED, there is the question + * of whether to use IPPROTO_IP/IP_TTL or IPPROTO_IPV6/IPV6_UNICAST_HOPS. + * + * Here we try first to use the socket family, and if that fails on + * AF_INET6, then if the protocol family is AF_INET, then tries that. + */ +extern int +setsockopt_ttl (int sock_fd, int ttl) { - struct cmsghdr *cmsg; - void *ptr = NULL; + const char* name ; + int af ; + int ret ; - for (cmsg = ZCMSG_FIRSTHDR(msgh); - cmsg != NULL; - cmsg = CMSG_NXTHDR(msgh, cmsg)) - if (cmsg->cmsg_level == level && cmsg->cmsg_type) - return (ptr = CMSG_DATA(cmsg)); + af = sockunion_getsockfamily(sock_fd) ; + if (af < 0) + return af ; - return NULL; -} + if ((ttl < 1) || (ttl > MAXTTL)) + ttl = MAXTTL ; -#ifdef HAVE_IPV6 -/* Set IPv6 packet info to the socket. */ -int -setsockopt_ipv6_pktinfo (int sock_fd, int val) -{ - int ret; + ret = 0 ; + name = NULL ; -#ifdef IPV6_RECVPKTINFO /*2292bis-01*/ - ret = setsockopt(sock_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, sizeof(val)); - if (ret < 0) + while (1) { - int err = errno ; - zlog_warn ("cannot setsockopt IPV6_RECVPKTINFO: %s", errtoa(err, 0).str); - errno = err ; + switch (af) + { + case AF_INET: +#ifdef IP_TTL + ret = setsockopt (sock_fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)); + name = "IP_TTL" ; +#endif /* IP_TTL */ + break ; + +#ifdef HAVE_IPV6 + case AF_INET6: + ret = setsockopt (sock_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, + &ttl, sizeof(ttl)); + name = "IPV6_UNICAST_HOPS" ; + + if (ret < 0) + { + af = sockunion_getprotofamily(sock_fd) ; + if (af == AF_INET) + continue ; + } ; + break ; +#endif + + default: /* ignore unknown family */ + break ; + } ; + + break ; } ; -#else /*RFC2292*/ - ret = setsockopt(sock_fd, IPPROTO_IPV6, IPV6_PKTINFO, &val, sizeof(val)); + if (ret < 0) { int err = errno ; - zlog_warn ("cannot setsockopt IPV6_PKTINFO: %s", errtoa(err, 0).str); + zlog_warn("cannot set sockopt %s to %d on socket %d: %s", name, ttl, + sock_fd, errtoa(err, 0).str) ; errno = err ; } ; -#endif /* INIA_IPV6 */ - return ret; -} -/* Set multicast hops val to the socket. */ -int -setsockopt_ipv6_checksum (int sock_fd, int val) + return ret ; +} ; + +/*------------------------------------------------------------------------------ + * Set IP_MINTTL/IPV6_MINHOPCOUNT (GTSM), if available. + * + * The ttl given is the maximum number of hops to allow -- so will generally + * be 1 -- which is the same as IP_TTL/IPV6_UNICAST_HOPS. + * + * NB: to turn off GTSM, need to set ttl = MAXTTL. This code treats any ttl + * outside the range 1..MAXTTL as MAXTTL. + * + * The underlying mechanics want MAX_TTL - (ttl - 1) -- and may not + * accept a value of zero. + * + * Returns: >= 0 => OK + * < 0 => failed or not supported -- see errno + * EOPNOTSUPP if not supported + * + * Logs a LOG_WARNING message if fails (and is supported). + * + * NB: for AF_INET6 where have: IN6_IS_ADDR_V4MAPPED, there is the question + * of whether to use IPPROTO_IP/IP_TTL or IPPROTO_IPV6/IPV6_UNICAST_HOPS. + * + * Here we try first to use the socket family, and if that fails on + * AF_INET6, then if the protocol family is AF_INET, then tries that. + */ +extern int +setsockopt_minttl (int sock_fd, int ttl) { - int ret; + const char* name ; + int af ; + int minttl ; + int ret ; -#ifdef GNU_LINUX - ret = setsockopt(sock_fd, IPPROTO_RAW, IPV6_CHECKSUM, &val, sizeof(val)); + af = sockunion_getsockfamily(sock_fd) ; + if (af < 0) + return af ; + + ret = 0 ; + name = NULL ; + + if ((ttl < 1) || (ttl > MAXTTL)) + ttl = MAXTTL ; + + minttl = MAXTTL - (ttl - 1) ; + + while (1) + { + enum + { +#ifdef IP_MINTTL + have_ip_minttl = true, #else - ret = setsockopt(sock_fd, IPPROTO_IPV6, IPV6_CHECKSUM, &val, sizeof(val)); -#endif /* GNU_LINUX */ + have_ip_minttl = false, +#endif + ip_minttl = IP_MINTTL + 0 + } ; + +#ifdef HAVE_IPV6 + +# ifdef GNU_LINUX + /* The #include to bring in IPV6_MINHOPCOUNT is buried more or less as + * deep as we can get it, because it also redefines a number of things + * that we do not want redefined. + */ + #include <linux/in6.h> +# endif + + enum + { +# ifdef IPV6_MINHOPCOUNT + have_ipv6_minhopcount = true, +# else + have_ipv6_minhopcount = false, +# endif + ipv6_minhopcount = IPV6_MINHOPCOUNT + 0 + } ; +#endif /* HAVE_IPV6 */ + + switch (af) + { + case AF_INET: + name = "IP_MINTTL" ; + if (have_ip_minttl) + ret = setsockopt (sock_fd, IPPROTO_IP, ip_minttl, + &minttl, sizeof(minttl)); + else + { + ret = -1 ; + errno = EOPNOTSUPP ; + } ; + + break ; + +#ifdef HAVE_IPV6 + case AF_INET6: + name = "IPV6_MINHOPCOUNT" ; + if (have_ipv6_minhopcount) + ret = setsockopt (sock_fd, IPPROTO_IPV6, ipv6_minhopcount, + &minttl, sizeof(minttl)); + else + { + ret = -1 ; + errno = EOPNOTSUPP ; + } ; + + if (ret < 0) + { + af = sockunion_getprotofamily(sock_fd) ; + if (af == AF_INET) + continue ; + } ; + + break ; +#endif /* HAVE_IPV6 */ + + default: /* ignore unknown family */ + break ; + } ; + + break ; + } ; + if (ret < 0) { int err = errno ; - zlog_warn ("cannot setsockopt IPV6_CHECKSUM: %s", errtoa(err, 0).str); + zlog_warn("cannot set sockopt %s to %d on socket %d: %s", name, minttl, + sock_fd, errtoa(err, 0).str) ; errno = err ; } ; - return ret; -} -/* Set multicast hops val to the socket. */ -int -setsockopt_ipv6_multicast_hops (int sock_fd, int val) + return ret ; +} ; + +/*------------------------------------------------------------------------------ + * Set TCP MD5 signature socket option, if available. + * + * A NULL password or an empty password both signify unsetting the MD5 + * signature. + * + * Returns: >= 0 => OK (or not supported, but password NULL or empty) + * < 0 => failed or not supported, see errno. + * + * NB: returns EOPNOTSUPP if TCP MD5 is not supported and password is not NULL + * and is not empty. + * + * Logs a LOG_ERR message if fails (and is supported). + */ +extern int +setsockopt_tcp_signature (int sock_fd, sockunion su, const char *password) { - int ret; + int ret ; - ret = setsockopt(sock_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, - sizeof(val)); + if ((password != NULL) && (*password == '\0')) + password = NULL ; + + ret = 0 ; /* so far, so good */ + +#if defined(HAVE_TCP_MD5_LINUX24) && defined(GNU_LINUX) + /* Support for the old Linux 2.4 TCP-MD5 patch, taken from Hasso Tepper's + * version of the Quagga patch (based on work by Rick Payne, and Bruce + * Simpson) + */ +#define TCP_MD5_AUTH 13 +#define TCP_MD5_AUTH_ADD 1 +#define TCP_MD5_AUTH_DEL 2 + struct tcp_rfc2385_cmd { + u_int8_t command; /* Command - Add/Delete */ + u_int32_t address; /* IPV4 address associated */ + u_int8_t keylen; /* MD5 Key len (do NOT assume 0 terminated ascii) */ + void *key; /* MD5 Key */ + } cmd; + struct in_addr *addr = &su->sin.sin_addr; + + cmd.command = (password != NULL ? TCP_MD5_AUTH_ADD : TCP_MD5_AUTH_DEL); + cmd.address = addr->s_addr; + cmd.keylen = (password != NULL ? strlen (password) : 0); + cmd.key = password; + + ret = setsockopt (sock_fd, IPPROTO_TCP, TCP_MD5_AUTH, &cmd, sizeof cmd) ; if (ret < 0) { int err = errno ; - zlog_warn ("cannot setsockopt IPV6_MULTICAST_HOPS: %s", - errtoa(err, 0).str); + zlog_err("sockopt_tcp_signature: setsockopt(%d): %s", + sock_fd, errtoa(err, 0).str) ; errno = err ; + } + +#elif HAVE_DECL_TCP_MD5SIG + +# ifdef GNU_LINUX + struct tcp_md5sig md5sig ; +# ifdef HAVE_IPV6 + int saf ; + union sockunion sux[1] ; +# endif + + /* Testing reveals that in order to set an MD5 password for an AF_INET6 + * socket, the address passed in must be AF_INET6, even if what we are + * dealing with here is an IN6_IS_ADDR_V4MAPPED socket. + */ +# ifdef HAVE_IPV6 + saf = sockunion_getsockfamily(sock_fd) ; + if (saf < 0) + return saf ; + + if ((saf == AF_INET6) && (sockunion_family(su) == AF_INET)) + { + sockunion_copy (sux, su) ; + sockunion_map_ipv4 (sux) ; + su = sux ; /* substitute v4 mapped address */ } ; - return ret; -} +# endif -/* Set multicast hops val to the socket. */ -int -setsockopt_ipv6_unicast_hops (int sock_fd, int val) -{ - int ret; + /* Set address to AF_UNSPEC and key length and everything else to zero, + * then copy in the address and the key. + */ + memset (&md5sig, 0, sizeof (md5sig)) ; + confirm(AF_UNSPEC == 0) ; - ret = setsockopt(sock_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val)); - if (ret < 0) + memcpy (&md5sig.tcpm_addr, &su->sa, sockunion_get_len(su)) ; + + if (password != NULL) { - int err = errno ; - zlog_warn("cannot setsockopt IPV6_UNICAST_HOPS: %s", errtoa(err, 0).str); - errno = err ; + size_t keylen = strlen(password) ; + + if (md5sig.tcpm_keylen <= TCP_MD5SIG_MAXKEYLEN) + { + md5sig.tcpm_keylen = keylen ; + memcpy (md5sig.tcpm_key, password, keylen); + } + else + { + errno = EINVAL ; /* manufactured error */ + ret = -1 ; + } ; } ; - return ret; -} -int -setsockopt_ipv6_hoplimit (int sock_fd, int val) -{ - int ret; +# else + /* + * XXX Need to do PF_KEY operation here to add/remove an SA entry, + * and add/remove an SP entry for this peer's packet flows also. + */ + int md5sig = (password != NULL) ? 1 : 0; + +# endif /* GNU_LINUX */ + + if (ret >= 0) + { + ret = setsockopt(sock_fd, IPPROTO_TCP, TCP_MD5SIG, + &md5sig, sizeof(md5sig)) ; + if (ret < 0) + /* ENOENT is harmless. It is returned when we clear a password where + * one was not previously set. + */ + if ((errno == ENOENT) && (password == NULL)) + ret = 0 ; + } ; + +#else + + /* TCP MD5 is not supported */ + + if (password != NULL) + { + errno = EOPNOTSUPP ; /* manufactured error */ + ret = -1 ; + } ; + +#endif /* !HAVE_TCP_MD5SIG */ -#ifdef IPV6_RECVHOPLIMIT /*2292bis-01*/ - ret = setsockopt (sock_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &val, - sizeof(val)); if (ret < 0) { int err = errno ; - zlog_warn("cannot setsockopt IPV6_RECVHOPLIMIT: %s", errtoa(err, 0).str); + zlog_err("sockopt_tcp_signature: setsockopt(%d): %s", + sock_fd, errtoa(err, 0).str) ; errno = err ; } ; -#else /*RFC2292*/ - ret = setsockopt (sock_fd, IPPROTO_IPV6, IPV6_HOPLIMIT, &val, sizeof(val)); + + return ret ; +} ; + +/*------------------------------------------------------------------------------ + * Set SO_RCVBUF option on socket. + * + * Returns: >= 0 => OK + * < 0 => failed, see errno. + * + * Logs a LOG_ERR message if fails + */ +extern int +setsockopt_so_recvbuf (int sock_fd, int size) +{ + int ret; + + ret = setsockopt (sock_fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) ; if (ret < 0) { int err = errno ; - zlog_warn ("cannot setsockopt IPV6_HOPLIMIT: %s", errtoa(err, 0).str); + zlog_err ("cannot set sockopt SO_RCVBUF to %d on socket %d: %s", + size, sock_fd, errtoa(err, 0).str) ; errno = err ; } ; -#endif + return ret; } -/* Set multicast loop zero to the socket. */ -int -setsockopt_ipv6_multicast_loop (int sock_fd, int val) +/*------------------------------------------------------------------------------ + * Set SO_SNDBUF option on socket. + * + * Returns: >= 0 => OK + * < 0 => failed, see errno. + * + * Logs a LOG_ERR message if fails + */ +extern int +setsockopt_so_sendbuf (int sock_fd, int size) { - int ret; + int ret ; + + ret = setsockopt (sock_fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)); - ret = setsockopt (sock_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, - sizeof (val)); if (ret < 0) { int err = errno ; - zlog_warn ("cannot setsockopt IPV6_MULTICAST_LOOP: %s", - errtoa(err, 0).str); + zlog_err("cannot set sockopt SO_SNDBUF to %d on socket %d: %s", + size, sock_fd, errtoa(err, 0).str) ; errno = err ; } ; + return ret; } -static int -getsockopt_ipv6_ifindex (struct msghdr *msgh) +/*------------------------------------------------------------------------------ + * Get SO_SNDBUF option value from socket. + * + * Returns: >= 0 => OK == value of option + * < 0 => failed, see errno. + * + * Logs a LOG_ERR message if fails + */ +extern int +getsockopt_so_sendbuf (int sock_fd) { - struct in6_pktinfo *pktinfo; + u_int32_t optval; + socklen_t optlen = sizeof (optval); - pktinfo = getsockopt_cmsg_data (msgh, IPPROTO_IPV6, IPV6_PKTINFO); + int ret = getsockopt (sock_fd, SOL_SOCKET, SO_SNDBUF, &optval, &optlen); + if (ret < 0) + { + int err = errno ; + zlog_err ("cannot get sockopt SO_SNDBUF on socket %d: %s", + sock_fd, errtoa(err, 0).str) ; + errno = err ; + return ret; + } - return pktinfo->ipi6_ifindex; + return optval; } -#endif /* HAVE_IPV6 */ +/*------------------------------------------------------------------------------ + * Set IP_TOS option for AF_INET socket. + * + * Returns: >= 0 => OK + * < 0 => failed, see errno. + * + * Logs a LOG_ERR message if fails + */ +extern int +setsockopt_ipv4_tos(int sock_fd, int tos) +{ + int ret; -/* + ret = setsockopt (sock_fd, IPPROTO_IP, IP_TOS, &tos, sizeof (tos)); + if (ret < 0) + { + int err = errno ; + zlog_err("cannot set sockopt IP_TOS option %#x on socket %d: %s", + tos, sock_fd, errtoa(err, 0).str) ; + errno = err ; + } ; + return ret; +} ; + +/*------------------------------------------------------------------------------ * Process multicast socket options for IPv4 in an OS-dependent manner. * Supported options are IP_MULTICAST_IF and IP_{ADD,DROP}_MEMBERSHIP. * @@ -252,7 +619,7 @@ getsockopt_ipv6_ifindex (struct msghdr *msgh) * but this behavior should not be harmful if they behave the same way, * allow leaves, or implicitly leave all groups joined to down interfaces. */ -int +extern int setsockopt_multicast_ipv4(int sock_fd, int optname, struct in_addr if_addr /* required */, @@ -365,27 +732,83 @@ setsockopt_multicast_ipv4(int sock_fd, } +/*============================================================================== + * Set pktinfo and get ifindex etc + */ + +static int setsockopt_ipv4_pktinfo (int sock_fd, int val) ; +static int getsockopt_ipv4_ifindex (struct msghdr *msgh) ; + +#ifdef HAVE_IPV6 +static int getsockopt_ipv6_ifindex (struct msghdr *msgh) ; +#endif + +static void * getsockopt_cmsg_data (struct msghdr *msgh, int level, int type) ; + +/*------------------------------------------------------------------------------ + * Set IP_PKTINFO/IP_RECVIF or IPV6_RECVPKTINFO/IPV6_PKTINFO -- if available. + * + * Returns: >= 0 => OK + * < 0 => failed -- see errno + * + * Logs a LOG_ERR message if fails. + */ +extern int +setsockopt_pktinfo (int af, int sock_fd, int val) +{ + int ret = -1; + + switch (af) + { + case AF_INET: + ret = setsockopt_ipv4_pktinfo (sock_fd, val); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + ret = setsockopt_ipv6_pktinfo (sock_fd, val); + break; +#endif + default: + zlog_warn("setsockopt_ifindex: unknown address family %d", af) ; + ret = -1 ; + errno = EINVAL; + break ; + } + return ret; +} + +/*------------------------------------------------------------------------------ + * Set IP_PKTINFO or IP_RECVIF -- if available. + * + * Returns: >= 0 => OK + * < 0 => failed -- see errno + * + * Logs a LOG_ERR message if fails. + */ static int -setsockopt_ipv4_ifindex (int sock_fd, int val) +setsockopt_ipv4_pktinfo (int sock_fd, int val) { int ret; -#if defined (IP_PKTINFO) - ret = setsockopt (sock_fd, IPPROTO_IP, IP_PKTINFO, &val, sizeof (val)) ; - if (ret < 0) - { - int err = errno ; - zlog_warn ("Can't set IP_PKTINFO option for fd %d to %d: %s", - sock_fd, val, errtoa(err, 0).str); - errno = err ; - } ; -#elif defined (IP_RECVIF) - ret = setsockopt (sock_fd, IPPROTO_IP, IP_RECVIF, &val, sizeof (val)) ; +#if defined(IP_PKTINFO) || defined(IP_RECVIF) + + int opt ; + const char* name ; + +# if defined(IP_PKTINFO) + opt = IP_PKTINFO ; + name = "IP_PKTINFO" ; +# else + opt = IP_RECVIF ; + name = "IP_RECVIF" ; +# endif + + ret = setsockopt (sock_fd, IPPROTO_IP, opt, &val, sizeof (val)) ; if (ret < 0) { int err = errno ; - zlog_warn ("Can't set IP_RECVIF option for fd %d to %d: %s", - sock_fd, val, errtoa(err, 0).str); + zlog_err("cannot set sockopt %s to %d on socket %d: %s", name, + val, sock_fd, errtoa(err, 0).str); errno = err ; } ; #else @@ -393,53 +816,82 @@ setsockopt_ipv4_ifindex (int sock_fd, int val) #warning "Will not be able to receive link info." #warning "Things might be seriously broken.." /* XXX Does this ever happen? Should there be a zlog_warn message here? */ - ret = -1; + ret = -1; + errno = EOPNOTSUPP ; /* manufactured error */ #endif return ret; } -int -setsockopt_ipv4_tos(int sock_fd, int tos) +/*------------------------------------------------------------------------------ + * Set IPV6_RECVPKTINFO (RFC3542) or IPV6_RECVIF (RFC2292) -- if available. + * + * Returns: >= 0 => OK + * < 0 => failed -- see errno + * + * Logs a LOG_ERR message if fails. + */ +#ifdef HAVE_IPV6 +extern int +setsockopt_ipv6_pktinfo (int sock_fd, int val) { int ret; + int opt ; + const char* name ; - ret = setsockopt (sock_fd, IPPROTO_IP, IP_TOS, &tos, sizeof (tos)); +# ifdef IPV6_RECVPKTINFO + opt = IPV6_RECVPKTINFO ; /* RFC3542 == RFC2292-bis */ + name = "IPV6_RECVPKTINFO" ; +# else + opt = IPV6_PKTINFO ; /* RFC2292 */ + name = "IPV6_PKTINFO" ; +# endif + + ret = setsockopt(sock_fd, IPPROTO_IPV6, opt, &val, sizeof(val)); if (ret < 0) { int err = errno ; - zlog_warn ("Can't set IP_TOS option for fd %d to %#x: %s", - sock_fd, tos, errtoa(err, 0).str); + zlog_err("cannot set sockopt %s to %d on socket %d: %s", name, + val, sock_fd, errtoa(err, 0).str); errno = err ; } ; + return ret; } +#endif - -int -setsockopt_ifindex (int af, int sock_fd, int val) +/*------------------------------------------------------------------------------ + * Given a struct msghdr*, extract and return ifindex. + * + * Returns: > 0 => OK == ifindex + * 0 => none found or error + * + * Note: this is badly named, since it is not really a getsockopt() operation, + * but extracting data from a sendmsg/recvmsg struct msghdr. + * + * To have the ifindex returned, need to have setsockopt_pktinfo(). + */ +extern int +getsockopt_ifindex (int af, struct msghdr *msgh) { - int ret = -1; - switch (af) { case AF_INET: - ret = setsockopt_ipv4_ifindex (sock_fd, val); - break; + return (getsockopt_ipv4_ifindex (msgh)); + #ifdef HAVE_IPV6 case AF_INET6: - ret = setsockopt_ipv6_pktinfo (sock_fd, val); - break; + return (getsockopt_ipv6_ifindex (msgh)); #endif + default: - zlog_warn ("setsockopt_ifindex: unknown address family %d", af); - ret = -1 ; - errno = EINVAL; - break ; + zlog_warn ("getsockopt_ifindex: unknown address family %d", af); + return 0 ; } - return ret; } -/* +/*------------------------------------------------------------------------------ + * AF_INET: extract ifindex from struct msghdr, if can + * * Requires: msgh is not NULL and points to a valid struct msghdr, which * may or may not have control data about the incoming interface. * @@ -449,17 +901,17 @@ setsockopt_ifindex (int af, int sock_fd, int val) static int getsockopt_ipv4_ifindex (struct msghdr *msgh) { - /* XXX: initialize to zero? (Always overwritten, so just cosmetic.) */ - int ifindex = -1; + int ifindex ; #if defined(IP_PKTINFO) /* Linux pktinfo based ifindex retrieval */ struct in_pktinfo *pktinfo; - pktinfo = - (struct in_pktinfo *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_PKTINFO); - /* XXX Can pktinfo be NULL? Clean up post 0.98. */ - ifindex = pktinfo->ipi_ifindex; + pktinfo = getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_PKTINFO); + if (pktinfo != NULL) + ifindex = pktinfo->ipi_ifindex ; + else + ifindex = 0 ; #elif defined(IP_RECVIF) @@ -510,34 +962,73 @@ getsockopt_ipv4_ifindex (struct msghdr *msgh) return ifindex; } -/* return ifindex, 0 if none found */ -int -getsockopt_ifindex (int af, struct msghdr *msgh) +/*------------------------------------------------------------------------------ + * AF_INET6: extract ifindex from struct msghdr, if can + * + * Requires: msgh is not NULL and points to a valid struct msghdr, which + * may or may not have control data about the incoming interface. + * + * Returns the interface index (small integer >= 1) if it can be + * determined, or else 0. + */ +#ifdef HAVE_IPV6 +static int +getsockopt_ipv6_ifindex (struct msghdr *msgh) { - int ifindex = 0; + struct in6_pktinfo *pktinfo; - switch (af) - { - case AF_INET: - return (getsockopt_ipv4_ifindex (msgh)); - break; -#ifdef HAVE_IPV6 - case AF_INET6: - return (getsockopt_ipv6_ifindex (msgh)); - break; + pktinfo = getsockopt_cmsg_data (msgh, IPPROTO_IPV6, IPV6_PKTINFO); + + if (pktinfo != NULL) + return pktinfo->ipi6_ifindex; + else + return 0 ; +} #endif - default: - zlog_warn ("getsockopt_ifindex: unknown address family %d", af); - return (ifindex = 0); - } + +/*------------------------------------------------------------------------------ + * Scan msg_control portion of struct msghdr, looking for a cmsg with the given + * level and type. + * + * Requires: msgh is not NULL and points to a valid struct msghdr, which + * may or may not have control data about the incoming interface. + * + * Returns: address of data part of cmsg + * or: NULL => not found + */ +static void * +getsockopt_cmsg_data (struct msghdr *msgh, int level, int type) +{ + struct cmsghdr *cmsg; + + for (cmsg = ZCMSG_FIRSTHDR(msgh); + cmsg != NULL; + cmsg = CMSG_NXTHDR(msgh, cmsg)) + if ((cmsg->cmsg_level == level) && (cmsg->cmsg_type == type)) + return (void*)CMSG_DATA(cmsg); + + return NULL; } -/* swab iph between order system uses for IP_HDRINCL and host order */ -void +/*------------------------------------------------------------------------------ + * swab iph between order system uses for IP_HDRINCL and host order + * + * This is done before handing struct ip to the system. + * + * There are four u_short fields in the IPv4 header: + * + * u_short ip_len -- convert to network order, except as noted below + * u_short ip_id -- convert to network order + * u_short ip_off -- convert to network order, except as noted below + * u_short ip_sum -- which we don't touch -- set by kernel + */ +extern void sockopt_iphdrincl_swab_htosys (struct ip *iph) { - /* BSD and derived take iph in network order, except for - * ip_len and ip_off + /* BSD and derived take iph in network order, except for ip_len and ip_off. + * + * So if *not* BSD-like, then need to convert ip_len and ip_off to network + * order. */ #ifndef HAVE_IP_HDRINCL_BSD_ORDER iph->ip_len = htons(iph->ip_len); @@ -547,7 +1038,12 @@ sockopt_iphdrincl_swab_htosys (struct ip *iph) iph->ip_id = htons(iph->ip_id); } -void +/*------------------------------------------------------------------------------ + * swab iph between order system uses for IP_HDRINCL and host order + * + * This is done after receiving struct ip from the system -- see notes above. + */ +extern void sockopt_iphdrincl_swab_systoh (struct ip *iph) { #ifndef HAVE_IP_HDRINCL_BSD_ORDER @@ -559,190 +1055,171 @@ sockopt_iphdrincl_swab_systoh (struct ip *iph) } /*============================================================================== - * Set TCP MD5 signature socket option. + * IPv6 Stuff + */ +#ifdef HAVE_IPV6 + +/*------------------------------------------------------------------------------ + * Set IPV6_V6ONLY. * * Returns: >= 0 => OK - * < 0 => failed, see errno. + * < 0 => failed -- see errno * - * NB: returns ENOSYS if TCP MD5 is not supported + * Logs a LOG_ERR message if fails. */ -int -sockopt_tcp_signature (int sock_fd, union sockunion *su, const char *password) +extern int +setsockopt_ipv6_v6only(int sock_fd) { -#if defined(HAVE_TCP_MD5_LINUX24) && defined(GNU_LINUX) - int ret ; - - /* Support for the old Linux 2.4 TCP-MD5 patch, taken from Hasso Tepper's - * version of the Quagga patch (based on work by Rick Payne, and Bruce - * Simpson) - */ -#define TCP_MD5_AUTH 13 -#define TCP_MD5_AUTH_ADD 1 -#define TCP_MD5_AUTH_DEL 2 - struct tcp_rfc2385_cmd { - u_int8_t command; /* Command - Add/Delete */ - u_int32_t address; /* IPV4 address associated */ - u_int8_t keylen; /* MD5 Key len (do NOT assume 0 terminated ascii) */ - void *key; /* MD5 Key */ - } cmd; - struct in_addr *addr = &su->sin.sin_addr; - - cmd.command = (password != NULL ? TCP_MD5_AUTH_ADD : TCP_MD5_AUTH_DEL); - cmd.address = addr->s_addr; - cmd.keylen = (password != NULL ? strlen (password) : 0); - cmd.key = password; - - ret = setsockopt (sock_fd, IPPROTO_TCP, TCP_MD5_AUTH, &cmd, sizeof cmd) ; - - return (ret >= 0) ? 0 : -1 ; - -#elif HAVE_DECL_TCP_MD5SIG - int ret, err ; -# ifndef GNU_LINUX - /* - * XXX Need to do PF_KEY operation here to add/remove an SA entry, - * and add/remove an SP entry for this peer's packet flows also. - */ - int md5sig = password && *password ? 1 : 0; -# else - int keylen = password ? strlen (password) : 0 ; - struct tcp_md5sig md5sig ; - union sockunion *su2 ; - union sockunion susock ; + int ret; + int on = 1 ; - /* Figure out whether the socket and the sockunion are the same family.. - * adding AF_INET to AF_INET6 needs to be v4 mapped, you'd think.. - */ - ret = sockunion_getsockname(sock_fd, &susock) ; + ret = setsockopt (sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); if (ret < 0) - return ret ; - - if (susock.sa.sa_family == su->sa.sa_family) - su2 = su ; - else { - /* oops.. */ - su2 = &susock ; - - if (su2->sa.sa_family == AF_INET) - return 0 ; /* TODO: find out what this is doing ?? */ - -# ifdef HAVE_IPV6 - /* If this does not work, then all users of this sockopt will need to - * differentiate between IPv4 and IPv6, and keep separate sockets for - * each. - * - * Sadly, it doesn't seem to work at present. It's unknown whether - * this is a bug or not. - */ - if (su2->sa.sa_family == AF_INET6 - && su->sa.sa_family == AF_INET) - { - su2->sin6.sin6_family = AF_INET6; - /* V4Map the address */ - memset (&su2->sin6.sin6_addr, 0, sizeof (struct in6_addr)); - su2->sin6.sin6_addr.s6_addr[10] = 0xff ; - su2->sin6.sin6_addr.s6_addr[11] = 0xff ; -# ifdef s6_addr32 - su2->sin6.sin6_addr.s6_addr32[3] = su->sin.sin_addr.s_addr ; -# else - memcpy (&su2->sin6.sin6_addr.s6_addr[12], &su->sin.sin_addr, 4); -# endif - } -# endif + int err = errno ; + zlog_err ("cannot set sockopt IPV6_V6ONLY on socket %d: %s", + sock_fd, errtoa(err, 0).str) ; + errno = err ; } + return ret ; +} ; - memset (&md5sig, 0, sizeof (md5sig)); - memcpy (&md5sig.tcpm_addr, su2, sizeof (*su2)); - md5sig.tcpm_keylen = keylen; - if (keylen) - memcpy (md5sig.tcpm_key, password, keylen); - -# endif /* GNU_LINUX */ +/*------------------------------------------------------------------------------ + * Set IPV6_CHECKSUM + * + * Returns: >= 0 => OK + * < 0 => failed -- see errno + * + * Logs a LOG_ERR message if fails. + */ +extern int +setsockopt_ipv6_checksum (int sock_fd, int val) +{ + int ret; - err = 0 ; - ret = setsockopt(sock_fd, IPPROTO_TCP, TCP_MD5SIG, &md5sig, sizeof(md5sig)) ; +#ifdef GNU_LINUX + ret = setsockopt(sock_fd, IPPROTO_RAW, IPV6_CHECKSUM, &val, sizeof(val)); +#else + ret = setsockopt(sock_fd, IPPROTO_IPV6, IPV6_CHECKSUM, &val, sizeof(val)); +#endif /* GNU_LINUX */ if (ret < 0) { - err = errno ; - /* ENOENT is harmless. It is returned when we clear a password for which - one was not previously set. */ - if (err == ENOENT) - err = 0 ; - else - { - zlog_err ("sockopt_tcp_signature: setsockopt(%d): %s", + int err = errno ; + zlog_err("cannot set sockopt IPV6_CHECKSUM to %d on socket %d: %s", val, sock_fd, errtoa(err, 0).str) ; - errno = err ; - } ; - } - - return (err == 0) ? 0 : -1 ; - -#else + errno = err ; + } ; + return ret; +} - /* TCP MD5 is not supported */ +/*------------------------------------------------------------------------------ + * Set unicast hops val to the socket (cf IP_TTL). + * + * Returns: >= 0 => OK + * < 0 => failed -- see errno + * + * Logs a LOG_ERR message if fails. + */ +extern int +setsockopt_ipv6_unicast_hops (int sock_fd, int val) +{ + int ret; - if ((password == NULL) || (*password == '\0')) - return 0 ; /* OK if not required ! */ + ret = setsockopt(sock_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val)); + if (ret < 0) + { + int err = errno ; + zlog_err("cannot set sockopt IPV6_UNICAST_HOPS to %d on socket %d: %s", + val, sock_fd, errtoa(err, 0).str) ; + errno = err ; + } ; + return ret; +} - errno = ENOSYS ; /* manufactured error */ - return -1 ; +/*------------------------------------------------------------------------------ + * Set multicast hops val to the socket. + * + * Returns: >= 0 => OK + * < 0 => failed -- see errno + * + * Logs a LOG_ERR message if fails. + */ +extern int +setsockopt_ipv6_multicast_hops (int sock_fd, int val) +{ + int ret; -#endif /* !HAVE_TCP_MD5SIG */ -} ; + ret = setsockopt(sock_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, + sizeof(val)); + if (ret < 0) + { + int err = errno ; + zlog_err("cannot set sockopt IPV6_MULTICAST_HOPS to %d on socket %d: %s", + val, sock_fd, errtoa(err, 0).str) ; + errno = err ; + } ; + return ret; +} -/*============================================================================== - * Set TTL for socket +/*------------------------------------------------------------------------------ + * Set IPV6_RECVHOPLIMIT option (or IPV6_HOPLIMIT) * * Returns: >= 0 => OK - * < 0 => failed, see errno. + * < 0 => failed -- see errno + * + * Logs a LOG_ERR message if fails. */ -int -sockopt_ttl (int sock_fd, int ttl) +extern int +setsockopt_ipv6_hoplimit (int sock_fd, int val) { - const char* msg ; - int ret ; - int family ; - - ret = 0 ; - msg = NULL ; + int ret; + int opt ; + const char* name ; - family = sockunion_getsockfamily(sock_fd) ; - if (family < 0) - return -1 ; +# ifdef IPV6_RECVHOPLIMIT + opt = IPV6_RECVHOPLIMIT ; /* RFC3542 == RFC2292-bis */ + name = "IPV6_RECVHOPLIMIT" ; +# else + opt = IPV6_HOPLIMIT ; /* RFC2292 */ + name = "IPV6_HOPLIMIT" ; +# endif - switch (family) - { - case AF_INET: -#ifdef IP_TTL - ret = setsockopt (sock_fd, IPPROTO_IP, IP_TTL,(void*)&ttl, sizeof(int)); - msg = "IP_TTL" ; -#endif /* IP_TTL */ - break ; + ret = setsockopt(sock_fd, IPPROTO_IPV6, opt, &val, sizeof(val)); + if (ret < 0) + { + int err = errno ; + zlog_err("cannot set sockopt %s to %d on socket %d: %s", name, + val, sock_fd, errtoa(err, 0).str); + errno = err ; + } ; -#ifdef HAVE_IPV6 - case AF_INET6: - ret = setsockopt (sock_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, - (void*)&ttl, sizeof(int)); - msg = "IPV6_UNICAST_HOPS" ; - break ; -#endif + return ret; +} - default: /* ignore unknown family */ - ret = 0 ; - break ; - } ; +/*------------------------------------------------------------------------------ + * Set IPV6_MULTICAST_LOOP option + * + * Returns: >= 0 => OK + * < 0 => failed -- see errno + * + * Logs a LOG_ERR message if fails. + */ +extern int +setsockopt_ipv6_multicast_loop (int sock_fd, int val) +{ + int ret; - if (ret != 0) + ret = setsockopt (sock_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, + sizeof (val)); + if (ret < 0) { int err = errno ; - zlog (NULL, LOG_WARNING, - "cannot set sockopt %s %d to socket %d: %s", msg, ttl, sock_fd, - errtoa(err, 0).str) ; + zlog_err("cannot set sockopt IPV6_MULTICAST_LOOP to %d on socket %d: %s", + val, sock_fd, errtoa(err, 0).str); errno = err ; - return -1 ; } ; + return ret; +} +#endif /* HAVE_IPV6 */ + - return 0 ; -} ; diff --git a/lib/sockopt.h b/lib/sockopt.h index ad86f053..c706a74f 100644 --- a/lib/sockopt.h +++ b/lib/sockopt.h @@ -1,4 +1,4 @@ -/* Router advertisement +/* Setting and getting socket options -- utility functions. * Copyright (C) 1999 Kunihiro Ishiguro * * This file is part of GNU Zebra. @@ -24,18 +24,20 @@ #include "sockunion.h" +extern int setsockopt_reuseaddr (int sock_fd) ; +extern int setsockopt_reuseport (int sock_fd) ; +extern int setsockopt_broadcast (int sock_fd) ; + +extern int setsockopt_ttl (int sock_fd, int ttl); +extern int setsockopt_minttl (int sock_fd, int ttl); +extern int setsockopt_cork (int sock_fd, int onoff); + extern int setsockopt_so_recvbuf (int sock_fd, int size); -extern int setsockopt_so_sendbuf (const int sock_fd, int size); -extern int getsockopt_so_sendbuf (const int sock_fd); +extern int setsockopt_so_sendbuf (int sock_fd, int size); +extern int getsockopt_so_sendbuf (int sock_fd); -#ifdef HAVE_IPV6 -extern int setsockopt_ipv6_pktinfo (int, int); -extern int setsockopt_ipv6_checksum (int, int); -extern int setsockopt_ipv6_multicast_hops (int, int); -extern int setsockopt_ipv6_unicast_hops (int, int); -extern int setsockopt_ipv6_hoplimit (int, int); -extern int setsockopt_ipv6_multicast_loop (int, int); -#endif /* HAVE_IPV6 */ +extern int setsockopt_tcp_signature(int sock_fd, union sockunion *su, + const char *password); /* * It is OK to reference in6_pktinfo here without a protecting #if @@ -92,7 +94,7 @@ extern int setsockopt_multicast_ipv4(int sock_fd, int optname, extern int setsockopt_ipv4_tos(int sock_fd, int tos); /* Ask for, and get, ifindex, by whatever method is supported. */ -extern int setsockopt_ifindex (int, int, int); +extern int setsockopt_pktinfo (int, int, int); extern int getsockopt_ifindex (int, struct msghdr *); /* swab the fields in iph between the host order and system order expected @@ -101,7 +103,16 @@ extern int getsockopt_ifindex (int, struct msghdr *); extern void sockopt_iphdrincl_swab_htosys (struct ip *iph); extern void sockopt_iphdrincl_swab_systoh (struct ip *iph); -extern int sockopt_ttl (int sock_fd, int ttl); -extern int sockopt_tcp_signature(int sock_fd, union sockunion *su, - const char *password); +#ifdef HAVE_IPV6 + +extern int setsockopt_ipv6_v6only(int sock_fd) ; +extern int setsockopt_ipv6_pktinfo (int, int); +extern int setsockopt_ipv6_checksum (int, int); +extern int setsockopt_ipv6_multicast_hops (int, int); +extern int setsockopt_ipv6_unicast_hops (int, int); +extern int setsockopt_ipv6_hoplimit (int, int); +extern int setsockopt_ipv6_multicast_loop (int, int); + +#endif /* HAVE_IPV6 */ + #endif /*_ZEBRA_SOCKOPT_H */ diff --git a/lib/sockunion.c b/lib/sockunion.c index deac292c..9084c27e 100644 --- a/lib/sockunion.c +++ b/lib/sockunion.c @@ -249,11 +249,36 @@ sockunion_init_new(sockunion su, sa_family_t family) if (family != AF_UNSPEC) sockunion_set_family(su, family) ; + else + confirm(AF_UNSPEC == 0) ; return su ; } ; /*------------------------------------------------------------------------------ + * Get the length of the address in the given sockunion. + * + * Returns zero if AF_UNSPEC or not any known address family. + */ +extern int +sockunion_get_len(sockunion su) +{ + switch (su->sa.sa_family) + { + case AF_INET: + return sizeof(struct sockaddr_in) ; + +#ifdef HAVE_IPV6 + case AF_INET6: + return sizeof(struct sockaddr_in6) ; +#endif + + default: + return 0 ; + } ; +} ; + +/*------------------------------------------------------------------------------ * From the given string, fill in the given sockunion. * * Returns: 0 => OK -- sockunion filled in @@ -357,20 +382,19 @@ sockunion_su2str (union sockunion *su, enum MTYPE type) * If have an IPv6 mapped IPv4 address, convert it to an IPv4 address. */ extern void -sockunion_unmap_ipv4 (union sockunion *su) +sockunion_unmap_ipv4 (sockunion su) { #ifdef HAVE_IPV6 - union sockunion sux ; - struct sockaddr_in* su_in = &sux.sin ; - - if ( (su->sa.sa_family == AF_INET6) - && IN6_IS_ADDR_V4MAPPED (&su->sin6.sin6_addr) ) + if ( (sockunion_family(su) == AF_INET6) + && IN6_IS_ADDR_V4MAPPED (&su->sin6.sin6_addr) ) { - sockunion_init_new(&sux, AF_INET) ; - memcpy (&su_in->sin_addr, ((char *)&su->sin6.sin6_addr) + 12, 4) ; - su_in->sin_port = su->sin6.sin6_port ; - memcpy (su, &sux, sizeof(sux)) ; - confirm(sizeof(*su) == sizeof(sux)) ; + union sockunion sux[1] ; + + sockunion_init_new(sux, AF_INET) ; + memcpy (&sux->sin.sin_addr, &su->sin6.sin6_addr.s6_addr[12], 4) ; + sux->sin.sin_port = su->sin6.sin6_port ; + memcpy (su, sux, sizeof(*sux)) ; + confirm(sizeof(*su) == sizeof(*sux)) ; } #endif /* HAVE_IPV6 */ } @@ -379,20 +403,19 @@ sockunion_unmap_ipv4 (union sockunion *su) * If have an IPv4 address, convert it to an IPv6 mapped IPv4 address. */ extern void -sockunion_map_ipv4 (union sockunion *su) +sockunion_map_ipv4 (sockunion su) { #ifdef HAVE_IPV6 - union sockunion sux ; - struct sockaddr_in6* su_in6 = &sux.sin6 ; - - if (su->sa.sa_family == AF_INET) + if (sockunion_family(su) == AF_INET) { - sockunion_init_new(&sux, AF_INET6) ; - memset (((char *)&su_in6->sin6_addr) + 10, 0xFF, 2) ; - memcpy (((char *)&su_in6->sin6_addr) + 12, &su->sin.sin_addr, 4) ; - su_in6->sin6_port = su->sin.sin_port ; - memcpy (su, &sux, sizeof(sux)) ; - confirm(sizeof(*su) == sizeof(sux)) ; + union sockunion sux[1] ; + + sockunion_init_new(sux, AF_INET6) ; + memset (&sux->sin6.sin6_addr.s6_addr[10], 0xFF, 2) ; + memcpy (&sux->sin6.sin6_addr.s6_addr[12], &su->sin.sin_addr, 4) ; + sux->sin6.sin6_port = su->sin.sin_port ; + memcpy (su, sux, sizeof(*sux)) ; + confirm(sizeof(*su) == sizeof(*sux)) ; } #endif /* HAVE_IPV6 */ } @@ -414,7 +437,7 @@ sockunion_map_ipv4 (union sockunion *su) * -1 -- error -- not one of the above * -2 -- error -- one of the above */ -int +extern int sockunion_accept (int sock_fd, union sockunion *su) { socklen_t len; @@ -443,23 +466,22 @@ sockunion_accept (int sock_fd, union sockunion *su) * Returns: -1 : failed -- see errno * otherwise : socket * - * Logs a LOG_WARNING message if fails. + * Logs a LOG_ERR message if fails. */ extern int -sockunion_socket(sa_family_t family, int type, int protocol) +sockunion_socket(sockunion su, int type, int protocol) { int sock_fd ; int err ; - sock_fd = socket(family, type, protocol); + sock_fd = socket(sockunion_family(su), type, protocol); if (sock_fd >= 0) return sock_fd ; err = errno ; - zlog (NULL, LOG_WARNING, - "Cannot make socket family=%d, type=%d, protocol=%d: %s", - (int)family, type, protocol, errtoa(err, 0).str) ; + zlog_err("Cannot make socket family=%d, type=%d, protocol=%d: %s", + (int)sockunion_family(su), type, protocol, errtoa(err, 0).str) ; errno = err ; return -1; } @@ -470,15 +492,15 @@ sockunion_socket(sa_family_t family, int type, int protocol) * Returns: -1 : failed -- see errno * otherwise : socket * - * Logs a LOG_WARNING message if fails. + * Logs a LOG_ERR message if fails. */ -int -sockunion_stream_socket (union sockunion *su) +extern int +sockunion_stream_socket (sockunion su) { if (su->sa.sa_family == 0) su->sa.sa_family = AF_INET_UNION; - return sockunion_socket (su->sa.sa_family, SOCK_STREAM, 0); + return sockunion_socket (su, SOCK_STREAM, 0); } /*------------------------------------------------------------------------------ @@ -541,25 +563,24 @@ sockunion_connect(int sock_fd, union sockunion* peer_su, unsigned short port, /*------------------------------------------------------------------------------ * Start listening on given socket * - * Returns: 0 : OK (so far so good) - * < 0 : failed -- see errno + * Returns: >= 0 : OK (so far so good) + * < 0 : failed -- see errno * - * Logs a LOG_WARNING message if fails. + * Logs a LOG_ERR message if fails. */ extern int sockunion_listen(int sock_fd, int backlog) { - int ret, err ; + int ret ; ret = listen(sock_fd, backlog) ; - if (ret == 0) - return 0 ; - - err = errno ; - zlog (NULL, LOG_WARNING, "cannot listen on socket %d: %s", - sock_fd, errtoa(err, 0).str) ; - errno = err ; + if (ret < 0) + { + int err = errno ; + zlog_err("cannot listen on socket %d: %s", sock_fd, errtoa(err, 0).str) ; + errno = err ; + } ; return ret ; } ; @@ -567,28 +588,72 @@ sockunion_listen(int sock_fd, int backlog) /*------------------------------------------------------------------------------ * Bind socket to address/port. * - * Sets the given port into the sockunion su. + * If the 'any' parameter is true, sets the given sockunion to INADDR_ANY or + * the *socket* address family equivalent. * - * If the 'any' parameter is NULL, set the address part of sockunion to - * INADDR_ANY or the family equivalent. Note that for IPv6 this does not - * affect the flow/scope in the su. + * Sets the given port into the sockunion su. * * For good measure, sets sin_len or family equivalent if required. * + * If not 'any', and the given su does not have the same address family as the + * socket, then attempts to convert the su to the same family as the socket, + * by mapping or unmapping IPv4. + * * Performs bind() and logs a LOG_WARNING message if fails. * * Returns: >= 0 => OK * < 0 => failed -- see errno */ -int -sockunion_bind (int sock_fd, union sockunion *su, unsigned short port, - void* any) +extern int +sockunion_bind(int sock_fd, sockunion su, unsigned short port, bool any) { int sa_len ; int ret ; + int sock_family ; - if (any == NULL) - sockunion_set_addr_any(su) ; + sock_family = sockunion_getsockfamily(sock_fd) ; + + if (any) + { + /* Create an "any" -- of same family as the socket */ + sockunion_init_new(su, sock_family) ; + sockunion_set_addr_any(su) ; + } + else + { + /* Want to bind to a specific address. + * + * We provide bind with an address which matches the address family of + * the *socket*. + * + * If the socket is AF_INET, address may be AF_INET, or an AF_INET6 + * *provided* it is an IPv4 mapped address. + * + * If the socket is AF_INET6, address may be AF_INET or AF_NET6, and + * will map any IPv4 address. + * + * If we don't HAVE_IPV6, or we don't recognise an address family, + * then do nothing and let bind() return some sort of error. + */ +#ifdef HAVE_IPV6 + if (sock_family != sockunion_family(su)) + { + switch (sock_family) + { + case AF_INET: + sockunion_unmap_ipv4(su) ; /* unmap if AF_INET6 mapped IPv4 */ + break ; + + case AF_INET6: + sockunion_map_ipv4(su) ; /* map if AF_INET */ + break ; + + default: + break ; + } ; + } ; +#endif + } ; sa_len = sockunion_set_port(su, port) ; @@ -596,7 +661,7 @@ sockunion_bind (int sock_fd, union sockunion *su, unsigned short port, if (ret < 0) { int err = errno ; - zlog (NULL, LOG_WARNING, "cannot bind to %s port %d socket %d: %s", + zlog_warn("cannot bind to %s port %d socket %d: %s", sutoa(su).str, port, sock_fd, errtoa(err, 0).str) ; errno = err ; } ; @@ -605,72 +670,11 @@ sockunion_bind (int sock_fd, union sockunion *su, unsigned short port, } /*------------------------------------------------------------------------------ - * Set socket SO_REUSEADDR option - * - * Returns: >= 0 => OK - * < 0 => failed -- see errno - * - * Logs a LOG_WARNING message if fails. - */ -int -sockopt_reuseaddr (int sock_fd) -{ - int ret; - int on = 1; - - ret = setsockopt (sock_fd, SOL_SOCKET, SO_REUSEADDR, (void *) &on, - sizeof (on)); - if (ret < 0) - { - int err = errno ; - zlog (NULL, LOG_WARNING, - "cannot set sockopt SO_REUSEADDR to socket %d: %s", sock_fd, - errtoa(err, 0).str) ; - errno = err ; - } ; - - return ret ; -} - -/*------------------------------------------------------------------------------ - * Set socket SO_REUSEPORT option -- if it is locally supported. - * - * Returns: >= 0 => OK - * < 0 => failed -- see errno - * - * Logs a LOG_WARNING message if fails. - */ -int -sockopt_reuseport (int sock_fd) -{ - int ret; - -#ifdef SO_REUSEPORT - int on = 1; - ret = setsockopt (sock_fd, SOL_SOCKET, SO_REUSEPORT, - (void *) &on, sizeof (on)); -#else - ret = 0 ; -#endif - - if (ret < 0) - { - int err = errno ; - zlog (NULL, LOG_WARNING, - "cannot set sockopt SO_REUSEPORT to socket %d: %s", sock_fd, - errtoa(err, 0).str) ; - errno = err ; - } ; - - return ret ; -} ; - -/*------------------------------------------------------------------------------ - * If same family and same prefix return 1. + * If same (known) family and same prefix return 1, otherwise return 0. * * Returns 0 if same family, but not a known family. */ -int +extern int sockunion_same (union sockunion *su1, union sockunion *su2) { int ret = 0; @@ -686,7 +690,7 @@ sockunion_same (union sockunion *su1, union sockunion *su2) #ifdef HAVE_IPV6 case AF_INET6: ret = memcmp (&su1->sin6.sin6_addr, &su2->sin6.sin6_addr, - sizeof (struct in6_addr)); + sizeof (struct in6_addr)); return (ret == 0) ; #endif /* HAVE_IPV6 */ @@ -696,125 +700,142 @@ sockunion_same (union sockunion *su1, union sockunion *su2) } ; /*------------------------------------------------------------------------------ - * Get the address family the given socket is set to. + * Get local (getsockname) or remote (getpeername) address and port. * * Returns: >= 0 == the address family (AF_UNSPEC if fd sock_fd < 0) * < 0 => failed -- see errno * - * NB: gets the actual address family -- does NOT look for mapped IPv4. - */ -extern int -sockunion_getsockfamily(int sock_fd) -{ - union sockunion su ; - int ret ; - socklen_t len ; - - if (sock_fd < 0) - return AF_UNSPEC ; - - sockunion_init_new(&su, AF_UNSPEC) ; - len = sizeof(su) ; - - ret = getsockname(sock_fd, (struct sockaddr *)&su, &len) ; - if (ret < 0) - { - int err = errno ; - zlog_warn ("Failed in getsockname for socket %d: %s", - sock_fd, errtoa(err, 0).str) ; - errno = err ; - return -1 ; - } ; - - return su.sa.sa_family ; -} ; - -/*------------------------------------------------------------------------------ - * Get local or remote address and port. - * - * Returns: >= 0 => OK - * < 0 => failed (or unknown family) -- see errno - * - * If address is an IPv4 mapped IPv6 address, returns the IPv4 address. + * If "unmap": if address is an IPv4 mapped IPv6 address, returns AF_INET. * * NB: returns EAFNOSUPPORT if don't recognise the address family. + * + * Logs a LOG_ERR message if fails in getsockname/getpeername. */ static int -sockunion_get_name(int sock_fd, union sockunion* su, int local) +sockunion_get_name(int sock_fd, union sockunion* su, bool local, bool unmap) { int ret ; - + socklen_t len ; union { - struct sockaddr sa; - struct sockaddr_in sin; -#ifdef HAVE_IPV6 - struct sockaddr_in6 sin6; -#endif /* HAVE_IPV6 */ + union sockunion su ; char tmp_buffer[128]; } name ; - socklen_t len = sizeof(name) ; + memset(su, 0, sizeof(union sockunion)) ; + + confirm(AF_UNSPEC == 0) ; + if (sock_fd < 0) + return AF_UNSPEC ; + + len = sizeof(name) ; memset(&name, 0, len); - memset(su, 0, sizeof(union sockunion)) ; - if (local) - ret = getsockname(sock_fd, (struct sockaddr *)&name, &len) ; + if (local) + ret = getsockname(sock_fd, &name.su.sa, &len) ; else - ret = getpeername(sock_fd, (struct sockaddr *)&name, &len) ; + ret = getpeername(sock_fd, &name.su.sa, &len) ; if (ret < 0) { int err = errno ; - zlog_warn ("Cannot get %s address and port: %s", - local ? "local" : "remote", errtoa(err, 0).str) ; + zlog_err("failed in %s for socket %d: %s", + local ? "getsockname" : "getpeername", + sock_fd, errtoa(err, 0).str) ; errno = err ; - return ret ; } + else + { + ret = name.su.sa.sa_family ; - ret = 0 ; /* assume all will be well */ - switch (name.sa.sa_family) - { - case AF_INET: - memcpy(su, &name, sizeof (struct sockaddr_in)) ; - break ; + switch (ret) + { + case AF_INET: + su->sin = name.su.sin ; + break ; #ifdef HAVE_IPV6 - case AF_INET6: - memcpy(su, &name, sizeof (struct sockaddr_in6)) ; - sockunion_unmap_ipv4(su) ; - break ; + case AF_INET6: + su->sin6 = name.su.sin6 ; + if (unmap) + sockunion_unmap_ipv4(su) ; + break ; #endif /* HAVE_IPV6 */ - default: - errno = EAFNOSUPPORT ; - ret = -1 ; - } ; + default: + errno = EAFNOSUPPORT ; + ret = -1 ; + } ; + } ; return ret ; } ; /*------------------------------------------------------------------------------ - * Get local address and port -- ie getsockname(). + * Get the address family the given socket is set to. + * + * Returns: >= 0 == the address family (AF_UNSPEC if fd sock_fd < 0) + * < 0 => failed -- see errno + * + * NB: gets the actual address family -- does NOT look for mapped IPv4. + */ +extern int +sockunion_getsockfamily(int sock_fd) +{ + union sockunion su[1] ; + int ret ; + + ret = sockunion_get_name(sock_fd, su, true, /* true => local */ + false) ; /* false => don't unmap */ + return (ret >= 0) ? sockunion_family(su) : ret ; +} ; + +/*------------------------------------------------------------------------------ + * Get the address family the given socket's protocol is set to. + * + * If this is an AF_INET, that's easy. + * + * If this is an AF_INET6, then needs to look out for IN6_IS_ADDR_V4MAPPED. + * + * Returns: >= 0 == the address family (AF_UNSPEC if fd sock_fd < 0) + * < 0 => failed -- see errno + * + * NB: gets the underlying address family -- ie: looks for mapped IPv4. + */ +extern int +sockunion_getprotofamily(int sock_fd) +{ + union sockunion su[1] ; + int ret ; + + ret = sockunion_get_name(sock_fd, su, true, /* true => local */ + true) ; /* true => unmap */ + return (ret >= 0) ? sockunion_family(su) : ret ; +} ; + +/*------------------------------------------------------------------------------ + * Get local address and port -- ie getsockname(), except unmaps IPv4 mapped. * * See: sockunion_get_name() */ -int -sockunion_getsockname(int sock_fd, union sockunion* su_local) +extern int +sockunion_getsockname(int sock_fd, sockunion su_local) { - return sockunion_get_name(sock_fd, su_local, 1) ; + return sockunion_get_name(sock_fd, su_local, true, /* true => local */ + true) ; /* true => unmap */ } ; /*------------------------------------------------------------------------------ - * Get remote address and port -- ie getpeername(). + * Get remote address and port -- ie getpeername(), except unmaps IPv4 mapped. * * See: sockunion_get_name() */ -int -sockunion_getpeername (int sock_fd, union sockunion* su_remote) +extern int +sockunion_getpeername (int sock_fd, sockunion su_remote) { - return sockunion_get_name(sock_fd, su_remote, 0) ; + return sockunion_get_name(sock_fd, su_remote, false, /* false => remote */ + true) ; /* true => unmap */ } ; /*------------------------------------------------------------------------------ diff --git a/lib/sockunion.h b/lib/sockunion.h index 0b23ae63..eeae72d5 100644 --- a/lib/sockunion.h +++ b/lib/sockunion.h @@ -24,6 +24,7 @@ #define _ZEBRA_SOCKUNION_H #include "zebra.h" +#include <stdbool.h> #include "symtab.h" #include "prefix.h" #include "memory.h" @@ -101,36 +102,36 @@ struct sockunion_string /* Prototypes. */ extern sockunion sockunion_init_new(sockunion su, sa_family_t family) ; +extern int sockunion_get_len(sockunion su) ; extern int sockunion_set_port(sockunion su, in_port_t port) ; -extern int str2sockunion (const char *, union sockunion *); -extern const char *sockunion2str (union sockunion *, char *, size_t); +extern int str2sockunion (const char * str, sockunion su); +extern const char *sockunion2str (sockunion su, char* buf, size_t size); extern sockunion_string_t sutoa(sockunion su) ; -extern int sockunion_cmp (union sockunion *, union sockunion *); -extern int sockunion_same (union sockunion *, union sockunion *); - -extern char* sockunion_su2str (union sockunion* su, enum MTYPE type) ; -extern union sockunion *sockunion_str2su (const char *str); -extern struct in_addr sockunion_get_in_addr (union sockunion *su); -extern int sockunion_accept (int sock_fd, union sockunion *); -extern int sockunion_stream_socket (union sockunion *); -extern int sockopt_reuseaddr (int); -extern int sockopt_reuseport (int); -extern int sockunion_bind (int sock_fd, union sockunion *, - unsigned short, void* any); -extern int sockunion_socket (sa_family_t family, int type, int protocol) ; -extern int sockunion_connect (int sock_fd, union sockunion *su, - unsigned short port, unsigned int) ; +extern int sockunion_cmp (sockunion su1, sockunion su2); +extern int sockunion_same (sockunion su1, sockunion su2); + +extern char* sockunion_su2str (sockunion su, enum MTYPE type) ; +extern sockunion sockunion_str2su (const char *str); +extern struct in_addr sockunion_get_in_addr (sockunion su); +extern int sockunion_accept (int sock_fd, sockunion su); +extern int sockunion_stream_socket (sockunion su); +extern int sockunion_bind (int sock_fd, sockunion su, + unsigned short port, bool any) ; +extern int sockunion_socket (sockunion su, int type, int protocol) ; +extern int sockunion_connect (int sock_fd, sockunion su, + unsigned short port, unsigned int ifindex) ; extern int sockunion_listen(int sock_fd, int backlog) ; extern int sockunion_getsockfamily(int sock_fd) ; -extern int sockunion_getsockname (int, union sockunion*); -extern int sockunion_getpeername (int, union sockunion*); -extern void sockunion_unmap_ipv4 (union sockunion *su) ; -extern void sockunion_map_ipv4 (union sockunion *su) ; +extern int sockunion_getprotofamily(int sock_fd) ; +extern int sockunion_getsockname (int sock_fd, sockunion su); +extern int sockunion_getpeername (int sock_fd, sockunion su); +extern void sockunion_unmap_ipv4 (sockunion su) ; +extern void sockunion_map_ipv4 (sockunion su) ; -extern union sockunion *sockunion_dup (union sockunion *); +extern sockunion sockunion_dup (sockunion src); extern void sockunion_copy (sockunion dst, sockunion src) ; -extern void sockunion_free (union sockunion *); +extern void sockunion_free (sockunion su); extern sockunion sockunion_new_prefix(sockunion su, prefix p) ; extern sockunion sockunion_new_sockaddr(sockunion su, struct sockaddr* sa) ; diff --git a/lib/thread.c b/lib/thread.c index 7f8ff5f6..c130d876 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -438,8 +438,7 @@ DEFUN_CALL(show_thread_cpu, } static void -cpu_record_hash_clear (struct hash_backet *bucket, - void *args) +cpu_record_hash_clear (struct hash_backet *bucket, void *args) { thread_type *filter = args; struct cpu_thread_history *a = bucket->data; @@ -1217,7 +1216,6 @@ thread_timer_process (struct thread_list *list, struct timeval *timenow) /*------------------------------------------------------------------------------ * Move the given list of threads to the back of the THREAD_READY queue. */ -/* process a list en masse, e.g. for event thread lists */ static unsigned int thread_process (struct thread_list *list) { @@ -1246,9 +1244,9 @@ thread_fetch (struct thread_master *m, struct thread *fetch) fd_set readfd; fd_set writefd; fd_set exceptfd; - struct timeval timer_val; + struct timeval timer_val ; struct timeval timer_val_bg; - struct timeval *timer_wait; + struct timeval *timer_wait ; struct timeval *timer_wait_bg; while (1) diff --git a/lib/vty_io.c b/lib/vty_io.c index 33947342..81af4e5e 100644 --- a/lib/vty_io.c +++ b/lib/vty_io.c @@ -40,6 +40,7 @@ #include "filter.h" #include "privs.h" #include "sockunion.h" +#include "sockopt.h" #include "network.h" #include <arpa/telnet.h> diff --git a/lib/vty_io_term.c b/lib/vty_io_term.c index 61f35761..0660afb6 100644 --- a/lib/vty_io_term.c +++ b/lib/vty_io_term.c @@ -41,6 +41,7 @@ #include "filter.h" #include "privs.h" #include "sockunion.h" +#include "sockopt.h" #include "network.h" #include <arpa/telnet.h> @@ -1173,8 +1174,8 @@ static int uty_term_listen_open(sa_family_t family, int type, int protocol, struct sockaddr* sa, unsigned short port) { - union sockunion su ; - int sock ; + union sockunion su[1] ; + int sock_fd ; int ret ; VTY_ASSERT_LOCKED() ; @@ -1182,55 +1183,52 @@ uty_term_listen_open(sa_family_t family, int type, int protocol, /* Is there an address and is it for this family ? */ if ((sa != NULL) || (sa->sa_family == family)) /* Set up sockunion containing required family and address */ - sockunion_new_sockaddr(&su, sa) ; + sockunion_new_sockaddr(su, sa) ; else { /* no address or wrong family -- set up empty sockunion of * required family */ - sockunion_init_new(&su, family) ; + sockunion_init_new(su, family) ; sa = NULL ; } ; /* Open the socket and set its properties */ - sock = sockunion_socket(family, type, protocol) ; - if (sock < 0) + sock_fd = sockunion_socket(su, type, protocol) ; + if (sock_fd < 0) return -1 ; - ret = sockopt_reuseaddr (sock); + ret = setsockopt_reuseaddr (sock_fd); if (ret >= 0) - ret = sockopt_reuseport (sock); + ret = setsockopt_reuseport (sock_fd); if (ret >= 0) - ret = set_nonblocking(sock); + ret = set_nonblocking(sock_fd); -#if defined(HAVE_IPV6) && defined(IPV6_V6ONLY) - /* Want only IPV6 on ipv6 socket (not mapped addresses) +#ifdef HAVE_IPV6 + /* Want only IPv6 on AF_INET6 socket (not mapped addresses) * * This distinguishes 0.0.0.0 from :: -- without this, bind() will reject the * attempt to bind to :: after binding to 0.0.0.0. */ - if ((ret >= 0) && (sa->sa_family == AF_INET6)) - { - int on = 1; - ret = setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&on, sizeof(on)); - } + if ((ret >= 0) && (family == AF_INET6)) + ret = setsockopt_ipv6_v6only(sock_fd) ; #endif if (ret >= 0) - ret = sockunion_bind (sock, &su, port, sa) ; + ret = sockunion_bind (sock_fd, su, port, (sa == NULL)) ; if (ret >= 0) - ret = sockunion_listen (sock, 3); + ret = sockunion_listen (sock_fd, 3); if (ret < 0) { - close (sock); + close (sock_fd); return -1 ; } /* Socket is open -- set VTY_TERMINAL listener going */ - uty_add_listener(sock, uty_term_accept) ; + uty_add_listener(sock_fd, uty_term_accept) ; /* Return OK and signal whether used address or not */ return (sa != NULL) ? 1 : 0 ; diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index cb347451..236baf17 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -118,6 +118,7 @@ ospf6_interface_create (struct interface *ifp) oi->cost = 1; oi->state = OSPF6_INTERFACE_DOWN; oi->flag = 0; + oi->mtu_ignore = 0; /* Try to adjust I/O buffer size with IfMtu */ oi->ifmtu = ifp->mtu6; @@ -784,6 +785,8 @@ ospf6_interface_show (struct vty *vty, struct interface *ifp) { vty_out (vty, " Instance ID %d, Interface MTU %d (autodetect: %d)%s", oi->instance_id, oi->ifmtu, ifp->mtu6, VNL); + vty_out (vty, " MTU mismatch detection: %s%s", oi->mtu_ignore ? + "disabled" : "enabled", VNL); inet_ntop (AF_INET, &oi->area->area_id, strbuf, sizeof (strbuf)); vty_out (vty, " Area ID %s, Cost %hu%s", strbuf, oi->cost, @@ -1368,6 +1371,55 @@ DEFUN (no_ipv6_ospf6_passive, return CMD_SUCCESS; } +DEFUN (ipv6_ospf6_mtu_ignore, + ipv6_ospf6_mtu_ignore_cmd, + "ipv6 ospf6 mtu-ignore", + IP6_STR + OSPF6_STR + "Ignore MTU mismatch on this interface\n" + ) +{ + struct ospf6_interface *oi; + struct interface *ifp; + + ifp = (struct interface *) vty->index; + assert (ifp); + + oi = (struct ospf6_interface *) ifp->info; + if (oi == NULL) + oi = ospf6_interface_create (ifp); + assert (oi); + + oi->mtu_ignore = 1; + + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_ospf6_mtu_ignore, + no_ipv6_ospf6_mtu_ignore_cmd, + "no ipv6 ospf6 mtu-ignore", + NO_STR + IP6_STR + OSPF6_STR + "Ignore MTU mismatch on this interface\n" + ) +{ + struct ospf6_interface *oi; + struct interface *ifp; + + ifp = (struct interface *) vty->index; + assert (ifp); + + oi = (struct ospf6_interface *) ifp->info; + if (oi == NULL) + oi = ospf6_interface_create (ifp); + assert (oi); + + oi->mtu_ignore = 0; + + return CMD_SUCCESS; +} + DEFUN (ipv6_ospf6_advertise_prefix_list, ipv6_ospf6_advertise_prefix_list_cmd, "ipv6 ospf6 advertise prefix-list WORD", @@ -1495,6 +1547,9 @@ config_write_ospf6_interface (struct vty *vty) if (CHECK_FLAG (oi->flag, OSPF6_INTERFACE_PASSIVE)) vty_out (vty, " ipv6 ospf6 passive%s", VNL); + if (oi->mtu_ignore) + vty_out (vty, " ipv6 ospf6 mtu-ignore%s", VNL); + vty_out (vty, "!%s", VNL); } return 0; @@ -1547,6 +1602,9 @@ ospf6_interface_init (void) install_element (INTERFACE_NODE, &ipv6_ospf6_passive_cmd); install_element (INTERFACE_NODE, &no_ipv6_ospf6_passive_cmd); + install_element (INTERFACE_NODE, &ipv6_ospf6_mtu_ignore_cmd); + install_element (INTERFACE_NODE, &no_ipv6_ospf6_mtu_ignore_cmd); + install_element (INTERFACE_NODE, &ipv6_ospf6_advertise_prefix_list_cmd); install_element (INTERFACE_NODE, &no_ipv6_ospf6_advertise_prefix_list_cmd); } diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h index 878c29e2..cf758c07 100644 --- a/ospf6d/ospf6_interface.h +++ b/ospf6d/ospf6_interface.h @@ -76,6 +76,9 @@ struct ospf6_interface /* OSPF6 Interface flag */ char flag; + /* MTU mismatch check */ + u_char mtu_ignore; + /* Decision of DR Election */ u_int32_t drouter; u_int32_t bdrouter; diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c index d06eba26..51933b76 100644 --- a/ospf6d/ospf6_message.c +++ b/ospf6d/ospf6_message.c @@ -832,7 +832,7 @@ ospf6_dbdesc_recv (struct in6_addr *src, struct in6_addr *dst, ((caddr_t) oh + sizeof (struct ospf6_header)); /* Interface MTU check */ - if (ntohs (dbdesc->ifmtu) != oi->ifmtu) + if (!oi->mtu_ignore && ntohs (dbdesc->ifmtu) != oi->ifmtu) { if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) zlog_debug ("I/F MTU mismatch"); diff --git a/ospf6d/ospf6_network.c b/ospf6d/ospf6_network.c index 96b82af3..d9783385 100644 --- a/ospf6d/ospf6_network.c +++ b/ospf6d/ospf6_network.c @@ -96,7 +96,7 @@ ospf6_serv_sock (void) /* set socket options */ #if 1 - sockopt_reuseaddr (ospf6_sock); + setsockopt_reuseaddr (ospf6_sock); #else ospf6_set_reuseaddr (); #endif /*1*/ diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c index 0a8ac3e4..881771a7 100644 --- a/ospf6d/ospf6_zebra.c +++ b/ospf6d/ospf6_zebra.c @@ -132,6 +132,9 @@ ospf6_zebra_if_state_update (int command, struct zclient *zclient, struct interface *ifp; ifp = zebra_interface_state_read (zclient->ibuf); + if (ifp == NULL) + return 0; + if (IS_OSPF6_DEBUG_ZEBRA (RECV)) zlog_debug ("Zebra Interface state change: " "%s index %d flags %llx metric %d mtu %d", diff --git a/ospfd/ospf_api.c b/ospfd/ospf_api.c index 77383191..fc3b51dd 100644 --- a/ospfd/ospf_api.c +++ b/ospfd/ospf_api.c @@ -219,7 +219,7 @@ msg_print (struct msg *msg) #else /* ORIGINAL_CODING */ /* API message common header part. */ zlog_debug - ("API-msg [%s]: type(%d),len(%d),seq(%lu),data(%p),size(%lu)", + ("API-msg [%s]: type(%d),len(%d),seq(%lu),data(%p),size(%zd)", ospf_api_typename (msg->hdr.msgtype), msg->hdr.msgtype, ntohs (msg->hdr.msglen), (unsigned long) ntohl (msg->hdr.msgseq), STREAM_DATA (msg->s), STREAM_SIZE (msg->s)); diff --git a/ospfd/ospf_apiserver.c b/ospfd/ospf_apiserver.c index 026ae39a..a964e690 100644 --- a/ospfd/ospf_apiserver.c +++ b/ospfd/ospf_apiserver.c @@ -38,7 +38,8 @@ #include "log.h" #include "thread.h" #include "hash.h" -#include "sockunion.h" /* for inet_aton() */ +#include "sockunion.h" /* for inet_aton() */ +#include "sockopt.h" #include "buffer.h" #include <sys/types.h> @@ -617,8 +618,7 @@ ospf_apiserver_serv_sock_family (unsigned short port, int family) int accept_sock; int rc; - memset (&su, 0, sizeof (union sockunion)); - su.sa.sa_family = family; + sockunion_init_new(&su, family) ; /* Make new socket */ accept_sock = sockunion_stream_socket (&su); @@ -626,11 +626,11 @@ ospf_apiserver_serv_sock_family (unsigned short port, int family) return accept_sock; /* This is a server, so reuse address and port */ - sockopt_reuseaddr (accept_sock); - sockopt_reuseport (accept_sock); + setsockopt_reuseaddr (accept_sock); + setsockopt_reuseport (accept_sock); /* Bind socket to address and given port. */ - rc = sockunion_bind (accept_sock, &su, port, NULL); + rc = sockunion_bind (accept_sock, &su, port, true); /* true => any */ if (rc < 0) { close (accept_sock); /* Close socket */ @@ -1831,11 +1831,7 @@ ospf_apiserver_lsa11_originator (void *arg) /* Periodically refresh opaque LSAs so that they do not expire in other routers. */ -#if 0 -static void -#else extern struct ospf_lsa * -#endif ospf_apiserver_lsa_refresher (struct ospf_lsa *lsa) { struct ospf_apiserver *apiserv; @@ -1908,7 +1904,7 @@ ospf_apiserver_lsa_refresher (struct ospf_lsa *lsa) } out: - return NULL; + return new; } diff --git a/ospfd/ospf_apiserver.h b/ospfd/ospf_apiserver.h index f1412270..45716d9d 100644 --- a/ospfd/ospf_apiserver.h +++ b/ospfd/ospf_apiserver.h @@ -180,11 +180,7 @@ extern void ospf_apiserver_config_write_router (struct vty *vty); extern void ospf_apiserver_config_write_if (struct vty *vty, struct interface *ifp); extern void ospf_apiserver_show_info (struct vty *vty, struct ospf_lsa *lsa); extern int ospf_ospf_apiserver_lsa_originator (void *arg); -#if 0 -extern void ospf_apiserver_lsa_refresher (struct ospf_lsa *lsa); -#else -extern struct ospf_lsa * ospf_apiserver_lsa_refresher (struct ospf_lsa *lsa); -#endif +extern struct ospf_lsa *ospf_apiserver_lsa_refresher (struct ospf_lsa *lsa); extern void ospf_apiserver_flush_opaque_lsa (struct ospf_apiserver *apiserv, u_char lsa_type, u_char opaque_type); diff --git a/ospfd/ospf_ase.c b/ospfd/ospf_ase.c index 5bac3448..8ef545c2 100644 --- a/ospfd/ospf_ase.c +++ b/ospfd/ospf_ase.c @@ -451,6 +451,8 @@ ospf_ase_calculate_route (struct ospf *ospf, struct ospf_lsa * lsa) /* if there is a Intra/Inter area route to the N do not install external route */ if ( (rn = route_node_lookup (ospf->new_table, (struct prefix *) &p)) ) + if ((rn = route_node_lookup (ospf->new_table, + (struct prefix *) &p))) { route_unlock_node(rn); if (rn->info == NULL) @@ -461,8 +463,8 @@ ospf_ase_calculate_route (struct ospf *ospf, struct ospf_lsa * lsa) } /* Find a route to the same dest */ /* If there is no route, create new one. */ - if ( (rn = route_node_lookup (ospf->new_external_route, - (struct prefix *) &p)) ) + if ((rn = route_node_lookup (ospf->new_external_route, + (struct prefix *) &p))) route_unlock_node(rn); if (!rn || (or = rn->info) == NULL) diff --git a/ospfd/ospf_network.c b/ospfd/ospf_network.c index 1e2d44e6..dc06cb61 100644 --- a/ospfd/ospf_network.c +++ b/ospfd/ospf_network.c @@ -214,7 +214,7 @@ ospf_sock_init (void) zlog_warn ("IP_HDRINCL option not available"); #endif /* IP_HDRINCL */ - ret = setsockopt_ifindex (AF_INET, ospf_sock, 1); + ret = setsockopt_pktinfo (AF_INET, ospf_sock, 1); if (ret < 0) zlog_warn ("Can't set pktinfo option for fd %d", ospf_sock); diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index 242a2081..23eae565 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -687,6 +687,13 @@ ospf_write (struct thread *thread) iph.ip_tos = IPTOS_PREC_INTERNETCONTROL; iph.ip_len = (iph.ip_hl << OSPF_WRITE_IPHL_SHIFT) + op->length; +#if defined(__DragonFly__) + /* + * DragonFly's raw socket expects ip_len/ip_off in network byte order. + */ + iph.ip_len = htons(iph.ip_len); +#endif + #ifdef WANT_OSPF_WRITE_FRAGMENT /* XXX-MT: not thread-safe at all.. * XXX: this presumes this is only programme sending OSPF packets @@ -2126,6 +2133,15 @@ ospf_recv_packet (int fd, struct interface **ifp, struct stream *ibuf) ip_len = ip_len + (iph->ip_hl << 2); #endif +#if defined(__DragonFly__) + /* + * in DragonFly's raw socket, ip_len/ip_off are read + * in network byte order. + * As OpenBSD < 200311 adjust ip_len to strip IP header size! + */ + ip_len = ntohs(iph->ip_len) + (iph->ip_hl << 2); +#endif + ifindex = getsockopt_ifindex (AF_INET, &msgh); *ifp = if_lookup_by_index (ifindex); diff --git a/ospfd/ospf_te.c b/ospfd/ospf_te.c index 3467ad71..5970a51f 100644 --- a/ospfd/ospf_te.c +++ b/ospfd/ospf_te.c @@ -133,13 +133,7 @@ static void ospf_mpls_te_config_write_router (struct vty *vty); static void ospf_mpls_te_config_write_if (struct vty *vty, struct interface *ifp); static void ospf_mpls_te_show_info (struct vty *vty, struct ospf_lsa *lsa); static int ospf_mpls_te_lsa_originate (void *arg); - -#if 0 -static void ospf_mpls_te_lsa_refresh (struct ospf_lsa *lsa); -#else -static struct ospf_lsa * ospf_mpls_te_lsa_refresh (struct ospf_lsa *lsa); -#endif - +static struct ospf_lsa *ospf_mpls_te_lsa_refresh (struct ospf_lsa *lsa); static void ospf_mpls_te_lsa_schedule (struct mpls_te_link *lp, enum sched_opcode); static void del_mpls_te_link (void *val); @@ -1014,11 +1008,8 @@ ospf_mpls_te_lsa_originate (void *arg) out: return rc; } -#if 0 -static void -#else + static struct ospf_lsa * -#endif ospf_mpls_te_lsa_refresh (struct ospf_lsa *lsa) { struct mpls_te_link *lp; @@ -1079,7 +1070,7 @@ ospf_mpls_te_lsa_refresh (struct ospf_lsa *lsa) } out: - return NULL; + return new; } static void diff --git a/ripd/ripd.c b/ripd/ripd.c index 6a592b0a..9c951bf8 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -17,7 +17,7 @@ * You should have received a copy of the GNU General Public License * along with GNU Zebra; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. + * 02111-1307, USA. */ #include <zebra.h> @@ -66,20 +66,20 @@ long rip_global_queries = 0; union rip_miyagi_string rip_enabled_string = { .cp = "enabled" } ; union rip_miyagi_string rip_static_string = { .cp = "static" } ; - + /* Prototypes. */ static void rip_event (enum rip_event, int); static void rip_output_process (struct connected *, struct sockaddr_in *, int, u_char); static int rip_triggered_update (struct thread *); static int rip_update_jitter (unsigned long); - + /* RIP output routes type. */ enum { rip_all_route, rip_changed_route }; - + /* RIP command strings. */ static const struct message rip_msg[] = { @@ -91,22 +91,6 @@ static const struct message rip_msg[] = {RIP_POLL_ENTRY, "POLL ENTRY"}, {0, NULL}, }; - -/* Utility function to set boradcast option to the socket. */ -static int -sockopt_broadcast (int sock) -{ - int ret; - int on = 1; - - ret = setsockopt (sock, SOL_SOCKET, SO_BROADCAST, (char *) &on, sizeof on); - if (ret < 0) - { - zlog_warn ("can't set sockopt SO_BROADCAST to socket %d", sock); - return -1; - } - return 0; -} static int rip_route_rte (struct rip_info *rinfo) @@ -138,7 +122,7 @@ rip_garbage_collect (struct thread *t) /* Off timeout timer. */ RIP_TIMER_OFF (rinfo->t_timeout); - + /* Get route_node pointer. */ rp = rinfo->rp; @@ -165,7 +149,7 @@ rip_timeout (struct thread *t) rn = rinfo->rp; /* - The garbage-collection timer is set for 120 seconds. */ - RIP_TIMER_ON (rinfo->t_garbage_collect, rip_garbage_collect, + RIP_TIMER_ON (rinfo->t_garbage_collect, rip_garbage_collect, rip->garbage_time); rip_zebra_ipv4_delete ((struct prefix_ipv4 *)&rn->p, &rinfo->nexthop, @@ -205,7 +189,7 @@ rip_incoming_filter (struct prefix_ipv4 *p, struct rip_interface *ri) /* Input distribute-list filtering. */ if (ri->list[RIP_FILTER_IN]) { - if (access_list_apply (ri->list[RIP_FILTER_IN], + if (access_list_apply (ri->list[RIP_FILTER_IN], (struct prefix *) p) == FILTER_DENY) { if (IS_RIP_DEBUG_PACKET) @@ -216,7 +200,7 @@ rip_incoming_filter (struct prefix_ipv4 *p, struct rip_interface *ri) } if (ri->prefix[RIP_FILTER_IN]) { - if (prefix_list_apply (ri->prefix[RIP_FILTER_IN], + if (prefix_list_apply (ri->prefix[RIP_FILTER_IN], (struct prefix *) p) == PREFIX_DENY) { if (IS_RIP_DEBUG_PACKET) @@ -233,7 +217,7 @@ rip_incoming_filter (struct prefix_ipv4 *p, struct rip_interface *ri) if (dist->list[DISTRIBUTE_IN]) { alist = access_list_lookup (AFI_IP, dist->list[DISTRIBUTE_IN]); - + if (alist) { if (access_list_apply (alist, @@ -249,7 +233,7 @@ rip_incoming_filter (struct prefix_ipv4 *p, struct rip_interface *ri) if (dist->prefix[DISTRIBUTE_IN]) { plist = prefix_list_lookup (AFI_IP, dist->prefix[DISTRIBUTE_IN]); - + if (plist) { if (prefix_list_apply (plist, @@ -303,7 +287,7 @@ rip_outgoing_filter (struct prefix_ipv4 *p, struct rip_interface *ri) if (dist->list[DISTRIBUTE_OUT]) { alist = access_list_lookup (AFI_IP, dist->list[DISTRIBUTE_OUT]); - + if (alist) { if (access_list_apply (alist, @@ -319,7 +303,7 @@ rip_outgoing_filter (struct prefix_ipv4 *p, struct rip_interface *ri) if (dist->prefix[DISTRIBUTE_OUT]) { plist = prefix_list_lookup (AFI_IP, dist->prefix[DISTRIBUTE_OUT]); - + if (plist) { if (prefix_list_apply (plist, @@ -351,7 +335,7 @@ rip_nexthop_check (struct in_addr *addr) for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) { for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, ifc)) - { + { p = ifc->address; if (p->family == AF_INET @@ -490,9 +474,9 @@ rip_rte_process (struct rte *rte, struct sockaddr_in *from, /* Only connected routes may have a valid NULL distance */ if (rinfo->type != ZEBRA_ROUTE_CONNECT) old_dist = old_dist ? old_dist : ZEBRA_RIP_DISTANCE_DEFAULT; - /* If imported route does not have STRICT precedence, + /* If imported route does not have STRICT precedence, mark it as a ghost */ - if (new_dist > old_dist + if (new_dist > old_dist || rte->metric == RIP_METRIC_INFINITY) { route_unlock_node (rp); @@ -502,10 +486,10 @@ rip_rte_process (struct rte *rte, struct sockaddr_in *from, { RIP_TIMER_OFF (rinfo->t_timeout); RIP_TIMER_OFF (rinfo->t_garbage_collect); - + rp->info = NULL; if (rip_route_rte (rinfo)) - rip_zebra_ipv4_delete ((struct prefix_ipv4 *)&rp->p, + rip_zebra_ipv4_delete ((struct prefix_ipv4 *)&rp->p, &rinfo->nexthop, rinfo->metric); rip_info_free (rinfo); rinfo = NULL; @@ -720,7 +704,7 @@ rip_packet_dump (struct rip_packet *packet, int size, const char *sndrcv) /* Dump each routing table entry. */ rte = packet->rte; - + for (lim = (caddr_t) packet + size; (caddr_t) rte < lim; rte++) { if (packet->version == RIPv2) @@ -778,7 +762,7 @@ rip_packet_dump (struct rip_packet *packet, int size, const char *sndrcv) } else { - zlog_debug (" %s family %d tag %d metric %ld", + zlog_debug (" %s family %d tag %d metric %ld", inet_ntop (AF_INET, &rte->prefix, pbuf, BUFSIZ), ntohs (rte->family), ntohs (rte->tag), (u_long)ntohl (rte->metric)); @@ -837,7 +821,7 @@ rip_auth_simple_password (struct rte *rte, struct sockaddr_in *from, if (ri->auth_str) { auth_str = (char *) &rte->prefix; - + if (strncmp (auth_str, ri->auth_str, 16) == 0) return 1; } @@ -871,7 +855,7 @@ rip_auth_md5 (struct rip_packet *packet, struct sockaddr_in *from, u_char digest[RIP_AUTH_MD5_SIZE]; u_int16_t packet_len; char auth_str[RIP_AUTH_MD5_SIZE]; - + if (IS_RIP_DEBUG_EVENT) zlog_debug ("RIPv2 MD5 authentication from %s", inet_ntoa (from->sin_addr)); @@ -910,7 +894,7 @@ rip_auth_md5 (struct rip_packet *packet, struct sockaddr_in *from, /* retrieve authentication data */ md5data = (struct rip_md5_data *) (((u_char *) packet) + packet_len); - + memset (auth_str, 0, RIP_AUTH_MD5_SIZE); if (ri->key_chain) @@ -930,14 +914,14 @@ rip_auth_md5 (struct rip_packet *packet, struct sockaddr_in *from, if (auth_str[0] == 0) return 0; - + /* MD5 digest authentication. */ memset (&ctx, 0, sizeof(ctx)); MD5Init(&ctx); MD5Update(&ctx, packet, packet_len + RIP_HEADER_SIZE); MD5Update(&ctx, auth_str, RIP_AUTH_MD5_SIZE); MD5Final(digest, &ctx); - + if (memcmp (md5data->digest, digest, RIP_AUTH_MD5_SIZE) == 0) return packet_len; else @@ -953,7 +937,7 @@ rip_auth_md5 (struct rip_packet *packet, struct sockaddr_in *from, * */ static void -rip_auth_prepare_str_send (struct rip_interface *ri, struct key *key, +rip_auth_prepare_str_send (struct rip_interface *ri, struct key *key, char *auth_str, int len) { assert (ri || key); @@ -969,22 +953,22 @@ rip_auth_prepare_str_send (struct rip_interface *ri, struct key *key, /* Write RIPv2 simple password authentication information * - * auth_str is presumed to be 2 bytes and correctly prepared + * auth_str is presumed to be 2 bytes and correctly prepared * (left justified and zero padded). */ static void rip_auth_simple_write (struct stream *s, char *auth_str, int len) { assert (s && len == RIP_AUTH_SIMPLE_SIZE); - + stream_putw (s, RIP_FAMILY_AUTH); stream_putw (s, RIP_AUTH_SIMPLE_PASSWORD); stream_put (s, auth_str, RIP_AUTH_SIMPLE_SIZE); - + return; } -/* write RIPv2 MD5 "authentication header" +/* write RIPv2 MD5 "authentication header" * (uses the auth key data field) * * Digest offset field is set to 0. @@ -993,7 +977,7 @@ rip_auth_simple_write (struct stream *s, char *auth_str, int len) * length to the auth-data MD5 digest is known. */ static size_t -rip_auth_md5_ah_write (struct stream *s, struct rip_interface *ri, +rip_auth_md5_ah_write (struct stream *s, struct rip_interface *ri, struct key *key) { size_t doff = 0; @@ -1018,9 +1002,9 @@ rip_auth_md5_ah_write (struct stream *s, struct rip_interface *ri, else stream_putc (s, 1); - /* Auth Data Len. Set 16 for MD5 authentication data. Older ripds + /* Auth Data Len. Set 16 for MD5 authentication data. Older ripds * however expect RIP_HEADER_SIZE + RIP_AUTH_MD5_SIZE so we allow for this - * to be configurable. + * to be configurable. */ stream_putc (s, ri->md5_auth_len); @@ -1029,7 +1013,7 @@ rip_auth_md5_ah_write (struct stream *s, struct rip_interface *ri, arbitrary, but two suggestions are the time of the message's creation or a simple message counter. */ stream_putl (s, time (NULL)); - + /* Reserved field must be zero. */ stream_putl (s, 0); stream_putl (s, 0); @@ -1042,11 +1026,11 @@ rip_auth_md5_ah_write (struct stream *s, struct rip_interface *ri, * or 0 if this is not required */ static size_t -rip_auth_header_write (struct stream *s, struct rip_interface *ri, +rip_auth_header_write (struct stream *s, struct rip_interface *ri, struct key *key, char *auth_str, int len) { assert (ri->auth_type != RIP_NO_AUTH); - + switch (ri->auth_type) { case RIP_AUTH_SIMPLE_PASSWORD: @@ -1073,7 +1057,7 @@ rip_auth_md5_set (struct stream *s, struct rip_interface *ri, size_t doff, authentication. */ assert ((ri->auth_type == RIP_AUTH_MD5) && (authlen == RIP_AUTH_MD5_SIZE)); assert (doff > 0); - + /* Get packet length. */ len = stream_get_endp(s); @@ -1086,7 +1070,7 @@ rip_auth_md5_set (struct stream *s, struct rip_interface *ri, size_t doff, /* Set the digest offset length in the header */ stream_putw_at (s, doff, len); - + /* Set authentication data. */ stream_putw (s, RIP_FAMILY_AUTH); stream_putw (s, RIP_AUTH_DATA); @@ -1104,7 +1088,7 @@ rip_auth_md5_set (struct stream *s, struct rip_interface *ri, size_t doff, /* RIP routing information. */ static void -rip_response_process (struct rip_packet *packet, int size, +rip_response_process (struct rip_packet *packet, int size, struct sockaddr_in *from, struct connected *ifc) { caddr_t lim; @@ -1112,7 +1096,7 @@ rip_response_process (struct rip_packet *packet, int size, struct prefix_ipv4 ifaddr; struct prefix_ipv4 ifaddrclass; int subnetted; - + /* We don't know yet. */ subnetted = -1; ifaddr.prefixlen = 0 ; /* eliminate a compiler warning */ @@ -1130,7 +1114,7 @@ rip_response_process (struct rip_packet *packet, int size, /* The datagram's IPv4 source address should be checked to see whether the datagram is from a valid neighbor; the source of the datagram must be on a directly connected network (RFC2453 - Sec. 3.9.2) */ - if (if_lookup_address(from->sin_addr) == NULL) + if (if_lookup_address(from->sin_addr) == NULL) { zlog_info ("This datagram doesn't came from a valid neighbor: %s", inet_ntoa (from->sin_addr)); @@ -1177,7 +1161,7 @@ rip_response_process (struct rip_packet *packet, int size, zlog_info ("Network is net 0 or net 127 or it is not unicast network"); rip_peer_bad_route (from); continue; - } + } /* Convert metric value to host byte order. */ rte->metric = ntohl (rte->metric); @@ -1253,18 +1237,18 @@ rip_response_process (struct rip_packet *packet, int size, } } - /* For RIPv1, there won't be a valid netmask. + /* For RIPv1, there won't be a valid netmask. This is a best guess at the masks. If everyone was using old Ciscos before the 'ip subnet zero' option, it would be almost right too :-) - + Cisco summarize ripv1 advertisments to the classful boundary (/16 for class B's) except when the RIP packet does to inside the classful network in question. */ - if ((packet->version == RIPv1 && rte->prefix.s_addr != 0) - || (packet->version == RIPv2 + if ((packet->version == RIPv1 && rte->prefix.s_addr != 0) + || (packet->version == RIPv2 && (rte->prefix.s_addr != 0 && rte->mask.s_addr == 0))) { u_int32_t destination; @@ -1315,8 +1299,8 @@ rip_response_process (struct rip_packet *packet, int size, /* In case of RIPv2, if prefix in RTE is not netmask applied one ignore the entry. */ - if ((packet->version == RIPv2) - && (rte->mask.s_addr != 0) + if ((packet->version == RIPv2) + && (rte->mask.s_addr != 0) && ((rte->prefix.s_addr & rte->mask.s_addr) != rte->prefix.s_addr)) { zlog_warn ("RIPv2 address %s is not mask /%d applied one", @@ -1334,22 +1318,22 @@ rip_response_process (struct rip_packet *packet, int size, zlog_debug ("Default route with non-zero netmask. Set zero to netmask"); rte->mask.s_addr = 0; } - + /* Routing table updates. */ rip_rte_process (rte, from, ifc->ifp); } } /* Make socket for RIP protocol. */ -static int +static int rip_create_socket (struct sockaddr_in *from) { int ret; int sock; struct sockaddr_in addr; - + memset (&addr, 0, sizeof (struct sockaddr_in)); - + if (!from) { addr.sin_family = AF_INET; @@ -1360,21 +1344,21 @@ rip_create_socket (struct sockaddr_in *from) } else { memcpy(&addr, from, sizeof(addr)); } - + /* sending port must always be the RIP port */ addr.sin_port = htons (RIP_PORT_DEFAULT); - + /* Make datagram socket. */ sock = socket (AF_INET, SOCK_DGRAM, 0); - if (sock < 0) + if (sock < 0) { zlog_err("Cannot create UDP socket: %s", safe_strerror(errno)); exit (1); } - sockopt_broadcast (sock); - sockopt_reuseaddr (sock); - sockopt_reuseport (sock); + setsockopt_broadcast (sock); + setsockopt_reuseaddr (sock); + setsockopt_reuseport (sock); #ifdef RIP_RECVMSG setsockopt_pktinfo (sock); #endif /* RIP_RECVMSG */ @@ -1386,24 +1370,24 @@ rip_create_socket (struct sockaddr_in *from) zlog_err ("rip_create_socket: could not raise privs"); setsockopt_so_recvbuf (sock, RIP_UDP_RCV_BUF); if ( (ret = bind (sock, (struct sockaddr *) & addr, sizeof (addr))) < 0) - + { int save_errno = errno; if (ripd_privs.change (ZPRIVS_LOWER)) zlog_err ("rip_create_socket: could not lower privs"); - + zlog_err("%s: Can't bind socket %d to %s port %d: %s", __func__, - sock, inet_ntoa(addr.sin_addr), - (int) ntohs(addr.sin_port), + sock, inet_ntoa(addr.sin_addr), + (int) ntohs(addr.sin_port), safe_strerror(save_errno)); - + close (sock); return ret; } - + if (ripd_privs.change (ZPRIVS_LOWER)) zlog_err ("rip_create_socket: could not lower privs"); - + return sock; } @@ -1417,15 +1401,15 @@ rip_send_packet (u_char * buf, int size, struct sockaddr_in *to, { int ret, send_sock; struct sockaddr_in sin; - + assert (ifc != NULL); - + if (IS_RIP_DEBUG_PACKET) { #define ADDRESS_SIZE 20 char dst[ADDRESS_SIZE]; dst[ADDRESS_SIZE - 1] = '\0'; - + if (to) { strncpy (dst, inet_ntoa(to->sin_addr), ADDRESS_SIZE - 1); @@ -1440,19 +1424,19 @@ rip_send_packet (u_char * buf, int size, struct sockaddr_in *to, inet_ntoa(ifc->address->u.prefix4), dst, ifc->ifp->name); } - + if ( CHECK_FLAG (ifc->flags, ZEBRA_IFA_SECONDARY) ) { /* * ZEBRA_IFA_SECONDARY is set on linux when an interface is configured * with multiple addresses on the same subnet: the first address * on the subnet is configured "primary", and all subsequent addresses - * on that subnet are treated as "secondary" addresses. - * In order to avoid routing-table bloat on other rip listeners, + * on that subnet are treated as "secondary" addresses. + * In order to avoid routing-table bloat on other rip listeners, * we do not send out RIP packets with ZEBRA_IFA_SECONDARY source addrs. * XXX Since Linux is the only system for which the ZEBRA_IFA_SECONDARY * flag is set, we would end up sending a packet for a "secondary" - * source address on non-linux systems. + * source address on non-linux systems. */ if (IS_RIP_DEBUG_PACKET) zlog_debug("duplicate dropped"); @@ -1476,10 +1460,10 @@ rip_send_packet (u_char * buf, int size, struct sockaddr_in *to, else { struct sockaddr_in from; - + sin.sin_port = htons (RIP_PORT_DEFAULT); sin.sin_addr.s_addr = htonl (INADDR_RIP_GROUP); - + /* multicast send should bind to local interface address */ from.sin_family = AF_INET; from.sin_port = htons (RIP_PORT_DEFAULT); @@ -1487,11 +1471,11 @@ rip_send_packet (u_char * buf, int size, struct sockaddr_in *to, #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN from.sin_len = sizeof (struct sockaddr_in); #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ - + /* * we have to open a new socket for each packet because this * is the most portable way to bind to a different source - * ipv4 address for each packet. + * ipv4 address for each packet. */ if ( (send_sock = rip_create_socket (&from)) < 0) { @@ -1505,7 +1489,7 @@ rip_send_packet (u_char * buf, int size, struct sockaddr_in *to, sizeof (struct sockaddr_in)); if (IS_RIP_DEBUG_EVENT) - zlog_debug ("SEND to %s.%d", inet_ntoa(sin.sin_addr), + zlog_debug ("SEND to %s.%d", inet_ntoa(sin.sin_addr), ntohs (sin.sin_port)); if (ret < 0) @@ -1519,7 +1503,7 @@ rip_send_packet (u_char * buf, int size, struct sockaddr_in *to, /* Add redistributed route to RIP table. */ void -rip_redistribute_add (int type, int sub_type, struct prefix_ipv4 *p, +rip_redistribute_add (int type, int sub_type, struct prefix_ipv4 *p, unsigned int ifindex, struct in_addr *nexthop, unsigned int metric, unsigned char distance) { @@ -1538,7 +1522,7 @@ rip_redistribute_add (int type, int sub_type, struct prefix_ipv4 *p, if (rinfo) { - if (rinfo->type == ZEBRA_ROUTE_CONNECT + if (rinfo->type == ZEBRA_ROUTE_CONNECT && rinfo->sub_type == RIP_ROUTE_INTERFACE && rinfo->metric != RIP_METRIC_INFINITY) { @@ -1547,7 +1531,7 @@ rip_redistribute_add (int type, int sub_type, struct prefix_ipv4 *p, } /* Manually configured RIP route check. */ - if (rinfo->type == ZEBRA_ROUTE_RIP + if (rinfo->type == ZEBRA_ROUTE_RIP && ((rinfo->sub_type == RIP_ROUTE_STATIC) || (rinfo->sub_type == RIP_ROUTE_DEFAULT)) ) { @@ -1567,12 +1551,12 @@ rip_redistribute_add (int type, int sub_type, struct prefix_ipv4 *p, rinfo->metric); rp->info = NULL; rip_info_free (rinfo); - - route_unlock_node (rp); + + route_unlock_node (rp); } rinfo = rip_info_new (); - + rinfo->type = type; rinfo->sub_type = sub_type; rinfo->ifindex = ifindex; @@ -1606,7 +1590,7 @@ rip_redistribute_add (int type, int sub_type, struct prefix_ipv4 *p, /* Delete redistributed route from RIP table. */ void -rip_redistribute_delete (int type, int sub_type, struct prefix_ipv4 *p, +rip_redistribute_delete (int type, int sub_type, struct prefix_ipv4 *p, unsigned int ifindex) { int ret; @@ -1623,13 +1607,13 @@ rip_redistribute_delete (int type, int sub_type, struct prefix_ipv4 *p, rinfo = rp->info; if (rinfo != NULL - && rinfo->type == type - && rinfo->sub_type == sub_type + && rinfo->type == type + && rinfo->sub_type == sub_type && rinfo->ifindex == ifindex) { /* Perform poisoned reverse. */ rinfo->metric = RIP_METRIC_INFINITY; - RIP_TIMER_ON (rinfo->t_garbage_collect, + RIP_TIMER_ON (rinfo->t_garbage_collect, rip_garbage_collect, rip->garbage_time); RIP_TIMER_OFF (rinfo->t_timeout); rinfo->flags |= RIP_RTF_CHANGED; @@ -1646,7 +1630,7 @@ rip_redistribute_delete (int type, int sub_type, struct prefix_ipv4 *p, /* Response to request called from rip_read ().*/ static void -rip_request_process (struct rip_packet *packet, int size, +rip_request_process (struct rip_packet *packet, int size, struct sockaddr_in *from, struct connected *ifc) { caddr_t lim; @@ -1668,7 +1652,7 @@ rip_request_process (struct rip_packet *packet, int size, /* When passive interface is specified, suppress responses */ if (ri->passive) return; - + /* RIP peer update. */ rip_peer_update (from, packet->version); @@ -1687,7 +1671,7 @@ rip_request_process (struct rip_packet *packet, int size, if (lim == ((caddr_t) (rte + 1)) && ntohs (rte->family) == 0 && ntohl (rte->metric) == RIP_METRIC_INFINITY) - { + { struct prefix_ipv4 saddr; /* saddr will be used for determining which routes to split-horizon. @@ -1718,7 +1702,7 @@ rip_request_process (struct rip_packet *packet, int size, p.prefix = rte->prefix; p.prefixlen = ip_masklen (rte->mask); apply_mask_ipv4 (&p); - + rp = route_node_lookup (rip->table, (struct prefix *) &p); if (rp) { @@ -1743,7 +1727,7 @@ setsockopt_pktinfo (int sock) { int ret; int val = 1; - + ret = setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &val, sizeof(val)); if (ret < 0) zlog_warn ("Can't setsockopt IP_PKTINFO : %s", safe_strerror (errno)); @@ -1775,7 +1759,7 @@ rip_recvmsg (int sock, u_char *buf, int size, struct sockaddr_in *from, return ret; for (ptr = ZCMSG_FIRSTHDR(&msg); ptr != NULL; ptr = CMSG_NXTHDR(&msg, ptr)) - if (ptr->cmsg_level == IPPROTO_IP && ptr->cmsg_type == IP_PKTINFO) + if (ptr->cmsg_level == IPPROTO_IP && ptr->cmsg_type == IP_PKTINFO) { struct in_pktinfo *pktinfo; int i; @@ -1795,7 +1779,7 @@ rip_read_new (struct thread *t) char buf[RIP_PACKET_MAXSIZ]; struct sockaddr_in from; unsigned int ifindex; - + /* Fetch socket then register myself. */ sock = THREAD_FD (t); rip_event (RIP_READ, sock); @@ -1840,16 +1824,16 @@ rip_read (struct thread *t) memset (&from, 0, sizeof (struct sockaddr_in)); fromlen = sizeof (struct sockaddr_in); - len = recvfrom (sock, (char *)&rip_buf.buf, sizeof (rip_buf.buf), 0, + len = recvfrom (sock, (char *)&rip_buf.buf, sizeof (rip_buf.buf), 0, (struct sockaddr *) &from, &fromlen); - if (len < 0) + if (len < 0) { zlog_info ("recvfrom failed: %s", safe_strerror (errno)); return len; } /* Check is this packet comming from myself? */ - if (if_check_address (from.sin_addr)) + if (if_check_address (from.sin_addr)) { if (IS_RIP_DEBUG_PACKET) zlog_debug ("ignore packet comes from myself"); @@ -1858,7 +1842,7 @@ rip_read (struct thread *t) /* Which interface is this packet comes from. */ ifp = if_lookup_address (from.sin_addr); - + /* RIP packet received */ if (IS_RIP_DEBUG_EVENT) zlog_debug ("RECV packet from %s port %d on %s", @@ -1872,9 +1856,9 @@ rip_read (struct thread *t) inet_ntoa(from.sin_addr), ntohs (from.sin_port)); return -1; } - + ifc = connected_lookup_address (ifp, from.sin_addr); - + if (ifc == NULL) { zlog_info ("rip_read: cannot find connected address for packet from %s " @@ -1948,7 +1932,7 @@ rip_read (struct thread *t) if ((packet->version == RIPv1) && !(vrecv & RIPv1)) { if (IS_RIP_DEBUG_PACKET) - zlog_debug (" packet's v%d doesn't fit to if version spec", + zlog_debug (" packet's v%d doesn't fit to if version spec", packet->version); rip_peer_bad_packet (&from); return -1; @@ -1956,27 +1940,27 @@ rip_read (struct thread *t) if ((packet->version == RIPv2) && !(vrecv & RIPv2)) { if (IS_RIP_DEBUG_PACKET) - zlog_debug (" packet's v%d doesn't fit to if version spec", + zlog_debug (" packet's v%d doesn't fit to if version spec", packet->version); rip_peer_bad_packet (&from); return -1; } - + /* RFC2453 5.2 If the router is not configured to authenticate RIP-2 messages, then RIP-1 and unauthenticated RIP-2 messages will be accepted; authenticated RIP-2 messages shall be discarded. */ - if ((ri->auth_type == RIP_NO_AUTH) - && rtenum - && (packet->version == RIPv2) + if ((ri->auth_type == RIP_NO_AUTH) + && rtenum + && (packet->version == RIPv2) && (packet->rte->family == htons(RIP_FAMILY_AUTH))) { if (IS_RIP_DEBUG_EVENT) - zlog_debug ("packet RIPv%d is dropped because authentication disabled", + zlog_debug ("packet RIPv%d is dropped because authentication disabled", packet->version); rip_peer_bad_packet (&from); return -1; } - + /* RFC: If the router is configured to authenticate RIP-2 messages, then RIP-1 messages and RIP-2 messages which pass authentication @@ -1985,7 +1969,7 @@ rip_read (struct thread *t) security, RIP-1 messages should be ignored when authentication is in use (see section 4.1); otherwise, the routing information from authenticated messages will be propagated by RIP-1 routers in an - unauthenticated manner. + unauthenticated manner. */ /* We make an exception for RIPv1 REQUEST packets, to which we'll * always reply regardless of authentication settings, because: @@ -2002,7 +1986,7 @@ rip_read (struct thread *t) * routing-information freely, while still requiring RIPv2 * authentication for any RESPONSEs might be vaguely useful. */ - if (ri->auth_type != RIP_NO_AUTH + if (ri->auth_type != RIP_NO_AUTH && packet->version == RIPv1) { /* Discard RIPv1 messages other than REQUESTs */ @@ -2017,7 +2001,7 @@ rip_read (struct thread *t) else if (ri->auth_type != RIP_NO_AUTH) { const char *auth_desc; - + if (rtenum == 0) { /* There definitely is no authentication in the packet. */ @@ -2026,7 +2010,7 @@ rip_read (struct thread *t) rip_peer_bad_packet (&from); return -1; } - + /* First RTE must be an Authentication Family RTE */ if (packet->rte->family != htons(RIP_FAMILY_AUTH)) { @@ -2035,7 +2019,7 @@ rip_read (struct thread *t) rip_peer_bad_packet (&from); return -1; } - + /* Check RIPv2 authentication. */ switch (ntohs(packet->rte->tag)) { @@ -2043,14 +2027,14 @@ rip_read (struct thread *t) auth_desc = "simple"; ret = rip_auth_simple_password (packet->rte, &from, ifp); break; - + case RIP_AUTH_MD5: auth_desc = "MD5"; ret = rip_auth_md5 (packet, &from, len, ifp); /* Reset RIP packet length to trim MD5 data. */ len = ret; break; - + default: ret = 0; auth_desc = "unknown type"; @@ -2058,7 +2042,7 @@ rip_read (struct thread *t) zlog_debug ("RIPv2 Unknown authentication type %d", ntohs (packet->rte->tag)); } - + if (ret) { if (IS_RIP_DEBUG_PACKET) @@ -2072,7 +2056,7 @@ rip_read (struct thread *t) return -1; } } - + /* Process each command. */ switch (packet->command) { @@ -2085,12 +2069,12 @@ rip_read (struct thread *t) break; case RIP_TRACEON: case RIP_TRACEOFF: - zlog_info ("Obsolete command %s received, please sent it to routed", + zlog_info ("Obsolete command %s received, please sent it to routed", lookup (rip_msg, packet->command)); rip_peer_bad_packet (&from); break; case RIP_POLL_ENTRY: - zlog_info ("Obsolete command %s received", + zlog_info ("Obsolete command %s received", lookup (rip_msg, packet->command)); rip_peer_bad_packet (&from); break; @@ -2138,7 +2122,7 @@ rip_write_rte (int num, struct stream *s, struct prefix_ipv4 *p, /* Send update to the ifp or spcified neighbor. */ void -rip_output_process (struct connected *ifc, struct sockaddr_in *to, +rip_output_process (struct connected *ifc, struct sockaddr_in *to, int route_type, u_char version) { int ret; @@ -2177,7 +2161,7 @@ rip_output_process (struct connected *ifc, struct sockaddr_in *to, /* Get RIP interface. */ ri = ifc->ifp->info; - + /* If output interface is in simple password authentication mode, we need space for authentication data. */ if (ri->auth_type == RIP_AUTH_SIMPLE_PASSWORD) @@ -2219,7 +2203,7 @@ rip_output_process (struct connected *ifc, struct sockaddr_in *to, /* For RIPv1, if we are subnetted, output subnets in our network */ /* that have the same mask as the output "interface". For other */ /* networks, only the classfull version is output. */ - + if (version == RIPv1) { p = (struct prefix_ipv4 *) &rp->p; @@ -2247,7 +2231,7 @@ rip_output_process (struct connected *ifc, struct sockaddr_in *to, zlog_debug("RIPv1 mask check, %s/%d made it through", inet_ntoa (rp->p.u.prefix4), rp->p.prefixlen); } - else + else p = (struct prefix_ipv4 *) &rp->p; /* Apply output filters. */ @@ -2264,18 +2248,18 @@ rip_output_process (struct connected *ifc, struct sockaddr_in *to, /* if (split_horizon == rip_split_horizon) */ if (ri->split_horizon == RIP_SPLIT_HORIZON) { - /* - * We perform split horizon for RIP and connected route. + /* + * We perform split horizon for RIP and connected route. * For rip routes, we want to suppress the route if we would * end up sending the route back on the interface that we * learned it from, with a higher metric. For connected routes, * we suppress the route if the prefix is a subset of the - * source address that we are going to use for the packet + * source address that we are going to use for the packet * (in order to handle the case when multiple subnets are * configured on the same interface). */ if (rinfo->type == ZEBRA_ROUTE_RIP && - rinfo->ifindex == ifc->ifp->ifindex) + rinfo->ifindex == ifc->ifp->ifindex) continue; if (rinfo->type == ZEBRA_ROUTE_CONNECT && prefix_match((struct prefix *)p, ifc->address)) @@ -2302,8 +2286,8 @@ rip_output_process (struct connected *ifc, struct sockaddr_in *to, /* Interface route-map */ if (ri->routemap[RIP_FILTER_OUT]) { - ret = route_map_apply (ri->routemap[RIP_FILTER_OUT], - (struct prefix *) p, RMAP_RIP, + ret = route_map_apply (ri->routemap[RIP_FILTER_OUT], + (struct prefix *) p, RMAP_RIP, rinfo); if (ret == RMAP_DENYMATCH) @@ -2314,7 +2298,7 @@ rip_output_process (struct connected *ifc, struct sockaddr_in *to, continue; } } - + /* Apply redistribute route map - continue, if deny */ if (rip->route_map[rinfo->type].name && rinfo->sub_type != RIP_ROUTE_INTERFACE) @@ -2322,7 +2306,7 @@ rip_output_process (struct connected *ifc, struct sockaddr_in *to, ret = route_map_apply (rip->route_map[rinfo->type].map, (struct prefix *)p, RMAP_RIP, rinfo); - if (ret == RMAP_DENYMATCH) + if (ret == RMAP_DENYMATCH) { if (IS_RIP_DEBUG_PACKET) zlog_debug ("%s/%d is filtered by route-map", @@ -2344,7 +2328,7 @@ rip_output_process (struct connected *ifc, struct sockaddr_in *to, { /* If the route is not connected or localy generated one, use default-metric value*/ - if (rinfo->type != ZEBRA_ROUTE_RIP + if (rinfo->type != ZEBRA_ROUTE_RIP && rinfo->type != ZEBRA_ROUTE_CONNECT && rinfo->metric != RIP_METRIC_INFINITY) rinfo->metric_out = rip->default_metric; @@ -2358,17 +2342,17 @@ rip_output_process (struct connected *ifc, struct sockaddr_in *to, if (rinfo->metric_out > RIP_METRIC_INFINITY) rinfo->metric_out = RIP_METRIC_INFINITY; - /* Perform split-horizon with poisoned reverse + /* Perform split-horizon with poisoned reverse * for RIP and connected routes. **/ if (ri->split_horizon == RIP_SPLIT_HORIZON_POISONED_REVERSE) { - /* - * We perform split horizon for RIP and connected route. + /* + * We perform split horizon for RIP and connected route. * For rip routes, we want to suppress the route if we would * end up sending the route back on the interface that we * learned it from, with a higher metric. For connected routes, * we suppress the route if the prefix is a subset of the - * source address that we are going to use for the packet + * source address that we are going to use for the packet * (in order to handle the case when multiple subnets are * configured on the same interface). */ @@ -2379,20 +2363,20 @@ rip_output_process (struct connected *ifc, struct sockaddr_in *to, prefix_match((struct prefix *)p, ifc->address)) rinfo->metric_out = RIP_METRIC_INFINITY; } - + /* Prepare preamble, auth headers, if needs be */ if (num == 0) { stream_putc (s, RIP_RESPONSE); stream_putc (s, version); stream_putw (s, 0); - + /* auth header for !v1 && !no_auth */ if ( (ri->auth_type != RIP_NO_AUTH) && (version != RIPv1) ) - doff = rip_auth_header_write (s, ri, key, auth_str, + doff = rip_auth_header_write (s, ri, key, auth_str, RIP_AUTH_SIMPLE_SIZE); } - + /* Write RTE to the stream. */ num = rip_write_rte (num, s, p, version, rinfo); if (num == rtemax) @@ -2437,7 +2421,7 @@ rip_update_interface (struct connected *ifc, u_char version, int route_type) struct sockaddr_in to; /* When RIP version is 2 and multicast enable interface. */ - if (version == RIPv2 && if_is_multicast (ifc->ifp)) + if (version == RIPv2 && if_is_multicast (ifc->ifp)) { if (IS_RIP_DEBUG_EVENT) zlog_debug ("multicast announce on %s ", ifc->ifp->name); @@ -2445,7 +2429,7 @@ rip_update_interface (struct connected *ifc, u_char version, int route_type) rip_output_process (ifc, NULL, route_type, version); return; } - + /* If we can't send multicast packet, send it with unicast. */ if (if_is_broadcast (ifc->ifp) || if_is_pointopoint (ifc->ifp)) { @@ -2508,14 +2492,14 @@ rip_update_process (int route_type) if (ri->running) { - /* + /* * If there is no version configuration in the interface, - * use rip's version setting. + * use rip's version setting. */ int vsend = ((ri->ri_send == RI_RIP_UNSPEC) ? rip->version_send : ri->ri_send); - if (IS_RIP_DEBUG_EVENT) + if (IS_RIP_DEBUG_EVENT) zlog_debug("SEND UPDATE to %s ifindex %d", (ifp->name ? ifp->name : "_unknown_"), ifp->ifindex); @@ -2546,14 +2530,14 @@ rip_update_process (int route_type) inet_ntoa (p->prefix)); continue; } - + if ( (connected = connected_lookup_address (ifp, p->prefix)) == NULL) { zlog_warn ("Neighbor %s doesnt have connected network", inet_ntoa (p->prefix)); continue; } - + /* Set destination address and port */ memset (&to, 0, sizeof (struct sockaddr_in)); to.sin_addr = p->prefix; @@ -2619,7 +2603,7 @@ rip_triggered_interval (struct thread *t) rip_triggered_update (t); } return 0; -} +} /* Execute triggered update. */ static int @@ -2656,7 +2640,7 @@ rip_triggered_update (struct thread *t) update is triggered when the timer expires. */ interval = (random () % 5) + 1; - rip->t_triggered_interval = + rip->t_triggered_interval = thread_add_timer (master, rip_triggered_interval, NULL, interval); return 0; @@ -2680,7 +2664,7 @@ rip_redistribute_withdraw (int type) { /* Perform poisoned reverse. */ rinfo->metric = RIP_METRIC_INFINITY; - RIP_TIMER_ON (rinfo->t_garbage_collect, + RIP_TIMER_ON (rinfo->t_garbage_collect, rip_garbage_collect, rip->garbage_time); RIP_TIMER_OFF (rinfo->t_timeout); rinfo->flags |= RIP_RTF_CHANGED; @@ -2748,20 +2732,20 @@ rip_request_send (struct sockaddr_in *to, struct interface *ifp, rte = rip_packet.rte; rte->metric = htonl (RIP_METRIC_INFINITY); - if (connected) + if (connected) { - /* + /* * connected is only sent for ripv1 case, or when * interface does not support multicast. Caller loops * over each connected address for this case. */ - if (rip_send_packet ((u_char *) &rip_packet, sizeof (rip_packet), + if (rip_send_packet ((u_char *) &rip_packet, sizeof (rip_packet), to, connected) != sizeof (rip_packet)) return -1; else return sizeof (rip_packet); } - + /* send request on each connected network */ for (ALL_LIST_ELEMENTS (ifp->connected, node, nnode, connected)) { @@ -2772,13 +2756,13 @@ rip_request_send (struct sockaddr_in *to, struct interface *ifp, if (p->family != AF_INET) continue; - if (rip_send_packet ((u_char *) &rip_packet, sizeof (rip_packet), + if (rip_send_packet ((u_char *) &rip_packet, sizeof (rip_packet), to, connected) != sizeof (rip_packet)) return -1; } return sizeof (rip_packet); } - + static int rip_update_jitter (unsigned long time) { @@ -2788,14 +2772,14 @@ rip_update_jitter (unsigned long time) The RIPv2 RFC says jitter should be small compared to update_time. We consider 1/JITTER_BOUND to be small. */ - + int jitter_input = time; int jitter; - + if (jitter_input < JITTER_BOUND) jitter_input = JITTER_BOUND; - - jitter = (((rand () % ((jitter_input * 2) + 1)) - jitter_input)); + + jitter = (((rand () % ((jitter_input * 2) + 1)) - jitter_input)); return jitter/JITTER_BOUND; } @@ -2817,22 +2801,22 @@ rip_event (enum rip_event event, int sock) rip->t_update = NULL; } jitter = rip_update_jitter (rip->update_time); - rip->t_update = - thread_add_timer (master, rip_update, NULL, + rip->t_update = + thread_add_timer (master, rip_update, NULL, sock ? 2 : rip->update_time + jitter); break; case RIP_TRIGGERED_UPDATE: if (rip->t_triggered_interval) rip->trigger = 1; else if (! rip->t_triggered_update) - rip->t_triggered_update = + rip->t_triggered_update = thread_add_event (master, rip_triggered_update, NULL, 0); break; default: break; } } - + DEFUN (router_rip, router_rip_cmd, "router rip", @@ -2888,7 +2872,7 @@ DEFUN (rip_version, rip->version_recv = version; return CMD_SUCCESS; -} +} DEFUN (no_rip_version, no_rip_version_cmd, @@ -2901,7 +2885,7 @@ DEFUN (no_rip_version, rip->version_recv = RI_RIP_VERSION_1_AND_2; return CMD_SUCCESS; -} +} ALIAS (no_rip_version, no_rip_version_val_cmd, @@ -3049,21 +3033,21 @@ DEFUN (rip_timers, unsigned long RIP_TIMER_MIN = 5; update = strtoul (argv[0], &endptr, 10); - if (update > RIP_TIMER_MAX || update < RIP_TIMER_MIN || *endptr != '\0') + if (update > RIP_TIMER_MAX || update < RIP_TIMER_MIN || *endptr != '\0') { vty_out (vty, "update timer value error%s", VTY_NEWLINE); return CMD_WARNING; } - + timeout = strtoul (argv[1], &endptr, 10); - if (timeout > RIP_TIMER_MAX || timeout < RIP_TIMER_MIN || *endptr != '\0') + if (timeout > RIP_TIMER_MAX || timeout < RIP_TIMER_MIN || *endptr != '\0') { vty_out (vty, "timeout timer value error%s", VTY_NEWLINE); return CMD_WARNING; } - + garbage = strtoul (argv[2], &endptr, 10); - if (garbage > RIP_TIMER_MAX || garbage < RIP_TIMER_MIN || *endptr != '\0') + if (garbage > RIP_TIMER_MAX || garbage < RIP_TIMER_MIN || *endptr != '\0') { vty_out (vty, "garbage timer value error%s", VTY_NEWLINE); return CMD_WARNING; @@ -3108,7 +3092,7 @@ ALIAS (no_rip_timers, "Routing information timeout timer. Default is 180.\n" "Garbage collection timer. Default is 120.\n") - + struct route_table *rip_distance_table; struct rip_distance @@ -3286,7 +3270,7 @@ rip_distance_show (struct vty *vty) struct rip_distance *rdistance; int header = 1; char buf[BUFSIZ]; - + vty_out (vty, " Distance: (default is %d)%s", rip->distance ? rip->distance :ZEBRA_RIP_DISTANCE_DEFAULT, VTY_NEWLINE); @@ -3376,7 +3360,7 @@ DEFUN (no_rip_distance_source_access_list, rip_distance_unset (vty, argv[0], argv[1], argv[2]); return CMD_SUCCESS; } - + /* Print out routes update time. */ static void rip_vty_out_uptime (struct vty *vty, struct rip_info *rinfo) @@ -3442,7 +3426,7 @@ DEFUN (show_ip_rip, " (i) - interface%s%s" " Network Next Hop Metric From Tag Time%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); - + for (np = route_top (rip->table); np; np = route_next (np)) if ((rinfo = np->info) != NULL) { @@ -3453,20 +3437,20 @@ DEFUN (show_ip_rip, zebra_route_char(rinfo->type), rip_route_type_print (rinfo->sub_type), inet_ntoa (np->p.u.prefix4), np->p.prefixlen); - + len = 24 - len; if (len > 0) vty_out (vty, "%*s", len, " "); - if (rinfo->nexthop.s_addr) + if (rinfo->nexthop.s_addr) vty_out (vty, "%-20s %2d ", inet_ntoa (rinfo->nexthop), rinfo->metric); else vty_out (vty, "0.0.0.0 %2d ", rinfo->metric); /* Route which exist in kernel routing table. */ - if ((rinfo->type == ZEBRA_ROUTE_RIP) && + if ((rinfo->type == ZEBRA_ROUTE_RIP) && (rinfo->sub_type == RIP_ROUTE_RTE)) { vty_out (vty, "%-15s ", inet_ntoa (rinfo->from)); @@ -3483,7 +3467,7 @@ DEFUN (show_ip_rip, { if (rinfo->external_metric) { - len = vty_out (vty, "self (%s:%d)", + len = vty_out (vty, "self (%s:%d)", zebra_route_string(rinfo->type), rinfo->external_metric); len = 16 - len; @@ -3522,7 +3506,7 @@ DEFUN (show_ip_rip_status, vty_out (vty, "Routing Protocol is \"rip\"%s", VTY_NEWLINE); vty_out (vty, " Sending updates every %ld seconds with +/-50%%,", rip->update_time); - vty_out (vty, " next due in %lu seconds%s", + vty_out (vty, " next due in %lu seconds%s", thread_timer_remain_second(rip->t_update), VTY_NEWLINE); vty_out (vty, " Timeout after %ld seconds,", rip->timeout_time); @@ -3531,7 +3515,7 @@ DEFUN (show_ip_rip_status, /* Filtering status show. */ config_show_distribute (vty); - + /* Default metric information. */ vty_out (vty, " Default redistribution metric is %d%s", rip->default_metric, VTY_NEWLINE); @@ -3569,7 +3553,7 @@ DEFUN (show_ip_rip_status, receive_version = lookup (ri_version_msg, rip->version_recv); else receive_version = lookup (ri_version_msg, ri->ri_receive); - + vty_out (vty, " %-17s%-3s %-3s %s%s", ifp->name, send_version, receive_version, @@ -3579,7 +3563,7 @@ DEFUN (show_ip_rip_status, } vty_out (vty, " Routing for Networks:%s", VTY_NEWLINE); - config_write_rip_network (vty, 0); + config_write_rip_network (vty, 0); { int found_passive = 0; @@ -3621,16 +3605,16 @@ config_write_rip (struct vty *vty) /* Router RIP statement. */ vty_out (vty, "router rip%s", VTY_NEWLINE); write++; - + /* RIP version statement. Default is RIP version 2. */ if (rip->version_send != RI_RIP_VERSION_2 || rip->version_recv != RI_RIP_VERSION_1_AND_2) vty_out (vty, " version %d%s", rip->version_send, VTY_NEWLINE); - + /* RIP timer configuration. */ - if (rip->update_time != RIP_UPDATE_TIMER_DEFAULT - || rip->timeout_time != RIP_TIMEOUT_TIMER_DEFAULT + if (rip->update_time != RIP_UPDATE_TIMER_DEFAULT + || rip->timeout_time != RIP_TIMEOUT_TIMER_DEFAULT || rip->garbage_time != RIP_GARBAGE_TIMER_DEFAULT) vty_out (vty, " timers basic %lu %lu %lu%s", rip->update_time, @@ -3657,7 +3641,7 @@ config_write_rip (struct vty *vty) /* RIP enabled network and interface configuration. */ config_write_rip_network (vty, 1); - + /* RIP default metric configuration */ if (rip->default_metric != RIP_DEFAULT_METRIC_DEFAULT) vty_out (vty, " default-metric %d%s", @@ -3684,7 +3668,7 @@ config_write_rip (struct vty *vty) /* RIP static route configuration. */ for (rn = route_top (rip->route); rn; rn = route_next (rn)) if (rn->info) - vty_out (vty, " route %s/%d%s", + vty_out (vty, " route %s/%d%s", inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen, VTY_NEWLINE); @@ -3700,7 +3684,7 @@ static struct cmd_node rip_node = "%s(config-router)# ", 1 }; - + /* Distribute-list update functions. */ static void rip_distribute_update (struct distribute *dist) @@ -3791,7 +3775,7 @@ rip_distribute_update_all_wrapper(struct access_list *notused) { rip_distribute_update_all(NULL); } - + /* Delete all added rip route. */ void rip_clean (void) @@ -3810,7 +3794,7 @@ rip_clean (void) rinfo->sub_type == RIP_ROUTE_RTE) rip_zebra_ipv4_delete ((struct prefix_ipv4 *)&rp->p, &rinfo->nexthop, rinfo->metric); - + RIP_TIMER_OFF (rinfo->t_timeout); RIP_TIMER_OFF (rinfo->t_garbage_collect); @@ -3866,7 +3850,7 @@ rip_clean (void) XFREE (MTYPE_ROUTE_TABLE, rip->table); XFREE (MTYPE_ROUTE_TABLE, rip->route); XFREE (MTYPE_ROUTE_TABLE, rip->neighbor); - + XFREE (MTYPE_RIP, rip); rip = NULL; } @@ -3957,10 +3941,10 @@ rip_routemap_update_redistribute (void) if (rip) { - for (i = 0; i < ZEBRA_ROUTE_MAX; i++) + for (i = 0; i < ZEBRA_ROUTE_MAX; i++) { if (rip->route_map[i].name) - rip->route_map[i].map = + rip->route_map[i].map = route_map_lookup_by_name (rip->route_map[i].name); } } diff --git a/tests/aspath_test.c b/tests/aspath_test.c index ff9d4d5e..11008b25 100644 --- a/tests/aspath_test.c +++ b/tests/aspath_test.c @@ -6,6 +6,7 @@ #include "bgpd/bgpd.h" #include "bgpd/bgp_aspath.h" +#include "bgpd/bgp_attr.h" #define VT100_RESET "\x1b[0m" #define VT100_RED "\x1b[31m" @@ -407,7 +408,7 @@ static struct test_segment { "#ASNs = 0, data = seq(8466 3 52737 4096 3456)", { 0x2,0x0, 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x0d,0x80 }, 12, - { "", "", + { NULL, NULL, 0, 0, 0, 0, 0, 0 }, }, { /* 26 */ @@ -417,10 +418,190 @@ static struct test_segment { 0x2,0x2, 0x10,0x00, 0x0d,0x80 }, 14 , - { "", "", + { NULL, NULL, 0, 0, 0, 0, 0, 0 }, }, - { NULL, NULL, {0}, 0, { NULL, 0, 0 } } + { /* 27 */ + "invalid segment type", + "type=8(4096 3456)", + { 0x8,0x2, 0x10,0x00, 0x0d,0x80 }, + 14 + , + { NULL, NULL, + 0, 0, 0, 0, 0, 0 }, + }, { NULL, NULL, {0}, 0, { NULL, 0, 0 } } +}; + +/* */ +static struct aspath_tests { + const char *desc; + const struct test_segment *segment; + const char *shouldbe; /* String it should evaluate to */ + const enum as4 { AS4_DATA, AS2_DATA } + as4; /* whether data should be as4 or not (ie as2) */ + const int result; /* expected result for bgp_attr_parse */ + const int cap; /* capabilities to set for peer */ + const char attrheader [1024]; + size_t len; +} aspath_tests [] = +{ + /* 0 */ + { + "basic test", + &test_segments[0], + "8466 3 52737 4096", + AS2_DATA, 0, + 0, + { BGP_ATTR_FLAG_TRANS, + BGP_ATTR_AS_PATH, + 10, + }, + 3, + }, + /* 1 */ + { + "length too short", + &test_segments[0], + "8466 3 52737 4096", + AS2_DATA, -1, + 0, + { BGP_ATTR_FLAG_TRANS, + BGP_ATTR_AS_PATH, + 8, + }, + 3, + }, + /* 2 */ + { + "length too long", + &test_segments[0], + "8466 3 52737 4096", + AS2_DATA, -1, + 0, + { BGP_ATTR_FLAG_TRANS, + BGP_ATTR_AS_PATH, + 12, + }, + 3, + }, + /* 3 */ + { + "incorrect flag", + &test_segments[0], + "8466 3 52737 4096", + AS2_DATA, -1, + 0, + { BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL, + BGP_ATTR_AS_PATH, + 10, + }, + 3, + }, + /* 4 */ + { + "as4_path, with as2 format data", + &test_segments[0], + "8466 3 52737 4096", + AS2_DATA, -1, + 0, + { BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL, + BGP_ATTR_AS4_PATH, + 10, + }, + 3, + }, + /* 5 */ + { + "as4, with incorrect attr length", + &test_segments[0], + "8466 3 52737 4096", + AS4_DATA, -1, + PEER_CAP_AS4_RCV, + { BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL, + BGP_ATTR_AS4_PATH, + 10, + }, + 3, + }, + /* 6 */ + { + "basic 4-byte as-path", + &test_segments[0], + "8466 3 52737 4096", + AS4_DATA, 0, + PEER_CAP_AS4_RCV|PEER_CAP_AS4_ADV, + { BGP_ATTR_FLAG_TRANS, + BGP_ATTR_AS_PATH, + 18, + }, + 3, + }, + /* 7 */ + { + "4b AS_PATH: too short", + &test_segments[0], + "8466 3 52737 4096", + AS4_DATA, -1, + PEER_CAP_AS4_RCV|PEER_CAP_AS4_ADV, + { BGP_ATTR_FLAG_TRANS, + BGP_ATTR_AS_PATH, + 16, + }, + 3, + }, + /* 8 */ + { + "4b AS_PATH: too long", + &test_segments[0], + "8466 3 52737 4096", + AS4_DATA, -1, + PEER_CAP_AS4_RCV|PEER_CAP_AS4_ADV, + { BGP_ATTR_FLAG_TRANS, + BGP_ATTR_AS_PATH, + 20, + }, + 3, + }, + /* 9 */ + { + "4b AS_PATH: too long2", + &test_segments[0], + "8466 3 52737 4096", + AS4_DATA, -1, + PEER_CAP_AS4_RCV|PEER_CAP_AS4_ADV, + { BGP_ATTR_FLAG_TRANS, + BGP_ATTR_AS_PATH, + 22, + }, + 3, + }, + /* 10 */ + { + "4b AS_PATH: bad flags", + &test_segments[0], + "8466 3 52737 4096", + AS4_DATA, -1, + PEER_CAP_AS4_RCV|PEER_CAP_AS4_ADV, + { BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL, + BGP_ATTR_AS_PATH, + 18, + }, + 3, + }, + /* 11 */ + { + "4b AS_PATH: confed", + &test_segments[6], + "8466 3 52737 4096", + AS4_DATA, -1, + PEER_CAP_AS4_ADV, + { BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL, + BGP_ATTR_AS4_PATH, + 14, + }, + 3, + }, + { NULL, NULL, NULL, 0, 0, 0, { 0 }, 0 }, }; /* prepending tests */ @@ -430,21 +611,25 @@ static struct tests { struct test_spec sp; } prepend_tests[] = { + /* 0 */ { &test_segments[0], &test_segments[1], { "8466 3 52737 4096 8722 4", "8466 3 52737 4096 8722 4", 6, 0, NOT_ALL_PRIVATE, 4096, 1, 8466 }, }, + /* 1 */ { &test_segments[1], &test_segments[3], { "8722 4 8482 51457 {5204}", "8722 4 8482 51457 {5204}", 5, 0, NOT_ALL_PRIVATE, 5204, 1, 8722 } }, + /* 2 */ { &test_segments[3], &test_segments[4], { "8482 51457 {5204} 8467 59649 {4196,48658} {17322,30745}", "8482 51457 {5204} 8467 59649 {4196,48658} {17322,30745}", 7, 0, NOT_ALL_PRIVATE, 5204, 1, 8482 }, }, + /* 3 */ { &test_segments[4], &test_segments[5], { "8467 59649 {4196,48658} {17322,30745} 6435 59408 21665" " {2457,4369,61697} 1842 41590 51793", @@ -452,11 +637,13 @@ static struct tests { " {2457,4369,61697} 1842 41590 51793", 11, 0, NOT_ALL_PRIVATE, 61697, 1, 8467 } }, + /* 4 */ { &test_segments[5], &test_segments[6], - { "6435 59408 21665 {2457,4369,61697} 1842 41590 51793 (123 456 789)", - "6435 59408 21665 {2457,4369,61697} 1842 41590 51793 (123 456 789)", - 7, 3, NOT_ALL_PRIVATE, 123, 1, 6435 }, + { "6435 59408 21665 {2457,4369,61697} 1842 41590 51793", + "6435 59408 21665 {2457,4369,61697} 1842 41590 51793", + 7, 0, NOT_ALL_PRIVATE, 1842, 1, 6435 }, }, + /* 5 */ { &test_segments[6], &test_segments[7], { "(123 456 789) (123 456 789) (111 222)", "", @@ -682,6 +869,12 @@ validate (struct aspath *as, const struct test_spec *sp) static struct stream *s; struct aspath *asinout, *asconfeddel, *asstr, *as4; + if (as == NULL && sp->shouldbe == NULL) + { + printf ("Correctly failed to parse\n"); + return fails; + } + out = aspath_snmp_pathseg (as, &bytes); asinout = make_aspath (out, bytes, 0); @@ -790,8 +983,8 @@ validate (struct aspath *as, const struct test_spec *sp) printf ("private check: %d %d\n", sp->private_as, aspath_private_as_check (as)); } - aspath_unintern (asinout); - aspath_unintern (as4); + aspath_unintern (&asinout); + aspath_unintern (&as4); aspath_free (asconfeddel); aspath_free (asstr); @@ -835,7 +1028,9 @@ parse_test (struct test_segment *t) printf (FAILED "\n"); printf ("\n"); - aspath_unintern (asp); + + if (asp) + aspath_unintern (&asp); } /* prepend testing */ @@ -851,7 +1046,7 @@ prepend_test (struct tests *t) asp2 = make_aspath (t->test2->asdata, t->test2->len, 0); ascratch = aspath_dup (asp2); - aspath_unintern (asp2); + aspath_unintern (&asp2); asp2 = aspath_prepend (asp1, ascratch); @@ -863,7 +1058,7 @@ prepend_test (struct tests *t) printf ("%s!\n", FAILED); printf ("\n"); - aspath_unintern (asp1); + aspath_unintern (&asp1); aspath_free (asp2); } @@ -879,7 +1074,7 @@ empty_prepend_test (struct test_segment *t) asp2 = aspath_empty (); ascratch = aspath_dup (asp2); - aspath_unintern (asp2); + aspath_unintern (&asp2); asp2 = aspath_prepend (asp1, ascratch); @@ -891,7 +1086,8 @@ empty_prepend_test (struct test_segment *t) printf (FAILED "!\n"); printf ("\n"); - aspath_unintern (asp1); + if (asp1) + aspath_unintern (&asp1); aspath_free (asp2); } @@ -915,8 +1111,8 @@ as4_reconcile_test (struct tests *t) printf (FAILED "!\n"); printf ("\n"); - aspath_unintern (asp1); - aspath_unintern (asp2); + aspath_unintern (&asp1); + aspath_unintern (&asp2); aspath_free (ascratch); } @@ -941,8 +1137,8 @@ aggregate_test (struct tests *t) printf (FAILED "!\n"); printf ("\n"); - aspath_unintern (asp1); - aspath_unintern (asp2); + aspath_unintern (&asp1); + aspath_unintern (&asp2); aspath_free (ascratch); /* aspath_unintern (ascratch);*/ } @@ -989,34 +1185,113 @@ cmp_test () printf (OK "\n"); printf ("\n"); - aspath_unintern (asp1); - aspath_unintern (asp2); + aspath_unintern (&asp1); + aspath_unintern (&asp2); } } +static int +handle_attr_test (struct aspath_tests *t) +{ + struct bgp bgp = { 0 }; + struct peer peer = { 0 }; + struct attr attr = { 0 }; + int ret; + int initfail = failed; + struct aspath *asp; + size_t datalen; + char host[] = { "none" } ; + + asp = make_aspath (t->segment->asdata, t->segment->len, 0); + + peer.ibuf = stream_new (BGP_MAX_PACKET_SIZE); + peer.obuf = stream_fifo_new (); + peer.bgp = &bgp; + peer.host = host ; +#if 0 + peer.fd = -1; +#endif + peer.cap = t->cap; + + stream_write (peer.ibuf, t->attrheader, t->len); + datalen = aspath_put (peer.ibuf, asp, t->as4 == AS4_DATA); + + ret = bgp_attr_parse (&peer, &attr, t->len + datalen, NULL, NULL); + + if (ret != t->result) + { + printf ("bgp_attr_parse returned %d, expected %d\n", ret, t->result); + printf ("datalen %d\n", (int)datalen); + failed++; + } + if (ret != 0) + goto out; + + if (attr.aspath == NULL) + { + printf ("aspath is NULL!\n"); + failed++; + } + if (attr.aspath && strcmp (attr.aspath->str, t->shouldbe)) + { + printf ("attr str and 'shouldbe' mismatched!\n" + "attr str: %s\n" + "shouldbe: %s\n", + attr.aspath->str, t->shouldbe); + failed++; + } + +out: + if (attr.aspath) + aspath_unintern (&attr.aspath); + if (asp) + aspath_unintern (&asp); + return failed - initfail; +} + +static void +attr_test (struct aspath_tests *t) +{ + printf ("%s\n", t->desc); + printf ("%s\n\n", handle_attr_test (t) ? FAILED : OK); +} + int main (void) { int i = 0; - aspath_init(); + bgp_master_init (); + master = bm->master; + bgp_attr_init (); + while (test_segments[i].name) { + printf ("test %u\n", i); parse_test (&test_segments[i]); empty_prepend_test (&test_segments[i++]); } i = 0; while (prepend_tests[i].test1) - prepend_test (&prepend_tests[i++]); + { + printf ("prepend test %u\n", i); + prepend_test (&prepend_tests[i++]); + } i = 0; while (aggregate_tests[i].test1) - aggregate_test (&aggregate_tests[i++]); + { + printf ("aggregate test %u\n", i); + aggregate_test (&aggregate_tests[i++]); + } i = 0; while (reconcile_tests[i].test1) - as4_reconcile_test (&reconcile_tests[i++]); + { + printf ("reconcile test %u\n", i); + as4_reconcile_test (&reconcile_tests[i++]); + } i = 0; @@ -1026,6 +1301,14 @@ main (void) empty_get_test(); + i = 0; + + while (aspath_tests[i].desc) + { + printf ("aspath_attr test %d\n", i); + attr_test (&aspath_tests[i++]); + } + printf ("failures: %d\n", failed); printf ("aspath count: %ld\n", aspath_count()); diff --git a/tests/ecommunity_test.c b/tests/ecommunity_test.c index da48865c..3a67228d 100644 --- a/tests/ecommunity_test.c +++ b/tests/ecommunity_test.c @@ -98,7 +98,7 @@ validate (struct ecommunity *ecom, const struct test_spec *sp) str1, (etmp && str2) ? str2 : "NULL"); } - ecommunity_free (etmp); + ecommunity_free (&etmp); XFREE (MTYPE_ECOMMUNITY_STR, str1); XFREE (MTYPE_ECOMMUNITY_STR, str2); @@ -123,7 +123,7 @@ parse_test (struct test_segment *t) printf ("failed\n"); printf ("\n"); - ecommunity_unintern (ecom); + ecommunity_unintern (&ecom); } diff --git a/tools/multiple-bgpd.sh b/tools/multiple-bgpd.sh index 028ad696..d6a38ed4 100644 --- a/tools/multiple-bgpd.sh +++ b/tools/multiple-bgpd.sh @@ -25,13 +25,14 @@ for H in `seq 1 ${NUM}` ; do NEXTAS=$((${ASBASE} + $NEXT)) PREVADDR="${PREFIX}${PREV}" PREVAS=$((${ASBASE} + $PREV)) + ASN=$((64560+${H})) # Edit config to suit. cat > "$CONF" <<- EOF password whatever service advanced-vty ! - router bgp $((64560+${H})) + router bgp ${ASN} bgp router-id ${ADDR} network 10.${H}.1.0/24 pathlimit 1 network 10.${H}.2.0/24 pathlimit 2 @@ -40,6 +41,7 @@ for H in `seq 1 ${NUM}` ; do neighbor default update-source ${ADDR} neighbor default capability orf prefix-list both neighbor default soft-reconfiguration inbound + neighbor default route-map test out neighbor ${NEXTADDR} remote-as ${NEXTAS} neighbor ${NEXTADDR} peer-group default neighbor ${PREVADDR} remote-as ${PREVAS} @@ -53,10 +55,15 @@ for H in `seq 1 ${NUM}` ; do neighbor default activate neighbor default capability orf prefix-list both neighbor default default-originate + neighbor default route-map test out neighbor ${NEXTADDR} peer-group default neighbor ${PREVADDR} peer-group default exit-address-family ! + route-map test permit 10 + set extcommunity rt ${ASN}:1 + set extcommunity soo ${ASN}:2 + set community ${ASN}:1 line vty ! end diff --git a/zebra/irdp_main.c b/zebra/irdp_main.c index c297979c..0658c397 100644 --- a/zebra/irdp_main.c +++ b/zebra/irdp_main.c @@ -29,7 +29,7 @@ */ /* - * Thanks to Jens Låås at Swedish University of Agricultural Sciences + * Thanks to Jens L��s at Swedish University of Agricultural Sciences * for reviewing and tests. */ @@ -108,7 +108,7 @@ irdp_sock_init (void) return ret; }; - ret = setsockopt_ifindex (AF_INET, sock, 1); + ret = setsockopt_pktinfo (AF_INET, sock, 1); if (ret < 0) { zlog_warn ("IRDP: can't do irdp sockopt %s", safe_strerror(errno)); close(sock); diff --git a/zebra/zebra_routemap.c b/zebra/zebra_routemap.c index 808dcf74..b3111b8e 100644 --- a/zebra/zebra_routemap.c +++ b/zebra/zebra_routemap.c @@ -642,17 +642,14 @@ route_set_src (void *rule, struct prefix *prefix, static void * route_set_src_compile (const char *arg) { - sa_family_t family; union g_addr src, *psrc; - if (inet_pton(AF_INET, arg, &src.ipv4) > 0) - family = AF_INET; + if (inet_pton(AF_INET, arg, &src.ipv4) != 1 #ifdef HAVE_IPV6 - else if (inet_pton(AF_INET6, arg, &src.ipv6) > 0) - family = AF_INET6; + && inet_pton(AF_INET6, arg, &src.ipv6) != 1 #endif /* HAVE_IPV6 */ - else - return NULL; + ) + return NULL; psrc = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (union g_addr)); *psrc = src; diff --git a/zebra/zserv.c b/zebra/zserv.c index dc3d432b..398b74c8 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -1360,8 +1360,8 @@ zebra_serv () #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); - sockopt_reuseaddr (accept_sock); - sockopt_reuseport (accept_sock); + setsockopt_reuseaddr (accept_sock); + setsockopt_reuseport (accept_sock); if ( zserv_privs.change(ZPRIVS_RAISE) ) zlog (NULL, LOG_ERR, "Can't raise privileges"); |