summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog8
-rw-r--r--NEWS3
-rw-r--r--bgpd/ChangeLog25
-rw-r--r--bgpd/bgp_aspath.c82
-rw-r--r--bgpd/bgp_aspath.h3
-rw-r--r--bgpd/bgp_attr.c53
-rw-r--r--bgpd/bgp_attr.h5
-rw-r--r--bgpd/bgp_main.c10
-rw-r--r--bgpd/bgp_route.c10
-rw-r--r--bgpd/bgp_routemap.c115
-rwxr-xr-xconfigure.ac4
-rw-r--r--lib/ChangeLog12
-rw-r--r--lib/stream.c4
-rw-r--r--lib/stream.h4
-rw-r--r--lib/workqueue.c16
-rw-r--r--lib/workqueue.h3
-rw-r--r--lib/zebra.h2
-rw-r--r--ospf6d/ospf6_main.c3
-rw-r--r--ospfd/ospf_main.c3
-rw-r--r--ripd/ChangeLog4
-rw-r--r--ripd/rip_main.c3
-rw-r--r--ripd/ripd.c2
-rw-r--r--ripngd/ripng_main.c3
-rw-r--r--tests/ChangeLog4
-rw-r--r--tests/Makefile.am6
-rw-r--r--tests/bgp_mp_attr_test.c535
-rw-r--r--tools/multiple-bgpd.sh27
-rw-r--r--zebra/ChangeLog21
-rw-r--r--zebra/connected.c13
-rw-r--r--zebra/main.c3
-rw-r--r--zebra/rib.h16
-rw-r--r--zebra/rt_netlink.c55
-rw-r--r--zebra/zebra_rib.c160
-rw-r--r--zebra/zserv.h1
34 files changed, 1115 insertions, 103 deletions
diff --git a/ChangeLog b/ChangeLog
index ae97cf37..245dd133 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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
diff --git a/NEWS b/NEWS
index 74d258fc..e9d3a998 100644
--- a/NEWS
+++ b/NEWS
@@ -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 */