diff options
-rw-r--r-- | ChangeLog | 8 | ||||
-rw-r--r-- | NEWS | 3 | ||||
-rw-r--r-- | bgpd/ChangeLog | 25 | ||||
-rw-r--r-- | bgpd/bgp_aspath.c | 82 | ||||
-rw-r--r-- | bgpd/bgp_aspath.h | 3 | ||||
-rw-r--r-- | bgpd/bgp_attr.c | 53 | ||||
-rw-r--r-- | bgpd/bgp_attr.h | 5 | ||||
-rw-r--r-- | bgpd/bgp_main.c | 10 | ||||
-rw-r--r-- | bgpd/bgp_route.c | 10 | ||||
-rw-r--r-- | bgpd/bgp_routemap.c | 115 | ||||
-rwxr-xr-x | configure.ac | 4 | ||||
-rw-r--r-- | lib/ChangeLog | 12 | ||||
-rw-r--r-- | lib/stream.c | 4 | ||||
-rw-r--r-- | lib/stream.h | 4 | ||||
-rw-r--r-- | lib/workqueue.c | 16 | ||||
-rw-r--r-- | lib/workqueue.h | 3 | ||||
-rw-r--r-- | lib/zebra.h | 2 | ||||
-rw-r--r-- | ospf6d/ospf6_main.c | 3 | ||||
-rw-r--r-- | ospfd/ospf_main.c | 3 | ||||
-rw-r--r-- | ripd/ChangeLog | 4 | ||||
-rw-r--r-- | ripd/rip_main.c | 3 | ||||
-rw-r--r-- | ripd/ripd.c | 2 | ||||
-rw-r--r-- | ripngd/ripng_main.c | 3 | ||||
-rw-r--r-- | tests/ChangeLog | 4 | ||||
-rw-r--r-- | tests/Makefile.am | 6 | ||||
-rw-r--r-- | tests/bgp_mp_attr_test.c | 535 | ||||
-rw-r--r-- | tools/multiple-bgpd.sh | 27 | ||||
-rw-r--r-- | zebra/ChangeLog | 21 | ||||
-rw-r--r-- | zebra/connected.c | 13 | ||||
-rw-r--r-- | zebra/main.c | 3 | ||||
-rw-r--r-- | zebra/rib.h | 16 | ||||
-rw-r--r-- | zebra/rt_netlink.c | 55 | ||||
-rw-r--r-- | zebra/zebra_rib.c | 160 | ||||
-rw-r--r-- | zebra/zserv.h | 1 |
34 files changed, 1115 insertions, 103 deletions
@@ -1,3 +1,11 @@ +2008-06-10 Paul Jakma <paul@jakma.org> + + * configure.ac: Bump version to 0.99.10 + +2008-05-29 Martin Nagy <mnagy@redhat.com> + + * */*main.c: Sanity check port numbers before using. + 2008-01-30 Peter Szilagyi <sp615@hszk.bme.hu> * lib/stream.h: Remove named 'new' parameter in prototype @@ -3,6 +3,9 @@ - [bgpd] 4-byte AS support added - [bgpd] MRT format changes to version 2. Those relying on bgpd MRT table dumps may need to update their tools. +- [bgpd] Added new route-map set statement: "as-path exclude" +- Zebra RIB updates queue has evolved into a multi-level + structure to address RIB consistency issues. * Changes in Quagga 0.99.2 diff --git a/bgpd/ChangeLog b/bgpd/ChangeLog index f3b6a8c1..2da28213 100644 --- a/bgpd/ChangeLog +++ b/bgpd/ChangeLog @@ -1,3 +1,28 @@ +2008-06-07 Paul Jakma <paul@jakma.org> + + * bgp_attr.{c,h}: (bgp_mp_{un,}reach_parse) export, for unit tests. + * bgp_attr.c: (bgp_mp_reach_parse) Add logging. Tighten length test + to bounds check against the attribute length rather than the + stream length.. + +2008-06-01 jfletche@gmail.com + + * bgp_attr.c: (bgp_attr_aspathlimit) fix silly bug in flags check + that was causing BGP to drop sessions if it received a + aspath-limit with partial set. Fixes bug #419. + +2008-04-10 Denis Ovsienko + + * bgp_aspath.[ch]: (aspath_filter_exclude) New function allows + filtering out arbitrary ASns from AS_PATH attribute. + * bgp_aspath.[ch]: (aspath_print_vty) Accept suffix to let calling + functions signal, if they want the separator or not. + * bgp_route.c: (route_vty_out, route_vty_out_tmp, damp_route_vty_out, + flap_route_vty_out, route_vty_out_detail) Fix aspath_print_vty() + calls to have AS_PATH output nicely. + * bgp_routemap.c: Introduce "set as-path exclude" route-map command + to employ new filtering functionality. + 2008-03-13 Paul Jakma <paul.jakma@sun.com> * (various) Remove 0 entries from struct message's, unneeded due to diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c index d7e985d4..38c9caa6 100644 --- a/bgpd/bgp_aspath.c +++ b/bgpd/bgp_aspath.c @@ -1225,6 +1225,81 @@ aspath_prepend (struct aspath *as1, struct aspath *as2) /* Not reached */ } +/* Iterate over AS_PATH segments and wipe all occurences of the + * listed AS numbers. Hence some segments may lose some or even + * all data on the way, the operation is implemented as a smarter + * version of aspath_dup(), which allocates memory to hold the new + * data, not the original. The new AS path is returned. + */ +struct aspath * +aspath_filter_exclude (struct aspath * source, struct aspath * exclude_list) +{ + struct assegment * srcseg, * exclseg, * lastseg; + struct aspath * newpath; + + newpath = aspath_new(); + lastseg = NULL; + + for (srcseg = source->segments; srcseg; srcseg = srcseg->next) + { + unsigned i, y, newlen = 0, done = 0, skip_as; + struct assegment * newseg; + + /* Find out, how much ASns are we going to pick from this segment. + * We can't perform filtering right inline, because the size of + * the new segment isn't known at the moment yet. + */ + for (i = 0; i < srcseg->length; i++) + { + skip_as = 0; + for (exclseg = exclude_list->segments; exclseg && !skip_as; exclseg = exclseg->next) + for (y = 0; y < exclseg->length; y++) + if (srcseg->as[i] == exclseg->as[y]) + { + skip_as = 1; + // There's no sense in testing the rest of exclusion list, bail out. + break; + } + if (!skip_as) + newlen++; + } + /* newlen is now the number of ASns to copy */ + if (!newlen) + continue; + + /* Actual copying. Allocate memory and iterate once more, performing filtering. */ + newseg = assegment_new (srcseg->type, newlen); + for (i = 0; i < srcseg->length; i++) + { + skip_as = 0; + for (exclseg = exclude_list->segments; exclseg && !skip_as; exclseg = exclseg->next) + for (y = 0; y < exclseg->length; y++) + if (srcseg->as[i] == exclseg->as[y]) + { + skip_as = 1; + break; + } + if (skip_as) + continue; + newseg->as[done++] = srcseg->as[i]; + } + /* At his point newlen must be equal to done, and both must be positive. Append + * the filtered segment to the gross result. */ + if (!lastseg) + newpath->segments = newseg; + else + lastseg->next = newseg; + lastseg = newseg; + } + aspath_str_update (newpath); + /* We are happy returning even an empty AS_PATH, because the administrator + * might expect this very behaviour. There's a mean to avoid this, if necessary, + * by having a match rule against certain AS_PATH regexps in the route-map index. + */ + aspath_free (source); + return newpath; +} + /* Add specified AS to the leftmost of aspath. */ static struct aspath * aspath_add_one_as (struct aspath *aspath, as_t asno, u_char type) @@ -1741,11 +1816,16 @@ aspath_print (struct aspath *as) } /* Printing functions */ +/* Feed the AS_PATH to the vty; the suffix string follows it only in case + * AS_PATH wasn't empty. + */ void -aspath_print_vty (struct vty *vty, const char *format, struct aspath *as) +aspath_print_vty (struct vty *vty, const char *format, struct aspath *as, const char * suffix) { assert (format); vty_out (vty, format, as->str); + if (strlen (as->str) && strlen (suffix)) + vty_out (vty, "%s", suffix); } static void diff --git a/bgpd/bgp_aspath.h b/bgpd/bgp_aspath.h index 3bb616f7..d8b41fa9 100644 --- a/bgpd/bgp_aspath.h +++ b/bgpd/bgp_aspath.h @@ -69,6 +69,7 @@ extern struct aspath *aspath_parse (struct stream *, size_t, int); 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 *); +extern struct aspath *aspath_filter_exclude (struct aspath *, struct aspath *); extern struct aspath *aspath_add_seq (struct aspath *, as_t); extern struct aspath *aspath_add_confed_seq (struct aspath *, as_t); extern int aspath_cmp_left (struct aspath *, struct aspath *); @@ -81,7 +82,7 @@ extern void aspath_free (struct aspath *); extern struct aspath *aspath_intern (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 *); +extern void aspath_print_vty (struct vty *, const char *, struct aspath *, const char *); extern void aspath_print_all_vty (struct vty *); extern unsigned int aspath_key_make (void *); extern int aspath_loop_check (struct aspath *, as_t); diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 26f62f5a..19a0869f 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -695,7 +695,8 @@ bgp_attr_aspathlimit (struct peer *peer, bgp_size_t length, total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3); - if (flag != (BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL)) + if (!CHECK_FLAG(flag, BGP_ATTR_FLAG_TRANS) + || !CHECK_FLAG(flag, BGP_ATTR_FLAG_OPTIONAL)) { zlog (peer->log, LOG_ERR, "AS-Pathlimit attribute flag isn't transitive %d", flag); @@ -804,7 +805,7 @@ bgp_attr_aspath (struct peer *peer, bgp_size_t length, || ! CHECK_FLAG (flag, BGP_ATTR_FLAG_TRANS)) { zlog (peer->log, LOG_ERR, - "Origin attribute flag isn't transitive %d", flag); + "As-Path attribute flag isn't transitive %d", flag); bgp_notify_send_with_data (peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR, @@ -1258,7 +1259,7 @@ bgp_attr_cluster_list (struct peer *peer, bgp_size_t length, } /* Multiprotocol reachability information parse. */ -static int +int bgp_mp_reach_parse (struct peer *peer, bgp_size_t length, struct attr *attr, struct bgp_nlri *mp_update) { @@ -1276,8 +1277,13 @@ bgp_mp_reach_parse (struct peer *peer, bgp_size_t length, struct attr *attr, /* safe to read statically sized header? */ #define BGP_MP_REACH_MIN_SIZE 5 +#define LEN_LEFT (length - (stream_get_getp(s) - start)) if ((length > STREAM_READABLE(s)) || (length < BGP_MP_REACH_MIN_SIZE)) - return -1; + { + zlog_info ("%s: %s sent invalid length, %lu", + __func__, peer->host, (unsigned long)length); + return -1; + } /* Load AFI, SAFI. */ afi = stream_getw (s); @@ -1286,8 +1292,12 @@ bgp_mp_reach_parse (struct peer *peer, bgp_size_t length, struct attr *attr, /* Get nexthop length. */ attre->mp_nexthop_len = stream_getc (s); - if (STREAM_READABLE(s) < attre->mp_nexthop_len) - return -1; + if (LEN_LEFT < attre->mp_nexthop_len) + { + zlog_info ("%s: %s, MP nexthop length, %u, goes past end of attribute", + __func__, peer->host, attre->mp_nexthop_len); + return -1; + } /* Nexthop length check. */ switch (attre->mp_nexthop_len) @@ -1329,13 +1339,17 @@ bgp_mp_reach_parse (struct peer *peer, bgp_size_t length, struct attr *attr, break; #endif /* HAVE_IPV6 */ default: - zlog_info ("Wrong multiprotocol next hop length: %d", - attre->mp_nexthop_len); + zlog_info ("%s: (%s) Wrong multiprotocol next hop length: %d", + __func__, peer->host, attre->mp_nexthop_len); return -1; } - if (!STREAM_READABLE(s)) - return -1; + if (!LEN_LEFT) + { + zlog_info ("%s: (%s) Failed to read SNPA and NLRI(s)", + __func__, peer->host); + return -1; + } { u_char val; @@ -1345,15 +1359,23 @@ bgp_mp_reach_parse (struct peer *peer, bgp_size_t length, struct attr *attr, } /* must have nrli_len, what is left of the attribute */ - nlri_len = length - (stream_get_getp(s) - start); + nlri_len = LEN_LEFT; if ((!nlri_len) || (nlri_len > STREAM_READABLE(s))) - return -1; + { + zlog_info ("%s: (%s) Failed to read NLRI", + __func__, peer->host); + return -1; + } if (safi != BGP_SAFI_VPNV4) { ret = bgp_nlri_sanity_check (peer, afi, stream_pnt (s), nlri_len); - if (ret < 0) - return -1; + if (ret < 0) + { + zlog_info ("%s: (%s) NLRI doesn't pass sanity check", + __func__, peer->host); + return -1; + } } mp_update->afi = afi; @@ -1364,10 +1386,11 @@ bgp_mp_reach_parse (struct peer *peer, bgp_size_t length, struct attr *attr, stream_forward_getp (s, nlri_len); return 0; +#undef LEN_LEFT } /* Multiprotocol unreachable parse */ -static int +int bgp_mp_unreach_parse (struct peer *peer, bgp_size_t length, struct bgp_nlri *mp_withdraw) { diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index e152b9f4..9647ccf8 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -176,4 +176,9 @@ extern void cluster_unintern (struct cluster_list *); /* Transit attribute prototypes. */ 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 *); + #endif /* _QUAGGA_BGP_ATTR_H */ diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index e6d34afc..2089c6b5 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -203,6 +203,7 @@ main (int argc, char **argv) int dryrun = 0; char *progname; struct thread thread; + int tmp_port; /* Set umask before anything for security */ umask (0027); @@ -238,7 +239,11 @@ main (int argc, char **argv) pid_file = optarg; break; case 'p': - bm->port = atoi (optarg); + tmp_port = atoi (optarg); + if (tmp_port <= 0 || tmp_port > 0xffff) + bm->port = BGP_PORT_DEFAULT; + else + bm->port = tmp_port; break; case 'A': vty_addr = optarg; @@ -252,7 +257,8 @@ main (int argc, char **argv) break; } vty_port = atoi (optarg); - vty_port = (vty_port ? vty_port : BGP_VTY_PORT); + if (vty_port <= 0 || vty_port > 0xffff) + vty_port = BGP_VTY_PORT; break; case 'r': retain_mode = 1; diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 9ddeca54..4fbc4bab 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -5694,7 +5694,7 @@ route_vty_out (struct vty *vty, struct prefix *p, /* Print aspath */ if (attr->aspath) - aspath_print_vty (vty, "%s ", attr->aspath); + aspath_print_vty (vty, "%s", attr->aspath, " "); /* Print origin */ vty_out (vty, "%s", bgp_origin_str[attr->origin]); @@ -5759,7 +5759,7 @@ route_vty_out_tmp (struct vty *vty, struct prefix *p, /* Print aspath */ if (attr->aspath) - aspath_print_vty (vty, "%s ", attr->aspath); + aspath_print_vty (vty, "%s", attr->aspath, " "); /* Print origin */ vty_out (vty, "%s", bgp_origin_str[attr->origin]); @@ -5859,7 +5859,7 @@ damp_route_vty_out (struct vty *vty, struct prefix *p, { /* Print aspath */ if (attr->aspath) - aspath_print_vty (vty, "%s ", attr->aspath); + aspath_print_vty (vty, "%s", attr->aspath, " "); /* Print origin */ vty_out (vty, "%s", bgp_origin_str[attr->origin]); @@ -5922,7 +5922,7 @@ flap_route_vty_out (struct vty *vty, struct prefix *p, { /* Print aspath */ if (attr->aspath) - aspath_print_vty (vty, "%s ", attr->aspath); + aspath_print_vty (vty, "%s", attr->aspath, " "); /* Print origin */ vty_out (vty, "%s", bgp_origin_str[attr->origin]); @@ -5950,7 +5950,7 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p, if (aspath_count_hops (attr->aspath) == 0) vty_out (vty, "Local"); else - aspath_print_vty (vty, "%s", attr->aspath); + aspath_print_vty (vty, "%s", attr->aspath, ""); } if (CHECK_FLAG (binfo->flags, BGP_INFO_REMOVED)) diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index b246e2ab..b93b2682 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -94,6 +94,7 @@ o Local extention set ipv6 next-hop global: Done set ipv6 next-hop local : Done set pathlimit ttl : Done + set as-path exclude : Done match pathlimit as : Done */ @@ -1274,6 +1275,64 @@ struct route_map_rule_cmd route_set_aspath_prepend_cmd = route_set_aspath_prepend_free, }; +/* `set as-path exclude ASn' */ + +/* For ASN exclude mechanism. + * Iterate over ASns requested and filter them from the given AS_PATH one by one. + * Make a deep copy of existing AS_PATH, but for the first ASn only. + */ +static route_map_result_t +route_set_aspath_exclude (void *rule, struct prefix *dummy, route_map_object_t type, void *object) +{ + struct aspath * new_path, * exclude_path; + struct bgp_info *binfo; + + if (type == RMAP_BGP) + { + exclude_path = rule; + binfo = object; + if (binfo->attr->aspath->refcnt) + new_path = aspath_dup (binfo->attr->aspath); + else + new_path = binfo->attr->aspath; + binfo->attr->aspath = aspath_filter_exclude (new_path, exclude_path); + } + return RMAP_OKAY; +} + +/* FIXME: consider using route_set_aspath_prepend_compile() and + * route_set_aspath_prepend_free(), which two below function are + * exact clones of. + */ + +/* Compile function for as-path exclude. */ +static void * +route_set_aspath_exclude_compile (const char *arg) +{ + struct aspath *aspath; + + aspath = aspath_str2aspath (arg); + if (! aspath) + return NULL; + return aspath; +} + +static void +route_set_aspath_exclude_free (void *rule) +{ + struct aspath *aspath = rule; + aspath_free (aspath); +} + +/* Set ASn exlude rule structure. */ +struct route_map_rule_cmd route_set_aspath_exclude_cmd = +{ + "as-path exclude", + route_set_aspath_exclude, + route_set_aspath_exclude_compile, + route_set_aspath_exclude_free, +}; + /* `set community COMMUNITY' */ struct rmap_com_set { @@ -2996,7 +3055,7 @@ DEFUN (set_aspath_prepend, set_aspath_prepend_cmd, "set as-path prepend .<1-65535>", SET_STR - "Prepend string for a BGP AS-path attribute\n" + "Transform BGP AS_PATH attribute\n" "Prepend to the as-path\n" "AS number\n") { @@ -3015,7 +3074,7 @@ DEFUN (no_set_aspath_prepend, "no set as-path prepend", NO_STR SET_STR - "Prepend string for a BGP AS-path attribute\n" + "Transform BGP AS_PATH attribute\n" "Prepend to the as-path\n") { int ret; @@ -3035,10 +3094,56 @@ ALIAS (no_set_aspath_prepend, "no set as-path prepend .<1-65535>", NO_STR SET_STR - "Prepend string for a BGP AS-path attribute\n" + "Transform BGP AS_PATH attribute\n" "Prepend to the as-path\n" "AS number\n") +DEFUN (set_aspath_exclude, + set_aspath_exclude_cmd, + "set as-path exclude .<1-65535>", + SET_STR + "Transform BGP AS-path attribute\n" + "Exclude from the as-path\n" + "AS number\n") +{ + int ret; + char *str; + + str = argv_concat (argv, argc, 0); + ret = bgp_route_set_add (vty, vty->index, "as-path exclude", str); + XFREE (MTYPE_TMP, str); + return ret; +} + +DEFUN (no_set_aspath_exclude, + no_set_aspath_exclude_cmd, + "no set as-path exclude", + NO_STR + SET_STR + "Transform BGP AS_PATH attribute\n" + "Exclude from the as-path\n") +{ + int ret; + char *str; + + if (argc == 0) + return bgp_route_set_delete (vty, vty->index, "as-path exclude", NULL); + + str = argv_concat (argv, argc, 0); + ret = bgp_route_set_delete (vty, vty->index, "as-path exclude", str); + XFREE (MTYPE_TMP, str); + return ret; +} + +ALIAS (no_set_aspath_exclude, + no_set_aspath_exclude_val_cmd, + "no set as-path exclude .<1-65535>", + NO_STR + SET_STR + "Transform BGP AS_PATH attribute\n" + "Exclude from the as-path\n" + "AS number\n") + DEFUN (set_community, set_community_cmd, "set community .AA:NN", @@ -3731,6 +3836,7 @@ bgp_route_map_init (void) route_map_install_set (&route_set_weight_cmd); route_map_install_set (&route_set_metric_cmd); route_map_install_set (&route_set_aspath_prepend_cmd); + route_map_install_set (&route_set_aspath_exclude_cmd); route_map_install_set (&route_set_origin_cmd); route_map_install_set (&route_set_atomic_aggregate_cmd); route_map_install_set (&route_set_aggregator_as_cmd); @@ -3799,8 +3905,11 @@ bgp_route_map_init (void) install_element (RMAP_NODE, &no_set_metric_cmd); install_element (RMAP_NODE, &no_set_metric_val_cmd); install_element (RMAP_NODE, &set_aspath_prepend_cmd); + install_element (RMAP_NODE, &set_aspath_exclude_cmd); install_element (RMAP_NODE, &no_set_aspath_prepend_cmd); install_element (RMAP_NODE, &no_set_aspath_prepend_val_cmd); + install_element (RMAP_NODE, &no_set_aspath_exclude_cmd); + install_element (RMAP_NODE, &no_set_aspath_exclude_val_cmd); install_element (RMAP_NODE, &set_origin_cmd); install_element (RMAP_NODE, &no_set_origin_cmd); install_element (RMAP_NODE, &no_set_origin_val_cmd); diff --git a/configure.ac b/configure.ac index b9eb4430..d85af676 100755 --- a/configure.ac +++ b/configure.ac @@ -5,10 +5,10 @@ ## Copyright (c) 1996, 97, 98, 99, 2000 Kunihiro Ishiguro <kunihiro@zebra.org> ## Portions Copyright (c) 2003 Paul Jakma <paul@dishone.st> ## -## $Id: configure.ac,v 1.141 2008/01/11 16:33:59 ajs Exp $ +## $Id: configure.ac,v 1.142 2008/06/10 21:25:38 paul Exp $ AC_PREREQ(2.53) -AC_INIT(Quagga, 0.99.9, [http://bugzilla.quagga.net]) +AC_INIT(Quagga, 0.99.10, [http://bugzilla.quagga.net]) AC_CONFIG_SRCDIR(lib/zebra.h) dnl ----------------------------------- diff --git a/lib/ChangeLog b/lib/ChangeLog index da0fa8ca..681bdebc 100644 --- a/lib/ChangeLog +++ b/lib/ChangeLog @@ -1,3 +1,15 @@ +2008-06-07 Paul Jakma <paul@jakma.org> + + * stream.{c,h}: (stream_{put,write}) add const qualifier to source + argument. Change u_char to void *. + +2008-06-02 Denis Ovsienko + + * workqueue.[ch]: completely drop WQ_AIM_HEAD flag and + work_queue_aim_head() function, they aren't needed any more + with the new meta queue structure; (work_queue_run) don't + increment the counter on work item requeueing + 2008-02-28 Paul Jakma <paul.jakma@sun.com> * log.c: (mes_lookup) Sowmini Varadhan diagnosed a problem where diff --git a/lib/stream.c b/lib/stream.c index 7034d904..983330ff 100644 --- a/lib/stream.c +++ b/lib/stream.c @@ -458,7 +458,7 @@ stream_get_ipv4 (struct stream *s) * stream_write() is saner */ void -stream_put (struct stream *s, void *src, size_t size) +stream_put (struct stream *s, const void *src, size_t size) { /* XXX: CHECK_SIZE has strange semantics. It should be deprecated */ @@ -833,7 +833,7 @@ stream_recvmsg (struct stream *s, int fd, struct msghdr *msgh, int flags, /* Write data to buffer. */ size_t -stream_write (struct stream *s, u_char *ptr, size_t size) +stream_write (struct stream *s, const void *ptr, size_t size) { CHECK_SIZE(s, size); diff --git a/lib/stream.h b/lib/stream.h index 715a083d..3e4ba7b4 100644 --- a/lib/stream.h +++ b/lib/stream.h @@ -150,7 +150,7 @@ extern void stream_forward_getp (struct stream *, size_t); extern void stream_forward_endp (struct stream *, size_t); /* steam_put: NULL source zeroes out size_t bytes of stream */ -extern void stream_put (struct stream *, void *, size_t); +extern void stream_put (struct stream *, const void *, size_t); extern int stream_putc (struct stream *, u_char); extern int stream_putc_at (struct stream *, size_t, u_char); extern int stream_putw (struct stream *, u_int16_t); @@ -200,7 +200,7 @@ extern ssize_t stream_recvmsg (struct stream *s, int fd, struct msghdr *, extern ssize_t stream_recvfrom (struct stream *s, int fd, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen); -extern size_t stream_write (struct stream *, u_char *, size_t); +extern size_t stream_write (struct stream *, const void *, size_t); /* reset the stream. See Note above */ extern void stream_reset (struct stream *); diff --git a/lib/workqueue.c b/lib/workqueue.c index 8880b9e2..1d32d241 100644 --- a/lib/workqueue.c +++ b/lib/workqueue.c @@ -67,7 +67,6 @@ work_queue_new (struct thread_master *m, const char *queue_name) new->name = XSTRDUP (MTYPE_WORK_QUEUE_NAME, queue_name); new->master = m; SET_FLAG (new->flags, WQ_UNPLUGGED); - UNSET_FLAG (new->flags, WQ_AIM_HEAD); if ( (new->items = list_new ()) == NULL) { @@ -131,10 +130,7 @@ work_queue_add (struct work_queue *wq, void *data) } item->data = data; - if (CHECK_FLAG (wq->flags, WQ_AIM_HEAD)) - listnode_add_after (wq->items, NULL, item); - else - listnode_add (wq->items, item); + listnode_add (wq->items, item); work_queue_schedule (wq, wq->spec.hold); @@ -231,15 +227,6 @@ work_queue_unplug (struct work_queue *wq) work_queue_schedule (wq, wq->spec.hold); } -void -work_queue_aim_head (struct work_queue *wq, const unsigned aim_head) -{ - if (aim_head) - SET_FLAG (wq->flags, WQ_AIM_HEAD); - else - UNSET_FLAG (wq->flags, WQ_AIM_HEAD); -} - /* timer thread to process a work queue * will reschedule itself if required, * otherwise work_queue_item_add @@ -317,6 +304,7 @@ work_queue_run (struct thread *thread) } case WQ_REQUEUE: { + item->ran--; work_queue_item_requeue (wq, node); break; } diff --git a/lib/workqueue.h b/lib/workqueue.h index 3150c32e..f59499a0 100644 --- a/lib/workqueue.h +++ b/lib/workqueue.h @@ -48,7 +48,6 @@ struct work_queue_item }; #define WQ_UNPLUGGED (1 << 0) /* available for draining */ -#define WQ_AIM_HEAD (1 << 1) /* add new items before list head, not after tail */ struct work_queue { @@ -119,8 +118,6 @@ extern void work_queue_add (struct work_queue *, void *); extern void work_queue_plug (struct work_queue *wq); /* unplug the queue, allow it to be drained again */ extern void work_queue_unplug (struct work_queue *wq); -/* control the value for WQ_AIM_HEAD flag */ -extern void work_queue_aim_head (struct work_queue *wq, const unsigned); /* Helpers, exported for thread.c and command.c */ extern int work_queue_run (struct thread *); diff --git a/lib/zebra.h b/lib/zebra.h index 150aa2c5..2716460f 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -162,6 +162,8 @@ typedef int socklen_t; #ifdef HAVE_NETLINK #include <linux/netlink.h> #include <linux/rtnetlink.h> +#include <linux/filter.h> +#include <stddef.h> #else #define RT_TABLE_MAIN 0 #endif /* HAVE_NETLINK */ diff --git a/ospf6d/ospf6_main.c b/ospf6d/ospf6_main.c index 8380bc89..680f4b7f 100644 --- a/ospf6d/ospf6_main.c +++ b/ospf6d/ospf6_main.c @@ -227,7 +227,8 @@ main (int argc, char *argv[], char *envp[]) break; } vty_port = atoi (optarg); - vty_port = (vty_port ? vty_port : OSPF6_VTY_PORT); + if (vty_port <= 0 || vty_port > 0xffff) + vty_port = OSPF6_VTY_PORT; break; case 'u': ospf6d_privs.user = optarg; diff --git a/ospfd/ospf_main.c b/ospfd/ospf_main.c index 27a12dd0..1a200a8f 100644 --- a/ospfd/ospf_main.c +++ b/ospfd/ospf_main.c @@ -245,7 +245,8 @@ main (int argc, char **argv) break; } vty_port = atoi (optarg); - vty_port = (vty_port ? vty_port : OSPF_VTY_PORT); + if (vty_port <= 0 || vty_port > 0xffff) + vty_port = OSPF_VTY_PORT; break; case 'u': ospfd_privs.user = optarg; diff --git a/ripd/ChangeLog b/ripd/ChangeLog index ecf353d0..60baef5d 100644 --- a/ripd/ChangeLog +++ b/ripd/ChangeLog @@ -1,3 +1,7 @@ +2008-05-29 Stephen Hemminger <stephen.hemminger@vyatta.com> + + * ripd.c: (rip_auth_md5) fix bogus empty string test + 2008-03-13 Paul Jakma <paul.jakma@sun.com> * ripd.c/rip_interface.c: Remove 0 entries from rip_msg diff --git a/ripd/rip_main.c b/ripd/rip_main.c index dfcd6c26..0b29107d 100644 --- a/ripd/rip_main.c +++ b/ripd/rip_main.c @@ -236,7 +236,8 @@ main (int argc, char **argv) break; } vty_port = atoi (optarg); - vty_port = (vty_port ? vty_port : RIP_VTY_PORT); + if (vty_port <= 0 || vty_port > 0xffff) + vty_port = RIP_VTY_PORT; break; case 'r': retain_mode = 1; diff --git a/ripd/ripd.c b/ripd/ripd.c index c5e42705..62d8691c 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -926,7 +926,7 @@ rip_auth_md5 (struct rip_packet *packet, struct sockaddr_in *from, else if (ri->auth_str) strncpy (auth_str, ri->auth_str, RIP_AUTH_MD5_SIZE); - if (! auth_str) + if (auth_str[0] == 0) return 0; /* MD5 digest authentication. */ diff --git a/ripngd/ripng_main.c b/ripngd/ripng_main.c index 70553910..a18ce9de 100644 --- a/ripngd/ripng_main.c +++ b/ripngd/ripng_main.c @@ -240,7 +240,8 @@ main (int argc, char **argv) break; } vty_port = atoi (optarg); - vty_port = (vty_port ? vty_port : RIPNG_VTY_PORT); + if (vty_port <= 0 || vty_port > 0xffff) + vty_port = RIPNG_VTY_PORT; break; case 'r': retain_mode = 1; diff --git a/tests/ChangeLog b/tests/ChangeLog index 098afb55..18696519 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,7 @@ +2008-06-07 Paul Jakma <paul@jakma.org + + * bgp_mp_attr_test.c: MP_(UN)REACH_NLRI unit tests + 2008-02-23 Paul Jakma <paul.jakma@sun.com> * aspath_test.c: Test for 0-ASN sequences that still have data. diff --git a/tests/Makefile.am b/tests/Makefile.am index 2045496e..d00485f1 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -2,7 +2,9 @@ INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib DEFS = @DEFS@ $(LOCAL_OPTS) -DSYSCONFDIR=\"$(sysconfdir)/\" noinst_PROGRAMS = testsig testbuffer testmemory heavy heavywq heavythread \ - aspathtest testprivs teststream testbgpcap ecommtest + aspathtest testprivs teststream testbgpcap ecommtest \ + testbgpmpattr + testsig_SOURCES = test-sig.c testbuffer_SOURCES = test-buffer.c testmemory_SOURCES = test-memory.c @@ -14,6 +16,7 @@ heavythread_SOURCES = heavy-thread.c main.c aspathtest_SOURCES = aspath_test.c testbgpcap_SOURCES = bgp_capability_test.c ecommtest_SOURCES = ecommunity_test.c +testbgpmpattr_SOURCES = bgp_mp_attr_test.c testsig_LDADD = ../lib/libzebra.la @LIBCAP@ testbuffer_LDADD = ../lib/libzebra.la @LIBCAP@ @@ -26,3 +29,4 @@ heavythread_LDADD = ../lib/libzebra.la @LIBCAP@ -lm aspathtest_LDADD = ../lib/libzebra.la @LIBCAP@ -lm ../bgpd/libbgp.a testbgpcap_LDADD = ../lib/libzebra.la @LIBCAP@ -lm ../bgpd/libbgp.a ecommtest_LDADD = ../lib/libzebra.la @LIBCAP@ -lm ../bgpd/libbgp.a +testbgpmpattr_LDADD = ../lib/libzebra.la @LIBCAP@ -lm ../bgpd/libbgp.a diff --git a/tests/bgp_mp_attr_test.c b/tests/bgp_mp_attr_test.c new file mode 100644 index 00000000..dde0df2f --- /dev/null +++ b/tests/bgp_mp_attr_test.c @@ -0,0 +1,535 @@ +#include <zebra.h> + +#include "vty.h" +#include "stream.h" +#include "privs.h" +#include "memory.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_open.h" +#include "bgpd/bgp_debug.h" + +#define VT100_RESET "\x1b[0m" +#define VT100_RED "\x1b[31m" +#define VT100_GREEN "\x1b[32m" +#define VT100_YELLOW "\x1b[33m" + + +#define CAPABILITY 0 +#define DYNCAP 1 +#define OPT_PARAM 2 + +/* need these to link in libbgp */ +struct zebra_privs_t *bgpd_privs = NULL; +struct thread_master *master = NULL; + +static int failed = 0; +static int tty = 0; + +/* test segments to parse and validate, and use for other tests */ +static struct test_segment { + const char *name; + const char *desc; + const u_char data[1024]; + int len; +#define SHOULD_PARSE 0 +#define SHOULD_ERR -1 + int parses; /* whether it should parse or not */ + + /* AFI/SAFI validation */ + afi_t afi; + safi_t safi; +#define VALID_AFI 1 +#define INVALID_AFI 0 + int afi_valid; +} mp_reach_segments [] = +{ + { "IPv6", + "IPV6 MP Reach, global nexthop, 1 NLRI", + { + /* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST, + /* nexthop bytes */ 16, + /* Nexthop (global) */ 0xff, 0xfe, 0x1, 0x2, + 0xaa, 0xbb, 0xcc, 0xdd, + 0x3, 0x4, 0x5, 0x6, + 0xa1, 0xa2, 0xa3, 0xa4, + /* SNPA (defunct, MBZ) */ 0x0, + /* NLRI tuples */ 32, 0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */ + }, + (4 + 16 + 1 + 5), + SHOULD_PARSE, + AFI_IP6, SAFI_UNICAST, VALID_AFI, + }, + { "IPv6-2", + "IPV6 MP Reach, global nexthop, 2 NLRIs", + { + /* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST, + /* nexthop bytes */ 16, + /* Nexthop (global) */ 0xff, 0xfe, 0x1, 0x2, /* ffee:102:... */ + 0xaa, 0xbb, 0xcc, 0xdd, + 0x3, 0x4, 0x5, 0x6, + 0xa1, 0xa2, 0xa3, 0xa4, + /* SNPA (defunct, MBZ) */ 0x0, + /* NLRI tuples */ 32, + 0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */ + 64, + 0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */ + 0x0, 0x2, 0x0, 0x3, + }, + (4 + 16 + 1 + 5 + 9), + SHOULD_PARSE, + AFI_IP6, SAFI_UNICAST, VALID_AFI, + }, + { "IPv6-default", + "IPV6 MP Reach, global nexthop, 2 NLRIs + default", + { + /* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST, + /* nexthop bytes */ 16, + /* Nexthop (global) */ 0xff, 0xfe, 0x1, 0x2, + 0xaa, 0xbb, 0xcc, 0xdd, + 0x3, 0x4, 0x5, 0x6, + 0xa1, 0xa2, 0xa3, 0xa4, + /* SNPA (defunct, MBZ) */ 0x0, + /* NLRI tuples */ 32, + 0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */ + 64, + 0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */ + 0x0, 0x2, 0x0, 0x3, + 0x0, /* ::/0 */ + }, + (4 + 16 + 1 + 5 + 9 + 1), + SHOULD_PARSE, + AFI_IP6, SAFI_UNICAST, VALID_AFI, + }, + { "IPv6-lnh", + "IPV6 MP Reach, global+local nexthops, 2 NLRIs + default", + { + /* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST, + /* nexthop bytes */ 32, + /* Nexthop (global) */ 0xff, 0xfe, 0x1, 0x2, /* fffe:102:... */ + 0xaa, 0xbb, 0xcc, 0xdd, + 0x3, 0x4, 0x5, 0x6, + 0xa1, 0xa2, 0xa3, 0xa4, + /* Nexthop (local) */ 0xfe, 0x80, 0x0, 0x0, /* fe80::210:2ff:.. */ + 0x0, 0x0, 0x0, 0x0, + 0x2, 0x10, 0x2, 0xff, + 0x1, 0x2, 0x3, 0x4, + /* SNPA (defunct, MBZ) */ 0x0, + /* NLRI tuples */ 32, + 0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */ + 64, + 0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */ + 0x0, 0x2, 0x0, 0x3, + 0x0, /* ::/0 */ + }, + (4 + 32 + 1 + 5 + 9 + 1), + SHOULD_PARSE, + AFI_IP6, SAFI_UNICAST, VALID_AFI, + }, + { "IPv6-nhlen", + "IPV6 MP Reach, inappropriate nexthop length", + { + /* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST, + /* nexthop bytes */ 4, + /* Nexthop (global) */ 0xff, 0xfe, 0x1, 0x2, /* fffe:102:... */ + 0xaa, 0xbb, 0xcc, 0xdd, + 0x3, 0x4, 0x5, 0x6, + 0xa1, 0xa2, 0xa3, 0xa4, + /* Nexthop (local) */ 0xfe, 0x80, 0x0, 0x0, /* fe80::210:2ff:.. */ + 0x0, 0x0, 0x0, 0x0, + 0x2, 0x10, 0x2, 0xff, + 0x1, 0x2, 0x3, 0x4, + /* SNPA (defunct, MBZ) */ 0x0, + /* NLRI tuples */ 32, + 0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */ + 64, + 0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */ + 0x0, 0x2, 0x0, 0x3, + 0x0, /* ::/0 */ + }, + (4 + 32 + 1 + 5 + 9 + 1), + SHOULD_ERR, + AFI_IP6, SAFI_UNICAST, VALID_AFI, + }, + { "IPv6-nhlen2", + "IPV6 MP Reach, invalid nexthop length", + { + /* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST, + /* nexthop bytes */ 5, + /* Nexthop (global) */ 0xff, 0xfe, 0x1, 0x2, /* fffe:102:... */ + 0xaa, 0xbb, 0xcc, 0xdd, + 0x3, 0x4, 0x5, 0x6, + 0xa1, 0xa2, 0xa3, 0xa4, + /* Nexthop (local) */ 0xfe, 0x80, 0x0, 0x0, /* fe80::210:2ff:.. */ + 0x0, 0x0, 0x0, 0x0, + 0x2, 0x10, 0x2, 0xff, + 0x1, 0x2, 0x3, 0x4, + /* SNPA (defunct, MBZ) */ 0x0, + /* NLRI tuples */ 32, + 0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */ + 64, + 0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */ + 0x0, 0x2, 0x0, 0x3, + 0x0, /* ::/0 */ + }, + (4 + 32 + 1 + 5 + 9 + 1), + SHOULD_ERR, + AFI_IP6, SAFI_UNICAST, VALID_AFI, + }, + { "IPv6-nhlen3", + "IPV6 MP Reach, nexthop length overflow", + { + /* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST, + /* nexthop bytes */ 32, + /* Nexthop (global) */ 0xff, 0xfe, 0x1, 0x2, /* fffe:102:... */ + 0xaa, 0xbb, 0xcc, 0xdd, + 0x3, 0x4, 0x5, 0x6, + 0xa1, 0xa2, 0xa3, 0xa4, + }, + (4 + 16), + SHOULD_ERR, + AFI_IP6, SAFI_UNICAST, VALID_AFI, + }, + { "IPv6-nhlen4", + "IPV6 MP Reach, nexthop length short", + { + /* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST, + /* nexthop bytes */ 16, + /* Nexthop (global) */ 0xff, 0xfe, 0x1, 0x2, /* fffe:102:... */ + 0xaa, 0xbb, 0xcc, 0xdd, + 0x3, 0x4, 0x5, 0x6, + 0xa1, 0xa2, 0xa3, 0xa4, + /* Nexthop (local) */ 0xfe, 0x80, 0x0, 0x0, /* fe80::210:2ff:.. */ + 0x0, 0x0, 0x0, 0x0, + 0x2, 0x10, 0x2, 0xff, + 0x1, 0x2, 0x3, 0x4, + /* SNPA (defunct, MBZ) */ 0x0, + /* NLRI tuples */ 32, + 0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */ + 64, + 0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */ + 0x0, 0x2, 0x0, 0x3, + 0x0, /* ::/0 */ + }, + (4 + 32 + 1 + 5 + 9 + 1), + SHOULD_ERR, + AFI_IP6, SAFI_UNICAST, VALID_AFI, + }, + { "IPv6-nlri", + "IPV6 MP Reach, NLRI bitlen overflow", + { + /* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST, + /* nexthop bytes */ 32, + /* Nexthop (global) */ 0xff, 0xfe, 0x1, 0x2, /* fffe:102:... */ + 0xaa, 0xbb, 0xcc, 0xdd, + 0x3, 0x4, 0x5, 0x6, + 0xa1, 0xa2, 0xa3, 0xa4, + /* Nexthop (local) */ 0xfe, 0x80, 0x0, 0x0, /* fe80::210:2ff:.. */ + 0x0, 0x0, 0x0, 0x0, + 0x2, 0x10, 0x2, 0xff, + 0x1, 0x2, 0x3, 0x4, + /* SNPA (defunct, MBZ) */ 0x0, + /* NLRI tuples */ 120, + 0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */ + 64, + 0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */ + 0x0, 0x2, 0x0, 0x3, + 0, /* ::/0 */ + }, + (4 + 32 + 1 + 5 + 9 + 1), + SHOULD_ERR, + AFI_IP6, SAFI_UNICAST, VALID_AFI, + }, + { "IPv4", + "IPv4 MP Reach, 2 NLRIs + default", + { + /* AFI / SAFI */ 0x0, AFI_IP, SAFI_UNICAST, + /* nexthop bytes */ 4, + /* Nexthop */ 192, 168, 0, 1, + /* SNPA (defunct, MBZ) */ 0x0, + /* NLRI tuples */ 16, 10, 1, /* 10.1/16 */ + 17, 10, 2, 3, /* 10.2.3/17 */ + 0, /* 0/0 */ + }, + (4 + 4 + 1 + 3 + 4 + 1), + SHOULD_PARSE, + AFI_IP, SAFI_UNICAST, VALID_AFI, + }, + { "IPv4-nhlen", + "IPv4 MP Reach, nexthop lenth overflow", + { + /* AFI / SAFI */ 0x0, AFI_IP, SAFI_UNICAST, + /* nexthop bytes */ 32, + /* Nexthop */ 192, 168, 0, 1, + /* SNPA (defunct, MBZ) */ 0x0, + /* NLRI tuples */ 16, 10, 1, /* 10.1/16 */ + 17, 10, 2, 3, /* 10.2.3/17 */ + 0, /* 0/0 */ + }, + (4 + 4 + 1 + 3 + 4 + 1), + SHOULD_ERR, + AFI_IP, SAFI_UNICAST, VALID_AFI, + }, + { "IPv4-nlrilen", + "IPv4 MP Reach, nlri lenth overflow", + { + /* AFI / SAFI */ 0x0, AFI_IP, SAFI_UNICAST, + /* nexthop bytes */ 4, + /* Nexthop */ 192, 168, 0, 1, + /* SNPA (defunct, MBZ) */ 0x0, + /* NLRI tuples */ 16, 10, 1, /* 10.1/16 */ + 30, 10, + 0, /* 0/0 */ + }, + (4 + 4 + 1 + 3 + 2 + 1), + SHOULD_ERR, + AFI_IP, SAFI_UNICAST, VALID_AFI, + }, + { "IPv4-vpnv4", + "IPv4/VPNv4 MP Reach, RD, Nexthop, 3 NLRIs", + { + /* AFI / SAFI */ 0x0, AFI_IP, BGP_SAFI_VPNV4, + /* nexthop bytes */ 12, + /* RD */ 0, 0, 1, 2, + 0, 0xff, 3, 4, + /* Nexthop */ 192, 168, 0, 1, + /* SNPA (defunct, MBZ) */ 0x0, + /* NLRI tuples */ 16, 10, 1, /* 10.1/16 */ + 17, 10, 2, 3, /* 10.2.3/17 */ + 0, /* 0/0 */ + }, + (4 + 12 + 1 + 3 + 4 + 1), + SHOULD_PARSE, + AFI_IP, SAFI_UNICAST, VALID_AFI, + }, + /* From bug #385 */ + { "IPv6-bug", + "IPv6, global nexthop, 1 default NLRI", + { + /* AFI / SAFI */ 0x0, 0x2, 0x1, + /* nexthop bytes */ 0x20, + /* Nexthop (global) */ 0x20, 0x01, 0x04, 0x70, + 0x00, 0x01, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, + /* Nexthop (local) */ 0xfe, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x02, 0x0c, 0xdb, 0xff, + 0xfe, 0xfe, 0xeb, 0x00, + /* SNPA (defunct, MBZ) */ 0, + /* NLRI tuples */ /* Should have 0 here for ::/0, but dont */ + }, + 37, + SHOULD_ERR, + AFI_IP6, SAFI_UNICAST, VALID_AFI, + }, + + { NULL, NULL, {0}, 0, 0} +}; + +/* MP_UNREACH_NLRI tests */ +static struct test_segment mp_unreach_segments [] = +{ + { "IPv6-unreach", + "IPV6 MP Unreach, 1 NLRI", + { + /* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST, + /* NLRI tuples */ 32, 0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */ + }, + (3 + 5), + SHOULD_PARSE, + AFI_IP6, SAFI_UNICAST, VALID_AFI, + }, + { "IPv6-unreach2", + "IPV6 MP Unreach, 2 NLRIs", + { + /* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST, + /* NLRI tuples */ 32, + 0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */ + 64, + 0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */ + 0x0, 0x2, 0x0, 0x3, + }, + (3 + 5 + 9), + SHOULD_PARSE, + AFI_IP6, SAFI_UNICAST, VALID_AFI, + }, + { "IPv6-unreach-default", + "IPV6 MP Unreach, 2 NLRIs + default", + { + /* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST, + /* NLRI tuples */ 32, + 0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */ + 64, + 0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */ + 0x0, 0x2, 0x0, 0x3, + 0x0, /* ::/0 */ + }, + (3 + 5 + 9 + 1), + SHOULD_PARSE, + AFI_IP6, SAFI_UNICAST, VALID_AFI, + }, + { "IPv6-unreach-nlri", + "IPV6 MP Unreach, NLRI bitlen overflow", + { + /* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST, + /* NLRI tuples */ 120, + 0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */ + 64, + 0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */ + 0x0, 0x2, 0x0, 0x3, + 0, /* ::/0 */ + }, + (3 + 5 + 9 + 1), + SHOULD_ERR, + AFI_IP6, SAFI_UNICAST, VALID_AFI, + }, + { "IPv4-unreach", + "IPv4 MP Unreach, 2 NLRIs + default", + { + /* AFI / SAFI */ 0x0, AFI_IP, SAFI_UNICAST, + /* NLRI tuples */ 16, 10, 1, /* 10.1/16 */ + 17, 10, 2, 3, /* 10.2.3/17 */ + 0, /* 0/0 */ + }, + (3 + 3 + 4 + 1), + SHOULD_PARSE, + AFI_IP, SAFI_UNICAST, VALID_AFI, + }, + { "IPv4-unreach-nlrilen", + "IPv4 MP Unreach, nlri length overflow", + { + /* AFI / SAFI */ 0x0, AFI_IP, SAFI_UNICAST, + /* NLRI tuples */ 16, 10, 1, /* 10.1/16 */ + 30, 10, + 0, /* 0/0 */ + }, + (3 + 3 + 2 + 1), + SHOULD_ERR, + AFI_IP, SAFI_UNICAST, VALID_AFI, + }, + { "IPv4-unreach-vpnv4", + "IPv4/VPNv4 MP Unreach, RD, 3 NLRIs", + { + /* AFI / SAFI */ 0x0, AFI_IP, BGP_SAFI_VPNV4, + /* nexthop bytes */ 12, + /* RD */ 0, 0, 1, 2, + 0, 0xff, 3, 4, + /* Nexthop */ 192, 168, 0, 1, + /* SNPA (defunct, MBZ) */ 0x0, + /* NLRI tuples */ 16, 10, 1, /* 10.1/16 */ + 17, 10, 2, 3, /* 10.2.3/17 */ + 0, /* 0/0 */ + }, + (3 + 3 + 4 + 1), + SHOULD_PARSE, + AFI_IP, SAFI_UNICAST, VALID_AFI, + }, + { NULL, NULL, {0}, 0, 0} +}; + + +/* basic parsing test */ +static void +parse_test (struct peer *peer, struct test_segment *t, int type) +{ + int ret; + int oldfailed = failed; + struct attr attr; + struct bgp_nlri nlri; +#define RANDOM_FUZZ 35 + + stream_reset (peer->ibuf); + stream_put (peer->ibuf, NULL, RANDOM_FUZZ); + stream_set_getp (peer->ibuf, RANDOM_FUZZ); + + stream_write (peer->ibuf, t->data, t->len); + + printf ("%s: %s\n", t->name, t->desc); + + if (type == BGP_ATTR_MP_REACH_NLRI) + ret = bgp_mp_reach_parse (peer, t->len, &attr, &nlri); + else + ret = bgp_mp_unreach_parse (peer, t->len, &nlri); + + if (!ret) + { + safi_t safi = t->safi; + + if (bgp_afi_safi_valid_indices (t->afi, &safi) != t->afi_valid) + failed++; + + printf ("MP: %u/%u (%u): recv %u, nego %u\n", + t->afi, t->safi, safi, + peer->afc_recv[t->afi][safi], + peer->afc_nego[t->afi][safi]); + } + + printf ("parsed?: %s\n", ret ? "no" : "yes"); + + if (ret != t->parses) + failed++; + + if (tty) + printf ("%s", (failed > oldfailed) ? VT100_RED "failed!" VT100_RESET + : VT100_GREEN "OK" VT100_RESET); + else + printf ("%s", (failed > oldfailed) ? "failed!" : "OK" ); + + if (failed) + printf (" (%u)", failed); + + printf ("\n\n"); +} + +static struct bgp *bgp; +static as_t asn = 100; + +int +main (void) +{ + struct peer *peer; + int i, j; + + conf_bgp_debug_fsm = -1UL; + conf_bgp_debug_events = -1UL; + conf_bgp_debug_packet = -1UL; + conf_bgp_debug_normal = -1UL; + conf_bgp_debug_as4 = -1UL; + term_bgp_debug_fsm = -1UL; + term_bgp_debug_events = -1UL; + term_bgp_debug_packet = -1UL; + term_bgp_debug_normal = -1UL; + term_bgp_debug_as4 = -1UL; + + master = thread_master_create (); + bgp_master_init (); + + if (fileno (stdout) >= 0) + tty = isatty (fileno (stdout)); + + if (bgp_get (&bgp, &asn, NULL)) + return -1; + + peer = peer_create_accept (bgp); + peer->host = "foo"; + + for (i = AFI_IP; i < AFI_MAX; i++) + for (j = SAFI_UNICAST; j < SAFI_MAX; j++) + { + peer->afc[i][j] = 1; + peer->afc_adv[i][j] = 1; + } + + i = 0; + while (mp_reach_segments[i].name) + parse_test (peer, &mp_reach_segments[i++], BGP_ATTR_MP_REACH_NLRI); + + i = 0; + while (mp_unreach_segments[i].name) + parse_test (peer, &mp_unreach_segments[i++], BGP_ATTR_MP_UNREACH_NLRI); + + printf ("failures: %d\n", failed); + return failed; +} diff --git a/tools/multiple-bgpd.sh b/tools/multiple-bgpd.sh index 40977544..001e91c6 100644 --- a/tools/multiple-bgpd.sh +++ b/tools/multiple-bgpd.sh @@ -7,9 +7,12 @@ VTYBASE=2610 ASBASE=64560 BGPD=/path/to/bgpd PREFIX=192.168.145 +CONFBASE=/tmp +PIDBASE=/var/run/quagga +CHOWNSTR=quagga:quagga for H in `seq 1 ${NUM}` ; do - CONF=/etc/quagga/bgpd${H}.conf + CONF="${CONFBASE}"/bgpd${H}.conf ADDR=${PREFIX}.${H} if [ ! -e "$CONF" ] ; then @@ -39,17 +42,33 @@ for H in `seq 1 ${NUM}` ; do neighbor ${NEXTADDR} peer-group default neighbor ${PREVADDR} remote-as ${PREVAS} neighbor ${PREVADDR} peer-group default + ! + address-family ipv6 + network fffe:${H}::/48 + network fffe:${H}:1::/48 pathlimit 1 + network fffe:${H}:2::/48 pathlimit 3 + network fffe:${H}:3::/48 pathlimit 3 + neighbor default activate + neighbor default capability orf prefix-list both + neighbor default default-originate + neighbor ${NEXTADDR} peer-group default + neighbor ${PREVADDR} peer-group default + exit-address-family + ! + line vty + ! + end EOF - chown quagga:quagga "$CONF" + chown ${CHOWNSTR} "$CONF" fi # You may want to automatically add configure a local address # on a loop interface. # # Solaris: ifconfig vni${H} plumb ${ADDR}/32 up # Linux: ip address add ${ADDR}/32 dev lo 2> /dev/null - ${BGPD} -i /var/run/quagga/bgpd${H}.pid \ + ${BGPD} -i "${PIDBASE}"/bgpd${H}.pid \ -l ${ADDR} \ - -f /etc/quagga/bgpd${H}.conf \ + -f "${CONF}" \ -P $((${VTYBASE}+${H})) \ -d done diff --git a/zebra/ChangeLog b/zebra/ChangeLog index d9cae283..6483f2c7 100644 --- a/zebra/ChangeLog +++ b/zebra/ChangeLog @@ -1,3 +1,24 @@ +2008-06-02 Denis Ovsienko + + * connected.c: (connected_up_ipv4, connected_down_ipv4, + connected_up_ipv6, connected_down_ipv6): don't call + work_queue_aim_head() + * rib.h: adjust RIB_ROUTE_QUEUED macro for meta_queue, + declare meta_queue structure + * zebra_rib.c: (process_subq, meta_queue_process, rib_meta_queue_add, + meta_queue_new) new functions; (rib_queue_add) don't try checking + RIB_QUEUE_ADDED flag, rib_meta_queue_add() does it better, take care + of the meta queue instead; (rib_queue_init) initialize the meta queue + as well; (rib_lookup_and_pushup) don't call work_queue_aim_head(); + (rib_process) only do actual processing, don't do deallocation; + * zserv.h: include meta_queue field into zebra_t structure + +2008-05-29 Stephen Hemminger <stephen.hemminger@vyatta.com> + + * rt_netlink.c: (netlink_install_filter) BPF filter to catch and + drop responses to zebra's own route messages. + (kernel_init) add BPF filter on the netlink socket. + 2008-02-26 Denis Ovsienko * zebra_rib.[ch]: (rib_lookup_and_pushup) New function, which makes sure, that if_set_prefix() has nothing in its way of assigning an address. diff --git a/zebra/connected.c b/zebra/connected.c index 8bf1d337..ad3e9607 100644 --- a/zebra/connected.c +++ b/zebra/connected.c @@ -188,15 +188,8 @@ connected_up_ipv4 (struct interface *ifp, struct connected *ifc) if (prefix_ipv4_any (&p)) return; - /* Always push arriving/departing connected routes into the head of - * the working queue to make possible proper validation of the rest - * of the RIB queue (which will contain the whole RIB after the first - * call to rib_update()). - */ - work_queue_aim_head (zebrad.ribq, 1); rib_add_ipv4 (ZEBRA_ROUTE_CONNECT, 0, &p, NULL, NULL, ifp->ifindex, RT_TABLE_MAIN, ifp->metric, 0); - work_queue_aim_head (zebrad.ribq, 0); rib_update (); } @@ -302,9 +295,7 @@ connected_down_ipv4 (struct interface *ifp, struct connected *ifc) return; /* Same logic as for connected_up_ipv4(): push the changes into the head. */ - work_queue_aim_head (zebrad.ribq, 1); rib_delete_ipv4 (ZEBRA_ROUTE_CONNECT, 0, &p, NULL, ifp->ifindex, 0); - work_queue_aim_head (zebrad.ribq, 0); rib_update (); } @@ -349,10 +340,8 @@ connected_up_ipv6 (struct interface *ifp, struct connected *ifc) return; #endif - work_queue_aim_head (zebrad.ribq, 1); rib_add_ipv6 (ZEBRA_ROUTE_CONNECT, 0, &p, NULL, ifp->ifindex, 0, ifp->metric, 0); - work_queue_aim_head (zebrad.ribq, 0); rib_update (); } @@ -426,9 +415,7 @@ connected_down_ipv6 (struct interface *ifp, struct connected *ifc) if (IN6_IS_ADDR_UNSPECIFIED (&p.prefix)) return; - work_queue_aim_head (zebrad.ribq, 1); rib_delete_ipv6 (ZEBRA_ROUTE_CONNECT, 0, &p, NULL, ifp->ifindex, 0); - work_queue_aim_head (zebrad.ribq, 0); rib_update (); } diff --git a/zebra/main.c b/zebra/main.c index 6019260f..61750f1d 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -275,7 +275,8 @@ main (int argc, char **argv) break; } vty_port = atoi (optarg); - vty_port = (vty_port ? vty_port : ZEBRA_VTY_PORT); + if (vty_port <= 0 || vty_port > 0xffff) + vty_port = ZEBRA_VTY_PORT; break; case 'r': retain_mode = 1; diff --git a/zebra/rib.h b/zebra/rib.h index 9621f2c8..887ed3c2 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -40,7 +40,7 @@ struct rib { /* Status Flags for the *route_node*, but kept in the head RIB.. */ u_char rn_status; -#define RIB_ROUTE_QUEUED (1 << 0) +#define RIB_ROUTE_QUEUED(x) (1 << (x)) /* Link list. */ struct rib *next; @@ -83,6 +83,20 @@ struct rib u_char nexthop_fib_num; }; +/* meta-queue structure: + * sub-queue 0: connected, kernel + * sub-queue 1: static + * sub-queue 2: RIP, RIPng, OSPF, OSPF6, IS-IS + * sub-queue 3: iBGP, eBGP + * sub-queue 4: any other origin (if any) + */ +#define MQ_SIZE 5 +struct meta_queue +{ + struct list *subq[MQ_SIZE]; + u_int32_t size; /* sum of lengths of all subqueues */ +}; + /* Static route information. */ struct static_ipv4 { diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 5b592f94..0bf2d9eb 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -1938,6 +1938,56 @@ kernel_read (struct thread *thread) return 0; } +/* Filter out messages from self that occur on listener socket */ +static void netlink_install_filter (int sock) +{ + /* + * Filter is equivalent to netlink_route_change + * + * if (h->nlmsg_type == RTM_DELROUTE || h->nlmsg_type == RTM_NEWROUTE) { + * if (rtm->rtm_type != RTM_UNICAST) + * return 0; + * if (rtm->rtm_flags & RTM_F_CLONED) + * return 0; + * if (rtm->rtm_protocol == RTPROT_REDIRECT) + * return 0; + * if (rtm->rtm_protocol == RTPROT_KERNEL) + * return 0; + * if (rtm->rtm_protocol == RTPROT_ZEBRA && h->nlmsg_type == RTM_NEWROUTE) + * return 0; + * } + * return 0xffff; + */ + struct sock_filter filter[] = { + /* 0*/ BPF_STMT(BPF_LD|BPF_ABS|BPF_H, offsetof(struct nlmsghdr, nlmsg_type)), + /* 1*/ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(RTM_DELROUTE), 1, 0), + /* 2*/ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(RTM_NEWROUTE), 0, 11), + /* 3*/ BPF_STMT(BPF_LD|BPF_ABS|BPF_B, + sizeof(struct nlmsghdr) + offsetof(struct rtmsg, rtm_type)), + /* 4*/ BPF_JUMP(BPF_JMP|BPF_B, RTN_UNICAST, 0, 8), + /* 5*/ BPF_STMT(BPF_LD|BPF_ABS|BPF_B, + sizeof(struct nlmsghdr) + offsetof(struct rtmsg, rtm_flags)), + /* 6*/ BPF_JUMP(BPF_JMP|BPF_JSET|BPF_K, RTM_F_CLONED, 6, 0), + /* 7*/ BPF_STMT(BPF_LD|BPF_ABS|BPF_B, + sizeof(struct nlmsghdr) + offsetof(struct rtmsg, rtm_protocol)), + /* 8*/ BPF_JUMP(BPF_JMP+ BPF_B, RTPROT_REDIRECT, 4, 0), + /* 9*/ BPF_JUMP(BPF_JMP+ BPF_B, RTPROT_KERNEL, 0, 1), + /*10*/ BPF_JUMP(BPF_JMP+ BPF_B, RTPROT_ZEBRA, 0, 3), + /*11*/ BPF_STMT(BPF_LD|BPF_ABS|BPF_H, offsetof(struct nlmsghdr, nlmsg_type)), + /*12*/ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(RTM_NEWROUTE), 0, 1), + /*13*/ BPF_STMT(BPF_RET|BPF_K, 0), /* drop */ + /*14*/ BPF_STMT(BPF_RET|BPF_K, 0xffff), /* keep */ + }; + + struct sock_fprog prog = { + .len = sizeof(filter) / sizeof(filter[0]), + .filter = filter, + }; + + if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &prog, sizeof(prog)) < 0) + zlog_warn ("Can't install socket filter: %s\n", safe_strerror(errno)); +} + /* Exported interface function. This function simply calls netlink_socket (). */ void @@ -1954,5 +2004,8 @@ kernel_init (void) /* Register kernel socket. */ if (netlink.sock > 0) - thread_add_read (zebrad.master, kernel_read, NULL, netlink.sock); + { + netlink_install_filter (netlink.sock); + thread_add_read (zebrad.master, kernel_read, NULL, netlink.sock); + } } diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index c6af3290..4cb72ba8 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -981,15 +981,14 @@ rib_uninstall (struct route_node *rn, struct rib *rib) static void rib_unlink (struct route_node *, struct rib *); /* Core function for processing routing information base. */ -static wq_item_status -rib_process (struct work_queue *wq, void *data) +static void +rib_process (struct route_node *rn) { struct rib *rib; struct rib *next; struct rib *fib = NULL; struct rib *select = NULL; struct rib *del = NULL; - struct route_node *rn = data; int installed = 0; struct nexthop *nexthop = NULL; char buf[INET6_ADDRSTRLEN]; @@ -1177,10 +1176,95 @@ rib_process (struct work_queue *wq, void *data) end: if (IS_ZEBRA_DEBUG_RIB_Q) zlog_debug ("%s: %s/%d: rn %p dequeued", __func__, buf, rn->p.prefixlen, rn); - if (rn->info) - UNSET_FLAG (((struct rib *)rn->info)->rn_status, RIB_ROUTE_QUEUED); - route_unlock_node (rn); /* rib queue lock */ - return WQ_SUCCESS; +} + +/* Take a list of route_node structs and return 1, if there was a record picked from + * it and processed by rib_process(). Don't process more, than one RN record; operate + * only in the specified sub-queue. + */ +unsigned int +process_subq (struct list * subq, u_char qindex) +{ + struct listnode *lnode; + struct route_node *rnode; + if (!(lnode = listhead (subq))) + return 0; + rnode = listgetdata (lnode); + rib_process (rnode); + if (rnode->info) /* The first RIB record is holding the flags bitmask. */ + UNSET_FLAG (((struct rib *)rnode->info)->rn_status, RIB_ROUTE_QUEUED(qindex)); + route_unlock_node (rnode); + list_delete_node (subq, lnode); + return 1; +} + +/* Dispatch the meta queue by picking, processing and unlocking the next RN from + * a non-empty sub-queue with lowest priority. wq is equal to zebra->ribq and data + * is pointed to the meta queue structure. + */ +static wq_item_status +meta_queue_process (struct work_queue *dummy, void *data) +{ + struct meta_queue * mq = data; + u_char i; + for (i = 0; i < MQ_SIZE; i++) + if (process_subq (mq->subq[i], i)) + { + mq->size--; + break; + } + return mq->size ? WQ_REQUEUE : WQ_SUCCESS; +} + +/* Look into the RN and queue it into one or more priority queues, increasing the size + * for each data push done. + */ +void rib_meta_queue_add (struct meta_queue *mq, struct route_node *rn) +{ + u_char qindex; + struct rib *rib; + char buf[INET6_ADDRSTRLEN]; + if (IS_ZEBRA_DEBUG_RIB_Q) + inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); + for (rib = rn->info; rib; rib = rib->next) + { + switch (rib->type) + { + case ZEBRA_ROUTE_KERNEL: + case ZEBRA_ROUTE_CONNECT: + qindex = 0; + break; + case ZEBRA_ROUTE_STATIC: + qindex = 1; + break; + case ZEBRA_ROUTE_RIP: + case ZEBRA_ROUTE_RIPNG: + case ZEBRA_ROUTE_OSPF: + case ZEBRA_ROUTE_OSPF6: + case ZEBRA_ROUTE_ISIS: + qindex = 2; + break; + case ZEBRA_ROUTE_BGP: + qindex = 3; + break; + default: + qindex = 4; + break; + } + /* Invariant: at this point we always have rn->info set. */ + if (CHECK_FLAG (((struct rib *)rn->info)->rn_status, RIB_ROUTE_QUEUED(qindex))) + { + if (IS_ZEBRA_DEBUG_RIB_Q) + zlog_debug ("%s: %s/%d: rn %p is already queued in sub-queue %u", __func__, buf, rn->p.prefixlen, rn, qindex); + continue; + } + SET_FLAG (((struct rib *)rn->info)->rn_status, RIB_ROUTE_QUEUED(qindex)); + listnode_add (mq->subq[qindex], rn); + route_lock_node (rn); + mq->size++; + if (IS_ZEBRA_DEBUG_RIB_Q) + zlog_debug ("%s: %s/%d: queued rn %p into sub-queue %u", __func__, buf, rn->p.prefixlen, rn, qindex); + } } /* Add route_node to work queue and schedule processing */ @@ -1202,17 +1286,6 @@ rib_queue_add (struct zebra_t *zebra, struct route_node *rn) return; } - /* Route-table node already queued, so nothing to do */ - if (CHECK_FLAG (((struct rib *)rn->info)->rn_status, RIB_ROUTE_QUEUED)) - { - if (IS_ZEBRA_DEBUG_RIB_Q) - zlog_debug ("%s: %s/%d: rn %p already queued", __func__, buf, - rn->p.prefixlen, rn); - return; - } - - route_lock_node (rn); /* rib queue lock */ - if (IS_ZEBRA_DEBUG_RIB_Q) zlog_info ("%s: %s/%d: work queue added", __func__, buf, rn->p.prefixlen); @@ -1221,13 +1294,21 @@ rib_queue_add (struct zebra_t *zebra, struct route_node *rn) if (zebra->ribq == NULL) { zlog_err ("%s: work_queue does not exist!", __func__); - route_unlock_node (rn); return; } - - work_queue_add (zebra->ribq, rn); - SET_FLAG (((struct rib *)rn->info)->rn_status, RIB_ROUTE_QUEUED); + /* The RIB queue should normally be either empty or holding the only work_queue_item + * element. In the latter case this element would hold a pointer to the meta queue + * structure, which must be used to actually queue the route nodes to process. So + * create the MQ holder, if necessary, then push the work into it in any case. + * This semantics was introduced after 0.99.9 release. + */ + + /* Should I invent work_queue_empty() and use it, or it's Ok to do as follows? */ + if (!zebra->ribq->items->count) + work_queue_add (zebra->ribq, zebra->mq); + + rib_meta_queue_add (zebra->mq, rn); if (IS_ZEBRA_DEBUG_RIB_Q) zlog_debug ("%s: %s/%d: rn %p queued", __func__, buf, rn->p.prefixlen, rn); @@ -1235,6 +1316,30 @@ rib_queue_add (struct zebra_t *zebra, struct route_node *rn) return; } +/* Create new meta queue. A destructor function doesn't seem to be necessary here. */ +struct meta_queue * +meta_queue_new () +{ + struct meta_queue *new; + unsigned i, failed = 0; + + if ((new = XCALLOC (MTYPE_WORK_QUEUE, sizeof (struct meta_queue))) == NULL) + return NULL; + for (i = 0; i < MQ_SIZE; i++) + if ((new->subq[i] = list_new ()) == NULL) + failed = 1; + if (failed) + { + for (i = 0; i < MQ_SIZE; i++) + if (new->subq[i]) + list_delete (new->subq[i]); + XFREE (MTYPE_WORK_QUEUE, new); + return NULL; + } + new->size = 0; + return new; +} + /* initialise zebra rib work queue */ static void rib_queue_init (struct zebra_t *zebra) @@ -1249,12 +1354,17 @@ rib_queue_init (struct zebra_t *zebra) } /* fill in the work queue spec */ - zebra->ribq->spec.workfunc = &rib_process; + zebra->ribq->spec.workfunc = &meta_queue_process; zebra->ribq->spec.errorfunc = NULL; /* XXX: TODO: These should be runtime configurable via vty */ zebra->ribq->spec.max_retries = 3; zebra->ribq->spec.hold = rib_process_hold_time; + if (!(zebra->mq = meta_queue_new ())) + { + zlog_err ("%s: could not initialise meta queue!", __func__); + return; + } return; } @@ -1663,11 +1773,7 @@ void rib_lookup_and_pushup (struct prefix_ipv4 * p) } } if (changed) - { - work_queue_aim_head (zebrad.ribq, 1); rib_queue_add (&zebrad, rn); - work_queue_aim_head (zebrad.ribq, 0); - } } int diff --git a/zebra/zserv.h b/zebra/zserv.h index 5e223776..87a33a45 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -80,6 +80,7 @@ struct zebra_t /* rib work queue */ struct work_queue *ribq; + struct meta_queue *mq; }; /* Count prefix size from mask length */ |