From 9d3f9705d8b386ccf006c106967c700141e5d049 Mon Sep 17 00:00:00 2001 From: Andrew Certain Date: Wed, 7 Nov 2012 23:50:07 +0000 Subject: [PATCH 001/482] bgpd: add replace-as modifier for BGP neighbor Added replace-as modifier for BGP neighbors when using local-as. If the replace-as modifier is specified, only the replacement AS as specified by the local-as modifier is prepended to the AS_PATH, not the process's AS. In bgp_attr.c, I decided that if (peer->change_local_as) { /* If replace-as is specified, we only use the change_local_as when advertising routes. */ if( ! CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS) ) { aspath = aspath_add_seq (aspath, peer->local_as); } aspath = aspath_add_seq (aspath, peer->change_local_as); } else { aspath = aspath_add_seq (aspath, peer->local_as); } was clearer than the alternative that didn't duplicate the prepending of the process's AS: /* First, append the process local AS unless we have an alternate local_as * and we're replacing it (as opposed to just prepending it). */ if (! (peer->change_local_as && CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS) ) ) { aspath = aspath_add_seq (aspath, peer->local_as); } if (peer->change_local_as) aspath = aspath_add_seq (aspath, peer->change_local_as); } But I could be convinced otherwise. Signed-off-by: David Lamparter --- bgpd/bgp_attr.c | 11 +++++++++-- bgpd/bgp_vty.c | 45 +++++++++++++++++++++++++++++++++++++++++---- bgpd/bgpd.c | 29 +++++++++++++++++++++++++---- bgpd/bgpd.h | 4 +++- 4 files changed, 78 insertions(+), 11 deletions(-) diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index cdc99ab3..f48a5438 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -2085,9 +2085,16 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, } else { - aspath = aspath_add_seq (aspath, peer->local_as); - if (peer->change_local_as) + if (peer->change_local_as) { + /* If replace-as is specified, we only use the change_local_as when + advertising routes. */ + if( ! CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS) ) { + aspath = aspath_add_seq (aspath, peer->local_as); + } aspath = aspath_add_seq (aspath, peer->change_local_as); + } else { + aspath = aspath_add_seq (aspath, peer->local_as); + } } } else if (peer->sort == BGP_PEER_CONFED) diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index cb7ff1fa..0f288948 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -1570,7 +1570,7 @@ DEFUN (neighbor_local_as, if (! peer) return CMD_WARNING; - ret = peer_local_as_set (peer, atoi (argv[1]), 0); + ret = peer_local_as_set (peer, atoi (argv[1]), 0, 0); return bgp_vty_return (vty, ret); } @@ -1590,10 +1590,32 @@ DEFUN (neighbor_local_as_no_prepend, if (! peer) return CMD_WARNING; - ret = peer_local_as_set (peer, atoi (argv[1]), 1); + ret = peer_local_as_set (peer, atoi (argv[1]), 1, 0); return bgp_vty_return (vty, ret); } +DEFUN (neighbor_local_as_no_prepend_replace_as, + neighbor_local_as_no_prepend_replace_as_cmd, + NEIGHBOR_CMD2 "local-as " CMD_AS_RANGE " no-prepend replace-as", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Specify a local-as number\n" + "AS number used as local AS\n" + "Do not prepend local-as to updates from ebgp peers\n" + "Do not prepend local-as to updates from ibgp peers\n") +{ + struct peer *peer; + int ret; + + peer = peer_and_group_lookup_vty (vty, argv[0]); + if (! peer) + return CMD_WARNING; + + ret = peer_local_as_set (peer, atoi (argv[1]), 1, 1); + return bgp_vty_return (vty, ret); +} + + DEFUN (no_neighbor_local_as, no_neighbor_local_as_cmd, NO_NEIGHBOR_CMD2 "local-as", @@ -1631,6 +1653,17 @@ ALIAS (no_neighbor_local_as, "Specify a local-as number\n" "AS number used as local AS\n" "Do not prepend local-as to updates from ebgp peers\n") + +ALIAS (no_neighbor_local_as, + no_neighbor_local_as_val3_cmd, + NO_NEIGHBOR_CMD2 "local-as " CMD_AS_RANGE " no-prepend replace-as", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Specify a local-as number\n" + "AS number used as local AS\n" + "Do not prepend local-as to updates from ebgp peers\n" + "Do not prepend local-as to updates from ibgp peers\n") DEFUN (neighbor_password, neighbor_password_cmd, @@ -7494,10 +7527,12 @@ bgp_show_peer (struct vty *vty, struct peer *p) /* Configured IP address. */ vty_out (vty, "BGP neighbor is %s, ", p->host); vty_out (vty, "remote AS %u, ", p->as); - vty_out (vty, "local AS %u%s, ", + vty_out (vty, "local AS %u%s%s, ", p->change_local_as ? p->change_local_as : p->local_as, CHECK_FLAG (p->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND) ? - " no-prepend" : ""); + " no-prepend" : "", + CHECK_FLAG (p->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS) ? + " replace-as" : ""); vty_out (vty, "%s link%s", p->as == p->local_as ? "internal" : "external", VTY_NEWLINE); @@ -9175,9 +9210,11 @@ bgp_vty_init (void) /* "neighbor local-as" commands. */ install_element (BGP_NODE, &neighbor_local_as_cmd); install_element (BGP_NODE, &neighbor_local_as_no_prepend_cmd); + install_element (BGP_NODE, &neighbor_local_as_no_prepend_replace_as_cmd); install_element (BGP_NODE, &no_neighbor_local_as_cmd); install_element (BGP_NODE, &no_neighbor_local_as_val_cmd); install_element (BGP_NODE, &no_neighbor_local_as_val2_cmd); + install_element (BGP_NODE, &no_neighbor_local_as_val3_cmd); /* "neighbor password" commands. */ install_element (BGP_NODE, &neighbor_password_cmd); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 69c8c0a3..908bdd94 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -962,6 +962,7 @@ peer_as_change (struct peer *peer, as_t as) { peer->change_local_as = 0; UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND); + UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS); } } @@ -1830,6 +1831,7 @@ peer_group_bind (struct bgp *bgp, union sockunion *su, { group->conf->change_local_as = 0; UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND); + UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS); } } @@ -3405,7 +3407,7 @@ peer_allowas_in_unset (struct peer *peer, afi_t afi, safi_t safi) } int -peer_local_as_set (struct peer *peer, as_t as, int no_prepend) +peer_local_as_set (struct peer *peer, as_t as, int no_prepend, int replace_as) { struct bgp *bgp = peer->bgp; struct peer_group *group; @@ -3421,9 +3423,14 @@ peer_local_as_set (struct peer *peer, as_t as, int no_prepend) if (peer_group_active (peer)) return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + if (peer->as == as) + return BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS_REMOTE_AS; + if (peer->change_local_as == as && ((CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND) && no_prepend) - || (! CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND) && ! no_prepend))) + || (! CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND) && ! no_prepend)) && + ((CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS) && replace_as) + || (! CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS) && ! replace_as))) return 0; peer->change_local_as = as; @@ -3432,6 +3439,11 @@ peer_local_as_set (struct peer *peer, as_t as, int no_prepend) else UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND); + if (replace_as) + SET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS); + else + UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS); + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) { if (peer->status == Established) @@ -3455,6 +3467,11 @@ peer_local_as_set (struct peer *peer, as_t as, int no_prepend) else UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND); + if (replace_as) + SET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS); + else + UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS); + if (peer->status == Established) { peer->last_reset = PEER_DOWN_LOCAL_AS_CHANGE; @@ -3482,6 +3499,7 @@ peer_local_as_unset (struct peer *peer) peer->change_local_as = 0; UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND); + UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS); if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) { @@ -3502,6 +3520,7 @@ peer_local_as_unset (struct peer *peer) { peer->change_local_as = 0; UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND); + UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS); if (peer->status == Established) { @@ -4770,10 +4789,12 @@ bgp_config_write_peer (struct vty *vty, struct bgp *bgp, /* local-as. */ if (peer->change_local_as) if (! peer_group_active (peer)) - vty_out (vty, " neighbor %s local-as %u%s%s", addr, + vty_out (vty, " neighbor %s local-as %u%s%s%s", addr, peer->change_local_as, CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND) ? - " no-prepend" : "", VTY_NEWLINE); + " no-prepend" : "", + CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS) ? + " replace-as" : "", VTY_NEWLINE); /* Description. */ if (peer->desc) diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 63e326af..0746f0dd 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -381,6 +381,7 @@ struct peer #define PEER_FLAG_DYNAMIC_CAPABILITY (1 << 5) /* dynamic capability */ #define PEER_FLAG_DISABLE_CONNECTED_CHECK (1 << 6) /* disable-connected-check */ #define PEER_FLAG_LOCAL_AS_NO_PREPEND (1 << 7) /* local-as no-prepend */ +#define PEER_FLAG_LOCAL_AS_REPLACE_AS (1 << 8) /* local-as no-prepend replace-as */ /* NSF mode (graceful restart) */ u_char nsf[AFI_MAX][SAFI_MAX]; @@ -814,6 +815,7 @@ enum bgp_clear_type #define BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK -30 #define BGP_ERR_NO_IBGP_WITH_TTLHACK -31 #define BGP_ERR_MAX -32 +#define BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS_REMOTE_AS -33 extern struct bgp_master *bm; @@ -941,7 +943,7 @@ extern int peer_distribute_unset (struct peer *, afi_t, safi_t, int); extern int peer_allowas_in_set (struct peer *, afi_t, safi_t, int); extern int peer_allowas_in_unset (struct peer *, afi_t, safi_t); -extern int peer_local_as_set (struct peer *, as_t, int); +extern int peer_local_as_set (struct peer *, as_t, int, int); extern int peer_local_as_unset (struct peer *); extern int peer_prefix_list_set (struct peer *, afi_t, safi_t, int, const char *); From 8b366b9cfd1f3ec1a45e0f82db7fdb27e3bb3594 Mon Sep 17 00:00:00 2001 From: Andrew Certain Date: Wed, 7 Nov 2012 23:50:08 +0000 Subject: [PATCH 002/482] bgpd: Fixed out-of-date comment When going through the code to write the documentation for local-as, I discovered that one of the comments was out-of-date. Signed-off-by: David Lamparter --- bgpd/bgp_attr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index f48a5438..2cbd7bc3 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -1709,7 +1709,7 @@ bgp_attr_unknown (struct bgp_attr_parser_args *args) } /* Read attribute of update packet. This function is called from - bgp_update() in bgpd.c. */ + bgp_update_receive() in bgp_packet.c. */ bgp_attr_parse_ret_t bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, struct bgp_nlri *mp_update, struct bgp_nlri *mp_withdraw) From 5aebb9c77fc2257c9d9df72db66668fabb24fc52 Mon Sep 17 00:00:00 2001 From: Andrew Certain Date: Wed, 7 Nov 2012 23:50:09 +0000 Subject: [PATCH 003/482] bgpd: document bgp neighbor local-as peer command Signed-off-by: David Lamparter --- doc/bgpd.texi | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/doc/bgpd.texi b/doc/bgpd.texi index 63834600..dd37d3ec 100644 --- a/doc/bgpd.texi +++ b/doc/bgpd.texi @@ -339,6 +339,27 @@ routes. @deffnx {BGP} {no neighbor @var{peer} maximum-prefix @var{number}} {} @end deffn +@deffn {BGP} {neighbor @var{peer} local-as @var{as-number}} {} +@deffnx {BGP} {neighbor @var{peer} local-as @var{as-number} no-prepend} {} +@deffnx {BGP} {neighbor @var{peer} local-as @var{as-number} no-prepend replace-as} {} +@deffnx {BGP} {no neighbor @var{peer} local-as} {} +Specify an alternate AS for this BGP process when interacting with the +specified peer. With no modifiers, the specified local-as is prepended to +the received AS_PATH when receiving routing updates from the peer, and +prepended to the outgoing AS_PATH (after the process local AS) when +transmitting local routes to the peer. + +If the no-prepend attribute is specified, then the supplied local-as is not +prepended to the received AS_PATH. + +If the replace-as attribute is specified, then only the supplied local-as is +prepended to the AS_PATH when transmitting local-route updates to this peer. + +Note that replace-as can only be specified if no-prepend is. + +This command is only allowed for eBGP peers. +@end deffn + @node Peer filtering @subsection Peer filtering From 9fd92e3c4bdcc78e0f0d94d53a2d4c7b0e893fcb Mon Sep 17 00:00:00 2001 From: Avneesh Sachdev Date: Tue, 13 Nov 2012 22:48:53 +0000 Subject: [PATCH 004/482] zebra: add structure to hold per-prefix state in RIB Add the rib_dest_t structure to hold per-prefix state in the routing information base. This gives us an appropriate place to maintain the queueing state of a route_node. Queuing state was previously being stored on the first rib in the list of ribs hanging off the route_node. * zebra/rib.h - Add new structure rib_dest_t. - Remove the rn_status field from 'struct rib', it is no longer required. - Add macros (RNODE_FOREACH_RIB, RNODE_FOREACH_RIB_SAFE) for walking all 'struct ribs' corresponding to a route_node. These hide the fact that there is an intermediate rib_dest_t structure. - Add a few utility inlines to go between a rib_dest_t and associated structures. * zebra/zebra_rib.c - rib_link()/rib_unlink() Tweak for new behavior, where the 'info' pointer of a route_node points to a rib_dest_t. The list of ribs for a prefix now hangs off of the dest. Change the way we ref count route_nodes. We now hold a single ref count on a route_node if there is a corresponding rib_dest_t. - Maintain the queuing state of a route_node on the flags field of the rib_dest_t. - Add the rib_gc_dest() function, which deletes a rib_dest_t if it is no longer required. A rib_dest_t can be deleted iff there are no struct ribs hanging off of it. - Call rib_gc_dest() any time we unlink a rib from the rib_dest_t. Currently we only need to call it once, just before we return from rib_process(). * zebra/{redistribute,zebra_rib,zebra_snmp,zebra_vty}.c Use new macros to walk over route_node ribs. * lib/memtypes.c Add memory type for rib_dest_t. Signed-off-by: Avneesh Sachdev Signed-off-by: David Lamparter --- lib/memtypes.c | 1 + zebra/redistribute.c | 8 +- zebra/rib.h | 118 +++++++++++++++++++++++- zebra/zebra_rib.c | 213 ++++++++++++++++++++++++++++--------------- zebra/zebra_snmp.c | 10 +- zebra/zebra_vty.c | 24 ++--- 6 files changed, 276 insertions(+), 98 deletions(-) diff --git a/lib/memtypes.c b/lib/memtypes.c index bbf96929..76dece29 100644 --- a/lib/memtypes.c +++ b/lib/memtypes.c @@ -82,6 +82,7 @@ struct memory_list memory_list_zebra[] = { MTYPE_RIB_QUEUE, "RIB process work queue" }, { MTYPE_STATIC_IPV4, "Static IPv4 route" }, { MTYPE_STATIC_IPV6, "Static IPv6 route" }, + { MTYPE_RIB_DEST, "RIB destination" }, { -1, NULL }, }; diff --git a/zebra/redistribute.c b/zebra/redistribute.c index 4276f1d0..47658248 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -107,7 +107,7 @@ zebra_redistribute_default (struct zserv *client) rn = route_node_lookup (table, (struct prefix *)&p); if (rn) { - for (newrib = rn->info; newrib; newrib = newrib->next) + RNODE_FOREACH_RIB (rn, newrib) if (CHECK_FLAG (newrib->flags, ZEBRA_FLAG_SELECTED) && newrib->distance != DISTANCE_INFINITY) zsend_route_multipath (ZEBRA_IPV4_ROUTE_ADD, client, &rn->p, newrib); @@ -127,7 +127,7 @@ zebra_redistribute_default (struct zserv *client) rn = route_node_lookup (table, (struct prefix *)&p6); if (rn) { - for (newrib = rn->info; newrib; newrib = newrib->next) + RNODE_FOREACH_RIB (rn, newrib) if (CHECK_FLAG (newrib->flags, ZEBRA_FLAG_SELECTED) && newrib->distance != DISTANCE_INFINITY) zsend_route_multipath (ZEBRA_IPV6_ROUTE_ADD, client, &rn->p, newrib); @@ -148,7 +148,7 @@ zebra_redistribute (struct zserv *client, int type) table = vrf_table (AFI_IP, SAFI_UNICAST, 0); if (table) for (rn = route_top (table); rn; rn = route_next (rn)) - for (newrib = rn->info; newrib; newrib = newrib->next) + RNODE_FOREACH_RIB (rn, newrib) if (CHECK_FLAG (newrib->flags, ZEBRA_FLAG_SELECTED) && newrib->type == type && newrib->distance != DISTANCE_INFINITY @@ -159,7 +159,7 @@ zebra_redistribute (struct zserv *client, int type) table = vrf_table (AFI_IP6, SAFI_UNICAST, 0); if (table) for (rn = route_top (table); rn; rn = route_next (rn)) - for (newrib = rn->info; newrib; newrib = newrib->next) + RNODE_FOREACH_RIB (rn, newrib) if (CHECK_FLAG (newrib->flags, ZEBRA_FLAG_SELECTED) && newrib->type == type && newrib->distance != DISTANCE_INFINITY diff --git a/zebra/rib.h b/zebra/rib.h index 1b85c81e..084f3516 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -24,6 +24,7 @@ #define _ZEBRA_RIB_H #include "prefix.h" +#include "table.h" #define DISTANCE_INFINITY 255 @@ -38,10 +39,6 @@ union g_addr { struct rib { - /* Status Flags for the *route_node*, but kept in the head RIB.. */ - u_char rn_status; -#define RIB_ROUTE_QUEUED(x) (1 << (x)) - /* Link list. */ struct rib *next; struct rib *prev; @@ -97,6 +94,57 @@ struct meta_queue u_int32_t size; /* sum of lengths of all subqueues */ }; +/* + * Structure that represents a single destination (prefix). + */ +typedef struct rib_dest_t_ +{ + + /* + * Back pointer to the route node for this destination. This helps + * us get to the prefix that this structure is for. + */ + struct route_node *rnode; + + /* + * Doubly-linked list of routes for this prefix. + */ + struct rib *routes; + + /* + * Flags, see below. + */ + u_int32_t flags; + +} rib_dest_t; + +#define RIB_ROUTE_QUEUED(x) (1 << (x)) + +/* + * The maximum qindex that can be used. + */ +#define ZEBRA_MAX_QINDEX (MQ_SIZE - 1) + +/* + * Macro to iterate over each route for a destination (prefix). + */ +#define RIB_DEST_FOREACH_ROUTE(dest, rib) \ + for ((rib) = (dest) ? (dest)->routes : NULL; (rib); (rib) = (rib)->next) + +/* + * Same as above, but allows the current node to be unlinked. + */ +#define RIB_DEST_FOREACH_ROUTE_SAFE(dest, rib, next) \ + for ((rib) = (dest) ? (dest)->routes : NULL; \ + (rib) && ((next) = (rib)->next, 1); \ + (rib) = (next)) + +#define RNODE_FOREACH_RIB(rn, rib) \ + RIB_DEST_FOREACH_ROUTE (rib_dest_from_rnode (rn), rib) + +#define RNODE_FOREACH_RIB_SAFE(rn, rib, next) \ + RIB_DEST_FOREACH_ROUTE_SAFE (rib_dest_from_rnode (rn), rib, next) + /* Static route information. */ struct static_ipv4 { @@ -307,4 +355,66 @@ static_delete_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate, #endif /* HAVE_IPV6 */ +extern int rib_gc_dest (struct route_node *rn); + +/* + * Inline functions. + */ + +/* + * rib_dest_from_rnode + */ +static inline rib_dest_t * +rib_dest_from_rnode (struct route_node *rn) +{ + return (rib_dest_t *) rn->info; +} + +/* + * rnode_to_ribs + * + * Returns a pointer to the list of routes corresponding to the given + * route_node. + */ +static inline struct rib * +rnode_to_ribs (struct route_node *rn) +{ + rib_dest_t *dest; + + dest = rib_dest_from_rnode (rn); + if (!dest) + return NULL; + + return dest->routes; +} + +/* + * rib_dest_prefix + */ +static inline struct prefix * +rib_dest_prefix (rib_dest_t *dest) +{ + return &dest->rnode->p; +} + +/* + * rib_dest_af + * + * Returns the address family that the destination is for. + */ +static inline u_char +rib_dest_af (rib_dest_t *dest) +{ + return dest->rnode->p.family; +} + +/* + * rib_dest_table + */ +static inline struct route_table * +rib_dest_table (rib_dest_t *dest) +{ + return dest->rnode->table; +} + #endif /*_ZEBRA_RIB_H */ diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 0e29e616..f0d5c9d9 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -351,7 +351,7 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, return 0; /* Pick up selected route. */ - for (match = rn->info; match; match = match->next) + RNODE_FOREACH_RIB (rn, match) { if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED)) continue; @@ -452,7 +452,7 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, return 0; /* Pick up selected route. */ - for (match = rn->info; match; match = match->next) + RNODE_FOREACH_RIB (rn, match) { if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED)) continue; @@ -543,7 +543,7 @@ rib_match_ipv4 (struct in_addr addr) route_unlock_node (rn); /* Pick up selected route. */ - for (match = rn->info; match; match = match->next) + RNODE_FOREACH_RIB (rn, match) { if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED)) continue; @@ -601,7 +601,7 @@ rib_lookup_ipv4 (struct prefix_ipv4 *p) /* Unlock node. */ route_unlock_node (rn); - for (match = rn->info; match; match = match->next) + RNODE_FOREACH_RIB (rn, match) { if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED)) continue; @@ -658,7 +658,7 @@ rib_lookup_ipv4_route (struct prefix_ipv4 *p, union sockunion * qgate) route_unlock_node (rn); /* Find out if a "selected" RR for the discovered RIB entry exists ever. */ - for (match = rn->info; match; match = match->next) + RNODE_FOREACH_RIB (rn, match) { if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED)) continue; @@ -725,7 +725,7 @@ rib_match_ipv6 (struct in6_addr *addr) route_unlock_node (rn); /* Pick up selected route. */ - for (match = rn->info; match; match = match->next) + RNODE_FOREACH_RIB (rn, match) { if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED)) continue; @@ -975,6 +975,61 @@ rib_uninstall (struct route_node *rn, struct rib *rib) static void rib_unlink (struct route_node *, struct rib *); +/* + * rib_can_delete_dest + * + * Returns TRUE if the given dest can be deleted from the table. + */ +static int +rib_can_delete_dest (rib_dest_t *dest) +{ + if (dest->routes) + { + return 0; + } + + return 1; +} + +/* + * rib_gc_dest + * + * Garbage collect the rib dest corresponding to the given route node + * if appropriate. + * + * Returns TRUE if the dest was deleted, FALSE otherwise. + */ +int +rib_gc_dest (struct route_node *rn) +{ + rib_dest_t *dest; + char buf[INET6_ADDRSTRLEN]; + + dest = rib_dest_from_rnode (rn); + if (!dest) + return 0; + + if (!rib_can_delete_dest (dest)) + return 0; + + if (IS_ZEBRA_DEBUG_RIB) + { + inet_ntop (rn->p.family, &rn->p.u.prefix, buf, sizeof (buf)); + zlog_debug ("%s: %s/%d: removing dest from table", __func__, + buf, rn->p.prefixlen); + } + + dest->rnode = NULL; + XFREE (MTYPE_RIB_DEST, dest); + rn->info = NULL; + + /* + * Release the one reference that we keep on the route node. + */ + route_unlock_node (rn); + return 1; +} + /* Core function for processing routing information base. */ static void rib_process (struct route_node *rn) @@ -993,13 +1048,8 @@ rib_process (struct route_node *rn) if (IS_ZEBRA_DEBUG_RIB || IS_ZEBRA_DEBUG_RIB_Q) inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); - for (rib = rn->info; rib; rib = next) + RNODE_FOREACH_RIB_SAFE (rn, rib, next) { - /* The next pointer is saved, because current pointer - * may be passed to rib_unlink() in the middle of iteration. - */ - next = rib->next; - /* Currently installed rib. */ if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) { @@ -1017,7 +1067,7 @@ rib_process (struct route_node *rn) if (IS_ZEBRA_DEBUG_RIB) zlog_debug ("%s: %s/%d: rn %p, removing rib %p", __func__, buf, rn->p.prefixlen, rn, rib); - rib_unlink (rn, rib); + rib_unlink (rn, rib); } else del = rib; @@ -1074,7 +1124,7 @@ rib_process (struct route_node *rn) /* metric tie-breaks equal distance */ if (rib->metric <= select->metric) select = rib; - } /* for (rib = rn->info; rib; rib = next) */ + } /* RNODE_FOREACH_RIB_SAFE */ /* After the cycle is finished, the following pointers will be set: * select --- the winner RIB entry, if any was found, otherwise NULL @@ -1171,6 +1221,11 @@ rib_process (struct route_node *rn) end: if (IS_ZEBRA_DEBUG_RIB_Q) zlog_debug ("%s: %s/%d: rn %p dequeued", __func__, buf, rn->p.prefixlen, rn); + + /* + * Check if the dest can be deleted now. + */ + rib_gc_dest (rn); } /* Take a list of route_node structs and return 1, if there was a record @@ -1189,8 +1244,9 @@ process_subq (struct list * subq, u_char qindex) 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)); + if (rnode->info) + UNSET_FLAG (rib_dest_from_rnode (rnode)->flags, RIB_ROUTE_QUEUED (qindex)); + #if 0 else { @@ -1223,7 +1279,9 @@ meta_queue_process (struct work_queue *dummy, void *data) return mq->size ? WQ_REQUEUE : WQ_SUCCESS; } -/* Map from rib types to queue type (priority) in meta queue */ +/* + * Map from rib types to queue type (priority) in meta queue + */ static const u_char meta_queue_map[ZEBRA_ROUTE_MAX] = { [ZEBRA_ROUTE_SYSTEM] = 4, [ZEBRA_ROUTE_KERNEL] = 0, @@ -1251,12 +1309,13 @@ rib_meta_queue_add (struct meta_queue *mq, struct route_node *rn) 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) + RNODE_FOREACH_RIB (rn, rib) { u_char qindex = meta_queue_map[rib->type]; /* Invariant: at this point we always have rn->info set. */ - if (CHECK_FLAG (((struct rib *)rn->info)->rn_status, RIB_ROUTE_QUEUED(qindex))) + if (CHECK_FLAG (rib_dest_from_rnode (rn)->flags, + RIB_ROUTE_QUEUED (qindex))) { if (IS_ZEBRA_DEBUG_RIB_Q) zlog_debug ("%s: %s/%d: rn %p is already queued in sub-queue %u", @@ -1264,7 +1323,7 @@ rib_meta_queue_add (struct meta_queue *mq, struct route_node *rn) continue; } - SET_FLAG (((struct rib *)rn->info)->rn_status, RIB_ROUTE_QUEUED(qindex)); + SET_FLAG (rib_dest_from_rnode (rn)->flags, RIB_ROUTE_QUEUED (qindex)); listnode_add (mq->subq[qindex], rn); route_lock_node (rn); mq->size++; @@ -1286,7 +1345,7 @@ rib_queue_add (struct zebra_t *zebra, struct route_node *rn) inet_ntop (AF_INET, &rn->p.u.prefix, buf, INET_ADDRSTRLEN); /* Pointless to queue a route_node with no RIB entries to add or remove */ - if (!rn->info) + if (!rnode_to_ribs (rn)) { zlog_debug ("%s: called for route_node (%p, %d) with no ribs", __func__, rn, rn->lock); @@ -1395,17 +1454,16 @@ rib_queue_init (struct zebra_t *zebra) * |-> set RIB_ENTRY_REMOVE | * rib_delnode (RIB freed) * - * - * Queueing state for a route_node is kept in the head RIB entry, this - * state must be preserved as and when the head RIB entry of a - * route_node is changed by rib_unlink / rib_link. A small complication, - * but saves having to allocate a dedicated object for this. + * The 'info' pointer of a route_node points to a rib_dest_t + * ('dest'). Queueing state for a route_node is kept on the dest. The + * dest is created on-demand by rib_link() and is kept around at least + * as long as there are ribs hanging off it (@see rib_gc_dest()). * * Refcounting (aka "locking" throughout the GNU Zebra and Quagga code): * * - route_nodes: refcounted by: - * - RIBs attached to route_node: - * - managed by: rib_link/unlink + * - dest attached to route_node: + * - managed by: rib_link/rib_gc_dest * - route_node processing queue * - managed by: rib_addqueue, rib_process. * @@ -1416,12 +1474,11 @@ static void rib_link (struct route_node *rn, struct rib *rib) { struct rib *head; + rib_dest_t *dest; char buf[INET6_ADDRSTRLEN]; - + assert (rib && rn); - route_lock_node (rn); /* rn route table reference */ - if (IS_ZEBRA_DEBUG_RIB) { inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); @@ -1429,18 +1486,28 @@ rib_link (struct route_node *rn, struct rib *rib) buf, rn->p.prefixlen, rn, rib); } - head = rn->info; - if (head) + dest = rib_dest_from_rnode (rn); + if (!dest) { if (IS_ZEBRA_DEBUG_RIB) - zlog_debug ("%s: %s/%d: new head, rn_status copied over", __func__, - buf, rn->p.prefixlen); + { + zlog_debug ("%s: %s/%d: adding dest to table", __func__, + buf, rn->p.prefixlen); + } + + dest = XCALLOC (MTYPE_RIB_DEST, sizeof (rib_dest_t)); + route_lock_node (rn); /* rn route table reference */ + rn->info = dest; + dest->rnode = rn; + } + + head = dest->routes; + if (head) + { head->prev = rib; - /* Transfer the rn status flags to the new head RIB */ - rib->rn_status = head->rn_status; } rib->next = head; - rn->info = rib; + dest->routes = rib; rib_queue_add (&zebrad, rn); } @@ -1465,11 +1532,21 @@ rib_addnode (struct route_node *rn, struct rib *rib) rib_link (rn, rib); } +/* + * rib_unlink + * + * Detach a rib structure from a route_node. + * + * Note that a call to rib_unlink() should be followed by a call to + * rib_gc_dest() at some point. This allows a rib_dest_t that is no + * longer required to be deleted. + */ static void rib_unlink (struct route_node *rn, struct rib *rib) { struct nexthop *nexthop, *next; char buf[INET6_ADDRSTRLEN]; + rib_dest_t *dest; assert (rn && rib); @@ -1480,6 +1557,8 @@ rib_unlink (struct route_node *rn, struct rib *rib) __func__, buf, rn->p.prefixlen, rn, rib); } + dest = rib_dest_from_rnode (rn); + if (rib->next) rib->next->prev = rib->prev; @@ -1487,15 +1566,7 @@ rib_unlink (struct route_node *rn, struct rib *rib) rib->prev->next = rib->next; else { - rn->info = rib->next; - - if (rn->info) - { - if (IS_ZEBRA_DEBUG_RIB) - zlog_debug ("%s: %s/%d: rn %p, rib %p, new head copy", - __func__, buf, rn->p.prefixlen, rn, rib); - rib->next->rn_status = rib->rn_status; - } + dest->routes = rib->next; } /* free RIB and nexthops */ @@ -1506,7 +1577,6 @@ rib_unlink (struct route_node *rn, struct rib *rib) } XFREE (MTYPE_RIB, rib); - route_unlock_node (rn); /* rn route table reference */ } static void @@ -1561,7 +1631,7 @@ rib_add_ipv4 (int type, int flags, struct prefix_ipv4 *p, /* If same type of route are installed, treat it as a implicit withdraw. */ - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) continue; @@ -1717,7 +1787,7 @@ void rib_lookup_and_dump (struct prefix_ipv4 * p) route_unlock_node (rn); /* let's go */ - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { zlog_debug ( @@ -1764,7 +1834,7 @@ void rib_lookup_and_pushup (struct prefix_ipv4 * p) * RIBQ record already on head. This is necessary for proper revalidation * of the rest of the RIB. */ - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED) && ! RIB_SYSTEM_ROUTE (rib)) @@ -1816,7 +1886,7 @@ rib_add_ipv4_multipath (struct prefix_ipv4 *p, struct rib *rib, safi_t safi) /* If same type of route are installed, treat it as a implicit withdraw. */ - for (same = rn->info; same; same = same->next) + RNODE_FOREACH_RIB (rn, same) { if (CHECK_FLAG (same->status, RIB_ENTRY_REMOVED)) continue; @@ -1907,7 +1977,7 @@ rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p, } /* Lookup same type route. */ - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) continue; @@ -2000,7 +2070,7 @@ static_install_ipv4 (struct prefix *p, struct static_ipv4 *si) /* Lookup existing route */ rn = route_node_get (table, p); - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) continue; @@ -2096,7 +2166,7 @@ static_uninstall_ipv4 (struct prefix *p, struct static_ipv4 *si) if (! rn) return; - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) continue; @@ -2355,7 +2425,7 @@ rib_add_ipv6 (int type, int flags, struct prefix_ipv6 *p, /* If same type of route are installed, treat it as a implicit withdraw. */ - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) continue; @@ -2458,7 +2528,7 @@ rib_delete_ipv6 (int type, int flags, struct prefix_ipv6 *p, } /* Lookup same type route. */ - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { if (CHECK_FLAG(rib->status, RIB_ENTRY_REMOVED)) continue; @@ -2551,7 +2621,7 @@ static_install_ipv6 (struct prefix *p, struct static_ipv6 *si) /* Lookup existing route */ rn = route_node_get (table, p); - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { if (CHECK_FLAG(rib->status, RIB_ENTRY_REMOVED)) continue; @@ -2648,7 +2718,7 @@ static_uninstall_ipv6 (struct prefix *p, struct static_ipv6 *si) if (! rn) return; - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) continue; @@ -2844,13 +2914,13 @@ rib_update (void) table = vrf_table (AFI_IP, SAFI_UNICAST, 0); if (table) for (rn = route_top (table); rn; rn = route_next (rn)) - if (rn->info) + if (rnode_to_ribs (rn)) rib_queue_add (&zebrad, rn); table = vrf_table (AFI_IP6, SAFI_UNICAST, 0); if (table) for (rn = route_top (table); rn; rn = route_next (rn)) - if (rn->info) + if (rnode_to_ribs (rn)) rib_queue_add (&zebrad, rn); } @@ -2865,10 +2935,8 @@ rib_weed_table (struct route_table *table) if (table) for (rn = route_top (table); rn; rn = route_next (rn)) - for (rib = rn->info; rib; rib = next) + RNODE_FOREACH_RIB_SAFE (rn, rib, next) { - next = rib->next; - if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) continue; @@ -2897,10 +2965,8 @@ rib_sweep_table (struct route_table *table) if (table) for (rn = route_top (table); rn; rn = route_next (rn)) - for (rib = rn->info; rib; rib = next) + RNODE_FOREACH_RIB_SAFE (rn, rib, next) { - next = rib->next; - if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) continue; @@ -2933,9 +2999,8 @@ rib_score_proto_table (u_char proto, struct route_table *table) if (table) for (rn = route_top (table); rn; rn = route_next (rn)) - for (rib = rn->info; rib; rib = next) + RNODE_FOREACH_RIB_SAFE (rn, rib, next) { - next = rib->next; if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) continue; if (rib->type == proto) @@ -2965,11 +3030,13 @@ rib_close_table (struct route_table *table) if (table) for (rn = route_top (table); rn; rn = route_next (rn)) - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { - if (! RIB_SYSTEM_ROUTE (rib) - && CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) - rib_uninstall_kernel (rn, rib); + if (!CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) + continue; + + if (! RIB_SYSTEM_ROUTE (rib)) + rib_uninstall_kernel (rn, rib); } } diff --git a/zebra/zebra_snmp.c b/zebra/zebra_snmp.c index f52bbcb8..e06e1443 100644 --- a/zebra/zebra_snmp.c +++ b/zebra/zebra_snmp.c @@ -150,7 +150,7 @@ ipFwNumber (struct variable *v, oid objid[], size_t *objid_len, /* Return number of routing entries. */ result = 0; for (rn = route_top (table); rn; rn = route_next (rn)) - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) result++; return (u_char *)&result; @@ -175,7 +175,7 @@ ipCidrNumber (struct variable *v, oid objid[], size_t *objid_len, /* Return number of routing entries. */ result = 0; for (rn = route_top (table); rn; rn = route_next (rn)) - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) result++; return (u_char *)&result; @@ -369,7 +369,7 @@ get_fwtable_route_node(struct variable *v, oid objid[], size_t *objid_len, { if (!in_addr_cmp(&(*np)->p.u.prefix, (u_char *)&dest)) { - for (*rib = (*np)->info; *rib; *rib = (*rib)->next) + RNODE_FOREACH_RIB (*np, *rib) { if (!in_addr_cmp((u_char *)&(*rib)->nexthop->gate.ipv4, (u_char *)&nexthop)) @@ -388,12 +388,12 @@ get_fwtable_route_node(struct variable *v, oid objid[], size_t *objid_len, /* Check destination first */ if (in_addr_cmp(&np2->p.u.prefix, (u_char *)&dest) > 0) - for (rib2 = np2->info; rib2; rib2 = rib2->next) + RNODE_FOREACH_RIB (np2, rib2) check_replace(np2, rib2, np, rib); if (in_addr_cmp(&np2->p.u.prefix, (u_char *)&dest) == 0) { /* have to look at each rib individually */ - for (rib2 = np2->info; rib2; rib2 = rib2->next) + RNODE_FOREACH_RIB (np2, rib2) { int proto2, policy2; diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 743c13fe..d07b09c8 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -535,7 +535,7 @@ vty_show_ip_route_detail (struct vty *vty, struct route_node *rn) struct rib *rib; struct nexthop *nexthop; - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { vty_out (vty, "Routing entry for %s/%d%s", inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen, @@ -822,7 +822,7 @@ DEFUN (show_ip_route, /* Show all IPv4 routes. */ for (rn = route_top (table); rn; rn = route_next (rn)) - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { if (first) { @@ -863,7 +863,7 @@ DEFUN (show_ip_route_prefix_longer, /* Show matched type IPv4 routes. */ for (rn = route_top (table); rn; rn = route_next (rn)) - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) if (prefix_match (&p, &rn->p)) { if (first) @@ -896,7 +896,7 @@ DEFUN (show_ip_route_supernets, /* Show matched type IPv4 routes. */ for (rn = route_top (table); rn; rn = route_next (rn)) - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { addr = ntohl (rn->p.u.prefix4.s_addr); @@ -942,7 +942,7 @@ DEFUN (show_ip_route_protocol, /* Show matched type IPv4 routes. */ for (rn = route_top (table); rn; rn = route_next (rn)) - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) if (rib->type == type) { if (first) @@ -1046,7 +1046,7 @@ vty_show_ip_route_summary (struct vty *vty, struct route_table *table) memset (&rib_cnt, 0, sizeof(rib_cnt)); memset (&fib_cnt, 0, sizeof(fib_cnt)); for (rn = route_top (table); rn; rn = route_next (rn)) - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) { rib_cnt[ZEBRA_ROUTE_TOTAL]++; @@ -1219,7 +1219,7 @@ DEFUN (show_ip_mroute, /* Show all IPv4 routes. */ for (rn = route_top (table); rn; rn = route_next (rn)) - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { if (first) { @@ -1546,7 +1546,7 @@ vty_show_ipv6_route_detail (struct vty *vty, struct route_node *rn) struct nexthop *nexthop; char buf[BUFSIZ]; - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { vty_out (vty, "Routing entry for %s/%d%s", inet_ntop (AF_INET6, &rn->p.u.prefix6, buf, BUFSIZ), @@ -1795,7 +1795,7 @@ DEFUN (show_ipv6_route, /* Show all IPv6 route. */ for (rn = route_top (table); rn; rn = route_next (rn)) - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { if (first) { @@ -1836,7 +1836,7 @@ DEFUN (show_ipv6_route_prefix_longer, /* Show matched type IPv6 routes. */ for (rn = route_top (table); rn; rn = route_next (rn)) - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) if (prefix_match (&p, &rn->p)) { if (first) @@ -1876,7 +1876,7 @@ DEFUN (show_ipv6_route_protocol, /* Show matched type IPv6 routes. */ for (rn = route_top (table); rn; rn = route_next (rn)) - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) if (rib->type == type) { if (first) @@ -2008,7 +2008,7 @@ DEFUN (show_ipv6_mroute, /* Show all IPv6 route. */ for (rn = route_top (table); rn; rn = route_next (rn)) - for (rib = rn->info; rib; rib = rib->next) + RNODE_FOREACH_RIB (rn, rib) { if (first) { From 1b5ed1b054b955275bb7cf0f80fb7767094bc28b Mon Sep 17 00:00:00 2001 From: Avneesh Sachdev Date: Tue, 13 Nov 2012 22:48:54 +0000 Subject: [PATCH 005/482] zebra: add way to determine VRF/AFI/SAFI of table Add some code that allows us to determine which VRF and AFI/SAFI a given RIB table corresponds to. * zebra/rib.h Add rib_table_info_t structure, which contains information about the VRF, AFI and SAFI that a table is for. * zebra/zebra_rib.c - Add the vrf_table_create() function, which creates a table and sets its 'info' pointer to a newly created rib_table_info_t. The 'info' pointer allows us to go from a route_node or a table to the associated vrf. - vrf_alloc(): Use vrf_create_table() to create tables. * lib/memtypes.c Add memory type for rib_table_info_t. Signed-off-by: Avneesh Sachdev Signed-off-by: David Lamparter --- lib/memtypes.c | 1 + zebra/rib.h | 36 ++++++++++++++++++++++++++++++++++++ zebra/zebra_rib.c | 29 +++++++++++++++++++++++++---- 3 files changed, 62 insertions(+), 4 deletions(-) diff --git a/lib/memtypes.c b/lib/memtypes.c index 76dece29..50b6fa42 100644 --- a/lib/memtypes.c +++ b/lib/memtypes.c @@ -83,6 +83,7 @@ struct memory_list memory_list_zebra[] = { MTYPE_STATIC_IPV4, "Static IPv4 route" }, { MTYPE_STATIC_IPV6, "Static IPv6 route" }, { MTYPE_RIB_DEST, "RIB destination" }, + { MTYPE_RIB_TABLE_INFO, "RIB table info" }, { -1, NULL }, }; diff --git a/zebra/rib.h b/zebra/rib.h index 084f3516..c98d99a4 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -268,6 +268,24 @@ struct vrf struct route_table *stable[AFI_MAX][SAFI_MAX]; }; +/* + * rib_table_info_t + * + * Structure that is hung off of a route_table that holds information about + * the table. + */ +typedef struct rib_table_info_t_ +{ + + /* + * Back pointer to vrf. + */ + struct vrf *vrf; + afi_t afi; + safi_t safi; + +} rib_table_info_t; + extern struct nexthop *nexthop_ifindex_add (struct rib *, unsigned int); extern struct nexthop *nexthop_ifname_add (struct rib *, char *); extern struct nexthop *nexthop_blackhole_add (struct rib *); @@ -361,6 +379,15 @@ extern int rib_gc_dest (struct route_node *rn); * Inline functions. */ +/* + * rib_table_info + */ +static inline rib_table_info_t * +rib_table_info (struct route_table *table) +{ + return (rib_table_info_t *) table->info; +} + /* * rib_dest_from_rnode */ @@ -417,4 +444,13 @@ rib_dest_table (rib_dest_t *dest) return dest->rnode->table; } +/* + * rib_dest_vrf + */ +static inline struct vrf * +rib_dest_vrf (rib_dest_t *dest) +{ + return rib_table_info (rib_dest_table (dest))->vrf; +} + #endif /*_ZEBRA_RIB_H */ diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index f0d5c9d9..2cbee935 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -74,6 +74,27 @@ static const struct /* Vector for routing table. */ static vector vrf_vector; +/* + * vrf_table_create + */ +static void +vrf_table_create (struct vrf *vrf, afi_t afi, safi_t safi) +{ + rib_table_info_t *info; + struct route_table *table; + + assert (!vrf->table[afi][safi]); + + table = route_table_init (); + vrf->table[afi][safi] = table; + + info = XCALLOC (MTYPE_RIB_TABLE_INFO, sizeof (*info)); + info->vrf = vrf; + info->afi = afi; + info->safi = safi; + table->info = info; +} + /* Allocate new VRF. */ static struct vrf * vrf_alloc (const char *name) @@ -87,12 +108,12 @@ vrf_alloc (const char *name) vrf->name = XSTRDUP (MTYPE_VRF_NAME, name); /* Allocate routing table and static table. */ - vrf->table[AFI_IP][SAFI_UNICAST] = route_table_init (); - vrf->table[AFI_IP6][SAFI_UNICAST] = route_table_init (); + vrf_table_create (vrf, AFI_IP, SAFI_UNICAST); + vrf_table_create (vrf, AFI_IP6, SAFI_UNICAST); vrf->stable[AFI_IP][SAFI_UNICAST] = route_table_init (); vrf->stable[AFI_IP6][SAFI_UNICAST] = route_table_init (); - vrf->table[AFI_IP][SAFI_MULTICAST] = route_table_init (); - vrf->table[AFI_IP6][SAFI_MULTICAST] = route_table_init (); + vrf_table_create (vrf, AFI_IP, SAFI_MULTICAST); + vrf_table_create (vrf, AFI_IP6, SAFI_MULTICAST); vrf->stable[AFI_IP][SAFI_MULTICAST] = route_table_init (); vrf->stable[AFI_IP6][SAFI_MULTICAST] = route_table_init (); From 0915bb0ce2ca6b5fee2cd214be4499eeeaf1c9af Mon Sep 17 00:00:00 2001 From: Avneesh Sachdev Date: Tue, 13 Nov 2012 22:48:55 +0000 Subject: [PATCH 006/482] zebra: add iterator for walking all tables in RIB * lib/zebra.h Add macro ZEBRA_NUM_OF, which returns the number of elements in a static array. * zebra/rib.h Add the rib_tables_iter_t structure and associated functions, which allow one to walk all tables in the rib. * zebra/zebra_rib.c - Add vrf_id_get_next() to retrieve the first VRF id (if any) that is greater than a given VRF id. - Add rib_tables_iter_next(). Signed-off-by: Avneesh Sachdev Signed-off-by: David Lamparter --- lib/zebra.h | 2 + zebra/rib.h | 52 +++++++++++++++++++++++ zebra/zebra_rib.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 157 insertions(+) diff --git a/lib/zebra.h b/lib/zebra.h index f8a6be30..404b832b 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -387,6 +387,8 @@ struct in_pktinfo #define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif +#define ZEBRA_NUM_OF(x) (sizeof (x) / sizeof (x[0])) + /* For old definition. */ #ifndef IN6_ARE_ADDR_EQUAL #define IN6_ARE_ADDR_EQUAL IN6_IS_ADDR_EQUAL diff --git a/zebra/rib.h b/zebra/rib.h index c98d99a4..4f99d714 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -286,6 +286,25 @@ typedef struct rib_table_info_t_ } rib_table_info_t; +typedef enum +{ + RIB_TABLES_ITER_S_INIT, + RIB_TABLES_ITER_S_ITERATING, + RIB_TABLES_ITER_S_DONE +} rib_tables_iter_state_t; + +/* + * Structure that holds state for iterating over all tables in the + * Routing Information Base. + */ +typedef struct rib_tables_iter_t_ +{ + uint32_t vrf_id; + int afi_safi_ix; + + rib_tables_iter_state_t state; +} rib_tables_iter_t; + extern struct nexthop *nexthop_ifindex_add (struct rib *, unsigned int); extern struct nexthop *nexthop_ifname_add (struct rib *, char *); extern struct nexthop *nexthop_blackhole_add (struct rib *); @@ -374,6 +393,7 @@ static_delete_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate, #endif /* HAVE_IPV6 */ extern int rib_gc_dest (struct route_node *rn); +extern struct route_table *rib_tables_iter_next (rib_tables_iter_t *iter); /* * Inline functions. @@ -453,4 +473,36 @@ rib_dest_vrf (rib_dest_t *dest) return rib_table_info (rib_dest_table (dest))->vrf; } +/* + * rib_tables_iter_init + */ +static inline void +rib_tables_iter_init (rib_tables_iter_t *iter) + +{ + memset (iter, 0, sizeof (*iter)); + iter->state = RIB_TABLES_ITER_S_INIT; +} + +/* + * rib_tables_iter_started + * + * Returns TRUE if this iterator has started iterating over the set of + * tables. + */ +static inline int +rib_tables_iter_started (rib_tables_iter_t *iter) +{ + return iter->state != RIB_TABLES_ITER_S_INIT; +} + +/* + * rib_tables_iter_cleanup + */ +static inline void +rib_tables_iter_cleanup (rib_tables_iter_t *iter) +{ + iter->state = RIB_TABLES_ITER_S_DONE; +} + #endif /*_ZEBRA_RIB_H */ diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 2cbee935..5c75b909 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -3077,3 +3077,106 @@ rib_init (void) /* VRF initialization. */ vrf_init (); } + +/* + * vrf_id_get_next + * + * Get the first vrf id that is greater than the given vrf id if any. + * + * Returns TRUE if a vrf id was found, FALSE otherwise. + */ +static inline int +vrf_id_get_next (uint32_t id, uint32_t *next_id_p) +{ + while (++id < vector_active (vrf_vector)) + { + if (vrf_lookup (id)) + { + *next_id_p = id; + return 1; + } + } + + return 0; +} + +/* + * rib_tables_iter_next + * + * Returns the next table in the iteration. + */ +struct route_table * +rib_tables_iter_next (rib_tables_iter_t *iter) +{ + struct route_table *table; + + /* + * Array that helps us go over all AFI/SAFI combinations via one + * index. + */ + static struct { + afi_t afi; + safi_t safi; + } afi_safis[] = { + { AFI_IP, SAFI_UNICAST }, + { AFI_IP, SAFI_MULTICAST }, + { AFI_IP6, SAFI_UNICAST }, + { AFI_IP6, SAFI_MULTICAST }, + }; + + table = NULL; + + switch (iter->state) + { + + case RIB_TABLES_ITER_S_INIT: + iter->vrf_id = 0; + iter->afi_safi_ix = -1; + + /* Fall through */ + + case RIB_TABLES_ITER_S_ITERATING: + iter->afi_safi_ix++; + while (1) + { + + while (iter->afi_safi_ix < (int) ZEBRA_NUM_OF (afi_safis)) + { + table = vrf_table (afi_safis[iter->afi_safi_ix].afi, + afi_safis[iter->afi_safi_ix].safi, + iter->vrf_id); + if (table) + break; + + iter->afi_safi_ix++; + } + + /* + * Found another table in this vrf. + */ + if (table) + break; + + /* + * Done with all tables in the current vrf, go to the next + * one. + */ + if (!vrf_id_get_next (iter->vrf_id, &iter->vrf_id)) + break; + + iter->afi_safi_ix = 0; + } + + break; + + case RIB_TABLES_ITER_S_DONE: + return NULL; + } + + if (table) + iter->state = RIB_TABLES_ITER_S_ITERATING; + else + iter->state = RIB_TABLES_ITER_S_DONE; + + return table; +} From 78deec450cfa5ddcad290c13b36dd1d187da213e Mon Sep 17 00:00:00 2001 From: Avneesh Sachdev Date: Tue, 13 Nov 2012 22:48:56 +0000 Subject: [PATCH 007/482] zebra: extern/extract some functions from rt_netlink.c * zebra/{rib.h,zebra_rib.c} Add nexthop_type_to_str(), which returns a human-readable string corresponding to a nexthop type. * zebra/rt_netlink.[hc] - Add new header file that exposes some existing and new netlink-related functions from rt_netlink.c to the rest of zebra. addattr32 addattr_l rta_addattr_l nl_msg_type_to_str (new) nl_rtproto_to_str (new) - Use nexthop_type_to_str() instead of the static array 'nexthop_types_desc'. Signed-off-by: Avneesh Sachdev Signed-off-by: David Lamparter --- zebra/rib.h | 1 + zebra/rt_netlink.c | 48 +++++++++++++++++++++++++--------------------- zebra/rt_netlink.h | 46 ++++++++++++++++++++++++++++++++++++++++++++ zebra/zebra_rib.c | 25 ++++++++++++++++++++++++ 4 files changed, 98 insertions(+), 22 deletions(-) create mode 100644 zebra/rt_netlink.h diff --git a/zebra/rib.h b/zebra/rib.h index 4f99d714..4ecfaa0d 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -305,6 +305,7 @@ typedef struct rib_tables_iter_t_ rib_tables_iter_state_t state; } rib_tables_iter_t; +extern const char *nexthop_type_to_str (enum nexthop_types_t nh_type); extern struct nexthop *nexthop_ifindex_add (struct rib *, unsigned int); extern struct nexthop *nexthop_ifname_add (struct rib *, char *); extern struct nexthop *nexthop_blackhole_add (struct rib *); diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index ab28ad27..fa446a56 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -43,7 +43,7 @@ #include "zebra/interface.h" #include "zebra/debug.h" -#define NL_PKT_BUF_SIZE 4096 +#include "rt_netlink.h" /* Socket interface to kernel */ struct nlsock @@ -68,20 +68,6 @@ static const struct message nlmsg_str[] = { {0, NULL} }; -static const char *nexthop_types_desc[] = -{ - "none", - "Directly connected", - "Interface route", - "IPv4 nexthop", - "IPv4 nexthop with ifindex", - "IPv4 nexthop with ifname", - "IPv6 nexthop", - "IPv6 nexthop with ifindex", - "IPv6 nexthop with ifname", - "Null0 nexthop", -}; - extern struct zebra_t zebrad; extern struct zebra_privs_t zserv_privs; @@ -1236,7 +1222,7 @@ netlink_route_read (void) /* Utility function comes from iproute2. Authors: Alexey Kuznetsov, */ -static int +int addattr_l (struct nlmsghdr *n, int maxlen, int type, void *data, int alen) { int len; @@ -1256,7 +1242,7 @@ addattr_l (struct nlmsghdr *n, int maxlen, int type, void *data, int alen) return 0; } -static int +int rta_addattr_l (struct rtattr *rta, int maxlen, int type, void *data, int alen) { int len; @@ -1278,7 +1264,7 @@ rta_addattr_l (struct rtattr *rta, int maxlen, int type, void *data, int alen) /* Utility function comes from iproute2. Authors: Alexey Kuznetsov, */ -static int +int addattr32 (struct nlmsghdr *n, int maxlen, int type, int data) { int len; @@ -1515,7 +1501,7 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib, inet_ntoa (p->u.prefix4), #endif /* HAVE_IPV6 */ - p->prefixlen, nexthop_types_desc[nexthop->rtype]); + p->prefixlen, nexthop_type_to_str (nexthop->rtype)); } if (nexthop->rtype == NEXTHOP_TYPE_IPV4 @@ -1580,7 +1566,7 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib, #else inet_ntoa (p->u.prefix4), #endif /* HAVE_IPV6 */ - p->prefixlen, nexthop_types_desc[nexthop->type]); + p->prefixlen, nexthop_type_to_str (nexthop->type)); } if (nexthop->type == NEXTHOP_TYPE_IPV4 @@ -1687,7 +1673,7 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib, #else inet_ntoa (p->u.prefix4), #endif /* HAVE_IPV6 */ - p->prefixlen, nexthop_types_desc[nexthop->rtype]); + p->prefixlen, nexthop_type_to_str (nexthop->rtype)); } if (nexthop->rtype == NEXTHOP_TYPE_IPV4 || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX) @@ -1761,7 +1747,7 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib, #else inet_ntoa (p->u.prefix4), #endif /* HAVE_IPV6 */ - p->prefixlen, nexthop_types_desc[nexthop->type]); + p->prefixlen, nexthop_type_to_str (nexthop->type)); } if (nexthop->type == NEXTHOP_TYPE_IPV4 || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) @@ -2023,3 +2009,21 @@ kernel_init (void) thread_add_read (zebrad.master, kernel_read, NULL, netlink.sock); } } + +/* + * nl_msg_type_to_str + */ +const char * +nl_msg_type_to_str (uint16_t msg_type) +{ + return lookup (nlmsg_str, msg_type); +} + +/* + * nl_rtproto_to_str + */ +const char * +nl_rtproto_to_str (u_char rtproto) +{ + return lookup (rtproto_str, rtproto); +} diff --git a/zebra/rt_netlink.h b/zebra/rt_netlink.h new file mode 100644 index 00000000..529fa517 --- /dev/null +++ b/zebra/rt_netlink.h @@ -0,0 +1,46 @@ +/* Header file exported by rt_netlink.c to zebra. + * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _ZEBRA_RT_NETLINK_H +#define _ZEBRA_RT_NETLINK_H + +#ifdef HAVE_NETLINK + +#define NL_PKT_BUF_SIZE 4096 + +extern int +addattr32 (struct nlmsghdr *n, int maxlen, int type, int data); +extern int +addattr_l (struct nlmsghdr *n, int maxlen, int type, void *data, int alen); + +extern int +rta_addattr_l (struct rtattr *rta, int maxlen, int type, void *data, int alen); + +extern const char * +nl_msg_type_to_str (uint16_t msg_type); + +extern const char * +nl_rtproto_to_str (u_char rtproto); + + +#endif /* HAVE_NETLINK */ + +#endif /* _ZEBRA_RT_NETLINK_H */ diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 5c75b909..29977123 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -170,6 +170,31 @@ vrf_static_table (afi_t afi, safi_t safi, u_int32_t id) return vrf->stable[afi][safi]; } +/* + * nexthop_type_to_str + */ +const char * +nexthop_type_to_str (enum nexthop_types_t nh_type) +{ + static const char *desc[] = { + "none", + "Directly connected", + "Interface route", + "IPv4 nexthop", + "IPv4 nexthop with ifindex", + "IPv4 nexthop with ifname", + "IPv6 nexthop", + "IPv6 nexthop with ifindex", + "IPv6 nexthop with ifname", + "Null0 nexthop", + }; + + if (nh_type >= ZEBRA_NUM_OF (desc)) + return ""; + + return desc[nh_type]; +} + /* Add nexthop to the end of the list. */ static void nexthop_add (struct rib *rib, struct nexthop *nexthop) From 04f7dd64dfa0f339208f0d4833276b7684ee3343 Mon Sep 17 00:00:00 2001 From: Avneesh Sachdev Date: Tue, 13 Nov 2012 22:48:57 +0000 Subject: [PATCH 008/482] lib: bring in sys/queue.h from FreeBSD tree Bring in sys/queue.h from the FreeBSD tree as lib/queue.h. This header implements lists of various flavors using inline linkages. The imported file corresponds to SVN revision 221843 (url below) and is available under the terms of the New BSD license (3-clause). http://svnweb.freebsd.org/base/head/sys/sys/queue.h?revision=221843 Signed-off-by: Avneesh Sachdev Signed-off-by: David Lamparter --- lib/queue.h | 637 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 637 insertions(+) create mode 100644 lib/queue.h diff --git a/lib/queue.h b/lib/queue.h new file mode 100644 index 00000000..70cffab1 --- /dev/null +++ b/lib/queue.h @@ -0,0 +1,637 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + * $FreeBSD$ + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +#include + +/* + * This file defines four types of data structures: singly-linked lists, + * singly-linked tail queues, lists and tail queues. + * + * A singly-linked list is headed by a single forward pointer. The elements + * are singly linked for minimum space and pointer manipulation overhead at + * the expense of O(n) removal for arbitrary elements. New elements can be + * added to the list after an existing element or at the head of the list. + * Elements being removed from the head of the list should use the explicit + * macro for this purpose for optimum efficiency. A singly-linked list may + * only be traversed in the forward direction. Singly-linked lists are ideal + * for applications with large datasets and few or no removals or for + * implementing a LIFO queue. + * + * A singly-linked tail queue is headed by a pair of pointers, one to the + * head of the list and the other to the tail of the list. The elements are + * singly linked for minimum space and pointer manipulation overhead at the + * expense of O(n) removal for arbitrary elements. New elements can be added + * to the list after an existing element, at the head of the list, or at the + * end of the list. Elements being removed from the head of the tail queue + * should use the explicit macro for this purpose for optimum efficiency. + * A singly-linked tail queue may only be traversed in the forward direction. + * Singly-linked tail queues are ideal for applications with large datasets + * and few or no removals or for implementing a FIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * For details on the use of these macros, see the queue(3) manual page. + * + * + * SLIST LIST STAILQ TAILQ + * _HEAD + + + + + * _HEAD_INITIALIZER + + + + + * _ENTRY + + + + + * _INIT + + + + + * _EMPTY + + + + + * _FIRST + + + + + * _NEXT + + + + + * _PREV - - - + + * _LAST - - + + + * _FOREACH + + + + + * _FOREACH_SAFE + + + + + * _FOREACH_REVERSE - - - + + * _FOREACH_REVERSE_SAFE - - - + + * _INSERT_HEAD + + + + + * _INSERT_BEFORE - + - + + * _INSERT_AFTER + + + + + * _INSERT_TAIL - - + + + * _CONCAT - - + + + * _REMOVE_AFTER + - + - + * _REMOVE_HEAD + - + - + * _REMOVE + + + + + * _SWAP + + + + + * + */ +#ifdef QUEUE_MACRO_DEBUG +/* Store the last 2 places the queue element or head was altered */ +struct qm_trace { + char * lastfile; + int lastline; + char * prevfile; + int prevline; +}; + +#define TRACEBUF struct qm_trace trace; +#define TRASHIT(x) do {(x) = (void *)-1;} while (0) +#define QMD_SAVELINK(name, link) void **name = (void *)&(link) + +#define QMD_TRACE_HEAD(head) do { \ + (head)->trace.prevline = (head)->trace.lastline; \ + (head)->trace.prevfile = (head)->trace.lastfile; \ + (head)->trace.lastline = __LINE__; \ + (head)->trace.lastfile = __FILE__; \ +} while (0) + +#define QMD_TRACE_ELEM(elem) do { \ + (elem)->trace.prevline = (elem)->trace.lastline; \ + (elem)->trace.prevfile = (elem)->trace.lastfile; \ + (elem)->trace.lastline = __LINE__; \ + (elem)->trace.lastfile = __FILE__; \ +} while (0) + +#else +#define QMD_TRACE_ELEM(elem) +#define QMD_TRACE_HEAD(head) +#define QMD_SAVELINK(name, link) +#define TRACEBUF +#define TRASHIT(x) +#endif /* QUEUE_MACRO_DEBUG */ + +/* + * Singly-linked List declarations. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List functions. + */ +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) + +#define SLIST_FIRST(head) ((head)->slh_first) + +#define SLIST_FOREACH(var, head, field) \ + for ((var) = SLIST_FIRST((head)); \ + (var); \ + (var) = SLIST_NEXT((var), field)) + +#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SLIST_FIRST((head)); \ + (var) && ((tvar) = SLIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \ + for ((varp) = &SLIST_FIRST((head)); \ + ((var) = *(varp)) != NULL; \ + (varp) = &SLIST_NEXT((var), field)) + +#define SLIST_INIT(head) do { \ + SLIST_FIRST((head)) = NULL; \ +} while (0) + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \ + SLIST_NEXT((slistelm), field) = (elm); \ +} while (0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \ + SLIST_FIRST((head)) = (elm); \ +} while (0) + +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.sle_next); \ + if (SLIST_FIRST((head)) == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = SLIST_FIRST((head)); \ + while (SLIST_NEXT(curelm, field) != (elm)) \ + curelm = SLIST_NEXT(curelm, field); \ + SLIST_REMOVE_AFTER(curelm, field); \ + } \ + TRASHIT(*oldnext); \ +} while (0) + +#define SLIST_REMOVE_AFTER(elm, field) do { \ + SLIST_NEXT(elm, field) = \ + SLIST_NEXT(SLIST_NEXT(elm, field), field); \ +} while (0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \ +} while (0) + +#define SLIST_SWAP(head1, head2, type) do { \ + struct type *swap_first = SLIST_FIRST(head1); \ + SLIST_FIRST(head1) = SLIST_FIRST(head2); \ + SLIST_FIRST(head2) = swap_first; \ +} while (0) + +/* + * Singly-linked Tail queue declarations. + */ +#define STAILQ_HEAD(name, type) \ +struct name { \ + struct type *stqh_first;/* first element */ \ + struct type **stqh_last;/* addr of last next element */ \ +} + +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } + +#define STAILQ_ENTRY(type) \ +struct { \ + struct type *stqe_next; /* next element */ \ +} + +/* + * Singly-linked Tail queue functions. + */ +#define STAILQ_CONCAT(head1, head2) do { \ + if (!STAILQ_EMPTY((head2))) { \ + *(head1)->stqh_last = (head2)->stqh_first; \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_INIT((head2)); \ + } \ +} while (0) + +#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) + +#define STAILQ_FIRST(head) ((head)->stqh_first) + +#define STAILQ_FOREACH(var, head, field) \ + for((var) = STAILQ_FIRST((head)); \ + (var); \ + (var) = STAILQ_NEXT((var), field)) + + +#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = STAILQ_FIRST((head)); \ + (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define STAILQ_INIT(head) do { \ + STAILQ_FIRST((head)) = NULL; \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_NEXT((tqelm), field) = (elm); \ +} while (0) + +#define STAILQ_INSERT_HEAD(head, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_FIRST((head)) = (elm); \ +} while (0) + +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + STAILQ_NEXT((elm), field) = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ +} while (0) + +#define STAILQ_LAST(head, type, field) \ + (STAILQ_EMPTY((head)) ? \ + NULL : \ + ((struct type *)(void *) \ + ((char *)((head)->stqh_last) - __offsetof(struct type, field)))) + +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) + +#define STAILQ_REMOVE(head, elm, type, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.stqe_next); \ + if (STAILQ_FIRST((head)) == (elm)) { \ + STAILQ_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = STAILQ_FIRST((head)); \ + while (STAILQ_NEXT(curelm, field) != (elm)) \ + curelm = STAILQ_NEXT(curelm, field); \ + STAILQ_REMOVE_AFTER(head, curelm, field); \ + } \ + TRASHIT(*oldnext); \ +} while (0) + +#define STAILQ_REMOVE_AFTER(head, elm, field) do { \ + if ((STAILQ_NEXT(elm, field) = \ + STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ +} while (0) + +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if ((STAILQ_FIRST((head)) = \ + STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_SWAP(head1, head2, type) do { \ + struct type *swap_first = STAILQ_FIRST(head1); \ + struct type **swap_last = (head1)->stqh_last; \ + STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_FIRST(head2) = swap_first; \ + (head2)->stqh_last = swap_last; \ + if (STAILQ_EMPTY(head1)) \ + (head1)->stqh_last = &STAILQ_FIRST(head1); \ + if (STAILQ_EMPTY(head2)) \ + (head2)->stqh_last = &STAILQ_FIRST(head2); \ +} while (0) + + +/* + * List declarations. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List functions. + */ + +#if (defined(_KERNEL) && defined(INVARIANTS)) +#define QMD_LIST_CHECK_HEAD(head, field) do { \ + if (LIST_FIRST((head)) != NULL && \ + LIST_FIRST((head))->field.le_prev != \ + &LIST_FIRST((head))) \ + panic("Bad list head %p first->prev != head", (head)); \ +} while (0) + +#define QMD_LIST_CHECK_NEXT(elm, field) do { \ + if (LIST_NEXT((elm), field) != NULL && \ + LIST_NEXT((elm), field)->field.le_prev != \ + &((elm)->field.le_next)) \ + panic("Bad link elm %p next->prev != elm", (elm)); \ +} while (0) + +#define QMD_LIST_CHECK_PREV(elm, field) do { \ + if (*(elm)->field.le_prev != (elm)) \ + panic("Bad link elm %p prev->next != elm", (elm)); \ +} while (0) +#else +#define QMD_LIST_CHECK_HEAD(head, field) +#define QMD_LIST_CHECK_NEXT(elm, field) +#define QMD_LIST_CHECK_PREV(elm, field) +#endif /* (_KERNEL && INVARIANTS) */ + +#define LIST_EMPTY(head) ((head)->lh_first == NULL) + +#define LIST_FIRST(head) ((head)->lh_first) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = LIST_FIRST((head)); \ + (var); \ + (var) = LIST_NEXT((var), field)) + +#define LIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = LIST_FIRST((head)); \ + (var) && ((tvar) = LIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define LIST_INIT(head) do { \ + LIST_FIRST((head)) = NULL; \ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + QMD_LIST_CHECK_NEXT(listelm, field); \ + if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\ + LIST_NEXT((listelm), field)->field.le_prev = \ + &LIST_NEXT((elm), field); \ + LIST_NEXT((listelm), field) = (elm); \ + (elm)->field.le_prev = &LIST_NEXT((listelm), field); \ +} while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + QMD_LIST_CHECK_PREV(listelm, field); \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + LIST_NEXT((elm), field) = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &LIST_NEXT((elm), field); \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + QMD_LIST_CHECK_HEAD((head), field); \ + if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \ + LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\ + LIST_FIRST((head)) = (elm); \ + (elm)->field.le_prev = &LIST_FIRST((head)); \ +} while (0) + +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_REMOVE(elm, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.le_next); \ + QMD_SAVELINK(oldprev, (elm)->field.le_prev); \ + QMD_LIST_CHECK_NEXT(elm, field); \ + QMD_LIST_CHECK_PREV(elm, field); \ + if (LIST_NEXT((elm), field) != NULL) \ + LIST_NEXT((elm), field)->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = LIST_NEXT((elm), field); \ + TRASHIT(*oldnext); \ + TRASHIT(*oldprev); \ +} while (0) + +#define LIST_SWAP(head1, head2, type, field) do { \ + struct type *swap_tmp = LIST_FIRST((head1)); \ + LIST_FIRST((head1)) = LIST_FIRST((head2)); \ + LIST_FIRST((head2)) = swap_tmp; \ + if ((swap_tmp = LIST_FIRST((head1))) != NULL) \ + swap_tmp->field.le_prev = &LIST_FIRST((head1)); \ + if ((swap_tmp = LIST_FIRST((head2))) != NULL) \ + swap_tmp->field.le_prev = &LIST_FIRST((head2)); \ +} while (0) + +/* + * Tail queue declarations. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ + TRACEBUF \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ + TRACEBUF \ +} + +/* + * Tail queue functions. + */ +#if (defined(_KERNEL) && defined(INVARIANTS)) +#define QMD_TAILQ_CHECK_HEAD(head, field) do { \ + if (!TAILQ_EMPTY(head) && \ + TAILQ_FIRST((head))->field.tqe_prev != \ + &TAILQ_FIRST((head))) \ + panic("Bad tailq head %p first->prev != head", (head)); \ +} while (0) + +#define QMD_TAILQ_CHECK_TAIL(head, field) do { \ + if (*(head)->tqh_last != NULL) \ + panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head)); \ +} while (0) + +#define QMD_TAILQ_CHECK_NEXT(elm, field) do { \ + if (TAILQ_NEXT((elm), field) != NULL && \ + TAILQ_NEXT((elm), field)->field.tqe_prev != \ + &((elm)->field.tqe_next)) \ + panic("Bad link elm %p next->prev != elm", (elm)); \ +} while (0) + +#define QMD_TAILQ_CHECK_PREV(elm, field) do { \ + if (*(elm)->field.tqe_prev != (elm)) \ + panic("Bad link elm %p prev->next != elm", (elm)); \ +} while (0) +#else +#define QMD_TAILQ_CHECK_HEAD(head, field) +#define QMD_TAILQ_CHECK_TAIL(head, headname) +#define QMD_TAILQ_CHECK_NEXT(elm, field) +#define QMD_TAILQ_CHECK_PREV(elm, field) +#endif /* (_KERNEL && INVARIANTS) */ + +#define TAILQ_CONCAT(head1, head2, field) do { \ + if (!TAILQ_EMPTY(head2)) { \ + *(head1)->tqh_last = (head2)->tqh_first; \ + (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ + (head1)->tqh_last = (head2)->tqh_last; \ + TAILQ_INIT((head2)); \ + QMD_TRACE_HEAD(head1); \ + QMD_TRACE_HEAD(head2); \ + } \ +} while (0) + +#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) + +#define TAILQ_FIRST(head) ((head)->tqh_first) + +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = TAILQ_FIRST((head)); \ + (var); \ + (var) = TAILQ_NEXT((var), field)) + +#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = TAILQ_FIRST((head)); \ + (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var); \ + (var) = TAILQ_PREV((var), headname, field)) + +#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \ + (var) = (tvar)) + +#define TAILQ_INIT(head) do { \ + TAILQ_FIRST((head)) = NULL; \ + (head)->tqh_last = &TAILQ_FIRST((head)); \ + QMD_TRACE_HEAD(head); \ +} while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + QMD_TAILQ_CHECK_NEXT(listelm, field); \ + if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\ + TAILQ_NEXT((elm), field)->field.tqe_prev = \ + &TAILQ_NEXT((elm), field); \ + else { \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + QMD_TRACE_HEAD(head); \ + } \ + TAILQ_NEXT((listelm), field) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \ + QMD_TRACE_ELEM(&(elm)->field); \ + QMD_TRACE_ELEM(&listelm->field); \ +} while (0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + QMD_TAILQ_CHECK_PREV(listelm, field); \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + TAILQ_NEXT((elm), field) = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \ + QMD_TRACE_ELEM(&(elm)->field); \ + QMD_TRACE_ELEM(&listelm->field); \ +} while (0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + QMD_TAILQ_CHECK_HEAD(head, field); \ + if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \ + TAILQ_FIRST((head))->field.tqe_prev = \ + &TAILQ_NEXT((elm), field); \ + else \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + TAILQ_FIRST((head)) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \ + QMD_TRACE_HEAD(head); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + QMD_TAILQ_CHECK_TAIL(head, field); \ + TAILQ_NEXT((elm), field) = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + QMD_TRACE_HEAD(head); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) + +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) + +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) + +#define TAILQ_REMOVE(head, elm, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.tqe_next); \ + QMD_SAVELINK(oldprev, (elm)->field.tqe_prev); \ + QMD_TAILQ_CHECK_NEXT(elm, field); \ + QMD_TAILQ_CHECK_PREV(elm, field); \ + if ((TAILQ_NEXT((elm), field)) != NULL) \ + TAILQ_NEXT((elm), field)->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else { \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + QMD_TRACE_HEAD(head); \ + } \ + *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \ + TRASHIT(*oldnext); \ + TRASHIT(*oldprev); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + +#define TAILQ_SWAP(head1, head2, type, field) do { \ + struct type *swap_first = (head1)->tqh_first; \ + struct type **swap_last = (head1)->tqh_last; \ + (head1)->tqh_first = (head2)->tqh_first; \ + (head1)->tqh_last = (head2)->tqh_last; \ + (head2)->tqh_first = swap_first; \ + (head2)->tqh_last = swap_last; \ + if ((swap_first = (head1)->tqh_first) != NULL) \ + swap_first->field.tqe_prev = &(head1)->tqh_first; \ + else \ + (head1)->tqh_last = &(head1)->tqh_first; \ + if ((swap_first = (head2)->tqh_first) != NULL) \ + swap_first->field.tqe_prev = &(head2)->tqh_first; \ + else \ + (head2)->tqh_last = &(head2)->tqh_first; \ +} while (0) + +#endif /* !_SYS_QUEUE_H_ */ From 443b993777e3e86fceb988f647d1c5b57661a182 Mon Sep 17 00:00:00 2001 From: Avneesh Sachdev Date: Tue, 13 Nov 2012 22:48:58 +0000 Subject: [PATCH 009/482] fpm: Add public header for Forwarding Plane Manager The Forwarding Plane Manager (FPM) is an optional component that may be used in scenarios where the router has a forwarding path that is distinct from the kernel, commonly a hardware-based fast path. It is responsible for programming forwarding information (such as routes and nexthops) in the fast path. In Quagga, the Routing Information Base is maintained in the 'zebra' infrastructure daemon. Routing protocols communicate their best routes to zebra, and zebra computes the best route across protocols for each prefix. This latter information comprises the bulk of the Forwarding Information Base. The new header file added by this patch, 'fpm/fpm.h', defines a point-to-point interface using which zebra can update the FPM about changes in routes. The communication takes place over a stream socket. The FPM listens on a well-known TCP port, and zebra initiates the connection. All messages sent over the connection start with a short 'FPM header'. In the case of route add/delete messages, the header is followed by a netlink message. Zebra should send a complete copy of the forwarding table(s) to the FPM, including routes that it may have picked up from the kernel. The FPM interface uses replace semantics. That is, if a 'route add' message for a prefix is followed by another 'route add' message, the information in the second message is complete by itself, and replaces the information sent in the first message. If the connection to the FPM goes down for some reason, the client (zebra) should send the FPM a complete copy of the forwarding table(s) when it reconnects. Signed-off-by: Avneesh Sachdev Signed-off-by: David Lamparter --- fpm/fpm.h | 273 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 273 insertions(+) create mode 100644 fpm/fpm.h diff --git a/fpm/fpm.h b/fpm/fpm.h new file mode 100644 index 00000000..96f05f48 --- /dev/null +++ b/fpm/fpm.h @@ -0,0 +1,273 @@ +/* + * Public definitions pertaining to the Forwarding Plane Manager component. + * + * Permission is granted to use, copy, modify and/or distribute this + * software under either one of the licenses below. + * + * Note that if you use other files from the Quagga tree directly or + * indirectly, then the licenses in those files still apply. + * + * Please retain both licenses below when modifying this code in the + * Quagga tree. + * + * Copyright (C) 2012 by Open Source Routing. + * Copyright (C) 2012 by Internet Systems Consortium, Inc. ("ISC") + */ + +/* + * License Option 1: GPL + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * License Option 2: ISC License + * + * Permission to use, copy, modify, and/or distribute this software + * for any purpose with or without fee is hereby granted, provided + * that the above copyright notice and this permission notice appear + * in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _FPM_H +#define _FPM_H + +/* + * The Forwarding Plane Manager (FPM) is an optional component that + * may be used in scenarios where the router has a forwarding path + * that is distinct from the kernel, commonly a hardware-based fast + * path. It is responsible for programming forwarding information + * (such as routes and nexthops) in the fast path. + * + * In Quagga, the Routing Information Base is maintained in the + * 'zebra' infrastructure daemon. Routing protocols communicate their + * best routes to zebra, and zebra computes the best route across + * protocols for each prefix. This latter information comprises the + * bulk of the Forwarding Information Base. + * + * This header file defines a point-to-point interface using which + * zebra can update the FPM about changes in routes. The communication + * takes place over a stream socket. The FPM listens on a well-known + * TCP port, and zebra initiates the connection. + * + * All messages sent over the connection start with a short FPM + * header, fpm_msg_hdr_t. In the case of route add/delete messages, + * the header is followed by a netlink message. Zebra should send a + * complete copy of the forwarding table(s) to the FPM, including + * routes that it may have picked up from the kernel. + * + * The FPM interface uses replace semantics. That is, if a 'route add' + * message for a prefix is followed by another 'route add' message, the + * information in the second message is complete by itself, and replaces + * the information sent in the first message. + * + * If the connection to the FPM goes down for some reason, the client + * (zebra) should send the FPM a complete copy of the forwarding + * table(s) when it reconnects. + */ + +#define FPM_DEFAULT_PORT 2620 + +/* + * Largest message that can be sent to or received from the FPM. + */ +#define FPM_MAX_MSG_LEN 4096 + +/* + * Header that precedes each fpm message to/from the FPM. + */ +typedef struct fpm_msg_hdr_t_ +{ + /* + * Protocol version. + */ + uint8_t version; + + /* + * Type of message, see below. + */ + uint8_t msg_type; + + /* + * Length of entire message, including the header, in network byte + * order. + * + * Note that msg_len is rounded up to make sure that message is at + * the desired alignment. This means that some payloads may need + * padding at the end. + */ + uint16_t msg_len; +} fpm_msg_hdr_t; + +/* + * The current version of the FPM protocol is 1. + */ +#define FPM_PROTO_VERSION 1 + +typedef enum fpm_msg_type_e_ { + FPM_MSG_TYPE_NONE = 0, + + /* + * Indicates that the payload is a completely formed netlink + * message. + */ + FPM_MSG_TYPE_NETLINK = 1, +} fpm_msg_type_e; + +/* + * The FPM message header is aligned to the same boundary as netlink + * messages (4). This means that a netlink message does not need + * padding when encapsulated in an FPM message. + */ +#define FPM_MSG_ALIGNTO 4 + +/* + * fpm_msg_align + * + * Round up the given length to the desired alignment. + */ +static inline size_t +fpm_msg_align (size_t len) +{ + return (len + FPM_MSG_ALIGNTO - 1) & ~(FPM_MSG_ALIGNTO - 1); +} + +/* + * The (rounded up) size of the FPM message header. This ensures that + * the message payload always starts at an aligned address. + */ +#define FPM_MSG_HDR_LEN (fpm_msg_align (sizeof (fpm_msg_hdr_t))) + +/* + * fpm_data_len_to_msg_len + * + * The length value that should be placed in the msg_len field of the + * header for a *payload* of size 'data_len'. + */ +static inline size_t +fpm_data_len_to_msg_len (size_t data_len) +{ + return fpm_msg_align (data_len) + FPM_MSG_HDR_LEN; +} + +/* + * fpm_msg_data + * + * Pointer to the payload of the given fpm header. + */ +static inline void * +fpm_msg_data (fpm_msg_hdr_t *hdr) +{ + return ((char*) hdr) + FPM_MSG_HDR_LEN; +} + +/* + * fpm_msg_len + */ +static inline size_t +fpm_msg_len (const fpm_msg_hdr_t *hdr) +{ + return ntohs (hdr->msg_len); +} + +/* + * fpm_msg_data_len + */ +static inline size_t +fpm_msg_data_len (const fpm_msg_hdr_t *hdr) +{ + return (fpm_msg_len (hdr) - FPM_MSG_HDR_LEN); +} + +/* + * fpm_msg_next + * + * Move to the next message in a buffer. + */ +static inline fpm_msg_hdr_t * +fpm_msg_next (fpm_msg_hdr_t *hdr, size_t *len) +{ + size_t msg_len; + + msg_len = fpm_msg_len (hdr); + + if (len) { + if (*len < msg_len) + { + assert(0); + return NULL; + } + *len -= msg_len; + } + + return (fpm_msg_hdr_t *) (((char*) hdr) + msg_len); +} + +/* + * fpm_msg_hdr_ok + * + * Returns TRUE if a message header looks well-formed. + */ +static inline int +fpm_msg_hdr_ok (const fpm_msg_hdr_t *hdr) +{ + size_t msg_len; + + if (hdr->msg_type == FPM_MSG_TYPE_NONE) + return 0; + + msg_len = fpm_msg_len (hdr); + + if (msg_len < FPM_MSG_HDR_LEN || msg_len > FPM_MAX_MSG_LEN) + return 0; + + if (fpm_msg_align (msg_len) != msg_len) + return 0; + + return 1; +} + +/* + * fpm_msg_ok + * + * Returns TRUE if a message looks well-formed. + * + * @param len The length in bytes from 'hdr' to the end of the buffer. + */ +static inline int +fpm_msg_ok (const fpm_msg_hdr_t *hdr, size_t len) +{ + if (len < FPM_MSG_HDR_LEN) + return 0; + + if (!fpm_msg_hdr_ok (hdr)) + return 0; + + if (fpm_msg_len (hdr) > len) + return 0; + + return 1; +} + +#endif /* _FPM_H */ From 5adc2528d386f037cc39e8029616295c3fec2db4 Mon Sep 17 00:00:00 2001 From: Avneesh Sachdev Date: Tue, 13 Nov 2012 22:48:59 +0000 Subject: [PATCH 010/482] zebra: add module to communicate routes to FPM Enhance zebra to send routes to the (optional) Forwarding Path Manager component using the interface defined by fpm/fpm.h. * configure.ac - Add --enable-fpm flag. The FPM-related code in zebra is activated only if the build is configured with '--enable-fpm'. - Add HAVE_NETLINK automake conditional. This allows us to conditionally build netlink-dependent C code. * zebra/{rib.h,zebra_rib.c} - Add the 'fpm_q_entries' field to the rib_dest_t structure. This allows dests to be placed on the fpm queue. - Define a couple new rib_dest_t flags that hold FPM-related state. - Invoke the zfpm_trigger_update() function for a route_node whenever the information to be sent to the FPM changes. - rib_can_delete_dest(): Return FALSE if we have to update the FPM about the given dest. This ensures that the dest is not deleted even if there are no ribs hanging off of it. * zebra/zebra_fpm.c This file holds most of the code for interacting with the FPM. - If quagga was configured with '--enable-fpm', periodically try to connect to the FPM. - When the connection comes up, enqueue all relevent dests to the FPM queue. - When the FPM socket is readable, dequeue the next rib_dest_t from the FPM queue, encode it in to a message and send the message to the FPM. - When the connection to the FPM goes down, remove all dests from the FPM queue, and then start trying to connect to the FPM again. - Expose the following new operational commands: show zebra fpm stats clear zebra fpm stats * zebra/zebra_fpm_netlink.c - zfpm_netlink_encode_route(): Function to encode information about a rib_dest_t in netlink format. * zebra/zebra_fpm_private.h Private header file for the zebra FPM module. * zebra/zebra_fpm.h Header file exported by zebra FPM module to the rest of zebra. * zebra/debug.c Add the 'debug zebra fpm' command. * zebra/main.c Initialize the zebra-FPM code on startup. * zebra/misc_null.c Add stub for zfpm_trigger_update(). * zebra/Makefile.am - Include new file zebra_fpm.c in build. - Include zebra_fpm_netlink.c in build if HAVE_NETLINK is defined. * vtysh/Makefile.am Include zebra_fpm.c in list of files that define cli commands. Signed-off-by: Avneesh Sachdev Signed-off-by: David Lamparter --- configure.ac | 7 + vtysh/Makefile.am | 3 +- zebra/Makefile.am | 7 +- zebra/debug.c | 37 + zebra/debug.h | 5 + zebra/main.c | 7 + zebra/misc_null.c | 7 + zebra/rib.h | 18 + zebra/zebra_fpm.c | 1581 +++++++++++++++++++++++++++++++++++++ zebra/zebra_fpm.h | 34 + zebra/zebra_fpm_netlink.c | 552 +++++++++++++ zebra/zebra_fpm_private.h | 56 ++ zebra/zebra_rib.c | 32 + 13 files changed, 2344 insertions(+), 2 deletions(-) create mode 100644 zebra/zebra_fpm.c create mode 100644 zebra/zebra_fpm.h create mode 100644 zebra/zebra_fpm_netlink.c create mode 100644 zebra/zebra_fpm_private.h diff --git a/configure.ac b/configure.ac index 425fe273..9bbe89f3 100755 --- a/configure.ac +++ b/configure.ac @@ -272,6 +272,8 @@ AC_ARG_ENABLE(time-check, [ --disable-time-check disable slow thread warning messages]) AC_ARG_ENABLE(pcreposix, [ --enable-pcreposix enable using PCRE Posix libs for regex functions]) +AC_ARG_ENABLE(fpm, +[ --enable-fpm enable Forwarding Plane Manager support]) if test x"${enable_gcc_ultra_verbose}" = x"yes" ; then CFLAGS="${CFLAGS} -W -Wcast-qual -Wstrict-prototypes" @@ -292,6 +294,10 @@ if test x"${enable_time_check}" != x"no" ; then fi fi +if test "${enable_fpm}" = "yes"; then + AC_DEFINE(HAVE_FPM,,Forwarding Plane Manager support) +fi + if test "${enable_broken_aliases}" = "yes"; then if test "${enable_netlink}" = "yes" then @@ -828,6 +834,7 @@ fi AC_SUBST(RT_METHOD) AC_SUBST(KERNEL_METHOD) AC_SUBST(OTHER_METHOD) +AM_CONDITIONAL([HAVE_NETLINK], [test "x$netlink" = "xyes"]) dnl -------------------------- dnl Determine IS-IS I/O method diff --git a/vtysh/Makefile.am b/vtysh/Makefile.am index 7550173c..5c325ec2 100644 --- a/vtysh/Makefile.am +++ b/vtysh/Makefile.am @@ -33,7 +33,8 @@ vtysh_cmd_FILES = $(top_srcdir)/bgpd/*.c $(top_srcdir)/isisd/*.c \ $(top_srcdir)/zebra/irdp_interface.c \ $(top_srcdir)/zebra/rtadv.c $(top_srcdir)/zebra/zebra_vty.c \ $(top_srcdir)/zebra/zserv.c $(top_srcdir)/zebra/router-id.c \ - $(top_srcdir)/zebra/zebra_routemap.c + $(top_srcdir)/zebra/zebra_routemap.c \ + $(top_srcdir)/zebra/zebra_fpm.c vtysh_cmd.c: $(vtysh_cmd_FILES) ./$(EXTRA_DIST) $(vtysh_cmd_FILES) > vtysh_cmd.c diff --git a/zebra/Makefile.am b/zebra/Makefile.am index ea962bf4..266812f8 100644 --- a/zebra/Makefile.am +++ b/zebra/Makefile.am @@ -19,6 +19,10 @@ ioctl_method = @IOCTL_METHOD@ otherobj = $(ioctl_method) $(ipforward) $(if_method) $(if_proc) \ $(rt_method) $(rtread_method) $(kernel_method) $(other_method) +if HAVE_NETLINK +othersrc = zebra_fpm_netlink.c +endif + AM_CFLAGS = $(PICFLAGS) AM_LDFLAGS = $(PILDFLAGS) @@ -29,7 +33,8 @@ noinst_PROGRAMS = testzebra zebra_SOURCES = \ zserv.c main.c interface.c connected.c zebra_rib.c zebra_routemap.c \ redistribute.c debug.c rtadv.c zebra_snmp.c zebra_vty.c \ - irdp_main.c irdp_interface.c irdp_packet.c router-id.c + irdp_main.c irdp_interface.c irdp_packet.c router-id.c zebra_fpm.c \ + $(othersrc) testzebra_SOURCES = test_main.c zebra_rib.c interface.c connected.c debug.c \ zebra_vty.c \ diff --git a/zebra/debug.c b/zebra/debug.c index 175029b8..7bfdb77d 100644 --- a/zebra/debug.c +++ b/zebra/debug.c @@ -29,6 +29,7 @@ unsigned long zebra_debug_event; unsigned long zebra_debug_packet; unsigned long zebra_debug_kernel; unsigned long zebra_debug_rib; +unsigned long zebra_debug_fpm; DEFUN (show_debugging_zebra, show_debugging_zebra_cmd, @@ -71,6 +72,9 @@ DEFUN (show_debugging_zebra, if (IS_ZEBRA_DEBUG_RIB_Q) vty_out (vty, " Zebra RIB queue debugging is on%s", VTY_NEWLINE); + if (IS_ZEBRA_DEBUG_FPM) + vty_out (vty, " Zebra FPM debugging is on%s", VTY_NEWLINE); + return CMD_SUCCESS; } @@ -169,6 +173,17 @@ DEFUN (debug_zebra_rib_q, return CMD_SUCCESS; } +DEFUN (debug_zebra_fpm, + debug_zebra_fpm_cmd, + "debug zebra fpm", + DEBUG_STR + "Zebra configuration\n" + "Debug zebra FPM events\n") +{ + SET_FLAG (zebra_debug_fpm, ZEBRA_DEBUG_FPM); + return CMD_SUCCESS; +} + DEFUN (no_debug_zebra_events, no_debug_zebra_events_cmd, "no debug zebra events", @@ -247,6 +262,18 @@ DEFUN (no_debug_zebra_rib_q, return CMD_SUCCESS; } +DEFUN (no_debug_zebra_fpm, + no_debug_zebra_fpm_cmd, + "no debug zebra fpm", + NO_STR + DEBUG_STR + "Zebra configuration\n" + "Debug zebra FPM events\n") +{ + zebra_debug_fpm = 0; + return CMD_SUCCESS; +} + /* Debug node. */ struct cmd_node debug_node = { @@ -302,6 +329,11 @@ config_write_debug (struct vty *vty) vty_out (vty, "debug zebra rib queue%s", VTY_NEWLINE); write++; } + if (IS_ZEBRA_DEBUG_FPM) + { + vty_out (vty, "debug zebra fpm%s", VTY_NEWLINE); + write++; + } return write; } @@ -312,6 +344,7 @@ zebra_debug_init (void) zebra_debug_packet = 0; zebra_debug_kernel = 0; zebra_debug_rib = 0; + zebra_debug_fpm = 0; install_node (&debug_node, config_write_debug); @@ -325,11 +358,13 @@ zebra_debug_init (void) install_element (ENABLE_NODE, &debug_zebra_kernel_cmd); install_element (ENABLE_NODE, &debug_zebra_rib_cmd); install_element (ENABLE_NODE, &debug_zebra_rib_q_cmd); + install_element (ENABLE_NODE, &debug_zebra_fpm_cmd); install_element (ENABLE_NODE, &no_debug_zebra_events_cmd); install_element (ENABLE_NODE, &no_debug_zebra_packet_cmd); install_element (ENABLE_NODE, &no_debug_zebra_kernel_cmd); install_element (ENABLE_NODE, &no_debug_zebra_rib_cmd); install_element (ENABLE_NODE, &no_debug_zebra_rib_q_cmd); + install_element (ENABLE_NODE, &no_debug_zebra_fpm_cmd); install_element (CONFIG_NODE, &debug_zebra_events_cmd); install_element (CONFIG_NODE, &debug_zebra_packet_cmd); @@ -338,9 +373,11 @@ zebra_debug_init (void) install_element (CONFIG_NODE, &debug_zebra_kernel_cmd); install_element (CONFIG_NODE, &debug_zebra_rib_cmd); install_element (CONFIG_NODE, &debug_zebra_rib_q_cmd); + install_element (CONFIG_NODE, &debug_zebra_fpm_cmd); install_element (CONFIG_NODE, &no_debug_zebra_events_cmd); install_element (CONFIG_NODE, &no_debug_zebra_packet_cmd); install_element (CONFIG_NODE, &no_debug_zebra_kernel_cmd); install_element (CONFIG_NODE, &no_debug_zebra_rib_cmd); install_element (CONFIG_NODE, &no_debug_zebra_rib_q_cmd); + install_element (CONFIG_NODE, &no_debug_zebra_fpm_cmd); } diff --git a/zebra/debug.h b/zebra/debug.h index 9d9412bc..d9231a22 100644 --- a/zebra/debug.h +++ b/zebra/debug.h @@ -36,6 +36,8 @@ #define ZEBRA_DEBUG_RIB 0x01 #define ZEBRA_DEBUG_RIB_Q 0x02 +#define ZEBRA_DEBUG_FPM 0x01 + /* Debug related macro. */ #define IS_ZEBRA_DEBUG_EVENT (zebra_debug_event & ZEBRA_DEBUG_EVENT) @@ -49,10 +51,13 @@ #define IS_ZEBRA_DEBUG_RIB (zebra_debug_rib & ZEBRA_DEBUG_RIB) #define IS_ZEBRA_DEBUG_RIB_Q (zebra_debug_rib & ZEBRA_DEBUG_RIB_Q) +#define IS_ZEBRA_DEBUG_FPM (zebra_debug_fpm & ZEBRA_DEBUG_FPM) + extern unsigned long zebra_debug_event; extern unsigned long zebra_debug_packet; extern unsigned long zebra_debug_kernel; extern unsigned long zebra_debug_rib; +extern unsigned long zebra_debug_fpm; extern void zebra_debug_init (void); diff --git a/zebra/main.c b/zebra/main.c index 50ac224e..742e0292 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -39,6 +39,7 @@ #include "zebra/router-id.h" #include "zebra/irdp.h" #include "zebra/rtadv.h" +#include "zebra/zebra_fpm.h" /* Zebra instance */ struct zebra_t zebrad = @@ -349,6 +350,12 @@ main (int argc, char **argv) zebra_snmp_init (); #endif /* HAVE_SNMP */ +#ifdef HAVE_FPM + zfpm_init (zebrad.master, 1, 0); +#else + zfpm_init (zebrad.master, 0, 0); +#endif + /* Process the configuration file. Among other configuration * directives we can meet those installing static routes. Such * requests will not be executed immediately, but queued in diff --git a/zebra/misc_null.c b/zebra/misc_null.c index 73594301..c8cc47d1 100644 --- a/zebra/misc_null.c +++ b/zebra/misc_null.c @@ -4,8 +4,15 @@ #include "zebra/rtadv.h" #include "zebra/irdp.h" #include "zebra/interface.h" +#include "zebra/zebra_fpm.h" void ifstat_update_proc (void) { return; } #pragma weak rtadv_config_write = ifstat_update_proc #pragma weak irdp_config_write = ifstat_update_proc #pragma weak ifstat_update_sysctl = ifstat_update_proc + +void +zfpm_trigger_update (struct route_node *rn, const char *reason) +{ + return; +} diff --git a/zebra/rib.h b/zebra/rib.h index 4ecfaa0d..e16ce68a 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -25,6 +25,7 @@ #include "prefix.h" #include "table.h" +#include "queue.h" #define DISTANCE_INFINITY 255 @@ -116,6 +117,11 @@ typedef struct rib_dest_t_ */ u_int32_t flags; + /* + * Linkage to put dest on the FPM processing queue. + */ + TAILQ_ENTRY(rib_dest_t_) fpm_q_entries; + } rib_dest_t; #define RIB_ROUTE_QUEUED(x) (1 << (x)) @@ -125,6 +131,18 @@ typedef struct rib_dest_t_ */ #define ZEBRA_MAX_QINDEX (MQ_SIZE - 1) +/* + * This flag indicates that a given prefix has been 'advertised' to + * the FPM to be installed in the forwarding plane. + */ +#define RIB_DEST_SENT_TO_FPM (1 << (ZEBRA_MAX_QINDEX + 1)) + +/* + * This flag is set when we need to send an update to the FPM about a + * dest. + */ +#define RIB_DEST_UPDATE_FPM (1 << (ZEBRA_MAX_QINDEX + 2)) + /* * Macro to iterate over each route for a destination (prefix). */ diff --git a/zebra/zebra_fpm.c b/zebra/zebra_fpm.c new file mode 100644 index 00000000..e02d1745 --- /dev/null +++ b/zebra/zebra_fpm.c @@ -0,0 +1,1581 @@ +/* + * Main implementation file for interface to Forwarding Plane Manager. + * + * Copyright (C) 2012 by Open Source Routing. + * Copyright (C) 2012 by Internet Systems Consortium, Inc. ("ISC") + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include + +#include "log.h" +#include "stream.h" +#include "thread.h" +#include "network.h" +#include "command.h" + +#include "zebra/rib.h" + +#include "fpm/fpm.h" +#include "zebra_fpm.h" +#include "zebra_fpm_private.h" + +/* + * Interval at which we attempt to connect to the FPM. + */ +#define ZFPM_CONNECT_RETRY_IVL 5 + +/* + * Sizes of outgoing and incoming stream buffers for writing/reading + * FPM messages. + */ +#define ZFPM_OBUF_SIZE (2 * FPM_MAX_MSG_LEN) +#define ZFPM_IBUF_SIZE (FPM_MAX_MSG_LEN) + +/* + * The maximum number of times the FPM socket write callback can call + * 'write' before it yields. + */ +#define ZFPM_MAX_WRITES_PER_RUN 10 + +/* + * Interval over which we collect statistics. + */ +#define ZFPM_STATS_IVL_SECS 10 + +/* + * Structure that holds state for iterating over all route_node + * structures that are candidates for being communicated to the FPM. + */ +typedef struct zfpm_rnodes_iter_t_ +{ + rib_tables_iter_t tables_iter; + route_table_iter_t iter; +} zfpm_rnodes_iter_t; + +/* + * Statistics. + */ +typedef struct zfpm_stats_t_ { + unsigned long connect_calls; + unsigned long connect_no_sock; + + unsigned long read_cb_calls; + + unsigned long write_cb_calls; + unsigned long write_calls; + unsigned long partial_writes; + unsigned long max_writes_hit; + unsigned long t_write_yields; + + unsigned long nop_deletes_skipped; + unsigned long route_adds; + unsigned long route_dels; + + unsigned long updates_triggered; + unsigned long redundant_triggers; + unsigned long non_fpm_table_triggers; + + unsigned long dests_del_after_update; + + unsigned long t_conn_down_starts; + unsigned long t_conn_down_dests_processed; + unsigned long t_conn_down_yields; + unsigned long t_conn_down_finishes; + + unsigned long t_conn_up_starts; + unsigned long t_conn_up_dests_processed; + unsigned long t_conn_up_yields; + unsigned long t_conn_up_aborts; + unsigned long t_conn_up_finishes; + +} zfpm_stats_t; + +/* + * States for the FPM state machine. + */ +typedef enum { + + /* + * In this state we are not yet ready to connect to the FPM. This + * can happen when this module is disabled, or if we're cleaning up + * after a connection has gone down. + */ + ZFPM_STATE_IDLE, + + /* + * Ready to talk to the FPM and periodically trying to connect to + * it. + */ + ZFPM_STATE_ACTIVE, + + /* + * In the middle of bringing up a TCP connection. Specifically, + * waiting for a connect() call to complete asynchronously. + */ + ZFPM_STATE_CONNECTING, + + /* + * TCP connection to the FPM is up. + */ + ZFPM_STATE_ESTABLISHED + +} zfpm_state_t; + +/* + * Globals. + */ +typedef struct zfpm_glob_t_ +{ + + /* + * True if the FPM module has been enabled. + */ + int enabled; + + struct thread_master *master; + + zfpm_state_t state; + + /* + * Port on which the FPM is running. + */ + int fpm_port; + + /* + * List of rib_dest_t structures to be processed + */ + TAILQ_HEAD (zfpm_dest_q, rib_dest_t_) dest_q; + + /* + * Stream socket to the FPM. + */ + int sock; + + /* + * Buffers for messages to/from the FPM. + */ + struct stream *obuf; + struct stream *ibuf; + + /* + * Threads for I/O. + */ + struct thread *t_connect; + struct thread *t_write; + struct thread *t_read; + + /* + * Thread to clean up after the TCP connection to the FPM goes down + * and the state that belongs to it. + */ + struct thread *t_conn_down; + + struct { + zfpm_rnodes_iter_t iter; + } t_conn_down_state; + + /* + * Thread to take actions once the TCP conn to the FPM comes up, and + * the state that belongs to it. + */ + struct thread *t_conn_up; + + struct { + zfpm_rnodes_iter_t iter; + } t_conn_up_state; + + unsigned long connect_calls; + time_t last_connect_call_time; + + /* + * Stats from the start of the current statistics interval up to + * now. These are the counters we typically update in the code. + */ + zfpm_stats_t stats; + + /* + * Statistics that were gathered in the last collection interval. + */ + zfpm_stats_t last_ivl_stats; + + /* + * Cumulative stats from the last clear to the start of the current + * statistics interval. + */ + zfpm_stats_t cumulative_stats; + + /* + * Stats interval timer. + */ + struct thread *t_stats; + + /* + * If non-zero, the last time when statistics were cleared. + */ + time_t last_stats_clear_time; + +} zfpm_glob_t; + +static zfpm_glob_t zfpm_glob_space; +static zfpm_glob_t *zfpm_g = &zfpm_glob_space; + +static int zfpm_read_cb (struct thread *thread); +static int zfpm_write_cb (struct thread *thread); + +static void zfpm_set_state (zfpm_state_t state, const char *reason); +static void zfpm_start_connect_timer (const char *reason); +static void zfpm_start_stats_timer (void); + +/* + * zfpm_thread_should_yield + */ +static inline int +zfpm_thread_should_yield (struct thread *t) +{ + return thread_should_yield (t); +} + +/* + * zfpm_state_to_str + */ +static const char * +zfpm_state_to_str (zfpm_state_t state) +{ + switch (state) + { + + case ZFPM_STATE_IDLE: + return "idle"; + + case ZFPM_STATE_ACTIVE: + return "active"; + + case ZFPM_STATE_CONNECTING: + return "connecting"; + + case ZFPM_STATE_ESTABLISHED: + return "established"; + + default: + return "unknown"; + } +} + +/* + * zfpm_get_time + */ +static time_t +zfpm_get_time (void) +{ + struct timeval tv; + + if (quagga_gettime (QUAGGA_CLK_MONOTONIC, &tv) < 0) + zlog_warn ("FPM: quagga_gettime failed!!"); + + return tv.tv_sec; +} + +/* + * zfpm_get_elapsed_time + * + * Returns the time elapsed (in seconds) since the given time. + */ +static time_t +zfpm_get_elapsed_time (time_t reference) +{ + time_t now; + + now = zfpm_get_time (); + + if (now < reference) + { + assert (0); + return 0; + } + + return now - reference; +} + +/* + * zfpm_is_table_for_fpm + * + * Returns TRUE if the the given table is to be communicated to the + * FPM. + */ +static inline int +zfpm_is_table_for_fpm (struct route_table *table) +{ + rib_table_info_t *info; + + info = rib_table_info (table); + + /* + * We only send the unicast tables in the main instance to the FPM + * at this point. + */ + if (info->vrf->id != 0) + return 0; + + if (info->safi != SAFI_UNICAST) + return 0; + + return 1; +} + +/* + * zfpm_rnodes_iter_init + */ +static inline void +zfpm_rnodes_iter_init (zfpm_rnodes_iter_t *iter) +{ + memset (iter, 0, sizeof (*iter)); + rib_tables_iter_init (&iter->tables_iter); + + /* + * This is a hack, but it makes implementing 'next' easier by + * ensuring that route_table_iter_next() will return NULL the first + * time we call it. + */ + route_table_iter_init (&iter->iter, NULL); + route_table_iter_cleanup (&iter->iter); +} + +/* + * zfpm_rnodes_iter_next + */ +static inline struct route_node * +zfpm_rnodes_iter_next (zfpm_rnodes_iter_t *iter) +{ + struct route_node *rn; + struct route_table *table; + + while (1) + { + rn = route_table_iter_next (&iter->iter); + if (rn) + return rn; + + /* + * We've made our way through this table, go to the next one. + */ + route_table_iter_cleanup (&iter->iter); + + while ((table = rib_tables_iter_next (&iter->tables_iter))) + { + if (zfpm_is_table_for_fpm (table)) + break; + } + + if (!table) + return NULL; + + route_table_iter_init (&iter->iter, table); + } + + return NULL; +} + +/* + * zfpm_rnodes_iter_pause + */ +static inline void +zfpm_rnodes_iter_pause (zfpm_rnodes_iter_t *iter) +{ + route_table_iter_pause (&iter->iter); +} + +/* + * zfpm_rnodes_iter_cleanup + */ +static inline void +zfpm_rnodes_iter_cleanup (zfpm_rnodes_iter_t *iter) +{ + route_table_iter_cleanup (&iter->iter); + rib_tables_iter_cleanup (&iter->tables_iter); +} + +/* + * zfpm_stats_init + * + * Initialize a statistics block. + */ +static inline void +zfpm_stats_init (zfpm_stats_t *stats) +{ + memset (stats, 0, sizeof (*stats)); +} + +/* + * zfpm_stats_reset + */ +static inline void +zfpm_stats_reset (zfpm_stats_t *stats) +{ + zfpm_stats_init (stats); +} + +/* + * zfpm_stats_copy + */ +static inline void +zfpm_stats_copy (const zfpm_stats_t *src, zfpm_stats_t *dest) +{ + memcpy (dest, src, sizeof (*dest)); +} + +/* + * zfpm_stats_compose + * + * Total up the statistics in two stats structures ('s1 and 's2') and + * return the result in the third argument, 'result'. Note that the + * pointer 'result' may be the same as 's1' or 's2'. + * + * For simplicity, the implementation below assumes that the stats + * structure is composed entirely of counters. This can easily be + * changed when necessary. + */ +static void +zfpm_stats_compose (const zfpm_stats_t *s1, const zfpm_stats_t *s2, + zfpm_stats_t *result) +{ + const unsigned long *p1, *p2; + unsigned long *result_p; + int i, num_counters; + + p1 = (const unsigned long *) s1; + p2 = (const unsigned long *) s2; + result_p = (unsigned long *) result; + + num_counters = (sizeof (zfpm_stats_t) / sizeof (unsigned long)); + + for (i = 0; i < num_counters; i++) + { + result_p[i] = p1[i] + p2[i]; + } +} + +/* + * zfpm_read_on + */ +static inline void +zfpm_read_on (void) +{ + assert (!zfpm_g->t_read); + assert (zfpm_g->sock >= 0); + + THREAD_READ_ON (zfpm_g->master, zfpm_g->t_read, zfpm_read_cb, 0, + zfpm_g->sock); +} + +/* + * zfpm_write_on + */ +static inline void +zfpm_write_on (void) +{ + assert (!zfpm_g->t_write); + assert (zfpm_g->sock >= 0); + + THREAD_WRITE_ON (zfpm_g->master, zfpm_g->t_write, zfpm_write_cb, 0, + zfpm_g->sock); +} + +/* + * zfpm_read_off + */ +static inline void +zfpm_read_off (void) +{ + THREAD_READ_OFF (zfpm_g->t_read); +} + +/* + * zfpm_write_off + */ +static inline void +zfpm_write_off (void) +{ + THREAD_WRITE_OFF (zfpm_g->t_write); +} + +/* + * zfpm_conn_up_thread_cb + * + * Callback for actions to be taken when the connection to the FPM + * comes up. + */ +static int +zfpm_conn_up_thread_cb (struct thread *thread) +{ + struct route_node *rnode; + zfpm_rnodes_iter_t *iter; + rib_dest_t *dest; + + assert (zfpm_g->t_conn_up); + zfpm_g->t_conn_up = NULL; + + iter = &zfpm_g->t_conn_up_state.iter; + + if (zfpm_g->state != ZFPM_STATE_ESTABLISHED) + { + zfpm_debug ("Connection not up anymore, conn_up thread aborting"); + zfpm_g->stats.t_conn_up_aborts++; + goto done; + } + + while ((rnode = zfpm_rnodes_iter_next (iter))) + { + dest = rib_dest_from_rnode (rnode); + + if (dest) + { + zfpm_g->stats.t_conn_up_dests_processed++; + zfpm_trigger_update (rnode, NULL); + } + + /* + * Yield if need be. + */ + if (!zfpm_thread_should_yield (thread)) + continue; + + zfpm_g->stats.t_conn_up_yields++; + zfpm_rnodes_iter_pause (iter); + zfpm_g->t_conn_up = thread_add_background (zfpm_g->master, + zfpm_conn_up_thread_cb, + 0, 0); + return 0; + } + + zfpm_g->stats.t_conn_up_finishes++; + + done: + zfpm_rnodes_iter_cleanup (iter); + return 0; +} + +/* + * zfpm_connection_up + * + * Called when the connection to the FPM comes up. + */ +static void +zfpm_connection_up (const char *detail) +{ + assert (zfpm_g->sock >= 0); + zfpm_read_on (); + zfpm_write_on (); + zfpm_set_state (ZFPM_STATE_ESTABLISHED, detail); + + /* + * Start thread to push existing routes to the FPM. + */ + assert (!zfpm_g->t_conn_up); + + zfpm_rnodes_iter_init (&zfpm_g->t_conn_up_state.iter); + + zfpm_debug ("Starting conn_up thread"); + zfpm_g->t_conn_up = thread_add_background (zfpm_g->master, + zfpm_conn_up_thread_cb, 0, 0); + zfpm_g->stats.t_conn_up_starts++; +} + +/* + * zfpm_connect_check + * + * Check if an asynchronous connect() to the FPM is complete. + */ +static void +zfpm_connect_check () +{ + int status; + socklen_t slen; + int ret; + + zfpm_read_off (); + zfpm_write_off (); + + slen = sizeof (status); + ret = getsockopt (zfpm_g->sock, SOL_SOCKET, SO_ERROR, (void *) &status, + &slen); + + if (ret >= 0 && status == 0) + { + zfpm_connection_up ("async connect complete"); + return; + } + + /* + * getsockopt() failed or indicated an error on the socket. + */ + close (zfpm_g->sock); + zfpm_g->sock = -1; + + zfpm_start_connect_timer ("getsockopt() after async connect failed"); + return; +} + +/* + * zfpm_conn_down_thread_cb + * + * Callback that is invoked to clean up state after the TCP connection + * to the FPM goes down. + */ +static int +zfpm_conn_down_thread_cb (struct thread *thread) +{ + struct route_node *rnode; + zfpm_rnodes_iter_t *iter; + rib_dest_t *dest; + + assert (zfpm_g->state == ZFPM_STATE_IDLE); + + assert (zfpm_g->t_conn_down); + zfpm_g->t_conn_down = NULL; + + iter = &zfpm_g->t_conn_down_state.iter; + + while ((rnode = zfpm_rnodes_iter_next (iter))) + { + dest = rib_dest_from_rnode (rnode); + + if (dest) + { + if (CHECK_FLAG (dest->flags, RIB_DEST_UPDATE_FPM)) + { + TAILQ_REMOVE (&zfpm_g->dest_q, dest, fpm_q_entries); + } + + UNSET_FLAG (dest->flags, RIB_DEST_UPDATE_FPM); + UNSET_FLAG (dest->flags, RIB_DEST_SENT_TO_FPM); + + zfpm_g->stats.t_conn_down_dests_processed++; + + /* + * Check if the dest should be deleted. + */ + rib_gc_dest(rnode); + } + + /* + * Yield if need be. + */ + if (!zfpm_thread_should_yield (thread)) + continue; + + zfpm_g->stats.t_conn_down_yields++; + zfpm_rnodes_iter_pause (iter); + zfpm_g->t_conn_down = thread_add_background (zfpm_g->master, + zfpm_conn_down_thread_cb, + 0, 0); + return 0; + } + + zfpm_g->stats.t_conn_down_finishes++; + zfpm_rnodes_iter_cleanup (iter); + + /* + * Start the process of connecting to the FPM again. + */ + zfpm_start_connect_timer ("cleanup complete"); + return 0; +} + +/* + * zfpm_connection_down + * + * Called when the connection to the FPM has gone down. + */ +static void +zfpm_connection_down (const char *detail) +{ + if (!detail) + detail = "unknown"; + + assert (zfpm_g->state == ZFPM_STATE_ESTABLISHED); + + zlog_info ("connection to the FPM has gone down: %s", detail); + + zfpm_read_off (); + zfpm_write_off (); + + stream_reset (zfpm_g->ibuf); + stream_reset (zfpm_g->obuf); + + if (zfpm_g->sock >= 0) { + close (zfpm_g->sock); + zfpm_g->sock = -1; + } + + /* + * Start thread to clean up state after the connection goes down. + */ + assert (!zfpm_g->t_conn_down); + zfpm_debug ("Starting conn_down thread"); + zfpm_rnodes_iter_init (&zfpm_g->t_conn_down_state.iter); + zfpm_g->t_conn_down = thread_add_background (zfpm_g->master, + zfpm_conn_down_thread_cb, 0, 0); + zfpm_g->stats.t_conn_down_starts++; + + zfpm_set_state (ZFPM_STATE_IDLE, detail); +} + +/* + * zfpm_read_cb + */ +static int +zfpm_read_cb (struct thread *thread) +{ + size_t already; + struct stream *ibuf; + uint16_t msg_len; + fpm_msg_hdr_t *hdr; + + zfpm_g->stats.read_cb_calls++; + assert (zfpm_g->t_read); + zfpm_g->t_read = NULL; + + /* + * Check if async connect is now done. + */ + if (zfpm_g->state == ZFPM_STATE_CONNECTING) + { + zfpm_connect_check(); + return 0; + } + + assert (zfpm_g->state == ZFPM_STATE_ESTABLISHED); + assert (zfpm_g->sock >= 0); + + ibuf = zfpm_g->ibuf; + + already = stream_get_endp (ibuf); + if (already < FPM_MSG_HDR_LEN) + { + ssize_t nbyte; + + nbyte = stream_read_try (ibuf, zfpm_g->sock, FPM_MSG_HDR_LEN - already); + if (nbyte == 0 || nbyte == -1) + { + zfpm_connection_down ("closed socket in read"); + return 0; + } + + if (nbyte != (ssize_t) (FPM_MSG_HDR_LEN - already)) + goto done; + + already = FPM_MSG_HDR_LEN; + } + + stream_set_getp (ibuf, 0); + + hdr = (fpm_msg_hdr_t *) stream_pnt (ibuf); + + if (!fpm_msg_hdr_ok (hdr)) + { + zfpm_connection_down ("invalid message header"); + return 0; + } + + msg_len = fpm_msg_len (hdr); + + /* + * Read out the rest of the packet. + */ + if (already < msg_len) + { + ssize_t nbyte; + + nbyte = stream_read_try (ibuf, zfpm_g->sock, msg_len - already); + + if (nbyte == 0 || nbyte == -1) + { + zfpm_connection_down ("failed to read message"); + return 0; + } + + if (nbyte != (ssize_t) (msg_len - already)) + goto done; + } + + zfpm_debug ("Read out a full fpm message"); + + /* + * Just throw it away for now. + */ + stream_reset (ibuf); + + done: + zfpm_read_on (); + return 0; +} + +/* + * zfpm_writes_pending + * + * Returns TRUE if we may have something to write to the FPM. + */ +static int +zfpm_writes_pending (void) +{ + + /* + * Check if there is any data in the outbound buffer that has not + * been written to the socket yet. + */ + if (stream_get_endp (zfpm_g->obuf) - stream_get_getp (zfpm_g->obuf)) + return 1; + + /* + * Check if there are any prefixes on the outbound queue. + */ + if (!TAILQ_EMPTY (&zfpm_g->dest_q)) + return 1; + + return 0; +} + +/* + * zfpm_encode_route + * + * Encode a message to the FPM with information about the given route. + * + * Returns the number of bytes written to the buffer. 0 or a negative + * value indicates an error. + */ +static inline int +zfpm_encode_route (rib_dest_t *dest, struct rib *rib, char *in_buf, + size_t in_buf_len) +{ +#ifndef HAVE_NETLINK + return 0; +#else + + int cmd; + + cmd = rib ? RTM_NEWROUTE : RTM_DELROUTE; + + return zfpm_netlink_encode_route (cmd, dest, rib, in_buf, in_buf_len); + +#endif /* HAVE_NETLINK */ +} + +/* + * zfpm_route_for_update + * + * Returns the rib that is to be sent to the FPM for a given dest. + */ +static struct rib * +zfpm_route_for_update (rib_dest_t *dest) +{ + struct rib *rib; + + RIB_DEST_FOREACH_ROUTE (dest, rib) + { + if (!CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) + continue; + + return rib; + } + + /* + * We have no route for this destination. + */ + return NULL; +} + +/* + * zfpm_build_updates + * + * Process the outgoing queue and write messages to the outbound + * buffer. + */ +static void +zfpm_build_updates (void) +{ + struct stream *s; + rib_dest_t *dest; + unsigned char *buf, *data, *buf_end; + size_t msg_len; + size_t data_len; + fpm_msg_hdr_t *hdr; + struct rib *rib; + int is_add, write_msg; + + s = zfpm_g->obuf; + + assert (stream_empty (s)); + + do { + + /* + * Make sure there is enough space to write another message. + */ + if (STREAM_WRITEABLE (s) < FPM_MAX_MSG_LEN) + break; + + buf = STREAM_DATA (s) + stream_get_endp (s); + buf_end = buf + STREAM_WRITEABLE (s); + + dest = TAILQ_FIRST (&zfpm_g->dest_q); + if (!dest) + break; + + assert (CHECK_FLAG (dest->flags, RIB_DEST_UPDATE_FPM)); + + hdr = (fpm_msg_hdr_t *) buf; + hdr->version = FPM_PROTO_VERSION; + hdr->msg_type = FPM_MSG_TYPE_NETLINK; + + data = fpm_msg_data (hdr); + + rib = zfpm_route_for_update (dest); + is_add = rib ? 1 : 0; + + write_msg = 1; + + /* + * If this is a route deletion, and we have not sent the route to + * the FPM previously, skip it. + */ + if (!is_add && !CHECK_FLAG (dest->flags, RIB_DEST_SENT_TO_FPM)) + { + write_msg = 0; + zfpm_g->stats.nop_deletes_skipped++; + } + + if (write_msg) { + data_len = zfpm_encode_route (dest, rib, (char *) data, buf_end - data); + + assert (data_len); + if (data_len) + { + msg_len = fpm_data_len_to_msg_len (data_len); + hdr->msg_len = htons (msg_len); + stream_forward_endp (s, msg_len); + + if (is_add) + zfpm_g->stats.route_adds++; + else + zfpm_g->stats.route_dels++; + } + } + + /* + * Remove the dest from the queue, and reset the flag. + */ + UNSET_FLAG (dest->flags, RIB_DEST_UPDATE_FPM); + TAILQ_REMOVE (&zfpm_g->dest_q, dest, fpm_q_entries); + + if (is_add) + { + SET_FLAG (dest->flags, RIB_DEST_SENT_TO_FPM); + } + else + { + UNSET_FLAG (dest->flags, RIB_DEST_SENT_TO_FPM); + } + + /* + * Delete the destination if necessary. + */ + if (rib_gc_dest (dest->rnode)) + zfpm_g->stats.dests_del_after_update++; + + } while (1); + +} + +/* + * zfpm_write_cb + */ +static int +zfpm_write_cb (struct thread *thread) +{ + struct stream *s; + int num_writes; + + zfpm_g->stats.write_cb_calls++; + assert (zfpm_g->t_write); + zfpm_g->t_write = NULL; + + /* + * Check if async connect is now done. + */ + if (zfpm_g->state == ZFPM_STATE_CONNECTING) + { + zfpm_connect_check (); + return 0; + } + + assert (zfpm_g->state == ZFPM_STATE_ESTABLISHED); + assert (zfpm_g->sock >= 0); + + num_writes = 0; + + do + { + int bytes_to_write, bytes_written; + + s = zfpm_g->obuf; + + /* + * If the stream is empty, try fill it up with data. + */ + if (stream_empty (s)) + { + zfpm_build_updates (); + } + + bytes_to_write = stream_get_endp (s) - stream_get_getp (s); + if (!bytes_to_write) + break; + + bytes_written = write (zfpm_g->sock, STREAM_PNT (s), bytes_to_write); + zfpm_g->stats.write_calls++; + num_writes++; + + if (bytes_written < 0) + { + if (ERRNO_IO_RETRY (errno)) + break; + + zfpm_connection_down ("failed to write to socket"); + return 0; + } + + if (bytes_written != bytes_to_write) + { + + /* + * Partial write. + */ + stream_forward_getp (s, bytes_written); + zfpm_g->stats.partial_writes++; + break; + } + + /* + * We've written out the entire contents of the stream. + */ + stream_reset (s); + + if (num_writes >= ZFPM_MAX_WRITES_PER_RUN) + { + zfpm_g->stats.max_writes_hit++; + break; + } + + if (zfpm_thread_should_yield (thread)) + { + zfpm_g->stats.t_write_yields++; + break; + } + } while (1); + + if (zfpm_writes_pending ()) + zfpm_write_on (); + + return 0; +} + +/* + * zfpm_connect_cb + */ +static int +zfpm_connect_cb (struct thread *t) +{ + int sock, ret; + struct sockaddr_in serv; + + assert (zfpm_g->t_connect); + zfpm_g->t_connect = NULL; + assert (zfpm_g->state == ZFPM_STATE_ACTIVE); + + sock = socket (AF_INET, SOCK_STREAM, 0); + if (sock < 0) + { + zfpm_debug ("Failed to create socket for connect(): %s", strerror(errno)); + zfpm_g->stats.connect_no_sock++; + return 0; + } + + set_nonblocking(sock); + + /* Make server socket. */ + memset (&serv, 0, sizeof (serv)); + serv.sin_family = AF_INET; + serv.sin_port = htons (zfpm_g->fpm_port); +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + serv.sin_len = sizeof (struct sockaddr_in); +#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ + serv.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + + /* + * Connect to the FPM. + */ + zfpm_g->connect_calls++; + zfpm_g->stats.connect_calls++; + zfpm_g->last_connect_call_time = zfpm_get_time (); + + ret = connect (sock, (struct sockaddr *) &serv, sizeof (serv)); + if (ret >= 0) + { + zfpm_g->sock = sock; + zfpm_connection_up ("connect succeeded"); + return 1; + } + + if (errno == EINPROGRESS) + { + zfpm_g->sock = sock; + zfpm_read_on (); + zfpm_write_on (); + zfpm_set_state (ZFPM_STATE_CONNECTING, "async connect in progress"); + return 0; + } + + zlog_info ("can't connect to FPM %d: %s", sock, safe_strerror (errno)); + close (sock); + + /* + * Restart timer for retrying connection. + */ + zfpm_start_connect_timer ("connect() failed"); + return 0; +} + +/* + * zfpm_set_state + * + * Move state machine into the given state. + */ +static void +zfpm_set_state (zfpm_state_t state, const char *reason) +{ + zfpm_state_t cur_state = zfpm_g->state; + + if (!reason) + reason = "Unknown"; + + if (state == cur_state) + return; + + zfpm_debug("beginning state transition %s -> %s. Reason: %s", + zfpm_state_to_str (cur_state), zfpm_state_to_str (state), + reason); + + switch (state) { + + case ZFPM_STATE_IDLE: + assert (cur_state == ZFPM_STATE_ESTABLISHED); + break; + + case ZFPM_STATE_ACTIVE: + assert (cur_state == ZFPM_STATE_IDLE || + cur_state == ZFPM_STATE_CONNECTING); + assert (zfpm_g->t_connect); + break; + + case ZFPM_STATE_CONNECTING: + assert (zfpm_g->sock); + assert (cur_state == ZFPM_STATE_ACTIVE); + assert (zfpm_g->t_read); + assert (zfpm_g->t_write); + break; + + case ZFPM_STATE_ESTABLISHED: + assert (cur_state == ZFPM_STATE_ACTIVE || + cur_state == ZFPM_STATE_CONNECTING); + assert (zfpm_g->sock); + assert (zfpm_g->t_read); + assert (zfpm_g->t_write); + break; + } + + zfpm_g->state = state; +} + +/* + * zfpm_calc_connect_delay + * + * Returns the number of seconds after which we should attempt to + * reconnect to the FPM. + */ +static long +zfpm_calc_connect_delay (void) +{ + time_t elapsed; + + /* + * Return 0 if this is our first attempt to connect. + */ + if (zfpm_g->connect_calls == 0) + { + return 0; + } + + elapsed = zfpm_get_elapsed_time (zfpm_g->last_connect_call_time); + + if (elapsed > ZFPM_CONNECT_RETRY_IVL) { + return 0; + } + + return ZFPM_CONNECT_RETRY_IVL - elapsed; +} + +/* + * zfpm_start_connect_timer + */ +static void +zfpm_start_connect_timer (const char *reason) +{ + long delay_secs; + + assert (!zfpm_g->t_connect); + assert (zfpm_g->sock < 0); + + assert(zfpm_g->state == ZFPM_STATE_IDLE || + zfpm_g->state == ZFPM_STATE_ACTIVE || + zfpm_g->state == ZFPM_STATE_CONNECTING); + + delay_secs = zfpm_calc_connect_delay(); + zfpm_debug ("scheduling connect in %ld seconds", delay_secs); + + THREAD_TIMER_ON (zfpm_g->master, zfpm_g->t_connect, zfpm_connect_cb, 0, + delay_secs); + zfpm_set_state (ZFPM_STATE_ACTIVE, reason); +} + +/* + * zfpm_is_enabled + * + * Returns TRUE if the zebra FPM module has been enabled. + */ +static inline int +zfpm_is_enabled (void) +{ + return zfpm_g->enabled; +} + +/* + * zfpm_conn_is_up + * + * Returns TRUE if the connection to the FPM is up. + */ +static inline int +zfpm_conn_is_up (void) +{ + if (zfpm_g->state != ZFPM_STATE_ESTABLISHED) + return 0; + + assert (zfpm_g->sock >= 0); + + return 1; +} + +/* + * zfpm_trigger_update + * + * The zebra code invokes this function to indicate that we should + * send an update to the FPM about the given route_node. + */ +void +zfpm_trigger_update (struct route_node *rn, const char *reason) +{ + rib_dest_t *dest; + char buf[INET6_ADDRSTRLEN]; + + /* + * Ignore if the connection is down. We will update the FPM about + * all destinations once the connection comes up. + */ + if (!zfpm_conn_is_up ()) + return; + + dest = rib_dest_from_rnode (rn); + + /* + * Ignore the trigger if the dest is not in a table that we would + * send to the FPM. + */ + if (!zfpm_is_table_for_fpm (rib_dest_table (dest))) + { + zfpm_g->stats.non_fpm_table_triggers++; + return; + } + + if (CHECK_FLAG (dest->flags, RIB_DEST_UPDATE_FPM)) { + zfpm_g->stats.redundant_triggers++; + return; + } + + if (reason) + { + zfpm_debug ("%s/%d triggering update to FPM - Reason: %s", + inet_ntop (rn->p.family, &rn->p.u.prefix, buf, sizeof (buf)), + rn->p.prefixlen, reason); + } + + SET_FLAG (dest->flags, RIB_DEST_UPDATE_FPM); + TAILQ_INSERT_TAIL (&zfpm_g->dest_q, dest, fpm_q_entries); + zfpm_g->stats.updates_triggered++; + + /* + * Make sure that writes are enabled. + */ + if (zfpm_g->t_write) + return; + + zfpm_write_on (); +} + +/* + * zfpm_stats_timer_cb + */ +static int +zfpm_stats_timer_cb (struct thread *t) +{ + assert (zfpm_g->t_stats); + zfpm_g->t_stats = NULL; + + /* + * Remember the stats collected in the last interval for display + * purposes. + */ + zfpm_stats_copy (&zfpm_g->stats, &zfpm_g->last_ivl_stats); + + /* + * Add the current set of stats into the cumulative statistics. + */ + zfpm_stats_compose (&zfpm_g->cumulative_stats, &zfpm_g->stats, + &zfpm_g->cumulative_stats); + + /* + * Start collecting stats afresh over the next interval. + */ + zfpm_stats_reset (&zfpm_g->stats); + + zfpm_start_stats_timer (); + + return 0; +} + +/* + * zfpm_stop_stats_timer + */ +static void +zfpm_stop_stats_timer (void) +{ + if (!zfpm_g->t_stats) + return; + + zfpm_debug ("Stopping existing stats timer"); + THREAD_TIMER_OFF (zfpm_g->t_stats); +} + +/* + * zfpm_start_stats_timer + */ +void +zfpm_start_stats_timer (void) +{ + assert (!zfpm_g->t_stats); + + THREAD_TIMER_ON (zfpm_g->master, zfpm_g->t_stats, zfpm_stats_timer_cb, 0, + ZFPM_STATS_IVL_SECS); +} + +/* + * Helper macro for zfpm_show_stats() below. + */ +#define ZFPM_SHOW_STAT(counter) \ + do { \ + vty_out (vty, "%-40s %10lu %16lu%s", #counter, total_stats.counter, \ + zfpm_g->last_ivl_stats.counter, VTY_NEWLINE); \ + } while (0) + +/* + * zfpm_show_stats + */ +static void +zfpm_show_stats (struct vty *vty) +{ + zfpm_stats_t total_stats; + time_t elapsed; + + vty_out (vty, "%s%-40s %10s Last %2d secs%s%s", VTY_NEWLINE, "Counter", + "Total", ZFPM_STATS_IVL_SECS, VTY_NEWLINE, VTY_NEWLINE); + + /* + * Compute the total stats up to this instant. + */ + zfpm_stats_compose (&zfpm_g->cumulative_stats, &zfpm_g->stats, + &total_stats); + + ZFPM_SHOW_STAT (connect_calls); + ZFPM_SHOW_STAT (connect_no_sock); + ZFPM_SHOW_STAT (read_cb_calls); + ZFPM_SHOW_STAT (write_cb_calls); + ZFPM_SHOW_STAT (write_calls); + ZFPM_SHOW_STAT (partial_writes); + ZFPM_SHOW_STAT (max_writes_hit); + ZFPM_SHOW_STAT (t_write_yields); + ZFPM_SHOW_STAT (nop_deletes_skipped); + ZFPM_SHOW_STAT (route_adds); + ZFPM_SHOW_STAT (route_dels); + ZFPM_SHOW_STAT (updates_triggered); + ZFPM_SHOW_STAT (non_fpm_table_triggers); + ZFPM_SHOW_STAT (redundant_triggers); + ZFPM_SHOW_STAT (dests_del_after_update); + ZFPM_SHOW_STAT (t_conn_down_starts); + ZFPM_SHOW_STAT (t_conn_down_dests_processed); + ZFPM_SHOW_STAT (t_conn_down_yields); + ZFPM_SHOW_STAT (t_conn_down_finishes); + ZFPM_SHOW_STAT (t_conn_up_starts); + ZFPM_SHOW_STAT (t_conn_up_dests_processed); + ZFPM_SHOW_STAT (t_conn_up_yields); + ZFPM_SHOW_STAT (t_conn_up_aborts); + ZFPM_SHOW_STAT (t_conn_up_finishes); + + if (!zfpm_g->last_stats_clear_time) + return; + + elapsed = zfpm_get_elapsed_time (zfpm_g->last_stats_clear_time); + + vty_out (vty, "%sStats were cleared %lu seconds ago%s", VTY_NEWLINE, + (unsigned long) elapsed, VTY_NEWLINE); +} + +/* + * zfpm_clear_stats + */ +static void +zfpm_clear_stats (struct vty *vty) +{ + if (!zfpm_is_enabled ()) + { + vty_out (vty, "The FPM module is not enabled...%s", VTY_NEWLINE); + return; + } + + zfpm_stats_reset (&zfpm_g->stats); + zfpm_stats_reset (&zfpm_g->last_ivl_stats); + zfpm_stats_reset (&zfpm_g->cumulative_stats); + + zfpm_stop_stats_timer (); + zfpm_start_stats_timer (); + + zfpm_g->last_stats_clear_time = zfpm_get_time(); + + vty_out (vty, "Cleared FPM stats%s", VTY_NEWLINE); +} + +/* + * show_zebra_fpm_stats + */ +DEFUN (show_zebra_fpm_stats, + show_zebra_fpm_stats_cmd, + "show zebra fpm stats", + SHOW_STR + "Zebra information\n" + "Forwarding Path Manager information\n" + "Statistics\n") +{ + zfpm_show_stats (vty); + return CMD_SUCCESS; +} + +/* + * clear_zebra_fpm_stats + */ +DEFUN (clear_zebra_fpm_stats, + clear_zebra_fpm_stats_cmd, + "clear zebra fpm stats", + CLEAR_STR + "Zebra information\n" + "Clear Forwarding Path Manager information\n" + "Statistics\n") +{ + zfpm_clear_stats (vty); + return CMD_SUCCESS; +} + +/** + * zfpm_init + * + * One-time initialization of the Zebra FPM module. + * + * @param[in] port port at which FPM is running. + * @param[in] enable TRUE if the zebra FPM module should be enabled + * + * Returns TRUE on success. + */ +int +zfpm_init (struct thread_master *master, int enable, uint16_t port) +{ + static int initialized = 0; + + if (initialized) { + return 1; + } + + initialized = 1; + + memset (zfpm_g, 0, sizeof (*zfpm_g)); + zfpm_g->master = master; + TAILQ_INIT(&zfpm_g->dest_q); + zfpm_g->sock = -1; + zfpm_g->state = ZFPM_STATE_IDLE; + + /* + * Netlink must currently be available for the Zebra-FPM interface + * to be enabled. + */ +#ifndef HAVE_NETLINK + enable = 0; +#endif + + zfpm_g->enabled = enable; + + zfpm_stats_init (&zfpm_g->stats); + zfpm_stats_init (&zfpm_g->last_ivl_stats); + zfpm_stats_init (&zfpm_g->cumulative_stats); + + install_element (ENABLE_NODE, &show_zebra_fpm_stats_cmd); + install_element (ENABLE_NODE, &clear_zebra_fpm_stats_cmd); + + if (!enable) { + return 1; + } + + if (!port) + port = FPM_DEFAULT_PORT; + + zfpm_g->fpm_port = port; + + zfpm_g->obuf = stream_new (ZFPM_OBUF_SIZE); + zfpm_g->ibuf = stream_new (ZFPM_IBUF_SIZE); + + zfpm_start_stats_timer (); + zfpm_start_connect_timer ("initialized"); + + return 1; +} diff --git a/zebra/zebra_fpm.h b/zebra/zebra_fpm.h new file mode 100644 index 00000000..44dec028 --- /dev/null +++ b/zebra/zebra_fpm.h @@ -0,0 +1,34 @@ +/* + * Header file exported by the zebra FPM module to zebra. + * + * Copyright (C) 2012 by Open Source Routing. + * Copyright (C) 2012 by Internet Systems Consortium, Inc. ("ISC") + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _ZEBRA_FPM_H +#define _ZEBRA_FPM_H + +/* + * Externs. + */ +extern int zfpm_init (struct thread_master *master, int enable, uint16_t port); +extern void zfpm_trigger_update (struct route_node *rn, const char *reason); + +#endif /* _ZEBRA_FPM_H */ diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c new file mode 100644 index 00000000..90d3afb2 --- /dev/null +++ b/zebra/zebra_fpm_netlink.c @@ -0,0 +1,552 @@ +/* + * Code for encoding/decoding FPM messages that are in netlink format. + * + * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro + * Copyright (C) 2012 by Open Source Routing. + * Copyright (C) 2012 by Internet Systems Consortium, Inc. ("ISC") + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include + +#include "log.h" +#include "rib.h" + +#include "rt_netlink.h" + +#include "zebra_fpm_private.h" + +/* + * addr_to_a + * + * Returns string representation of an address of the given AF. + */ +static inline const char * +addr_to_a (u_char af, void *addr) +{ + if (!addr) + return ""; + + switch (af) + { + + case AF_INET: + return inet_ntoa (*((struct in_addr *) addr)); + +#ifdef HAVE_IPV6 + case AF_INET6: + return inet6_ntoa (*((struct in6_addr *) addr)); +#endif + + default: + return ""; + } +} + +/* + * prefix_addr_to_a + * + * Convience wrapper that returns a human-readable string for the + * address in a prefix. + */ +static const char * +prefix_addr_to_a (struct prefix *prefix) +{ + if (!prefix) + return ""; + + return addr_to_a (prefix->family, &prefix->u.prefix); +} + +/* + * af_addr_size + * + * The size of an address in a given address family. + */ +static size_t +af_addr_size (u_char af) +{ + switch (af) + { + + case AF_INET: + return 4; + +#ifdef HAVE_IPV6 + case AF_INET6: + return 16; +#endif + + default: + assert(0); + return 16; + } +} + +/* + * netlink_nh_info_t + * + * Holds information about a single nexthop for netlink. These info + * structures are transient and may contain pointers into rib + * data structures for convenience. + */ +typedef struct netlink_nh_info_t_ +{ + uint32_t if_index; + union g_addr *gateway; + + /* + * Information from the struct nexthop from which this nh was + * derived. For debug purposes only. + */ + int recursive; + enum nexthop_types_t type; +} netlink_nh_info_t; + +/* + * netlink_route_info_t + * + * A structure for holding information for a netlink route message. + */ +typedef struct netlink_route_info_t_ +{ + uint16_t nlmsg_type; + u_char rtm_type; + uint32_t rtm_table; + u_char rtm_protocol; + u_char af; + struct prefix *prefix; + uint32_t *metric; + int num_nhs; + + /* + * Nexthop structures. We keep things simple for now by enforcing a + * maximum of 64 in case MULTIPATH_NUM is 0; + */ + netlink_nh_info_t nhs[MAX (MULTIPATH_NUM, 64)]; + union g_addr *pref_src; +} netlink_route_info_t; + +/* + * netlink_route_info_add_nh + * + * Add information about the given nexthop to the given route info + * structure. + * + * Returns TRUE if a nexthop was added, FALSE otherwise. + */ +static int +netlink_route_info_add_nh (netlink_route_info_t *ri, struct nexthop *nexthop) +{ + netlink_nh_info_t nhi; + union g_addr *src; + + memset (&nhi, 0, sizeof (nhi)); + src = NULL; + + if (ri->num_nhs >= (int) ZEBRA_NUM_OF (ri->nhs)) + return 0; + + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + { + nhi.recursive = 1; + nhi.type = nexthop->rtype; + + if (nexthop->rtype == NEXTHOP_TYPE_IPV4 + || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX) + { + nhi.gateway = &nexthop->rgate; + if (nexthop->src.ipv4.s_addr) + src = &nexthop->src; + } + +#ifdef HAVE_IPV6 + if (nexthop->rtype == NEXTHOP_TYPE_IPV6 + || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX + || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME) + { + nhi.gateway = &nexthop->rgate; + } +#endif /* HAVE_IPV6 */ + + if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX + || nexthop->rtype == NEXTHOP_TYPE_IFNAME + || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX + || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX + || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME) + { + nhi.if_index = nexthop->rifindex; + if ((nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX + || nexthop->rtype == NEXTHOP_TYPE_IFINDEX) + && nexthop->src.ipv4.s_addr) + src = &nexthop->src; + } + + goto done; + } + + nhi.recursive = 0; + nhi.type = nexthop->type; + + if (nexthop->type == NEXTHOP_TYPE_IPV4 + || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) + { + nhi.gateway = &nexthop->gate; + if (nexthop->src.ipv4.s_addr) + src = &nexthop->src; + } + +#ifdef HAVE_IPV6 + if (nexthop->type == NEXTHOP_TYPE_IPV6 + || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME + || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) + { + nhi.gateway = &nexthop->gate; + } +#endif /* HAVE_IPV6 */ + if (nexthop->type == NEXTHOP_TYPE_IFINDEX + || nexthop->type == NEXTHOP_TYPE_IFNAME + || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) + { + nhi.if_index = nexthop->ifindex; + + if (nexthop->src.ipv4.s_addr) + src = &nexthop->src; + } + else if (nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX + || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME) + { + nhi.if_index = nexthop->ifindex; + } + + /* + * Fall through... + */ + + done: + if (!nhi.gateway && nhi.if_index == 0) + return 0; + + /* + * We have a valid nhi. Copy the structure over to the route_info. + */ + ri->nhs[ri->num_nhs] = nhi; + ri->num_nhs++; + + if (src && !ri->pref_src) + ri->pref_src = src; + + return 1; +} + +/* + * netlink_proto_from_route_type + */ +static u_char +netlink_proto_from_route_type (int type) +{ + switch (type) + { + case ZEBRA_ROUTE_KERNEL: + case ZEBRA_ROUTE_CONNECT: + return RTPROT_KERNEL; + + default: + return RTPROT_ZEBRA; + } +} + +/* + * netlink_route_info_fill + * + * Fill out the route information object from the given route. + * + * Returns TRUE on success and FALSE on failure. + */ +static int +netlink_route_info_fill (netlink_route_info_t *ri, int cmd, + rib_dest_t *dest, struct rib *rib) +{ + struct nexthop *nexthop = NULL; + int discard; + + memset (ri, 0, sizeof (*ri)); + + ri->prefix = rib_dest_prefix (dest); + ri->af = rib_dest_af (dest); + + ri->nlmsg_type = cmd; + ri->rtm_table = rib_dest_vrf (dest)->id; + ri->rtm_protocol = RTPROT_UNSPEC; + + /* + * An RTM_DELROUTE need not be accompanied by any nexthops, + * particularly in our communication with the FPM. + */ + if (cmd == RTM_DELROUTE && !rib) + goto skip; + + if (rib) + ri->rtm_protocol = netlink_proto_from_route_type (rib->type); + + if ((rib->flags & ZEBRA_FLAG_BLACKHOLE) || (rib->flags & ZEBRA_FLAG_REJECT)) + discard = 1; + else + discard = 0; + + if (cmd == RTM_NEWROUTE) + { + if (discard) + { + if (rib->flags & ZEBRA_FLAG_BLACKHOLE) + ri->rtm_type = RTN_BLACKHOLE; + else if (rib->flags & ZEBRA_FLAG_REJECT) + ri->rtm_type = RTN_UNREACHABLE; + else + assert (0); + } + else + ri->rtm_type = RTN_UNICAST; + } + + ri->metric = &rib->metric; + + if (discard) + { + goto skip; + } + + /* Multipath case. */ + if (rib->nexthop_active_num == 1 || MULTIPATH_NUM == 1) + { + for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) + { + + if ((cmd == RTM_NEWROUTE + && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + || (cmd == RTM_DELROUTE + && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))) + { + netlink_route_info_add_nh (ri, nexthop); + break; + } + } + } + else + { + for (nexthop = rib->nexthop; + nexthop && (MULTIPATH_NUM == 0 || ri->num_nhs < MULTIPATH_NUM); + nexthop = nexthop->next) + { + if ((cmd == RTM_NEWROUTE + && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + || (cmd == RTM_DELROUTE + && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))) + { + netlink_route_info_add_nh (ri, nexthop); + } + } + } + + /* If there is no useful nexthop then return. */ + if (ri->num_nhs == 0) + { + zfpm_debug ("netlink_encode_route(): No useful nexthop."); + return 0; + } + + skip: + return 1; +} + +/* + * netlink_route_info_encode + * + * Returns the number of bytes written to the buffer. 0 or a negative + * value indicates an error. + */ +static int +netlink_route_info_encode (netlink_route_info_t *ri, char *in_buf, + size_t in_buf_len) +{ + int bytelen; + int nexthop_num = 0; + size_t buf_offset; + netlink_nh_info_t *nhi; + + struct + { + struct nlmsghdr n; + struct rtmsg r; + char buf[1]; + } *req; + + req = (void *) in_buf; + + buf_offset = ((char *) req->buf) - ((char *) req); + + if (in_buf_len < buf_offset) { + assert(0); + return 0; + } + + memset (req, 0, buf_offset); + + bytelen = af_addr_size (ri->af); + + req->n.nlmsg_len = NLMSG_LENGTH (sizeof (struct rtmsg)); + req->n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; + req->n.nlmsg_type = ri->nlmsg_type; + req->r.rtm_family = ri->af; + req->r.rtm_table = ri->rtm_table; + req->r.rtm_dst_len = ri->prefix->prefixlen; + req->r.rtm_protocol = ri->rtm_protocol; + req->r.rtm_scope = RT_SCOPE_UNIVERSE; + + addattr_l (&req->n, in_buf_len, RTA_DST, &ri->prefix->u.prefix, bytelen); + + req->r.rtm_type = ri->rtm_type; + + /* Metric. */ + if (ri->metric) + addattr32 (&req->n, in_buf_len, RTA_PRIORITY, *ri->metric); + + if (ri->num_nhs == 0) + goto done; + + if (ri->num_nhs == 1) + { + nhi = &ri->nhs[0]; + + if (nhi->gateway) + { + addattr_l (&req->n, in_buf_len, RTA_GATEWAY, nhi->gateway, + bytelen); + } + + if (nhi->if_index) + { + addattr32 (&req->n, in_buf_len, RTA_OIF, nhi->if_index); + } + + goto done; + + } + + /* + * Multipath case. + */ + char buf[NL_PKT_BUF_SIZE]; + struct rtattr *rta = (void *) buf; + struct rtnexthop *rtnh; + + rta->rta_type = RTA_MULTIPATH; + rta->rta_len = RTA_LENGTH (0); + rtnh = RTA_DATA (rta); + + for (nexthop_num = 0; nexthop_num < ri->num_nhs; nexthop_num++) + { + nhi = &ri->nhs[nexthop_num]; + + rtnh->rtnh_len = sizeof (*rtnh); + rtnh->rtnh_flags = 0; + rtnh->rtnh_hops = 0; + rtnh->rtnh_ifindex = 0; + rta->rta_len += rtnh->rtnh_len; + + if (nhi->gateway) + { + rta_addattr_l (rta, sizeof (buf), RTA_GATEWAY, nhi->gateway, bytelen); + rtnh->rtnh_len += sizeof (struct rtattr) + bytelen; + } + + if (nhi->if_index) + { + rtnh->rtnh_ifindex = nhi->if_index; + } + + rtnh = RTNH_NEXT (rtnh); + } + + assert (rta->rta_len > RTA_LENGTH (0)); + addattr_l (&req->n, in_buf_len, RTA_MULTIPATH, RTA_DATA (rta), + RTA_PAYLOAD (rta)); + +done: + + if (ri->pref_src) + { + addattr_l (&req->n, in_buf_len, RTA_PREFSRC, &ri->pref_src, bytelen); + } + + assert (req->n.nlmsg_len < in_buf_len); + return req->n.nlmsg_len; +} + +/* + * zfpm_log_route_info + * + * Helper function to log the information in a route_info structure. + */ +static void +zfpm_log_route_info (netlink_route_info_t *ri, const char *label) +{ + netlink_nh_info_t *nhi; + int i; + + zfpm_debug ("%s : %s %s/%d, Proto: %s, Metric: %u", label, + nl_msg_type_to_str (ri->nlmsg_type), + prefix_addr_to_a (ri->prefix), ri->prefix->prefixlen, + nl_rtproto_to_str (ri->rtm_protocol), + ri->metric ? *ri->metric : 0); + + for (i = 0; i < ri->num_nhs; i++) + { + nhi = &ri->nhs[i]; + zfpm_debug(" Intf: %u, Gateway: %s, Recursive: %s, Type: %s", + nhi->if_index, addr_to_a (ri->af, nhi->gateway), + nhi->recursive ? "yes" : "no", + nexthop_type_to_str (nhi->type)); + } +} + +/* + * zfpm_netlink_encode_route + * + * Create a netlink message corresponding to the given route in the + * given buffer space. + * + * Returns the number of bytes written to the buffer. 0 or a negative + * value indicates an error. + */ +int +zfpm_netlink_encode_route (int cmd, rib_dest_t *dest, struct rib *rib, + char *in_buf, size_t in_buf_len) +{ + netlink_route_info_t ri_space, *ri; + + ri = &ri_space; + + if (!netlink_route_info_fill (ri, cmd, dest, rib)) + return 0; + + zfpm_log_route_info (ri, __FUNCTION__); + + return netlink_route_info_encode (ri, in_buf, in_buf_len); +} diff --git a/zebra/zebra_fpm_private.h b/zebra/zebra_fpm_private.h new file mode 100644 index 00000000..809a70a4 --- /dev/null +++ b/zebra/zebra_fpm_private.h @@ -0,0 +1,56 @@ +/* + * Private header file for the zebra FPM module. + * + * Copyright (C) 2012 by Open Source Routing. + * Copyright (C) 2012 by Internet Systems Consortium, Inc. ("ISC") + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _ZEBRA_FPM_PRIVATE_H +#define _ZEBRA_FPM_PRIVATE_H + +#include "zebra/debug.h" + +#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L + +#define zfpm_debug(...) \ + do { \ + if (IS_ZEBRA_DEBUG_FPM) zlog_debug("FPM: " __VA_ARGS__); \ + } while(0) + +#elif defined __GNUC__ + +#define zfpm_debug(_args...) \ + do { \ + if (IS_ZEBRA_DEBUG_FPM) zlog_debug("FPM: " _args); \ + } while(0) + +#else +static inline void zfpm_debug(const char *format, ...) { return; } +#endif + + +/* + * Externs + */ +extern int +zfpm_netlink_encode_route (int cmd, rib_dest_t *dest, struct rib *rib, + char *in_buf, size_t in_buf_len); + +#endif /* _ZEBRA_FPM_PRIVATE_H */ diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 29977123..a75d7215 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -40,6 +40,7 @@ #include "zebra/zserv.h" #include "zebra/redistribute.h" #include "zebra/debug.h" +#include "zebra/zebra_fpm.h" /* Default rtm_table for all clients */ extern struct zebra_t zebrad; @@ -961,6 +962,11 @@ rib_install_kernel (struct route_node *rn, struct rib *rib) int ret = 0; struct nexthop *nexthop; + /* + * Make sure we update the FPM any time we send new information to + * the kernel. + */ + zfpm_trigger_update (rn, "installing in kernel"); switch (PREFIX_FAMILY (&rn->p)) { case AF_INET: @@ -988,6 +994,12 @@ rib_uninstall_kernel (struct route_node *rn, struct rib *rib) int ret = 0; struct nexthop *nexthop; + /* + * Make sure we update the FPM any time we send new information to + * the kernel. + */ + zfpm_trigger_update (rn, "uninstalling from kernel"); + switch (PREFIX_FAMILY (&rn->p)) { case AF_INET: @@ -1012,6 +1024,8 @@ rib_uninstall (struct route_node *rn, struct rib *rib) { if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) { + zfpm_trigger_update (rn, "rib_uninstall"); + redistribute_delete (&rn->p, rib); if (! RIB_SYSTEM_ROUTE (rib)) rib_uninstall_kernel (rn, rib); @@ -1034,6 +1048,14 @@ rib_can_delete_dest (rib_dest_t *dest) return 0; } + /* + * Don't delete the dest if we have to update the FPM about this + * prefix. + */ + if (CHECK_FLAG (dest->flags, RIB_DEST_UPDATE_FPM) || + CHECK_FLAG (dest->flags, RIB_DEST_SENT_TO_FPM)) + return 0; + return 1; } @@ -1187,6 +1209,8 @@ rib_process (struct route_node *rn) __func__, buf, rn->p.prefixlen, select, fib); if (CHECK_FLAG (select->flags, ZEBRA_FLAG_CHANGED)) { + zfpm_trigger_update (rn, "updating existing route"); + redistribute_delete (&rn->p, select); if (! RIB_SYSTEM_ROUTE (select)) rib_uninstall_kernel (rn, select); @@ -1228,6 +1252,9 @@ rib_process (struct route_node *rn) if (IS_ZEBRA_DEBUG_RIB) zlog_debug ("%s: %s/%d: Removing existing route, fib %p", __func__, buf, rn->p.prefixlen, fib); + + zfpm_trigger_update (rn, "removing existing route"); + redistribute_delete (&rn->p, fib); if (! RIB_SYSTEM_ROUTE (fib)) rib_uninstall_kernel (rn, fib); @@ -1246,6 +1273,9 @@ rib_process (struct route_node *rn) if (IS_ZEBRA_DEBUG_RIB) zlog_debug ("%s: %s/%d: Adding route, select %p", __func__, buf, rn->p.prefixlen, select); + + zfpm_trigger_update (rn, "new route selected"); + /* Set real nexthop. */ nexthop_active_update (rn, select, 1); @@ -3081,6 +3111,8 @@ rib_close_table (struct route_table *table) if (!CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) continue; + zfpm_trigger_update (rn, NULL); + if (! RIB_SYSTEM_ROUTE (rib)) rib_uninstall_kernel (rn, rib); } From b9c24cd23091e817942d4eaab95712e84dbec8aa Mon Sep 17 00:00:00 2001 From: Avneesh Sachdev Date: Tue, 13 Nov 2012 22:49:00 +0000 Subject: [PATCH 011/482] doc: add blurbs on zebra FPM interface and commands Update documentation with some text on the zebra interface to the optional Forwarding Path Manager component, and the related cli commands. Signed-off-by: Avneesh Sachdev Signed-off-by: David Lamparter --- doc/main.texi | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/doc/main.texi b/doc/main.texi index a5759137..a6bf0d1c 100644 --- a/doc/main.texi +++ b/doc/main.texi @@ -11,6 +11,7 @@ different routing protocols. * Interface Commands:: Commands for zebra interfaces * Static Route Commands:: Commands for adding static routes * zebra Route Filtering:: Commands for zebra route filtering +* zebra FIB push interface:: Interface to optional FPM component * zebra Terminal Mode Commands:: Commands for zebra's VTY @end menu @@ -227,6 +228,54 @@ ip protocol rip route-map RM1 @end group @end example +@node zebra FIB push interface +@section zebra FIB push interface + +Zebra supports a 'FIB push' interface that allows an external +component to learn the forwarding information computed by the Quagga +routing suite. + +In Quagga, the Routing Information Base (RIB) resides inside +zebra. Routing protocols communicate their best routes to zebra, and +zebra computes the best route across protocols for each prefix. This +latter information makes up the Forwarding Information Base +(FIB). Zebra feeds the FIB to the kernel, which allows the IP stack in +the kernel to forward packets according to the routes computed by +Quagga. The kernel FIB is updated in an OS-specific way. For example, +the @code{netlink} interface is used on Linux, and route sockets are +used on FreeBSD. + +The FIB push interface aims to provide a cross-platform mechanism to +support scenarios where the router has a forwarding path that is +distinct from the kernel, commonly a hardware-based fast path. In +these cases, the FIB needs to be maintained reliably in the fast path +as well. We refer to the component that programs the forwarding plane +(directly or indirectly) as the Forwarding Plane Manager or FPM. + +The FIB push interface comprises of a TCP connection between zebra and +the FPM. The connection is initiated by zebra -- that is, the FPM acts +as the TCP server. + +The relevant zebra code kicks in when zebra is configured with the +@code{--enable-fpm} flag. Zebra periodically attempts to connect to +the well-known FPM port. Once the connection is up, zebra starts +sending messages containing routes over the socket to the FPM. Zebra +sends a complete copy of the forwarding table to the FPM, including +routes that it may have picked up from the kernel. The existing +interaction of zebra with the kernel remains unchanged -- that is, the +kernel continues to receive FIB updates as before. + +The format of the messages exchanged with the FPM is defined by the +file @file{fpm/fpm.h} in the quagga tree. + +The zebra FPM interface uses replace semantics. That is, if a 'route +add' message for a prefix is followed by another 'route add' message, +the information in the second message is complete by itself, and +replaces the information sent in the first message. + +If the connection to the FPM goes down for some reason, zebra sends +the FPM a complete copy of the forwarding table(s) when it reconnects. + @node zebra Terminal Mode Commands @section zebra Terminal Mode Commands @@ -271,3 +320,13 @@ If so, the box can't work as a router. @deffn Command {show ipv6forward} {} Display whether the host's IP v6 forwarding is enabled or not. @end deffn + +@deffn Command {show zebra fpm stats} {} +Display statistics related to the zebra code that interacts with the +optional Forwarding Plane Manager (FPM) component. +@end deffn + +@deffn Command {clear zebra fpm stats} {} +Reset statistics related to the zebra code that interacts with the +optional Forwarding Plane Manager (FPM) component. +@end deffn From 324ed1f87aedf30a45aea951edb2e717c5f9b7fb Mon Sep 17 00:00:00 2001 From: Avneesh Sachdev Date: Tue, 13 Nov 2012 22:49:01 +0000 Subject: [PATCH 012/482] zebra: include intf when sending IPv4 nexthop to FPM * zebra/zebra_fpm_netlink.c Change the zebra FPM code to include an interface index when encoding a nexthop even if the protocol only provided a gateway address (e.g, NEXTHOP_TYPE_IPV4). Signed-off-by: Avneesh Sachdev Signed-off-by: David Lamparter --- zebra/zebra_fpm_netlink.c | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c index 90d3afb2..67bcf0a1 100644 --- a/zebra/zebra_fpm_netlink.c +++ b/zebra/zebra_fpm_netlink.c @@ -167,6 +167,7 @@ netlink_route_info_add_nh (netlink_route_info_t *ri, struct nexthop *nexthop) { nhi.recursive = 1; nhi.type = nexthop->rtype; + nhi.if_index = nexthop->rifindex; if (nexthop->rtype == NEXTHOP_TYPE_IPV4 || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX) @@ -186,15 +187,9 @@ netlink_route_info_add_nh (netlink_route_info_t *ri, struct nexthop *nexthop) #endif /* HAVE_IPV6 */ if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX - || nexthop->rtype == NEXTHOP_TYPE_IFNAME - || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX - || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX - || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME) + || nexthop->rtype == NEXTHOP_TYPE_IFNAME) { - nhi.if_index = nexthop->rifindex; - if ((nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX - || nexthop->rtype == NEXTHOP_TYPE_IFINDEX) - && nexthop->src.ipv4.s_addr) + if (nexthop->src.ipv4.s_addr) src = &nexthop->src; } @@ -203,6 +198,7 @@ netlink_route_info_add_nh (netlink_route_info_t *ri, struct nexthop *nexthop) nhi.recursive = 0; nhi.type = nexthop->type; + nhi.if_index = nexthop->ifindex; if (nexthop->type == NEXTHOP_TYPE_IPV4 || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) @@ -220,20 +216,13 @@ netlink_route_info_add_nh (netlink_route_info_t *ri, struct nexthop *nexthop) nhi.gateway = &nexthop->gate; } #endif /* HAVE_IPV6 */ + if (nexthop->type == NEXTHOP_TYPE_IFINDEX - || nexthop->type == NEXTHOP_TYPE_IFNAME - || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) + || nexthop->type == NEXTHOP_TYPE_IFNAME) { - nhi.if_index = nexthop->ifindex; - if (nexthop->src.ipv4.s_addr) src = &nexthop->src; } - else if (nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX - || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME) - { - nhi.if_index = nexthop->ifindex; - } /* * Fall through... From 0be793e674c8b076f0e8bf327257f15803480f49 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 27 Nov 2012 01:34:56 +0000 Subject: [PATCH 013/482] build: include git info If enabled with --with-pkg-gitversion on ./configure, this will append git version strings and branch information at the following places: - overall version number: 0.99.21-g0123456 - login motd and show version: tag information + git id + branches Sample output: Hello, this is Quagga (version 0.99.21-g14b49ad-dirty). Copyright 1996-2005 Kunihiro Ishiguro, et al. This is a git build of quagga_0_99_21_release-106-g14b49ad-dirty Associated branch(es): local:master [v2]: fix build without gitinfo (add "else" branch) [v2]: fix for repos without any tags (different git describe output) Signed-off-by: David Lamparter --- configure.ac | 16 ++++++++++++---- lib/.gitignore | 2 ++ lib/Makefile.am | 24 +++++++++++++++++++++++- lib/command.c | 4 ++-- lib/gitversion.pl | 47 +++++++++++++++++++++++++++++++++++++++++++++++ lib/version.h.in | 13 ++++++++++++- 6 files changed, 98 insertions(+), 8 deletions(-) create mode 100644 lib/gitversion.pl diff --git a/configure.ac b/configure.ac index 9bbe89f3..fb6efd7e 100755 --- a/configure.ac +++ b/configure.ac @@ -21,6 +21,7 @@ AC_CANONICAL_TARGET() AM_INIT_AUTOMAKE(1.6) AM_CONFIG_HEADER(config.h) +AC_PATH_PROG(PERL, perl) AC_CHECK_PROG([GAWK],[gawk],[gawk],[not-in-PATH]) if test "x$GAWK" = "xnot-in-PATH" ; then AC_MSG_ERROR([GNU awk is required for lib/memtype.h made by memtypes.awk. @@ -192,6 +193,12 @@ dnl ---------------------- AC_ARG_WITH(pkg-extra-version, AS_HELP_STRING([--with-pkg-extra-version=VER], [add extra version field, for packagers/distributions]), [EXTRAVERSION=$withval],) +AC_ARG_WITH(pkg-git-version, + AS_HELP_STRING([--with-pkg-git-version], [add git information to MOTD and build version string]), + [ if test "x$withval" != "xno"; then + with_pkg_git_version="1" + AC_DEFINE(GIT_VERSION, [1], [include git version info]) + fi ],) AC_ARG_ENABLE(vtysh, [ --enable-vtysh include integrated vty shell for Quagga]) AC_ARG_ENABLE(ipv6, @@ -398,11 +405,13 @@ dnl Add extra version string to package dnl name, string and version fields. dnl ----------------------------------- if test "x${EXTRAVERSION}" != "x" ; then - VERSION=${VERSION}${EXTRAVERSION} - PACKAGE_VERSION=${PACKAGE_VERSION}${EXTRAVERSION} - PACKAGE_STRING=${PACKAGE_STRING}${EXTRAVERSION} + VERSION="${VERSION}${EXTRAVERSION}" + PACKAGE_VERSION="${PACKAGE_VERSION}${EXTRAVERSION}" + PACKAGE_STRING="${PACKAGE_STRING}${EXTRAVERSION}" fi +AM_CONDITIONAL([GIT_VERSION], [test "x$with_pkg_git_version" != "x"]) + dnl ------------------------------------ dnl Check C keywords and standard types dnl ------------------------------------ @@ -611,7 +620,6 @@ dnl --------------------- case "${enable_vtysh}" in "yes") VTYSH="vtysh"; AC_DEFINE(VTYSH,,VTY shell) - AC_PATH_PROG(PERL, perl) dnl Vtysh uses libreadline, which looks for termcap functions at dnl configure time. We follow readlines search order. dnl The required procedures are in libtermcap on NetBSD, in diff --git a/lib/.gitignore b/lib/.gitignore index 00af85a6..02aa432c 100644 --- a/lib/.gitignore +++ b/lib/.gitignore @@ -5,6 +5,8 @@ Makefile.in *.la version.c version.h +gitversion.h +gitversion.h.tmp .deps .nfs* .libs diff --git a/lib/Makefile.am b/lib/Makefile.am index e00ad54d..690c18ff 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -14,7 +14,7 @@ libzebra_la_SOURCES = \ zclient.c sockopt.c smux.c agentx.c snmp.c md5.c if_rmap.c keychain.c privs.c \ sigevent.c pqueue.c jhash.c memtypes.c workqueue.c -BUILT_SOURCES = memtypes.h route_types.h +BUILT_SOURCES = memtypes.h route_types.h gitversion.h libzebra_la_DEPENDENCIES = @LIB_REGEX@ @@ -36,3 +36,25 @@ memtypes.h: $(srcdir)/memtypes.c $(srcdir)/memtypes.awk route_types.h: $(srcdir)/route_types.txt $(srcdir)/route_types.pl @PERL@ $(srcdir)/route_types.pl < $(srcdir)/route_types.txt > $@ + +if GIT_VERSION + +# bit of a trick here to always have up-to-date git stamps without triggering +# unneccessary rebuilds. .PHONY causes the .tmp file to be rebuilt always, +# but if we use that on gitversion.h it'll ripple through the .c file deps. +# (even if gitversion.h's file timestamp doesn't change, make will think it +# did, because of .PHONY...) + +.PHONY: gitversion.h.tmp +.SILENT: gitversion.h gitversion.h.tmp +GITH=gitversion.h +gitversion.h.tmp: $(srcdir)/../.git + @PERL@ $(srcdir)/gitversion.pl $(srcdir) > ${GITH}.tmp +gitversion.h: gitversion.h.tmp + { test -f ${GITH} && diff -s -q ${GITH}.tmp ${GITH}; } || cp -v ${GITH}.tmp ${GITH} + +else +.PHONY: gitversion.h +gitversion.h: + /bin/true +endif diff --git a/lib/command.c b/lib/command.c index 64563b5d..3b3fadac 100644 --- a/lib/command.c +++ b/lib/command.c @@ -84,7 +84,7 @@ static const char *default_motd = "\r\n\ Hello, this is " QUAGGA_PROGNAME " (version " QUAGGA_VERSION ").\r\n\ " QUAGGA_COPYRIGHT "\r\n\ -\r\n"; +" GIT_INFO "\r\n"; static const struct facility_map { @@ -2409,7 +2409,7 @@ DEFUN (show_version, { vty_out (vty, "Quagga %s (%s).%s", QUAGGA_VERSION, host.name?host.name:"", VTY_NEWLINE); - vty_out (vty, "%s%s", QUAGGA_COPYRIGHT, VTY_NEWLINE); + vty_out (vty, "%s%s%s", QUAGGA_COPYRIGHT, GIT_INFO, VTY_NEWLINE); return CMD_SUCCESS; } diff --git a/lib/gitversion.pl b/lib/gitversion.pl new file mode 100644 index 00000000..448f13d6 --- /dev/null +++ b/lib/gitversion.pl @@ -0,0 +1,47 @@ +#!/usr/bin/perl -w +use strict; + +my $dir = shift; +chdir $dir || die "$dir: $!\n"; + +my $gitdesc = `git describe --always --dirty || echo -- \"0-gUNKNOWN\"`; +chomp $gitdesc; +my $gitsuffix = ($gitdesc =~ /([0-9a-fA-F]{7}(-dirty)?)$/) ? $1 : "-gUNKNOWN"; + +printf STDERR "git suffix: %s\n", $gitsuffix; +printf "#define GIT_SUFFIX \"%s\"\n", $gitsuffix; + +my $gitcommit = `git log -1 --format=\"%H\" || echo DEADBEEF`; +chomp $gitcommit; +open(BRANCHES, "git branch -a -v --abbrev=40|") || die "git branch: $!\n"; +my @names = (); +while () { + chomp $_; + if (/\s+(.*?)\s+$gitcommit/) { + my $branch = $1; + if ($branch =~ /^remotes\/(.*?)(\/.*)$/) { + my $path = $2; + my $url = `git config --get "remote.$1.url"`; + chomp $url; + $url =~ s/^(git:|https?:|git@)\/\/github\.com/github/i; + $url =~ s/^(ssh|git):\/\/git\.sv\.gnu\.org\/srv\/git\//savannah:/i; + $url =~ s/^(ssh|git):\/\/git\.savannah\.nongnu\.org\//savannah:/i; + + push @names, $url.$path; + } else { + push @names, 'local:'.$branch; + } + } +} + +printf STDERR "git branches: %s\n", join(", ", @names); + +my $cr = "\\r\\n\\"; +printf < Date: Tue, 27 Nov 2012 01:10:24 +0000 Subject: [PATCH 014/482] isisd: do not add >63 IP addresses to hello RFC1195 s4.2 "Multiple IP Addresses per Interface" explicitly forbids us from adding multiple tuples of IP addresses, putting a hard cutoff at 63 IP addresses. * isisd/isis_tlv.c: cut off (and return success) at 63 addrs. Signed-off-by: David Lamparter Tested-by: Martin Winter --- isisd/isis_tlv.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/isisd/isis_tlv.c b/isisd/isis_tlv.c index f3b2c338..ed3e0e81 100644 --- a/isisd/isis_tlv.c +++ b/isisd/isis_tlv.c @@ -932,10 +932,9 @@ tlv_add_ip_addrs (struct list *ip_addrs, struct stream *stream) { if (pos - value + IPV4_MAX_BYTELEN > 255) { - retval = add_tlv (IPV4_ADDR, pos - value, value, stream); - if (retval != ISIS_OK) - return retval; - pos = value; + /* RFC 1195 s4.2: only one tuple of 63 allowed. */ + zlog_warn ("tlv_add_ip_addrs(): cutting off at 63 IP addresses"); + break; } *(u_int32_t *) pos = ipv4->prefix.s_addr; pos += IPV4_MAX_BYTELEN; From 19f78cebd2ce50f0d1f367cd759cdcfb1a500f59 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 27 Nov 2012 01:10:25 +0000 Subject: [PATCH 015/482] isisd: refuse adjacencies with our own system ID isisd would form an adjacency with another router despite the system IDs being identical. This would later cause an assertion failure like this: assertion=0x555555596db8 "isis_find_vertex (spftree->paths, id, vtype) == ((void *)0)", file=0x555555596c60 "isis_spf.c", line=515, function=0x555555597900 "isis_spf_add2tent") at log.c:619 which is caused by trying to add a path expected to not exist, but suddenly colliding due to the duplicate system ID. * isis_pdu.c: check for system ID collision on receiving Hello Signed-off-by: David Lamparter --- isisd/isis_pdu.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c index bfa1e4e9..083ddc72 100644 --- a/isisd/isis_pdu.c +++ b/isisd/isis_pdu.c @@ -988,6 +988,13 @@ process_lan_hello (int level, struct isis_circuit *circuit, u_char * ssnpa) } } + if (!memcmp (hdr.source_id, isis->sysid, ISIS_SYS_ID_LEN)) + { + zlog_warn ("ISIS-Adj (%s): duplicate system ID on interface %s", + circuit->area->area_tag, circuit->interface->name); + return ISIS_WARNING; + } + /* * Accept the level 1 adjacency only if a match between local and * remote area addresses is found From b72f345d2e078d4cd0559234d051214f58eef542 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 27 Nov 2012 01:10:26 +0000 Subject: [PATCH 016/482] isisd: drop hellos without supported protocol list isisd should not form adjacencies on receiving an IS-IS Hello without a list of supported protocols (cf. RFC 1195 s4.4 p32 "Maintaining Router Adjacencies") Also fixes memleaks in these error cases. * isisd/isis_pdu.c: improve TLVFLAG_NLPID handling Signed-off-by: David Lamparter Tested-by: Martin Winter --- isisd/isis_pdu.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c index 083ddc72..8a92789f 100644 --- a/isisd/isis_pdu.c +++ b/isisd/isis_pdu.c @@ -493,6 +493,13 @@ process_p2p_hello (struct isis_circuit *circuit) return ISIS_WARNING; } + if (!(found & TLVFLAG_NLPID)) + { + zlog_warn ("No supported protocols TLV in P2P IS to IS hello"); + free_tlvs (&tlvs); + return ISIS_WARNING; + } + /* 8.2.5.1 c) Authentication */ if (circuit->passwd.type) { @@ -550,9 +557,11 @@ process_p2p_hello (struct isis_circuit *circuit) tlvs_to_adj_area_addrs (&tlvs, adj); /* which protocol are spoken ??? */ - if (found & TLVFLAG_NLPID) - if (tlvs_to_adj_nlpids (&tlvs, adj)) - return ISIS_ERROR; + if (tlvs_to_adj_nlpids (&tlvs, adj)) + { + free_tlvs (&tlvs); + return ISIS_WARNING; + } /* we need to copy addresses to the adj */ if (found & TLVFLAG_IPV4_ADDR) @@ -973,6 +982,14 @@ process_lan_hello (int level, struct isis_circuit *circuit, u_char * ssnpa) goto out; } + if (!(found & TLVFLAG_NLPID)) + { + zlog_warn ("No supported protocols TLV in Level %d LAN IS to IS hello", + level); + retval = ISIS_WARNING; + goto out; + } + /* Verify authentication, either cleartext of HMAC MD5 */ if (circuit->passwd.type) { @@ -1103,8 +1120,11 @@ process_lan_hello (int level, struct isis_circuit *circuit, u_char * ssnpa) tlvs_to_adj_area_addrs (&tlvs, adj); /* which protocol are spoken ??? */ - if (found & TLVFLAG_NLPID) - tlvs_to_adj_nlpids (&tlvs, adj); + if (tlvs_to_adj_nlpids (&tlvs, adj)) + { + retval = ISIS_WARNING; + goto out; + } /* we need to copy addresses to the adj */ if (found & TLVFLAG_IPV4_ADDR) From de543de3d7682eaeb8c9b657e9a3bb4bcd17993d Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 27 Nov 2012 01:10:27 +0000 Subject: [PATCH 017/482] isisd: fix spftree_area_del typo causing SEGV spftree_area_del didn't clear the IPv6 L2 spftree due to a simple typo, leading to a SEGV on shutdown when the still-armed timer would try to run an IPv6 L2 SPF calculation with its data free'd already. Signed-off-by: David Lamparter --- isisd/isis_spf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index 07b855b4..1cb51138 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -365,7 +365,7 @@ spftree_area_del (struct isis_area *area) area->spftree[1] = NULL; } #ifdef HAVE_IPV6 - if (area->spftree[1] != NULL) + if (area->spftree6[1] != NULL) { isis_spftree_del (area->spftree6[1]); area->spftree6[1] = NULL; From f818c8f3fb9c98490df29e99aa9cddde8e0296d5 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Tue, 27 Nov 2012 01:10:28 +0000 Subject: [PATCH 018/482] isisd: save metric-style narrow isisd defaults to wide metric style. So if narrow metric style is configured, a matching setting should be written to the configuration, allowing a narrow metric-style setting to be saved. Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- isisd/isisd.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/isisd/isisd.c b/isisd/isisd.c index e8e3d9f0..47675f0a 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -2857,6 +2857,11 @@ isis_config_write (struct vty *vty) vty_out (vty, " metric-style transition%s", VTY_NEWLINE); write++; } + else + { + vty_out (vty, " metric-style narrow%s", VTY_NEWLINE); + write++; + } /* ISIS - overload-bit */ if (area->overload_bit) { From 318c8040abc1b8a737c941382e8aca82e546da09 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 27 Nov 2012 01:10:29 +0000 Subject: [PATCH 019/482] isisd: always join all IS-IS multicast groups The socket is only created once when an interface is brought up, and the multicast groups were joined according to configuration at that point. This breaks when later switching an interface to another IS-IS level. Since, for a separate conformance issue (ANVL ISIS-6.4), we should be inspecting the destination address anyway, the simplest fix here is to just join all groups unconditionally. There shouldn't be much traffic on these anyway, worst case we might be picking up some unrelated multicast groups due to NIC filter aliasing though... Signed-off-by: David Lamparter Tested-by: Martin Winter --- isisd/isis_dlpi.c | 10 +++------- isisd/isis_pfpacket.c | 12 +++++------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/isisd/isis_dlpi.c b/isisd/isis_dlpi.c index 73b6d3e7..0a82718d 100644 --- a/isisd/isis_dlpi.c +++ b/isisd/isis_dlpi.c @@ -442,13 +442,9 @@ open_dlpi_dev (struct isis_circuit *circuit) * 8.4.2 - Broadcast subnetwork IIH PDUs */ retval = 0; - if (circuit->is_type & IS_LEVEL_1) - { - retval |= dlpimcast (fd, ALL_L1_ISS); - retval |= dlpimcast (fd, ALL_ISS); - } - if (circuit->is_type & IS_LEVEL_2) - retval |= dlpimcast (fd, ALL_L2_ISS); + retval |= dlpimcast (fd, ALL_L1_ISS); + retval |= dlpimcast (fd, ALL_ISS); + retval |= dlpimcast (fd, ALL_L2_ISS); if (retval != 0) { diff --git a/isisd/isis_pfpacket.c b/isisd/isis_pfpacket.c index 42947b22..e5589aea 100644 --- a/isisd/isis_pfpacket.c +++ b/isisd/isis_pfpacket.c @@ -142,16 +142,14 @@ open_packet_socket (struct isis_circuit *circuit) * 8.4.2 - Broadcast subnetwork IIH PDUs * FIXME: is there a case only one will fail?? */ - if (circuit->is_type & IS_LEVEL_1) - /* joining ALL_L1_ISS */ - retval = isis_multicast_join (circuit->fd, 1, + /* joining ALL_L1_ISS */ + retval |= isis_multicast_join (circuit->fd, 1, circuit->interface->ifindex); - if (circuit->is_type & IS_LEVEL_2) - /* joining ALL_L2_ISS */ - retval = isis_multicast_join (circuit->fd, 2, + /* joining ALL_L2_ISS */ + retval |= isis_multicast_join (circuit->fd, 2, circuit->interface->ifindex); /* joining ALL_ISS (used in RFC 5309 p2p-over-lan as well) */ - retval = isis_multicast_join (circuit->fd, 3, + retval |= isis_multicast_join (circuit->fd, 3, circuit->interface->ifindex); } else From e8aca32f312cbef1cb0b0dd9e87b7e59dc9fa251 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 27 Nov 2012 01:10:30 +0000 Subject: [PATCH 020/482] isisd: address Coverity warnings this fixes a bunch of issues found by Coverity SCAN and flagged as "high" impact -- although, they're all rather minute issues. * isisd/isis_adjacency.c: one superfluous check, one possible NULL deref * isisd/isis_circuit.c: two prefix memory leaks * isisd/isis_csm.c: one missing break * isisd/isis_lsp.c: one possible NULL deref * isisd/isis_pfpacket.c: one error-case fd leak * isisd/isis_route.c: one isis_route_info memory leak * isisd/isis_routemap.c: one... fnord * isisd/isis_tlv.c: one infinite loop Reported-by: Coverity SCAN Signed-off-by: David Lamparter --- isisd/isis_adjacency.c | 4 ++-- isisd/isis_circuit.c | 4 ++++ isisd/isis_csm.c | 1 + isisd/isis_lsp.c | 4 +++- isisd/isis_pfpacket.c | 1 + isisd/isis_route.c | 6 +++++- isisd/isis_routemap.c | 3 +-- isisd/isis_spf.c | 2 +- isisd/isis_tlv.c | 1 + 9 files changed, 19 insertions(+), 7 deletions(-) diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c index 468b0a69..414885fc 100644 --- a/isisd/isis_adjacency.c +++ b/isisd/isis_adjacency.c @@ -207,7 +207,7 @@ isis_adj_state_change (struct isis_adjacency *adj, enum isis_adj_state new_state zlog_info ("%%ADJCHANGE: Adjacency to %s (%s) changed from %s to %s, %s", adj_name, - adj->circuit ? adj->circuit->interface->name : "no circuit", + adj->circuit->interface->name, adj_state2string (old_state), adj_state2string (new_state), reason ? reason : "unspecified"); @@ -427,7 +427,7 @@ isis_adj_print_vty (struct isis_adjacency *adj, struct vty *vty, char detail) vty_out (vty, ", Speaks: %s", nlpid2string (&adj->nlpids)); vty_out (vty, "%s", VTY_NEWLINE); vty_out (vty, " SNPA: %s", snpa_print (adj->snpa)); - if (adj->circuit->circ_type == CIRCUIT_T_BROADCAST) + if (adj->circuit && (adj->circuit->circ_type == CIRCUIT_T_BROADCAST)) { dyn = dynhn_find_by_id (adj->lanid); if (dyn) diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index c09c3a28..3d9fb473 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -327,6 +327,8 @@ isis_circuit_del_addr (struct isis_circuit *circuit, zlog_warn ("Nonexitant ip address %s removal attempt from \ circuit %d", buf, circuit->circuit_id); } + + prefix_ipv4_free (ipv4); } #ifdef HAVE_IPV6 if (connected->address->family == AF_INET6) @@ -370,6 +372,8 @@ isis_circuit_del_addr (struct isis_circuit *circuit, } else if (circuit->area) lsp_regenerate_schedule (circuit->area, circuit->is_type, 0); + + prefix_ipv6_free (ipv6); } #endif /* HAVE_IPV6 */ return; diff --git a/isisd/isis_csm.c b/isisd/isis_csm.c index 5d74a71b..a58ba490 100644 --- a/isisd/isis_csm.c +++ b/isisd/isis_csm.c @@ -101,6 +101,7 @@ isis_csm_state_change (int event, struct isis_circuit *circuit, void *arg) break; case ISIS_DISABLE: zlog_warn ("circuit already disabled"); + break; case IF_DOWN_FROM_Z: zlog_warn ("circuit already disconnected"); break; diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index 082e9dcd..f2a7923d 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -1631,7 +1631,7 @@ lsp_generate (struct isis_area *area, int level) static int lsp_regenerate (struct isis_area *area, int level) { - dict_t *lspdb = area->lspdb[level - 1]; + dict_t *lspdb; struct isis_lsp *lsp, *frag; struct listnode *node; u_char lspid[ISIS_SYS_ID_LEN + 2]; @@ -1640,6 +1640,8 @@ lsp_regenerate (struct isis_area *area, int level) if ((area == NULL) || (area->is_type & level) != level) return ISIS_ERROR; + lspdb = area->lspdb[level - 1]; + memset (lspid, 0, ISIS_SYS_ID_LEN + 2); memcpy (lspid, isis->sysid, ISIS_SYS_ID_LEN); diff --git a/isisd/isis_pfpacket.c b/isisd/isis_pfpacket.c index e5589aea..4bc8717a 100644 --- a/isisd/isis_pfpacket.c +++ b/isisd/isis_pfpacket.c @@ -129,6 +129,7 @@ open_packet_socket (struct isis_circuit *circuit) sizeof (struct sockaddr_ll)) < 0) { zlog_warn ("open_packet_socket(): bind() failed: %s", safe_strerror (errno)); + close (fd); return ISIS_WARNING; } diff --git a/isisd/isis_route.c b/isisd/isis_route.c index c99d9583..8ab470ce 100644 --- a/isisd/isis_route.c +++ b/isisd/isis_route.c @@ -438,7 +438,11 @@ isis_route_create (struct prefix *prefix, u_int32_t cost, u_int32_t depth, route_node = route_node_get (area->route_table6[level - 1], prefix); #endif /* HAVE_IPV6 */ else - return NULL; + { + isis_route_info_delete (rinfo_new); + return NULL; + } + rinfo_old = route_node->info; if (!rinfo_old) { diff --git a/isisd/isis_routemap.c b/isisd/isis_routemap.c index 558d3910..84a14ac5 100644 --- a/isisd/isis_routemap.c +++ b/isisd/isis_routemap.c @@ -69,8 +69,7 @@ isis_route_map_upd (const char *name) for (i = 0; i <= ZEBRA_ROUTE_MAX; i++) { if (isis->rmap[i].name) - isis->rmap[i].map = isis->rmap[i].map = - route_map_lookup_by_name (isis->rmap[i].name); + isis->rmap[i].map = route_map_lookup_by_name (isis->rmap[i].name); else isis->rmap[i].map = NULL; } diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index 1cb51138..e0a8d01c 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -1492,7 +1492,7 @@ isis_print_paths (struct vty *vty, struct list *paths, u_char *root_sysid) struct listnode *anode; struct isis_vertex *vertex; struct isis_adjacency *adj; - u_char buff[255]; + u_char buff[BUFSIZ]; vty_out (vty, "Vertex Type Metric " "Next-Hop Interface Parent%s", VTY_NEWLINE); diff --git a/isisd/isis_tlv.c b/isisd/isis_tlv.c index ed3e0e81..2c2415ae 100644 --- a/isisd/isis_tlv.c +++ b/isisd/isis_tlv.c @@ -712,6 +712,7 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected, Neighbor Extended Local Circuit ID (four octets, if Neighbor System ID is present) */ pnt += length; + value_len += length; } } else From 4fb7c84f1b5e282c7dc413a578d3f42353ac7fec Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Tue, 27 Nov 2012 19:51:59 +0000 Subject: [PATCH 021/482] isisd: fix metrics check for metric-style narrow When switching to narrow metric style, all configured circuits are verified to have a valid narrow style metric. Check te_metric instead of metric_default as the latter is only 8bit wide and may overflow for wide style metrics. Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- isisd/isisd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/isisd/isisd.c b/isisd/isisd.c index 47675f0a..28cd0519 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -2054,7 +2054,7 @@ validate_metric_style_narrow (struct vty *vty, struct isis_area *area) { if ((area->is_type & IS_LEVEL_1) && (circuit->is_type & IS_LEVEL_1) && - (circuit->metrics[0].metric_default > MAX_NARROW_LINK_METRIC)) + (circuit->te_metric[0] > MAX_NARROW_LINK_METRIC)) { vty_out (vty, "ISIS circuit %s metric is invalid%s", circuit->interface->name, VTY_NEWLINE); @@ -2062,7 +2062,7 @@ validate_metric_style_narrow (struct vty *vty, struct isis_area *area) } if ((area->is_type & IS_LEVEL_2) && (circuit->is_type & IS_LEVEL_2) && - (circuit->metrics[1].metric_default > MAX_NARROW_LINK_METRIC)) + (circuit->te_metric[1] > MAX_NARROW_LINK_METRIC)) { vty_out (vty, "ISIS circuit %s metric is invalid%s", circuit->interface->name, VTY_NEWLINE); From 478c1125cfcf92c3f2a574d6b76eea28126284dc Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Tue, 27 Nov 2012 19:52:00 +0000 Subject: [PATCH 022/482] isisd: verify metrics on metric-style transition When switching to metric-style transition, circuit metrics should also be verified to be in the narrow range 0..63. Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- isisd/isisd.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/isisd/isisd.c b/isisd/isisd.c index 28cd0519..ce6a2621 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -2092,19 +2092,22 @@ DEFUN (metric_style, area->newmetric = 1; area->oldmetric = 0; } - else if (strncmp (argv[0], "t", 1) == 0) - { - area->newmetric = 1; - area->oldmetric = 1; - } - else if (strncmp (argv[0], "n", 1) == 0) + else { ret = validate_metric_style_narrow (vty, area); if (ret != CMD_SUCCESS) return ret; - area->newmetric = 0; - area->oldmetric = 1; + if (strncmp (argv[0], "t", 1) == 0) + { + area->newmetric = 1; + area->oldmetric = 1; + } + else if (strncmp (argv[0], "n", 1) == 0) + { + area->newmetric = 0; + area->oldmetric = 1; + } } return CMD_SUCCESS; From e1a555b67cc868f95ea2b53c278bd8d091333e5f Mon Sep 17 00:00:00 2001 From: Nick Hilliard Date: Wed, 28 Nov 2012 14:39:56 +0000 Subject: [PATCH 023/482] isisd: fix ipv6 metric endianness the isis ipv6 reachability metric is transmitted in big endian / network format, but isis_spf_process_lsp() does not convert this into host endian format when mucking around with local cost + received metric. This patch fixes this problem and makes received ipv6 metrics work properly on little-endian machines. Signed-off-by: David Lamparter --- isisd/isis_spf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index e0a8d01c..fd93efa6 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -833,7 +833,7 @@ isis_spf_process_lsp (struct isis_spftree *spftree, struct isis_lsp *lsp, { assert (ip6reach->prefix_len <= IPV6_MAX_BITLEN); - dist = cost + ip6reach->metric; + dist = cost + ntohl(ip6reach->metric); vtype = (ip6reach->control_info & CTRL_INFO_DISTRIBUTION) ? VTYPE_IP6REACH_EXTERNAL : VTYPE_IP6REACH_INTERNAL; prefix.prefixlen = ip6reach->prefix_len; From fd76f41ac0267dc4cf5438ac8f5e23fae6810d31 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Thu, 13 Dec 2012 10:35:45 +0100 Subject: [PATCH 024/482] build: update ICC warning CFLAGS Intel's icc doesn't accept "-wd " anymore, it's "-wd" these days. But, anyhow, the warnings disabled in Quagga's configure.ac don't seem to appear anywhere at all, so let's just remove the option completely. Signed-off-by: David Lamparter --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index fb6efd7e..c1a4d8cd 100755 --- a/configure.ac +++ b/configure.ac @@ -129,7 +129,7 @@ AC_MSG_CHECKING([whether to set a default CFLAGS]) if test "x${cflags_specified}" = "x" ; then case ${COMPILER} in "ICC") - CFLAGS="-Os -g -Wall -wd 279,869,981" + CFLAGS="-Os -g -Wall" AC_MSG_RESULT([Intel default]) ;; "GCC") From 4209a88759c41aac54465b6d4389b7daaad6ac80 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Thu, 13 Dec 2012 10:59:00 +0100 Subject: [PATCH 025/482] build: fix dist tarball automake file lists haven't quite kept up with recent changes, time to fix them up so the dist tarball actually works... Signed-off-by: David Lamparter --- Makefile.am | 3 ++- lib/Makefile.am | 7 ++++++- zebra/Makefile.am | 3 ++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Makefile.am b/Makefile.am index 8371041b..37fea43b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -12,7 +12,8 @@ EXTRA_DIST = aclocal.m4 SERVICES TODO REPORTING-BUGS INSTALL.quagga.txt \ update-autotools \ vtysh/Makefile.in vtysh/Makefile.am \ tools/mrlg.cgi tools/rrcheck.pl tools/rrlookup.pl tools/zc.pl \ - tools/zebra.el tools/multiple-bgpd.sh + tools/zebra.el tools/multiple-bgpd.sh \ + fpm/fpm.h if HAVE_LATEX diff --git a/lib/Makefile.am b/lib/Makefile.am index 690c18ff..0ec5b4c9 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -29,7 +29,12 @@ pkginclude_HEADERS = \ privs.h sigevent.h pqueue.h jhash.h zassert.h memtypes.h \ workqueue.h route_types.h -EXTRA_DIST = regex.c regex-gnu.h memtypes.awk route_types.pl route_types.txt +EXTRA_DIST = \ + regex.c regex-gnu.h \ + queue.h \ + memtypes.awk \ + route_types.pl route_types.txt \ + gitversion.pl memtypes.h: $(srcdir)/memtypes.c $(srcdir)/memtypes.awk ($(GAWK) -f $(srcdir)/memtypes.awk $(srcdir)/memtypes.c > $@) diff --git a/zebra/Makefile.am b/zebra/Makefile.am index 266812f8..96f7bef3 100644 --- a/zebra/Makefile.am +++ b/zebra/Makefile.am @@ -42,7 +42,8 @@ testzebra_SOURCES = test_main.c zebra_rib.c interface.c connected.c debug.c \ noinst_HEADERS = \ connected.h ioctl.h rib.h rt.h zserv.h redistribute.h debug.h rtadv.h \ - interface.h ipforward.h irdp.h router-id.h kernel_socket.h + interface.h ipforward.h irdp.h router-id.h kernel_socket.h \ + rt_netlink.h zebra_fpm.h zebra_fpm_private.h zebra_LDADD = $(otherobj) ../lib/libzebra.la $(LIBCAP) $(LIB_IPV6) From a16dcf7c11d80775b07a0fa6f3ac5527190fb486 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Thu, 13 Dec 2012 11:20:50 +0100 Subject: [PATCH 026/482] build: check for .git in configure.ac Don't error out when someone tries using --with-pkg-git-version on something that isn't actually a git checkout (like a dist tarball). Signed-off-by: David Lamparter --- configure.ac | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/configure.ac b/configure.ac index c1a4d8cd..405693c9 100755 --- a/configure.ac +++ b/configure.ac @@ -195,10 +195,7 @@ AC_ARG_WITH(pkg-extra-version, [EXTRAVERSION=$withval],) AC_ARG_WITH(pkg-git-version, AS_HELP_STRING([--with-pkg-git-version], [add git information to MOTD and build version string]), - [ if test "x$withval" != "xno"; then - with_pkg_git_version="1" - AC_DEFINE(GIT_VERSION, [1], [include git version info]) - fi ],) + [ test "x$withval" != "xno" && with_pkg_git_version="yes" ]) AC_ARG_ENABLE(vtysh, [ --enable-vtysh include integrated vty shell for Quagga]) AC_ARG_ENABLE(ipv6, @@ -410,7 +407,14 @@ if test "x${EXTRAVERSION}" != "x" ; then PACKAGE_STRING="${PACKAGE_STRING}${EXTRAVERSION}" fi -AM_CONDITIONAL([GIT_VERSION], [test "x$with_pkg_git_version" != "x"]) +if test "x$with_pkg_git_version" = "xyes"; then + if test -d "${srcdir}/.git"; then + AC_DEFINE(GIT_VERSION, [1], [include git version info]) + else with_pkg_git_version="no" + AC_MSG_WARN([--with-pkg-git-version given, but this is not a git checkout]) + fi +fi +AM_CONDITIONAL([GIT_VERSION], [test "x$with_pkg_git_version" = "xyes"]) dnl ------------------------------------ dnl Check C keywords and standard types From 16a86b3e2f82d3e19a722e2ab34c2c77f62ed926 Mon Sep 17 00:00:00 2001 From: Ayan Banerjee Date: Mon, 3 Dec 2012 10:48:46 -0800 Subject: [PATCH 027/482] ospf: suppress delete using replacement After a SPF run, OSPF deletes routes that have changed in terms of any metric, type, and/or next-hops and re-adds them. Given that the Zebra-RIB already support replacement semantics, we suppress deletes for routes that will be added back again. This has the following advantages. It reduces the number of IPC messages between OSPF/Zebra. Also, in the current flow, a batch of route deletes were followed by a batch of adds even for say a metric change. With the change, routes are sent as "add" when they are modified. Zebra already implicitly deletes older routes. Signed-off-by: Ayan Banerjee Reviewed-by: Scott Feldman Reviewed-by: Dinesh Dutt Signed-off-by: Scott Feldman --- ospfd/ospf_route.c | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/ospfd/ospf_route.c b/ospfd/ospf_route.c index c3acba34..eb7829ac 100644 --- a/ospfd/ospf_route.c +++ b/ospfd/ospf_route.c @@ -126,6 +126,31 @@ ospf_route_table_free (struct route_table *rt) route_table_finish (rt); } +/* If a prefix exists in the new routing table, then return 1, + otherwise return 0. Since the ZEBRA-RIB does an implicit + withdraw, it is not necessary to send a delete, an add later + will act like an implicit delete. */ +static int +ospf_route_exist_new_table (struct route_table *rt, struct prefix_ipv4 *prefix) +{ + struct route_node *rn; + + assert (rt); + assert (prefix); + + rn = route_node_lookup (rt, (struct prefix *) prefix); + if (!rn) { + return 0; + } + route_unlock_node (rn); + + if (!rn->info) { + return 0; + } + + return 1; +} + /* If a prefix and a nexthop match any route in the routing table, then return 1, otherwise return 0. */ int @@ -223,13 +248,13 @@ ospf_route_delete_uniq (struct route_table *rt, struct route_table *cmprt) { if (or->type == OSPF_DESTINATION_NETWORK) { - if (! ospf_route_match_same (cmprt, - (struct prefix_ipv4 *) &rn->p, or)) + if (! ospf_route_exist_new_table (cmprt, + (struct prefix_ipv4 *) &rn->p)) ospf_zebra_delete ((struct prefix_ipv4 *) &rn->p, or); } else if (or->type == OSPF_DESTINATION_DISCARD) - if (! ospf_route_match_same (cmprt, - (struct prefix_ipv4 *) &rn->p, or)) + if (! ospf_route_exist_new_table (cmprt, + (struct prefix_ipv4 *) &rn->p)) ospf_zebra_delete_discard ((struct prefix_ipv4 *) &rn->p); } } From 4ba4fc857685bfe31c7127826652012a750367c5 Mon Sep 17 00:00:00 2001 From: Ayan Banerjee Date: Mon, 3 Dec 2012 11:17:24 -0800 Subject: [PATCH 028/482] ospf: forward ref. of areas for "max-metric router-lsa administrative" cmd In the event areas are created at a later point of time with respect to the playback of the "max-metric router-lsa administrative" command, those areas do not get into indefinite max-metric mode. This patch is inteneded to store the configuration and apply it to all future areas that may be created. In the process, some other bugs that were there with respect to restart etc are fixed up. Tested locally to see that the fix works across multiple areas and across multiple restarts. Signed-off-by: Ayan Banerjee Reviewed-by: Scott Feldman Reviewed-by: JR Rivers Signed-off-by: Scott Feldman --- ospfd/ospf_vty.c | 5 +++++ ospfd/ospfd.c | 7 ++++++- ospfd/ospfd.h | 3 +++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 97c8e8d6..a8807141 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -7020,6 +7020,10 @@ DEFUN (ospf_max_metric_router_lsa_admin, if (!CHECK_FLAG (area->stub_router_state, OSPF_AREA_IS_STUB_ROUTED)) ospf_router_lsa_update_area (area); } + + /* Allows for areas configured later to get the property */ + ospf->stub_router_admin_set = OSPF_STUB_ROUTER_ADMINISTRATIVE_SET; + return CMD_SUCCESS; } @@ -7047,6 +7051,7 @@ DEFUN (no_ospf_max_metric_router_lsa_admin, ospf_router_lsa_update_area (area); } } + ospf->stub_router_admin_set = OSPF_STUB_ROUTER_ADMINISTRATIVE_UNSET; return CMD_SUCCESS; } diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index e8405136..11a2dc5c 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -182,7 +182,8 @@ ospf_new (void) new->stub_router_startup_time = OSPF_STUB_ROUTER_UNCONFIGURED; new->stub_router_shutdown_time = OSPF_STUB_ROUTER_UNCONFIGURED; - + new->stub_router_admin_set = OSPF_STUB_ROUTER_ADMINISTRATIVE_UNSET; + /* Distribute parameter init. */ for (i = 0; i <= ZEBRA_ROUTE_MAX; i++) { @@ -676,6 +677,10 @@ ospf_area_get (struct ospf *ospf, struct in_addr area_id, int format) area->format = format; listnode_add_sort (ospf->areas, area); ospf_check_abr_status (ospf); + if (ospf->stub_router_admin_set == OSPF_STUB_ROUTER_ADMINISTRATIVE_SET) + { + SET_FLAG (area->stub_router_state, OSPF_AREA_ADMIN_STUB_ROUTED); + } } return area; diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h index bf825d17..dfaef1d4 100644 --- a/ospfd/ospfd.h +++ b/ospfd/ospfd.h @@ -195,6 +195,9 @@ struct ospf unsigned int stub_router_startup_time; /* seconds */ unsigned int stub_router_shutdown_time; /* seconds */ #define OSPF_STUB_ROUTER_UNCONFIGURED 0 + u_char stub_router_admin_set; +#define OSPF_STUB_ROUTER_ADMINISTRATIVE_SET 1 +#define OSPF_STUB_ROUTER_ADMINISTRATIVE_UNSET 0 #define OSPF_STUB_MAX_METRIC_SUMMARY_COST 0x00ff0000 From 91e6a0e5ca973c7183f638987b67aa370e9b484c Mon Sep 17 00:00:00 2001 From: Dinesh Dutt Date: Tue, 4 Dec 2012 10:46:37 -0800 Subject: [PATCH 029/482] ospf: Convert MAX_AGE LSA list to tree Store the MaxAge LSA list in a tree instead of a linked list for efficient access. Walking the list can be quite inefficient in some large systems and under certain tests. ospfd maintains the list of LSA's that have been MaxAge'd out in a separate linked list for removal by a remover/walker thread. When a new LSA is to be installed, the old LSA is ejected and when it is ejected, the MaxAge LSA list is traversed to ensure that the old LSA is also removed from this list if it exists on this list. When a large number (> 5K) MaxAge LSAs are bombarding the system, walking this list takes a significant time causing timers to fire and actions to be taken such as expiring neighbors due to expiry of DeadInterval (especially when timer is really low, <= 12s), creating a spiral of instability. By making this MaxAge LSA list be a tree, this problem is mitigated. Signed-off-by: Dinesh Dutt Reviewed-by: Ayan Banerjee Reviewed-by: Scott Feldman Reviewed-by: Shrijeet Mukherjee Signed-off-by: Scott Feldman --- ospfd/ospf_lsa.c | 52 +++++++++++++++++++++++++++++++++++++++-------- ospfd/ospf_lsdb.c | 21 +++++++++++-------- ospfd/ospf_lsdb.h | 1 + ospfd/ospf_vty.c | 23 +++++++++++++-------- ospfd/ospfd.c | 16 +++++++++++---- ospfd/ospfd.h | 2 +- 6 files changed, 83 insertions(+), 32 deletions(-) diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index e778251c..66c7e1c0 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -2828,7 +2828,7 @@ ospf_maxage_lsa_remover (struct thread *thread) { struct ospf *ospf = THREAD_ARG (thread); struct ospf_lsa *lsa; - struct listnode *node, *nnode; + struct route_node *rn; int reschedule = 0; ospf->t_maxage = NULL; @@ -2839,8 +2839,13 @@ ospf_maxage_lsa_remover (struct thread *thread) reschedule = !ospf_check_nbr_status (ospf); if (!reschedule) - for (ALL_LIST_ELEMENTS (ospf->maxage_lsa, node, nnode, lsa)) + for (rn = route_top(ospf->maxage_lsa); rn; rn = route_next(rn)) { + if ((lsa = rn->info) == NULL) + { + continue; + } + if (lsa->retransmit_counter > 0) { reschedule = 1; @@ -2893,13 +2898,22 @@ ospf_maxage_lsa_remover (struct thread *thread) void ospf_lsa_maxage_delete (struct ospf *ospf, struct ospf_lsa *lsa) { - struct listnode *n; + struct route_node *rn; + struct prefix_ls lsa_prefix; - if ((n = listnode_lookup (ospf->maxage_lsa, lsa))) + ls_prefix_set (&lsa_prefix, lsa); + + if ((rn = route_node_lookup(ospf->maxage_lsa, + (struct prefix *)&lsa_prefix))) { - list_delete_node (ospf->maxage_lsa, n); - UNSET_FLAG(lsa->flags, OSPF_LSA_IN_MAXAGE); - ospf_lsa_unlock (&lsa); /* maxage_lsa */ + if (rn->info == lsa) + { + UNSET_FLAG(lsa->flags, OSPF_LSA_IN_MAXAGE); + ospf_lsa_unlock (&lsa); /* maxage_lsa */ + rn->info = NULL; + route_unlock_node (rn); /* route_node_lookup */ + } + route_unlock_node (rn); /* route_node_lookup */ } } @@ -2911,6 +2925,9 @@ ospf_lsa_maxage_delete (struct ospf *ospf, struct ospf_lsa *lsa) void ospf_lsa_maxage (struct ospf *ospf, struct ospf_lsa *lsa) { + struct prefix_ls lsa_prefix; + struct route_node *rn; + /* When we saw a MaxAge LSA flooded to us, we put it on the list and schedule the MaxAge LSA remover. */ if (CHECK_FLAG(lsa->flags, OSPF_LSA_IN_MAXAGE)) @@ -2921,8 +2938,25 @@ ospf_lsa_maxage (struct ospf *ospf, struct ospf_lsa *lsa) return; } - listnode_add (ospf->maxage_lsa, ospf_lsa_lock (lsa)); - SET_FLAG(lsa->flags, OSPF_LSA_IN_MAXAGE); + ls_prefix_set (&lsa_prefix, lsa); + if ((rn = route_node_get (ospf->maxage_lsa, + (struct prefix *)&lsa_prefix)) != NULL) + { + if (rn->info != NULL) + { + route_unlock_node (rn); + } + else + { + rn->info = ospf_lsa_lock(lsa); + SET_FLAG(lsa->flags, OSPF_LSA_IN_MAXAGE); + } + } + else + { + zlog_err("Unable to allocate memory for maxage lsa\n"); + assert(0); + } if (IS_DEBUG_OSPF (lsa, LSA_FLOODING)) zlog_debug ("LSA[%s]: MaxAge LSA remover scheduled.", dump_lsa_key (lsa)); diff --git a/ospfd/ospf_lsdb.c b/ospfd/ospf_lsdb.c index ea9a3528..aad979a7 100644 --- a/ospfd/ospf_lsdb.c +++ b/ospfd/ospf_lsdb.c @@ -72,13 +72,16 @@ ospf_lsdb_cleanup (struct ospf_lsdb *lsdb) route_table_finish (lsdb->type[i].db); } -static void -lsdb_prefix_set (struct prefix_ls *lp, struct ospf_lsa *lsa) +void +ls_prefix_set (struct prefix_ls *lp, struct ospf_lsa *lsa) { - lp->family = 0; - lp->prefixlen = 64; - lp->id = lsa->data->id; - lp->adv_router = lsa->data->adv_router; + if (lp && lsa && lsa->data) + { + lp->family = 0; + lp->prefixlen = 64; + lp->id = lsa->data->id; + lp->adv_router = lsa->data->adv_router; + } } static void @@ -115,7 +118,7 @@ ospf_lsdb_add (struct ospf_lsdb *lsdb, struct ospf_lsa *lsa) struct route_node *rn; table = lsdb->type[lsa->data->type].db; - lsdb_prefix_set (&lp, lsa); + ls_prefix_set (&lp, lsa); rn = route_node_get (table, (struct prefix *)&lp); /* nothing to do? */ @@ -167,7 +170,7 @@ ospf_lsdb_delete (struct ospf_lsdb *lsdb, struct ospf_lsa *lsa) assert (lsa->data->type < OSPF_MAX_LSA); table = lsdb->type[lsa->data->type].db; - lsdb_prefix_set (&lp, lsa); + ls_prefix_set (&lp, lsa); if ((rn = route_node_lookup (table, (struct prefix *) &lp))) { if (rn->info == lsa) @@ -218,7 +221,7 @@ ospf_lsdb_lookup (struct ospf_lsdb *lsdb, struct ospf_lsa *lsa) struct ospf_lsa *find; table = lsdb->type[lsa->data->type].db; - lsdb_prefix_set (&lp, lsa); + ls_prefix_set (&lp, lsa); rn = route_node_lookup (table, (struct prefix *) &lp); if (rn) { diff --git a/ospfd/ospf_lsdb.h b/ospfd/ospf_lsdb.h index 4157b685..51ae45bf 100644 --- a/ospfd/ospf_lsdb.h +++ b/ospfd/ospf_lsdb.h @@ -66,6 +66,7 @@ extern struct ospf_lsdb *ospf_lsdb_new (void); extern void ospf_lsdb_init (struct ospf_lsdb *); extern void ospf_lsdb_free (struct ospf_lsdb *); extern void ospf_lsdb_cleanup (struct ospf_lsdb *); +extern void ls_prefix_set (struct prefix_ls *lp, struct ospf_lsa *lsa); extern void ospf_lsdb_add (struct ospf_lsdb *, struct ospf_lsa *); extern void ospf_lsdb_delete (struct ospf_lsdb *, struct ospf_lsa *); extern void ospf_lsdb_delete_all (struct ospf_lsdb *); diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index a8807141..3655cfe1 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -4035,21 +4035,26 @@ show_ip_ospf_database_summary (struct vty *vty, struct ospf *ospf, int self) static void show_ip_ospf_database_maxage (struct vty *vty, struct ospf *ospf) { - struct listnode *node; + struct route_node *rn; struct ospf_lsa *lsa; vty_out (vty, "%s MaxAge Link States:%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); - for (ALL_LIST_ELEMENTS_RO (ospf->maxage_lsa, node, lsa)) + for (rn = route_top (ospf->maxage_lsa); rn; rn = route_next (rn)) { - vty_out (vty, "Link type: %d%s", lsa->data->type, VTY_NEWLINE); - vty_out (vty, "Link State ID: %s%s", - inet_ntoa (lsa->data->id), VTY_NEWLINE); - vty_out (vty, "Advertising Router: %s%s", - inet_ntoa (lsa->data->adv_router), VTY_NEWLINE); - vty_out (vty, "LSA lock count: %d%s", lsa->lock, VTY_NEWLINE); - vty_out (vty, "%s", VTY_NEWLINE); + struct ospf_lsa *lsa; + + if ((lsa = rn->info) != NULL) + { + vty_out (vty, "Link type: %d%s", lsa->data->type, VTY_NEWLINE); + vty_out (vty, "Link State ID: %s%s", + inet_ntoa (lsa->data->id), VTY_NEWLINE); + vty_out (vty, "Advertising Router: %s%s", + inet_ntoa (lsa->data->adv_router), VTY_NEWLINE); + vty_out (vty, "LSA lock count: %d%s", lsa->lock, VTY_NEWLINE); + vty_out (vty, "%s", VTY_NEWLINE); + } } } diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index 11a2dc5c..d1de29d5 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -201,7 +201,7 @@ ospf_new (void) /* MaxAge init. */ new->maxage_delay = OSFP_LSA_MAXAGE_REMOVE_DELAY_DEFAULT; - new->maxage_lsa = list_new (); + new->maxage_lsa = route_table_init(); new->t_maxage_walker = thread_add_timer (master, ospf_lsa_maxage_walker, new, OSPF_LSA_MAXAGE_CHECK_INTERVAL); @@ -502,10 +502,18 @@ ospf_finish_final (struct ospf *ospf) ospf_lsdb_delete_all (ospf->lsdb); ospf_lsdb_free (ospf->lsdb); - for (ALL_LIST_ELEMENTS (ospf->maxage_lsa, node, nnode, lsa)) - ospf_lsa_unlock (&lsa); /* maxage_lsa */ + for (rn = route_top (ospf->maxage_lsa); rn; rn = route_next (rn)) + { + struct ospf_lsa *lsa; - list_delete (ospf->maxage_lsa); + if ((lsa = rn->info) != NULL) + { + ospf_lsa_unlock (&lsa); + rn->info = NULL; + } + route_unlock_node (rn); + } + route_table_finish (ospf->maxage_lsa); if (ospf->old_table) ospf_route_table_free (ospf->old_table); diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h index dfaef1d4..cc27f66e 100644 --- a/ospfd/ospfd.h +++ b/ospfd/ospfd.h @@ -248,7 +248,7 @@ struct ospf /* Time stamps. */ struct timeval ts_spf; /* SPF calculation time stamp. */ - struct list *maxage_lsa; /* List of MaxAge LSA for deletion. */ + struct route_table *maxage_lsa; /* List of MaxAge LSA for deletion. */ int redistribute; /* Num of redistributed protocols. */ /* Threads. */ From faf9875832539f00940904e1424f2e789fef52d4 Mon Sep 17 00:00:00 2001 From: Ayan Banerjee Date: Tue, 4 Dec 2012 10:49:12 -0800 Subject: [PATCH 030/482] ospf: Reduce MaxAge log level Reduce the log level for the MaxAge LSA reception when such an LSA does not exist in the database. Signed-off-by: Ayan Banerjee Reviewed-by: Scott Feldman Reviewed-by: Nolan Leake Signed-off-by: Scott Feldman --- ospfd/ospf_packet.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index ede59088..d79df535 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -1834,8 +1834,11 @@ ospf_ls_upd (struct ip *iph, struct ospf_header *ospfh, ospf_ls_ack_send (nbr, lsa); /* Discard LSA. */ - zlog_info ("Link State Update[%s]: LS age is equal to MaxAge.", - dump_lsa_key(lsa)); + if (IS_DEBUG_OSPF (lsa, LSA)) + { + zlog_debug ("Link State Update[%s]: LS age is equal to MaxAge.", + dump_lsa_key(lsa)); + } DISCARD_LSA (lsa, 3); } From 2345a2221aa0a02b96846beab6088815f41e791b Mon Sep 17 00:00:00 2001 From: Leonard Tracy Date: Tue, 4 Dec 2012 11:02:35 -0800 Subject: [PATCH 031/482] ospf: Fix type-4 network mask to 0 per RFC The OSPF RFC (2328) states that the network mask field of a type 4 LSA "is not meaningful and must be zero". OSPFD has been setting the mask as /32. This patch changes OSPFD to set the mask to 0 per the RFC Signed-off-by: Scott Feldman --- ospfd/ospf_lsa.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index 66c7e1c0..fb55f7ff 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -1340,12 +1340,8 @@ static void ospf_summary_asbr_lsa_body_set (struct stream *s, struct prefix *p, u_int32_t metric) { - struct in_addr mask; - - masklen2ip (p->prefixlen, &mask); - /* Put Network Mask. */ - stream_put_ipv4 (s, mask.s_addr); + stream_put_ipv4 (s, (u_int32_t) 0); /* Set # TOS. */ stream_putc (s, (u_char) 0); From 822d8f55199c0aee32c64f91532f637694d30e11 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 4 Dec 2012 11:11:41 -0800 Subject: [PATCH 032/482] ospf: fix apiserver enable The ospf_apiserver_enable flag was being cleared _after_ the "-a" command-line option set it to 1. Move up the initialisation, so enabling the OSPF API is actually possible. Reported-by: Rosario Mattera Signed-off-by: David Lamparter Signed-off-by: Scott Feldman --- ospfd/ospf_main.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ospfd/ospf_main.c b/ospfd/ospf_main.c index 1448c7d8..6d58b4ea 100644 --- a/ospfd/ospf_main.c +++ b/ospfd/ospf_main.c @@ -191,6 +191,11 @@ main (int argc, char **argv) /* Set umask before anything for security */ umask (0027); +#ifdef SUPPORT_OSPF_API + /* OSPF apiserver is disabled by default. */ + ospf_apiserver_enable = 0; +#endif /* SUPPORT_OSPF_API */ + /* get program name */ progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]); @@ -275,11 +280,6 @@ main (int argc, char **argv) /* OSPF master init. */ ospf_master_init (); -#ifdef SUPPORT_OSPF_API - /* OSPF apiserver is disabled by default. */ - ospf_apiserver_enable = 0; -#endif /* SUPPORT_OSPF_API */ - /* Initializations. */ master = om->master; From bda3c326a3fe843417726bfe1bc8d4bafbb51fa9 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Tue, 4 Dec 2012 11:31:16 -0800 Subject: [PATCH 033/482] ospfd: fix argument parsing for distribute-list Use the correct argument for the protocol lookup in ospf distribute-list commands. Signed-off-by: Christian Franke Signed-off-by: Scott Feldman --- ospfd/ospf_vty.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 3655cfe1..862ef95a 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -6042,7 +6042,7 @@ DEFUN (ospf_distribute_list_out, int source; /* Get distribute source. */ - source = proto_redistnum(AFI_IP, argv[0]); + source = proto_redistnum(AFI_IP, argv[1]); if (source < 0 || source == ZEBRA_ROUTE_OSPF) return CMD_WARNING; @@ -6061,7 +6061,7 @@ DEFUN (no_ospf_distribute_list_out, struct ospf *ospf = vty->index; int source; - source = proto_redistnum(AFI_IP, argv[0]); + source = proto_redistnum(AFI_IP, argv[1]); if (source < 0 || source == ZEBRA_ROUTE_OSPF) return CMD_WARNING; From 1a61ad1011c498c4ae66cc145db673e53ef02962 Mon Sep 17 00:00:00 2001 From: Andrew Certain Date: Tue, 4 Dec 2012 12:50:23 -0800 Subject: [PATCH 034/482] ospfd: Fixed typo bug in ospf_vty.h:ospf_neighbor Typo bug. ospf_nbr_nbma_poll_interval_set() was being sent priority instead of interval. Signed-off-by: Scott Feldman --- ospfd/ospf_vty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 862ef95a..45ddc5c4 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -2322,7 +2322,7 @@ DEFUN (ospf_neighbor, if (argc > 1) ospf_nbr_nbma_priority_set (ospf, nbr_addr, priority); if (argc > 2) - ospf_nbr_nbma_poll_interval_set (ospf, nbr_addr, priority); + ospf_nbr_nbma_poll_interval_set (ospf, nbr_addr, interval); return CMD_SUCCESS; } From fbc043a847149499436fe4083b6384bde43fe578 Mon Sep 17 00:00:00 2001 From: Andrew Certain Date: Tue, 4 Dec 2012 12:54:18 -0800 Subject: [PATCH 035/482] ospfd: Fixed signed/unsigned masking of negative metrics In the original code, negative metrics would be converted successfully by atoi() and then converted to an unsigned int that would always compare successfully against >= 0, leaving a large positive metric in the route map. Signed-off-by: Scott Feldman --- ospfd/ospf_routemap.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ospfd/ospf_routemap.c b/ospfd/ospf_routemap.c index adf81583..d0ebce66 100644 --- a/ospfd/ospf_routemap.c +++ b/ospfd/ospf_routemap.c @@ -443,12 +443,16 @@ static void * route_set_metric_compile (const char *arg) { u_int32_t *metric; + int32_t ret; metric = XCALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_int32_t)); - *metric = atoi (arg); + ret = atoi (arg); - if (*metric >= 0) - return metric; + if (ret >= 0) + { + *metric = (u_int32_t)ret; + return metric; + } XFREE (MTYPE_ROUTE_MAP_COMPILED, metric); return NULL; From f92c57f8ba4f1e856934ec1736be3cad62be4785 Mon Sep 17 00:00:00 2001 From: Andrew Certain Date: Tue, 4 Dec 2012 13:29:21 -0800 Subject: [PATCH 036/482] ospfd: Update comments to be more clear in packet processing Signed-off-by: Scott Feldman --- ospfd/ospf_packet.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index d79df535..a51db246 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -1761,7 +1761,11 @@ ospf_ls_upd (struct ip *iph, struct ospf_header *ospfh, ospf_lsa_discard (L); \ continue; } - /* Process each LSA received in the one packet. */ + /* Process each LSA received in the one packet. + * + * Numbers in parentheses, e.g. (1), (2), etc., and the corresponding + * text below are from the stepsin RFC 2328, Section 13. + */ for (ALL_LIST_ELEMENTS (lsas, node, nnode, lsa)) { struct ospf_lsa *ls_ret, *current; @@ -1785,11 +1789,11 @@ ospf_ls_upd (struct ip *iph, struct ospf_header *ospfh, listnode_delete (lsas, lsa); /* We don't need it in list anymore */ - /* Validate Checksum - Done above by ospf_ls_upd_list_lsa() */ + /* (1) Validate Checksum - Done above by ospf_ls_upd_list_lsa() */ - /* LSA Type - Done above by ospf_ls_upd_list_lsa() */ + /* (2) LSA Type - Done above by ospf_ls_upd_list_lsa() */ - /* Do not take in AS External LSAs if we are a stub or NSSA. */ + /* (3) Do not take in AS External LSAs if we are a stub or NSSA. */ /* Do not take in AS NSSA if this neighbor and we are not NSSA */ @@ -1821,19 +1825,19 @@ ospf_ls_upd (struct ip *iph, struct ospf_header *ospfh, current = ospf_lsa_lookup_by_header (oi->area, lsa->data); - /* If the LSA's LS age is equal to MaxAge, and there is currently + /* (4) If the LSA's LS age is equal to MaxAge, and there is currently no instance of the LSA in the router's link state database, and none of router's neighbors are in states Exchange or Loading, - then take the following actions. */ + then take the following actions: */ if (IS_LSA_MAXAGE (lsa) && !current && (ospf_nbr_count (oi, NSM_Exchange) + ospf_nbr_count (oi, NSM_Loading)) == 0) { - /* Response Link State Acknowledgment. */ + /* (4a) Response Link State Acknowledgment. */ ospf_ls_ack_send (nbr, lsa); - /* Discard LSA. */ + /* (4b) Discard LSA. */ if (IS_DEBUG_OSPF (lsa, LSA)) { zlog_debug ("Link State Update[%s]: LS age is equal to MaxAge.", @@ -1893,7 +1897,7 @@ ospf_ls_upd (struct ip *iph, struct ospf_header *ospfh, #endif /* HAVE_OPAQUE_LSA */ /* It might be happen that received LSA is self-originated network LSA, but - * router ID is cahnged. So, we should check if LSA is a network-LSA whose + * router ID is changed. So, we should check if LSA is a network-LSA whose * Link State ID is one of the router's own IP interface addresses but whose * Advertising Router is not equal to the router's own Router ID * According to RFC 2328 12.4.2 and 13.4 this LSA should be flushed. @@ -1932,7 +1936,9 @@ ospf_ls_upd (struct ip *iph, struct ospf_header *ospfh, /* (5) Find the instance of this LSA that is currently contained in the router's link state database. If there is no database copy, or the received LSA is more recent than - the database copy the following steps must be performed. */ + the database copy the following steps must be performed. + (The sub steps from RFC 2328 section 13 step (5) will be performed in + ospf_flood() ) */ if (current == NULL || (ret = ospf_lsa_more_recent (current, lsa)) < 0) From 7798b6321cd0c6281b1a119ac894ff3749e88cc1 Mon Sep 17 00:00:00 2001 From: Andrew Certain Date: Tue, 4 Dec 2012 13:33:24 -0800 Subject: [PATCH 037/482] lib: Changes to VTY string-parsing macros to remove warnings The VTY_GET_INTEGER_RANGE macro was being used also just to check the range on a variable that wasn't used (for the "no" version of a VTY command), so I split the macro into two. Also, since the variable is unsigned, if MIN is zero, you get a warning about comparing an unsigned number against 0, giving rise to slightly convoluted logic. Note that the previous two patches were found by the -Wtype-limits and -Wunused-variables warnings. Without the changes to these macros, these warnings are triggered erroneously, making it harder to find the real problems. Signed-off-by: Scott Feldman --- lib/vty.h | 46 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/lib/vty.h b/lib/vty.h index e5158687..1798585e 100644 --- a/lib/vty.h +++ b/lib/vty.h @@ -149,8 +149,8 @@ struct vty #define PRINTF_ATTRIBUTE(a,b) #endif /* __GNUC__ */ -/* Utility macros to convert VTY argument to unsigned long or integer. */ -#define VTY_GET_LONG(NAME,V,STR) \ +/* Utility macros to convert VTY argument to unsigned long */ +#define VTY_GET_ULONG(NAME,V,STR) \ do { \ char *endptr = NULL; \ errno = 0; \ @@ -162,20 +162,38 @@ do { \ } \ } while (0) -#define VTY_GET_INTEGER_RANGE(NAME,V,STR,MIN,MAX) \ -do { \ - unsigned long tmpl; \ - VTY_GET_LONG(NAME, tmpl, STR); \ - if ( (tmpl < (MIN)) || (tmpl > (MAX))) \ - { \ - vty_out (vty, "%% Invalid %s value%s", NAME, VTY_NEWLINE); \ - return CMD_WARNING; \ - } \ - (V) = tmpl; \ +/* + * The logic below ((TMPL) <= ((MIN) && (TMPL) != (MIN)) is + * done to circumvent the compiler complaining about + * comparing unsigned numbers against zero, if MIN is zero. + * NB: The compiler isn't smart enough to supress the warning + * if you write (MIN) != 0 && tmpl < (MIN). + */ +#define VTY_GET_INTEGER_RANGE_HEART(NAME,TMPL,STR,MIN,MAX) \ +do { \ + VTY_GET_ULONG(NAME, (TMPL), STR); \ + if ( ((TMPL) <= (MIN) && (TMPL) != (MIN)) || (TMPL) > (MAX) ) \ + { \ + vty_out (vty, "%% Invalid %s value%s", NAME, VTY_NEWLINE);\ + return CMD_WARNING; \ + } \ +} while (0) + +#define VTY_GET_INTEGER_RANGE(NAME,V,STR,MIN,MAX) \ +do { \ + unsigned long tmpl; \ + VTY_GET_INTEGER_RANGE_HEART(NAME,tmpl,STR,MIN,MAX); \ + (V) = tmpl; \ +} while (0) + +#define VTY_CHECK_INTEGER_RANGE(NAME,STR,MIN,MAX) \ +do { \ + unsigned long tmpl; \ + VTY_GET_INTEGER_RANGE_HEART(NAME,tmpl,STR,MIN,MAX); \ } while (0) -#define VTY_GET_INTEGER(NAME,V,STR) \ - VTY_GET_INTEGER_RANGE(NAME,V,STR,0U,UINT32_MAX) +#define VTY_GET_INTEGER(NAME,V,STR) \ + VTY_GET_INTEGER_RANGE(NAME,V,STR,0U,UINT32_MAX) #define VTY_GET_IPV4_ADDRESS(NAME,V,STR) \ do { \ From 703819a9314692f3a3b7f888887b0cebc15c18ae Mon Sep 17 00:00:00 2001 From: Andrew Certain Date: Tue, 4 Dec 2012 13:36:41 -0800 Subject: [PATCH 038/482] ospfd: Changed TE instance check to remove -Wtype-limits warning Since LEGAL_TE_INSTANCE_RANGE() was being passed an unsigned int, a warning was being thrown due to the compare against >= 0. Since this macro was used only in one place, I removed the macro for an explict compare against a constant for the MAX. Signed-off-by: Scott Feldman --- ospfd/ospf_te.c | 2 +- ospfd/ospf_te.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ospfd/ospf_te.c b/ospfd/ospf_te.c index 24e81052..587564a1 100644 --- a/ospfd/ospf_te.c +++ b/ospfd/ospf_te.c @@ -205,7 +205,7 @@ get_mpls_te_instance_value (void) { static u_int32_t seqno = 0; - if (LEGAL_TE_INSTANCE_RANGE (seqno + 1)) + if (seqno < MAX_LEGAL_TE_INSTANCE_NUM ) seqno += 1; else seqno = 1; /* Avoid zero. */ diff --git a/ospfd/ospf_te.h b/ospfd/ospf_te.h index e8511cdf..863d8ba8 100644 --- a/ospfd/ospf_te.h +++ b/ospfd/ospf_te.h @@ -41,7 +41,7 @@ * */ -#define LEGAL_TE_INSTANCE_RANGE(i) (0 <= (i) && (i) <= 0xffff) +#define MAX_LEGAL_TE_INSTANCE_NUM (0xffff) /* * 24 16 8 0 From de54b26caca7442af29656282e753b02aac6f093 Mon Sep 17 00:00:00 2001 From: Andrew Certain Date: Tue, 4 Dec 2012 13:40:58 -0800 Subject: [PATCH 039/482] ospfd: Update nsm_change_state to static scope, as it is not called from elsewhere Signed-off-by: Scott Feldman --- ospfd/ospf_nsm.c | 2 +- ospfd/ospf_nsm.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/ospfd/ospf_nsm.c b/ospfd/ospf_nsm.c index cbc31716..436896c2 100644 --- a/ospfd/ospf_nsm.c +++ b/ospfd/ospf_nsm.c @@ -643,7 +643,7 @@ nsm_notice_state_change (struct ospf_neighbor *nbr, int next_state, int event) #endif } -void +static void nsm_change_state (struct ospf_neighbor *nbr, int state) { struct ospf_interface *oi = nbr->oi; diff --git a/ospfd/ospf_nsm.h b/ospfd/ospf_nsm.h index 4f2ae808..9b7e14a4 100644 --- a/ospfd/ospf_nsm.h +++ b/ospfd/ospf_nsm.h @@ -81,7 +81,6 @@ /* Prototypes. */ extern int ospf_nsm_event (struct thread *); -extern void nsm_change_state (struct ospf_neighbor *, int); extern void ospf_check_nbr_loading (struct ospf_neighbor *); extern int ospf_db_summary_isempty (struct ospf_neighbor *); extern int ospf_db_summary_count (struct ospf_neighbor *); From 0798cee34f5c436cd2a4b6e1d9a1ca90eee94292 Mon Sep 17 00:00:00 2001 From: Andrew Certain Date: Tue, 4 Dec 2012 13:43:42 -0800 Subject: [PATCH 040/482] ospfd: compile warning cleanups A set of patches to clarify some comments as well as cleanup code that was causing warnings. After these patches, the code can be compiled with -Wall -Wsign-compare -Wpointer-arith -Wbad-function-cast -Wwrite-strings -Wmissing-prototypes -Wmissing-declarations -Wchar-subscripts -Wcast-qual -Wextra -Wno-unused-parameter -Wno-missing-field-initializers (what is current in trunk plus -Wextra -Wno-unused-parameter -Wno-missing-field-initializers). Signed-off-by: Scott Feldman --- ospfd/ospf_apiserver.c | 5 +---- ospfd/ospf_ism.c | 5 ++--- ospfd/ospf_lsa.c | 4 +++- ospfd/ospf_lsa.h | 1 + ospfd/ospf_network.c | 8 ++++---- ospfd/ospf_network.h | 2 +- ospfd/ospf_nsm.c | 5 ----- ospfd/ospf_opaque.c | 12 +++++++++--- ospfd/ospf_packet.c | 2 +- ospfd/ospf_vty.c | 10 +++------- ospfd/ospf_zebra.c | 9 +++------ ospfd/ospf_zebra.h | 2 +- ospfd/ospfd.c | 2 +- ospfd/ospfd.h | 2 +- 14 files changed, 31 insertions(+), 38 deletions(-) diff --git a/ospfd/ospf_apiserver.c b/ospfd/ospf_apiserver.c index 84d70ccc..db1ccda7 100644 --- a/ospfd/ospf_apiserver.c +++ b/ospfd/ospf_apiserver.c @@ -299,13 +299,10 @@ void ospf_apiserver_event (enum event event, int fd, struct ospf_apiserver *apiserv) { - struct thread *apiserver_serv_thread; - switch (event) { case OSPF_APISERVER_ACCEPT: - apiserver_serv_thread = - thread_add_read (master, ospf_apiserver_accept, apiserv, fd); + (void)thread_add_read (master, ospf_apiserver_accept, apiserv, fd); break; case OSPF_APISERVER_SYNC_READ: apiserv->t_sync_read = diff --git a/ospfd/ospf_ism.c b/ospfd/ospf_ism.c index db53882d..fa7d97f2 100644 --- a/ospfd/ospf_ism.c +++ b/ospfd/ospf_ism.c @@ -203,7 +203,6 @@ ospf_dr_election (struct ospf_interface *oi) struct in_addr old_dr, old_bdr; int old_state, new_state; struct list *el_list; - struct ospf_neighbor *dr, *bdr; /* backup current values. */ old_dr = DR (oi); @@ -216,8 +215,8 @@ ospf_dr_election (struct ospf_interface *oi) ospf_dr_eligible_routers (oi->nbrs, el_list); /* First election of DR and BDR. */ - bdr = ospf_elect_bdr (oi, el_list); - dr = ospf_elect_dr (oi, el_list); + ospf_elect_bdr (oi, el_list); + ospf_elect_dr (oi, el_list); new_state = ospf_ism_state (oi); diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index fb55f7ff..4f7fb00a 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -2733,7 +2733,9 @@ ospf_lsa_install (struct ospf *ospf, struct ospf_interface *oi, if (IS_LSA_SELF (lsa)) lsa->oi = oi; /* Specify outgoing ospf-interface for this LSA. */ else - ; /* Incoming "oi" for this LSA has set at LSUpd reception. */ + { + /* Incoming "oi" for this LSA has set at LSUpd reception. */ + } /* Fallthrough */ case OSPF_OPAQUE_AREA_LSA: case OSPF_OPAQUE_AS_LSA: diff --git a/ospfd/ospf_lsa.h b/ospfd/ospf_lsa.h index 6c95ff17..9ff2d920 100644 --- a/ospfd/ospf_lsa.h +++ b/ospfd/ospf_lsa.h @@ -243,6 +243,7 @@ extern int tv_cmp (struct timeval, struct timeval); extern int get_age (struct ospf_lsa *); extern u_int16_t ospf_lsa_checksum (struct lsa_header *); +extern int ospf_lsa_checksum_valid (struct lsa_header *); extern int ospf_lsa_refresh_delay (struct ospf_lsa *); extern const char *dump_lsa_key (struct ospf_lsa *); diff --git a/ospfd/ospf_network.c b/ospfd/ospf_network.c index 3e326a8c..900a5667 100644 --- a/ospfd/ospf_network.c +++ b/ospfd/ospf_network.c @@ -228,7 +228,7 @@ ospf_sock_init (void) } void -ospf_adjust_sndbuflen (struct ospf * ospf, int buflen) +ospf_adjust_sndbuflen (struct ospf * ospf, unsigned int buflen) { int ret, newbuflen; /* Check if any work has to be done at all. */ @@ -249,11 +249,11 @@ ospf_adjust_sndbuflen (struct ospf * ospf, int buflen) */ ret = setsockopt_so_sendbuf (ospf->fd, buflen); newbuflen = getsockopt_so_sendbuf (ospf->fd); - if (ret < 0 || newbuflen < buflen) - zlog_warn ("%s: tried to set SO_SNDBUF to %d, but got %d", + if (ret < 0 || newbuflen < 0 || newbuflen < (int) buflen) + zlog_warn ("%s: tried to set SO_SNDBUF to %u, but got %d", __func__, buflen, newbuflen); if (newbuflen >= 0) - ospf->maxsndbuflen = newbuflen; + ospf->maxsndbuflen = (unsigned int)newbuflen; else zlog_warn ("%s: failed to get SO_SNDBUF", __func__); if (ospfd_privs.change (ZPRIVS_LOWER)) diff --git a/ospfd/ospf_network.h b/ospfd/ospf_network.h index f6909912..e0a5c69d 100644 --- a/ospfd/ospf_network.h +++ b/ospfd/ospf_network.h @@ -34,6 +34,6 @@ extern int ospf_if_drop_alldrouters (struct ospf *, struct prefix *, unsigned int); extern int ospf_if_ipmulticast (struct ospf *, struct prefix *, unsigned int); extern int ospf_sock_init (void); -extern void ospf_adjust_sndbuflen (struct ospf *, int); +extern void ospf_adjust_sndbuflen (struct ospf *, unsigned int); #endif /* _ZEBRA_OSPF_NETWORK_H */ diff --git a/ospfd/ospf_nsm.c b/ospfd/ospf_nsm.c index 436896c2..fe4ddf5b 100644 --- a/ospfd/ospf_nsm.c +++ b/ospfd/ospf_nsm.c @@ -72,14 +72,11 @@ ospf_inactivity_timer (struct thread *thread) static int ospf_db_desc_timer (struct thread *thread) { - struct ospf_interface *oi; struct ospf_neighbor *nbr; nbr = THREAD_ARG (thread); nbr->t_db_desc = NULL; - oi = nbr->oi; - if (IS_DEBUG_OSPF (nsm, NSM_TIMERS)) zlog (NULL, LOG_DEBUG, "NSM[%s:%s]: Timer (DD Retransmit timer expire)", IF_NAME (nbr->oi), inet_ntoa (nbr->src)); @@ -787,11 +784,9 @@ ospf_nsm_event (struct thread *thread) int event; int next_state; struct ospf_neighbor *nbr; - struct in_addr router_id; nbr = THREAD_ARG (thread); event = THREAD_VAL (thread); - router_id = nbr->router_id; if (IS_DEBUG_OSPF (nsm, NSM_EVENTS)) zlog_debug ("NSM[%s:%s]: %s (%s)", IF_NAME (nbr->oi), diff --git a/ospfd/ospf_opaque.c b/ospfd/ospf_opaque.c index aa126e19..744952c9 100644 --- a/ospfd/ospf_opaque.c +++ b/ospfd/ospf_opaque.c @@ -223,9 +223,15 @@ ospf_opaque_type_name (u_char opaque_type) default: if (OPAQUE_TYPE_RANGE_UNASSIGNED (opaque_type)) name = "Unassigned"; - /* XXX warning: comparison is always true due to limited range of data type */ - else if (OPAQUE_TYPE_RANGE_RESERVED (opaque_type)) - name = "Private/Experimental"; + else + { + u_int32_t bigger_range = opaque_type; + /* + * Get around type-limits warning: comparison is always true due to limited range of data type + */ + if (OPAQUE_TYPE_RANGE_RESERVED (bigger_range)) + name = "Private/Experimental"; + } break; } return name; diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index a51db246..9a4587d9 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -1764,7 +1764,7 @@ ospf_ls_upd (struct ip *iph, struct ospf_header *ospfh, /* Process each LSA received in the one packet. * * Numbers in parentheses, e.g. (1), (2), etc., and the corresponding - * text below are from the stepsin RFC 2328, Section 13. + * text below are from the steps in RFC 2328, Section 13. */ for (ALL_LIST_ELEMENTS (lsas, node, nnode, lsa)) { diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 45ddc5c4..2ba8188c 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -1742,12 +1742,11 @@ DEFUN (no_ospf_area_default_cost, struct ospf *ospf = vty->index; struct ospf_area *area; struct in_addr area_id; - u_int32_t cost; int format; struct prefix_ipv4 p; VTY_GET_OSPF_AREA_ID_NO_BB ("default-cost", area_id, format, argv[0]); - VTY_GET_INTEGER_RANGE ("stub default cost", cost, argv[1], 0, 16777215); + VTY_CHECK_INTEGER_RANGE ("stub default cost", argv[1], 0, OSPF_LS_INFINITY); area = ospf_area_lookup_by_area_id (ospf, area_id); if (area == NULL) @@ -1933,7 +1932,6 @@ DEFUN (no_ospf_area_filter_list, struct ospf *ospf = vty->index; struct ospf_area *area; struct in_addr area_id; - struct prefix_list *plist; int format; VTY_GET_OSPF_AREA_ID (area_id, format, argv[0]); @@ -1941,7 +1939,6 @@ DEFUN (no_ospf_area_filter_list, if ((area = ospf_area_lookup_by_area_id (ospf, area_id)) == NULL) return CMD_SUCCESS; - plist = prefix_list_lookup (AFI_IP, argv[1]); if (strncmp (argv[2], "in", 2) == 0) { if (PREFIX_NAME_IN (area)) @@ -2394,11 +2391,10 @@ DEFUN (no_ospf_neighbor, { struct ospf *ospf = vty->index; struct in_addr nbr_addr; - int ret; VTY_GET_IPV4_ADDRESS ("neighbor address", nbr_addr, argv[0]); - ret = ospf_nbr_nbma_unset (ospf, nbr_addr); + (void)ospf_nbr_nbma_unset (ospf, nbr_addr); return CMD_SUCCESS; } @@ -5408,7 +5404,7 @@ DEFUN (ip_ospf_priority, "Address of interface") { struct interface *ifp = vty->index; - u_int32_t priority; + long priority; struct route_node *rn; struct in_addr addr; int ret; diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index f5f49f64..34a3b2a7 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -133,8 +133,8 @@ ospf_interface_delete (int command, struct zclient *zclient, if (IS_DEBUG_OSPF (zebra, ZEBRA_INTERFACE)) zlog_debug - ("Zebra: interface delete %s index %d flags %lld metric %d mtu %d", - ifp->name, ifp->ifindex, ifp->flags, ifp->metric, ifp->mtu); + ("Zebra: interface delete %s index %d flags %llx metric %d mtu %d", + ifp->name, ifp->ifindex, (unsigned long long)ifp->flags, ifp->metric, ifp->mtu); #ifdef HAVE_SNMP ospf_snmp_if_delete (ifp); @@ -1004,7 +1004,7 @@ ospf_distribute_list_update_timer (struct thread *thread) /* Update distribute-list and set timer to apply access-list. */ void -ospf_distribute_list_update (struct ospf *ospf, int type) +ospf_distribute_list_update (struct ospf *ospf, uintptr_t type) { struct route_table *rt; @@ -1217,7 +1217,6 @@ ospf_distance_unset (struct vty *vty, struct ospf *ospf, { int ret; struct prefix_ipv4 p; - u_char distance; struct route_node *rn; struct ospf_distance *odistance; @@ -1228,8 +1227,6 @@ ospf_distance_unset (struct vty *vty, struct ospf *ospf, return CMD_WARNING; } - distance = atoi (distance_str); - rn = route_node_lookup (ospf->distance_table, (struct prefix *) &p); if (!rn) { diff --git a/ospfd/ospf_zebra.h b/ospfd/ospf_zebra.h index fbb34442..148f6520 100644 --- a/ospfd/ospf_zebra.h +++ b/ospfd/ospf_zebra.h @@ -54,7 +54,7 @@ extern int ospf_redistribute_check (struct ospf *, struct external_info *, int *); extern int ospf_distribute_check_connected (struct ospf *, struct external_info *); -extern void ospf_distribute_list_update (struct ospf *, int); +extern void ospf_distribute_list_update (struct ospf *, uintptr_t); extern int ospf_is_type_redistributed (int); extern void ospf_distance_reset (struct ospf *); diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index d1de29d5..3e2b2348 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -223,7 +223,7 @@ ospf_new (void) } new->maxsndbuflen = getsockopt_so_sendbuf (new->fd); if (IS_DEBUG_OSPF (zebra, ZEBRA_INTERFACE)) - zlog_debug ("%s: starting with OSPF send buffer size %d", + zlog_debug ("%s: starting with OSPF send buffer size %u", __func__, new->maxsndbuflen); if ((new->ibuf = stream_new(OSPF_MAX_PACKET_SIZE+1)) == NULL) { diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h index cc27f66e..fb57bf51 100644 --- a/ospfd/ospfd.h +++ b/ospfd/ospfd.h @@ -273,7 +273,7 @@ struct ospf struct thread *t_write; struct thread *t_read; int fd; - int maxsndbuflen; + unsigned int maxsndbuflen; struct stream *ibuf; struct list *oi_write_q; From d265548ffb8da9ba3e1dd327efbff31c7cd15027 Mon Sep 17 00:00:00 2001 From: Vishal Kumar Date: Fri, 7 Dec 2012 14:47:58 -0800 Subject: [PATCH 041/482] ospfd: Corrected ospfd Type-4/Type-5 ls update handling This fix is for Type-4 LS updates handling at a ABR router where ospf daemon is not distributing Type-4 LS updates with correct LS-Age after learning about a ASBR router in a ospf network. Because of this Type-5 LS updates are not learnt in ospf network. Testing Scenario: This can be re-produced by restarting the ospfd daemon on DUT (mentioned in figure below)before the Hello time interval expires for area 0.0.0.1. ____ _______ ____ _________ | | area: 0.0.0.1 | | area: 0.0.0.0 | | area: 0.0.0.2 | | | R1 |---------------------|DUT/ABR|---------------------| R2 |------------------| R3/ASBR | |____| x.x.x.0/24 |_______| y.y.y.0/64 |____| z.z.z.0/24 |_________| In the above setup when ospfd is restarted (imp:before the Hello interval at R1 expires) and DUT learns about ASBR router R3 (Type-4) in the network from R2, but this ls-update is not propagates in area 0.0.0.1. So R1 never comes to know about the ASBR router in the network, so all the type-5 LS updates coming from R3 are not learnt by R1. Further if we again restart ospfd daemon it starts working fine. With the fix given this issue can be resolved. More Discussion on this is available at: http://www.gossamer-threads.com/lists/quagga/dev/23892 Signed-off-by: Scott Feldman --- ospfd/ospf_abr.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ospfd/ospf_abr.c b/ospfd/ospf_abr.c index f5edc99e..4770275d 100644 --- a/ospfd/ospf_abr.c +++ b/ospfd/ospf_abr.c @@ -1140,7 +1140,8 @@ ospf_abr_announce_rtr_to_area (struct prefix_ipv4 *p, u_int32_t cost, GET_METRIC (slsa->metric), cost); } - if (old && (GET_METRIC (slsa->metric) == cost)) + if (old && (GET_METRIC (slsa->metric) == cost) && + ((old->flags & OSPF_LSA_IN_MAXAGE) == 0)) { if (IS_DEBUG_OSPF_EVENT) zlog_debug ("ospf_abr_announce_rtr_to_area(): old summary approved"); From e0630cb4d61557f956318a088f68f1fc4d261ef3 Mon Sep 17 00:00:00 2001 From: Dinesh G Dutt Date: Mon, 7 Jan 2013 10:12:52 -0800 Subject: [PATCH 042/482] ospfd: Remove dup MaxAge LSA flood Stop additional, unnecessary flooding of MaxAge LSAs. When a MaxAge LSA is installed, if the LSA is prematurely aged or the LSA is not self-originated, the LSA is flushed. This results in a the LSA being flooded a second time and in some cases flooded back to the receiver (unless the receiver is also the advertising router). A MaxAge'd LSA has already been flooded in ospf_flood() as part of the LSA receive processing (ospf_ls_upd). A self-originated LSA will be flooded from the originate/refresh routine. Thus, in the install routine, a MaxAge'd LSA only needs to be added to the MaxAge LSA list. Signed-off-by: Dinesh G Dutt Signed-off-by: Scott Feldman --- ospfd/ospf_lsa.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index 4f7fb00a..dfd1a61e 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -2780,15 +2780,14 @@ ospf_lsa_install (struct ospf *ospf, struct ospf_interface *oi, If received LSA' ls_age is MaxAge, or lsa is being prematurely aged (it's getting flushed out of the area), set LSA on MaxAge LSA list. */ - if ((lsa->flags & OSPF_LSA_PREMATURE_AGE) || - (IS_LSA_MAXAGE (new) && !IS_LSA_SELF (new))) + if (IS_LSA_MAXAGE (new)) { if (IS_DEBUG_OSPF (lsa, LSA_INSTALL)) zlog_debug ("LSA[Type%d:%s]: Install LSA 0x%p, MaxAge", new->data->type, inet_ntoa (new->data->id), lsa); - ospf_lsa_flush (ospf, lsa); + ospf_lsa_maxage (ospf, lsa); } return new; From 327c4cdf7578debcc140f04f03d02479771c9e11 Mon Sep 17 00:00:00 2001 From: Doug VanLeuven Date: Fri, 14 Dec 2012 14:58:30 +0200 Subject: [PATCH 043/482] testzebra: pragma weak: detect systems with weak alias and provide alternative LLVM clang does not support #pragma weak (bug 3679) on OS X. There are other systems where the #pragma weak has varying syntax. Added m4 file from the autoconf archives: http://www.gnu.org/software/autoconf-archive/ax_sys_weak_alias.html Fix up zebra/*_null.c files to use #pragma weak alias or stub functions if not available. It's incomplete in that the different format #pragma enable easier fixes on need. Tested on 64bit OS X 10.7, FreeBSD 9.0 amd64 & i386 (32bit) using gcc & clang. Tested on linux 64bit. Signed-off-by: David Lamparter --- .gitignore | 1 + configure.ac | 16 ++ m4/ax_sys_weak_alias.m4 | 337 ++++++++++++++++++++++++++++++++++++++ zebra/ioctl_null.c | 13 ++ zebra/kernel_null.c | 14 ++ zebra/misc_null.c | 6 + zebra/redistribute_null.c | 29 ++++ 7 files changed, 416 insertions(+) create mode 100644 m4/ax_sys_weak_alias.m4 diff --git a/.gitignore b/.gitignore index 516ed611..48e6b38b 100644 --- a/.gitignore +++ b/.gitignore @@ -36,4 +36,5 @@ build *~ *.loT m4/*.m4 +!m4/ax_sys_weak_alias.m4 diff --git a/configure.ac b/configure.ac index 405693c9..c51a0d3e 100755 --- a/configure.ac +++ b/configure.ac @@ -1540,6 +1540,22 @@ if test "${enable_capabilities}" != "no"; then fi AC_SUBST(LIBCAP) +dnl --------------------------------------------------------------------------- +dnl http://www.gnu.org/software/autoconf-archive/ax_sys_weak_alias.html +dnl Check for and set one of the following = 1 +dnl HAVE_SYS_WEAK_ALIAS_ATTRIBUTE +dnl HAVE_SYS_WEAK_ALIAS_PRAGMA +dnl HAVE_SYS_WEAK_ALIAS_HPSECONDARY +dnl HAVE_SYS_WEAK_ALIAS_CRIDUPLICATE +dnl If any scheme is found, set +dnl HAVE_SYS_WEAK_ALIAS=1 +dnl The following variable is set to text value +dnl WEAK_ALIAS = "attribute" || "pragma" || "hpsecondary" || "criduplicate" || "no" +dnl If weak alias can cross object file boundaries +dnl WEAK_ALIAS_CROSSFILE = "yes" || "no" +dnl --------------------------------------------------------------------------- +AX_SYS_WEAK_ALIAS + dnl --------------------------- dnl check for glibc 'backtrace' dnl --------------------------- diff --git a/m4/ax_sys_weak_alias.m4 b/m4/ax_sys_weak_alias.m4 new file mode 100644 index 00000000..7c632a0e --- /dev/null +++ b/m4/ax_sys_weak_alias.m4 @@ -0,0 +1,337 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_sys_weak_alias.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_SYS_WEAK_ALIAS +# +# DESCRIPTION +# +# Determines whether weak aliases are supported on the system, and if so, +# what scheme is used to declare them. Also checks to see if aliases can +# cross object file boundaries, as some systems don't permit them to. +# +# Most systems permit something called a "weak alias" or "weak symbol." +# These aliases permit a library to provide a stub form of a routine +# defined in another library, thus allowing the first library to operate +# even if the other library is not linked. This macro will check for +# support of weak aliases, figure out what schemes are available, and +# determine some characteristics of the weak alias support -- primarily, +# whether a weak alias declared in one object file may be referenced from +# another object file. +# +# There are four known schemes of declaring weak symbols; each scheme is +# checked in turn, and the first one found is prefered. Note that only one +# of the mentioned preprocessor macros will be defined! +# +# 1. Function attributes +# +# This scheme was first introduced by the GNU C compiler, and attaches +# attributes to particular functions. It is among the easiest to use, and +# so is the first one checked. If this scheme is detected, the +# preprocessor macro HAVE_SYS_WEAK_ALIAS_ATTRIBUTE will be defined to 1. +# This scheme is used as in the following code fragment: +# +# void __weakf(int c) +# { +# /* Function definition... */ +# } +# +# void weakf(int c) __attribute__((weak, alias("__weakf"))); +# +# 2. #pragma weak +# +# This scheme is in use by many compilers other than the GNU C compiler. +# It is also particularly easy to use, and fairly portable -- well, as +# portable as these things get. If this scheme is detected first, the +# preprocessor macro HAVE_SYS_WEAK_ALIAS_PRAGMA will be defined to 1. This +# scheme is used as in the following code fragment: +# +# extern void weakf(int c); +# #pragma weak weakf = __weakf +# void __weakf(int c) +# { +# /* Function definition... */ +# } +# +# 3. #pragma _HP_SECONDARY_DEF +# +# This scheme appears to be in use by the HP compiler. As it is rather +# specialized, this is one of the last schemes checked. If it is the first +# one detected, the preprocessor macro HAVE_SYS_WEAK_ALIAS_HPSECONDARY +# will be defined to 1. This scheme is used as in the following code +# fragment: +# +# extern void weakf(int c); +# #pragma _HP_SECONDARY_DEF __weakf weakf +# void __weakf(int c) +# { +# /* Function definition... */ +# } +# +# 4. #pragma _CRI duplicate +# +# This scheme appears to be in use by the Cray compiler. As it is rather +# specialized, it too is one of the last schemes checked. If it is the +# first one detected, the preprocessor macro +# HAVE_SYS_WEAK_ALIAS_CRIDUPLICATE will be defined to 1. This scheme is +# used as in the following code fragment: +# +# extern void weakf(int c); +# #pragma _CRI duplicate weakf as __weakf +# void __weakf(int c) +# { +# /* Function definition... */ +# } +# +# In addition to the preprocessor macros listed above, if any scheme is +# found, the preprocessor macro HAVE_SYS_WEAK_ALIAS will also be defined +# to 1. +# +# Once a weak aliasing scheme has been found, a check will be performed to +# see if weak aliases are honored across object file boundaries. If they +# are, the HAVE_SYS_WEAK_ALIAS_CROSSFILE preprocessor macro is defined to +# 1. +# +# This Autoconf macro also makes two substitutions. The first, WEAK_ALIAS, +# contains the name of the scheme found (one of "attribute", "pragma", +# "hpsecondary", or "criduplicate"), or "no" if no weak aliasing scheme +# was found. The second, WEAK_ALIAS_CROSSFILE, is set to "yes" or "no" +# depending on whether or not weak aliases may cross object file +# boundaries. +# +# LICENSE +# +# Copyright (c) 2008 Kevin L. Mitchell +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 6 + +AU_ALIAS([KLM_SYS_WEAK_ALIAS], [AX_SYS_WEAK_ALIAS]) +AC_DEFUN([AX_SYS_WEAK_ALIAS], [ + # starting point: no aliasing scheme yet... + ax_sys_weak_alias=no + + # Figure out what kind of aliasing may be supported... + _AX_SYS_WEAK_ALIAS_ATTRIBUTE + _AX_SYS_WEAK_ALIAS_PRAGMA + _AX_SYS_WEAK_ALIAS_HPSECONDARY + _AX_SYS_WEAK_ALIAS_CRIDUPLICATE + + # Do we actually support aliasing? + AC_CACHE_CHECK([how to create weak aliases with $CC], + [ax_cv_sys_weak_alias], + [ax_cv_sys_weak_alias=$ax_sys_weak_alias]) + + # OK, set a #define + AS_IF([test $ax_cv_sys_weak_alias != no], [ + AC_DEFINE([HAVE_SYS_WEAK_ALIAS], 1, + [Define this if your system can create weak aliases]) + ]) + + # Can aliases cross object file boundaries? + _AX_SYS_WEAK_ALIAS_CROSSFILE + + # OK, remember the results + AC_SUBST([WEAK_ALIAS], [$ax_cv_sys_weak_alias]) + AC_SUBST([WEAK_ALIAS_CROSSFILE], [$ax_cv_sys_weak_alias_crossfile]) +]) + +AC_DEFUN([_AX_SYS_WEAK_ALIAS_ATTRIBUTE], +[ # Test whether compiler accepts __attribute__ form of weak aliasing + AC_CACHE_CHECK([whether $CC accepts function __attribute__((weak,alias()))], + [ax_cv_sys_weak_alias_attribute], [ + # We add -Werror if it's gcc to force an error exit if the weak attribute + # isn't understood + AS_IF([test $GCC = yes], [ + save_CFLAGS=$CFLAGS + CFLAGS=-Werror]) + + # Try linking with a weak alias... + AC_LINK_IFELSE([ + AC_LANG_PROGRAM([ +void __weakf(int c) {} +void weakf(int c) __attribute__((weak, alias("__weakf")));], + [weakf(0)])], + [ax_cv_sys_weak_alias_attribute=yes], + [ax_cv_sys_weak_alias_attribute=no]) + + # Restore original CFLAGS + AS_IF([test $GCC = yes], [ + CFLAGS=$save_CFLAGS]) + ]) + + # What was the result of the test? + AS_IF([test $ax_sys_weak_alias = no && + test $ax_cv_sys_weak_alias_attribute = yes], [ + ax_sys_weak_alias=attribute + AC_DEFINE([HAVE_SYS_WEAK_ALIAS_ATTRIBUTE], 1, + [Define this if weak aliases may be created with __attribute__]) + ]) +]) + +AC_DEFUN([_AX_SYS_WEAK_ALIAS_PRAGMA], +[ # Test whether compiler accepts #pragma form of weak aliasing + AC_CACHE_CHECK([whether $CC supports @%:@pragma weak], + [ax_cv_sys_weak_alias_pragma], [ + + # Try linking with a weak alias... + AC_LINK_IFELSE([ + AC_LANG_PROGRAM([ +extern void weakf(int c); +@%:@pragma weak weakf = __weakf +void __weakf(int c) {}], + [weakf(0)])], + [ax_cv_sys_weak_alias_pragma=yes], + [ax_cv_sys_weak_alias_pragma=no]) + ]) + + # What was the result of the test? + AS_IF([test $ax_sys_weak_alias = no && + test $ax_cv_sys_weak_alias_pragma = yes], [ + ax_sys_weak_alias=pragma + AC_DEFINE([HAVE_SYS_WEAK_ALIAS_PRAGMA], 1, + [Define this if weak aliases may be created with @%:@pragma weak]) + ]) +]) + +AC_DEFUN([_AX_SYS_WEAK_ALIAS_HPSECONDARY], +[ # Test whether compiler accepts _HP_SECONDARY_DEF pragma from HP... + AC_CACHE_CHECK([whether $CC supports @%:@pragma _HP_SECONDARY_DEF], + [ax_cv_sys_weak_alias_hpsecondary], [ + + # Try linking with a weak alias... + AC_LINK_IFELSE([ + AC_LANG_PROGRAM([ +extern void weakf(int c); +@%:@pragma _HP_SECONDARY_DEF __weakf weakf +void __weakf(int c) {}], + [weakf(0)])], + [ax_cv_sys_weak_alias_hpsecondary=yes], + [ax_cv_sys_weak_alias_hpsecondary=no]) + ]) + + # What was the result of the test? + AS_IF([test $ax_sys_weak_alias = no && + test $ax_cv_sys_weak_alias_hpsecondary = yes], [ + ax_sys_weak_alias=hpsecondary + AC_DEFINE([HAVE_SYS_WEAK_ALIAS_HPSECONDARY], 1, + [Define this if weak aliases may be created with @%:@pragma _HP_SECONDARY_DEF]) + ]) +]) + +AC_DEFUN([_AX_SYS_WEAK_ALIAS_CRIDUPLICATE], +[ # Test whether compiler accepts "_CRI duplicate" pragma from Cray + AC_CACHE_CHECK([whether $CC supports @%:@pragma _CRI duplicate], + [ax_cv_sys_weak_alias_criduplicate], [ + + # Try linking with a weak alias... + AC_LINK_IFELSE([ + AC_LANG_PROGRAM([ +extern void weakf(int c); +@%:@pragma _CRI duplicate weakf as __weakf +void __weakf(int c) {}], + [weakf(0)])], + [ax_cv_sys_weak_alias_criduplicate=yes], + [ax_cv_sys_weak_alias_criduplicate=no]) + ]) + + # What was the result of the test? + AS_IF([test $ax_sys_weak_alias = no && + test $ax_cv_sys_weak_alias_criduplicate = yes], [ + ax_sys_weak_alias=criduplicate + AC_DEFINE([HAVE_SYS_WEAK_ALIAS_CRIDUPLICATE], 1, + [Define this if weak aliases may be created with @%:@pragma _CRI duplicate]) + ]) +]) + +dnl Note: This macro is modeled closely on AC_LINK_IFELSE, and in fact +dnl depends on some implementation details of that macro, particularly +dnl its use of _AC_MSG_LOG_CONFTEST to log the failed test program and +dnl its use of ac_link for running the linker. +AC_DEFUN([_AX_SYS_WEAK_ALIAS_CROSSFILE], +[ # Check to see if weak aliases can cross object file boundaries + AC_CACHE_CHECK([whether $CC supports weak aliases across object file boundaries], + [ax_cv_sys_weak_alias_crossfile], [ + AS_IF([test $ax_cv_sys_weak_alias = no], + [ax_cv_sys_weak_alias_crossfile=no], [ +dnl Must build our own test files... + # conftest1 contains our weak alias definition... + cat >conftest1.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF + cat confdefs.h >>conftest1.$ac_ext + cat >>conftest1.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +@%:@ifndef HAVE_SYS_WEAK_ALIAS_ATTRIBUTE +extern void weakf(int c); +@%:@endif +@%:@if defined(HAVE_SYS_WEAK_ALIAS_PRAGMA) +@%:@pragma weak weakf = __weakf +@%:@elif defined(HAVE_SYS_WEAK_ALIAS_HPSECONDARY) +@%:@pragma _HP_SECONDARY_DEF __weakf weakf +@%:@elif defined(HAVE_SYS_WEAK_ALIAS_CRIDUPLICATE) +@%:@pragma _CRI duplicate weakf as __weakf +@%:@endif +void __weakf(int c) {} +@%:@ifdef HAVE_SYS_WEAK_ALIAS_ATTRIBUTE +void weakf(int c) __attribute((weak, alias("__weakf"))); +@%:@endif +_ACEOF + # And conftest2 contains our main routine that calls it + cat >conftest2.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF + cat confdefs.h >> conftest2.$ac_ext + cat >>conftest2.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +extern void weakf(int c); +int +main () +{ + weakf(0); + return 0; +} +_ACEOF + # We must remove the object files (if any) ourselves... + rm -f conftest2.$ac_objext conftest$ac_exeext + + # Change ac_link to compile *2* files together + save_aclink=$ac_link + ac_link=`echo "$ac_link" | \ + sed -e 's/conftest\(\.\$ac_ext\)/conftest1\1 conftest2\1/'` +dnl Substitute our own routine for logging the conftest +m4_pushdef([_AC_MSG_LOG_CONFTEST], +[echo "$as_me: failed program was:" >&AS_MESSAGE_LOG_FD +echo ">>> conftest1.$ac_ext" >&AS_MESSAGE_LOG_FD +sed "s/^/| /" conftest1.$ac_ext >&AS_MESSAGE_LOG_FD +echo ">>> conftest2.$ac_ext" >&AS_MESSAGE_LOG_FD +sed "s/^/| /" conftest2.$ac_ext >&AS_MESSAGE_LOG_FD +])dnl + # Since we created the files ourselves, don't use SOURCE argument + AC_LINK_IFELSE(, [ax_cv_sys_weak_alias_crossfile=yes], + [ax_cv_sys_weak_alias_crossfile=no]) +dnl Restore _AC_MSG_LOG_CONFTEST +m4_popdef([_AC_MSG_LOG_CONFTEST])dnl + # Restore ac_link + ac_link=$save_aclink + + # We must remove the object files (if any) and C files ourselves... + rm -f conftest1.$ac_ext conftest2.$ac_ext \ + conftest1.$ac_objext conftest2.$ac_objext + ]) + ]) + + # What were the results of the test? + AS_IF([test $ax_cv_sys_weak_alias_crossfile = yes], [ + AC_DEFINE([HAVE_SYS_WEAK_ALIAS_CROSSFILE], 1, + [Define this if weak aliases in other files are honored]) + ]) +]) diff --git a/zebra/ioctl_null.c b/zebra/ioctl_null.c index 6d8e13a0..5d046d36 100644 --- a/zebra/ioctl_null.c +++ b/zebra/ioctl_null.c @@ -19,16 +19,29 @@ int if_unset_prefix (struct interface *a, struct connected *b) } int if_prefix_add_ipv6 (struct interface *a, struct connected *b) { return 0; } +#ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA #pragma weak if_prefix_delete_ipv6 = if_prefix_add_ipv6 +#else +int if_prefix_delete_ipv6 (struct interface *a, struct connected *b) { return 0; } +#endif int if_ioctl (u_long a, caddr_t b) { return 0; } int if_set_flags (struct interface *a, uint64_t b) { return 0; } +#ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA #pragma weak if_unset_flags = if_set_flags +#else +int if_unset_flags (struct interface *a, uint64_t b) { return 0; } +#endif void if_get_flags (struct interface *a) { return; } +#ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA #pragma weak if_get_metric = if_get_flags #pragma weak if_get_mtu = if_get_flags +#else +/* void if_get_metric (struct interface *a) { return; } */ +/* void if_get_mtu (struct interface *a) { return; } */ +#endif #ifdef SOLARIS_IPV6 #pragma weak if_ioctl_ipv6 = if_ioctl diff --git a/zebra/kernel_null.c b/zebra/kernel_null.c index 6b96c6df..ec994a6b 100644 --- a/zebra/kernel_null.c +++ b/zebra/kernel_null.c @@ -9,9 +9,19 @@ #include "zebra/connected.h" int kernel_add_ipv4 (struct prefix *a, struct rib *b) { return 0; } +#ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA #pragma weak kernel_delete_ipv4 = kernel_add_ipv4 +#else +int kernel_delete_ipv4 (struct prefix *a, struct rib *b) { return 0; } +#endif + int kernel_add_ipv6 (struct prefix *a, struct rib *b) { return 0; } +#ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA_PRAGMA #pragma weak kernel_delete_ipv6 = kernel_add_ipv6 +#else +int kernel_delete_ipv6 (struct prefix *a, struct rib *b) { return 0; } +#endif + int kernel_delete_ipv6_old (struct prefix_ipv6 *dest, struct in6_addr *gate, unsigned int index, int flags, int table) { return 0; } @@ -38,4 +48,8 @@ int kernel_address_delete_ipv4 (struct interface *a, struct connected *b) } void kernel_init (void) { return; } +#ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA #pragma weak route_read = kernel_init +#else +void route_read (void) { return; } +#endif diff --git a/zebra/misc_null.c b/zebra/misc_null.c index c8cc47d1..06807267 100644 --- a/zebra/misc_null.c +++ b/zebra/misc_null.c @@ -7,9 +7,15 @@ #include "zebra/zebra_fpm.h" void ifstat_update_proc (void) { return; } +#ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA #pragma weak rtadv_config_write = ifstat_update_proc #pragma weak irdp_config_write = ifstat_update_proc #pragma weak ifstat_update_sysctl = ifstat_update_proc +#else +void rtadv_config_write (struct vty *vty, struct interface *ifp) { return; } +void irdp_config_write (struct vty *vty, struct interface *ifp) { return; } +void ifstat_update_sysctl (void) { return; } +#endif void zfpm_trigger_update (struct route_node *rn, const char *reason) diff --git a/zebra/redistribute_null.c b/zebra/redistribute_null.c index e57a73b9..54198c8e 100644 --- a/zebra/redistribute_null.c +++ b/zebra/redistribute_null.c @@ -6,21 +6,50 @@ void zebra_redistribute_add (int a, struct zserv *b, int c) { return; } +#ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA #pragma weak zebra_redistribute_delete = zebra_redistribute_add #pragma weak zebra_redistribute_default_add = zebra_redistribute_add #pragma weak zebra_redistribute_default_delete = zebra_redistribute_add +#else +void zebra_redistribute_delete (int a, struct zserv *b, int c) +{ return; } +void zebra_redistribute_default_add (int a, struct zserv *b, int c) +{ return; } +void zebra_redistribute_default_delete (int a, struct zserv *b, int c) +{ return; } +#endif void redistribute_add (struct prefix *a, struct rib *b) { return; } +#ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA #pragma weak redistribute_delete = redistribute_add +#else +void redistribute_delete (struct prefix *a, struct rib *b) +{ return; } +#endif void zebra_interface_up_update (struct interface *a) { return; } +#ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA #pragma weak zebra_interface_down_update = zebra_interface_up_update #pragma weak zebra_interface_add_update = zebra_interface_up_update #pragma weak zebra_interface_delete_update = zebra_interface_up_update +#else +void zebra_interface_down_update (struct interface *a) +{ return; } +void zebra_interface_add_update (struct interface *a) +{ return; } +void zebra_interface_delete_update (struct interface *a) +{ return; } +#endif void zebra_interface_address_add_update (struct interface *a, struct connected *b) { return; } +#ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA #pragma weak zebra_interface_address_delete_update = zebra_interface_address_add_update +#else +void zebra_interface_address_delete_update (struct interface *a, + struct connected *b) +{ return; } +#endif From 675769b1b17a068526d90786e1c35304656faba7 Mon Sep 17 00:00:00 2001 From: Hasso Tepper Date: Fri, 14 Dec 2012 14:58:31 +0200 Subject: [PATCH 044/482] build: MacOSX needs BSD struct ip_mreq hack too Signed-off-by: David Lamparter --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index c51a0d3e..b69c99d5 100755 --- a/configure.ac +++ b/configure.ac @@ -978,7 +978,7 @@ AC_CHECK_MEMBERS([struct ip_mreqn.imr_ifindex], [], [], QUAGGA_INCLUDES) AC_MSG_CHECKING([for BSD struct ip_mreq hack]) AC_TRY_COMPILE([#ifdef HAVE_SYS_PARAM_H #include -#endif],[#if (defined(__FreeBSD__) && ((__FreeBSD_version >= 500022 && __FreeBSD_version < 700000) || (__FreeBSD_version < 500000 && __FreeBSD_version >= 440000))) || (defined(__NetBSD__) && defined(__NetBSD_Version__) && __NetBSD_Version__ >= 106010000) || defined(__OpenBSD__) +#endif],[#if (defined(__FreeBSD__) && ((__FreeBSD_version >= 500022 && __FreeBSD_version < 700000) || (__FreeBSD_version < 500000 && __FreeBSD_version >= 440000))) || (defined(__NetBSD__) && defined(__NetBSD_Version__) && __NetBSD_Version__ >= 106010000) || defined(__OpenBSD__) || defined(__APPLE__) return (0); #else #error No support for BSD struct ip_mreq hack detected From 61be0ee8824b30e94ddd2bfe8b51fcfd330c41dd Mon Sep 17 00:00:00 2001 From: Hasso Tepper Date: Fri, 14 Dec 2012 14:58:32 +0200 Subject: [PATCH 045/482] build: Fix build for systems with no /bin/true There are systems with no /bin/true - it might have different path (/usr/bin/true) or even a shell builtin. Signed-off-by: David Lamparter --- lib/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Makefile.am b/lib/Makefile.am index 0ec5b4c9..d7c31edb 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -61,5 +61,5 @@ gitversion.h: gitversion.h.tmp else .PHONY: gitversion.h gitversion.h: - /bin/true + true endif From 913ff66ba30093d2fdc26af215f4bc0b3d359d75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=AB=98=E9=B9=8F?= Date: Wed, 21 Nov 2012 18:15:43 +0800 Subject: [PATCH 046/482] zebra: fix netlink NL_PKT_BUF_SIZE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change default value of variable NL_PKT_BUF_SIZE to 8192UL. Cf. NLMSG_GOODSIZE definition of linux in include/linux/netlink.h for detail. Previously, on platforms with a page size greater than 8192, if you had added too many interfaces, zebra would not have enough buffer space to get the entire interface list. This resulted in an incomplete interface list. From: 高鹏 [updated to apply after FPM patches] Signed-off-by: David Lamparter --- zebra/rt_netlink.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zebra/rt_netlink.h b/zebra/rt_netlink.h index 529fa517..452b3974 100644 --- a/zebra/rt_netlink.h +++ b/zebra/rt_netlink.h @@ -24,7 +24,7 @@ #ifdef HAVE_NETLINK -#define NL_PKT_BUF_SIZE 4096 +#define NL_PKT_BUF_SIZE 8192 extern int addattr32 (struct nlmsghdr *n, int maxlen, int type, int data); From a5c851c7ff41ef846e83d62394176ac1753ebf45 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 27 Nov 2012 03:21:44 +0100 Subject: [PATCH 047/482] lib: remove ALL_LIST_ELEMENTS dead code branch ALL_LIST_ELEMENTS is checking node == NULL twice, which is causing a whole slew of false positives in Coverity. In this particular case, addressing this in the code is reasonable; being a macro, this appears all over the place without easy remedy. Acked-by: Scott Feldman Signed-off-by: David Lamparter --- lib/linklist.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/linklist.h b/lib/linklist.h index f0ae3625..24a9e206 100644 --- a/lib/linklist.h +++ b/lib/linklist.h @@ -90,7 +90,7 @@ extern void list_add_list (struct list *, struct list *); #define ALL_LIST_ELEMENTS(list,node,nextnode,data) \ (node) = listhead(list), ((data) = NULL); \ (node) != NULL && \ - ((data) = listgetdata(node),(nextnode) = listnextnode(node), 1); \ + ((data) = listgetdata(node),(nextnode) = node->next, 1); \ (node) = (nextnode), ((data) = NULL) /* read-only list iteration macro. From ebbb5fca5ca899a9a125aa2770d3fdf857186bac Mon Sep 17 00:00:00 2001 From: "Jorge Boncompte [DTI2]" Date: Mon, 7 May 2012 15:17:33 +0000 Subject: [PATCH 048/482] bgpd: fix for leaked struct bgp_adj_[in|out] on peer shutdown If a peer with soft-reconfiguration configured is cleared, the function bgp_clear_route_table() doesn't free the bgp_adj_in and bgp_adj_out structures of route nodes that for some reason, ej. denied by a filter, don't have routes attached "rn->info == NULL". Signed-off-by: Jorge Boncompte [DTI2] Reviewed-by: Leonid Rosenboim Signed-off-by: David Lamparter --- bgpd/bgp_route.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 2412503d..9251d2c2 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -2810,9 +2810,6 @@ bgp_clear_route_table (struct peer *peer, afi_t afi, safi_t safi, struct bgp_info *ri; struct bgp_adj_in *ain; struct bgp_adj_out *aout; - - if (rn->info == NULL) - continue; /* XXX:TODO: This is suboptimal, every non-empty route_node is * queued for every clearing peer, regardless of whether it is From 24e50f2013e64a73b1f7ecdbd5688360002d09f7 Mon Sep 17 00:00:00 2001 From: "Jorge Boncompte [DTI2]" Date: Mon, 7 May 2012 15:17:33 +0000 Subject: [PATCH 049/482] bgpd: avoid heap fragmentation in bgp_clear_route_table In bgp_clear_route_table, moved cleanup code before the allocation of the work queue items. This returns the memory to the system allocator before allocating new and might therefore help avoiding heap fragmentation. * bgp_route.c: (bgp_clear_route_table) moved code blocks. Signed-off-by: Jorge Boncompte [DTI2] Reviewed-by: Leonid Rosenboim Signed-off-by: David Lamparter --- bgpd/bgp_route.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 9251d2c2..6155bc16 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -2842,6 +2842,21 @@ bgp_clear_route_table (struct peer *peer, afi_t afi, safi_t safi, * this may actually be achievable. It doesn't seem to be a huge * problem at this time, */ + for (ain = rn->adj_in; ain; ain = ain->next) + if (ain->peer == peer || purpose == BGP_CLEAR_ROUTE_MY_RSCLIENT) + { + bgp_adj_in_remove (rn, ain); + bgp_unlock_node (rn); + break; + } + for (aout = rn->adj_out; aout; aout = aout->next) + if (aout->peer == peer || purpose == BGP_CLEAR_ROUTE_MY_RSCLIENT) + { + bgp_adj_out_remove (rn, aout, peer, afi, safi); + bgp_unlock_node (rn); + break; + } + for (ri = rn->info; ri; ri = ri->next) if (ri->peer == peer || purpose == BGP_CLEAR_ROUTE_MY_RSCLIENT) { @@ -2857,21 +2872,6 @@ bgp_clear_route_table (struct peer *peer, afi_t afi, safi_t safi, work_queue_add (peer->clear_node_queue, cnq); break; } - - for (ain = rn->adj_in; ain; ain = ain->next) - if (ain->peer == peer || purpose == BGP_CLEAR_ROUTE_MY_RSCLIENT) - { - bgp_adj_in_remove (rn, ain); - bgp_unlock_node (rn); - break; - } - for (aout = rn->adj_out; aout; aout = aout->next) - if (aout->peer == peer || purpose == BGP_CLEAR_ROUTE_MY_RSCLIENT) - { - bgp_adj_out_remove (rn, aout, peer, afi, safi); - bgp_unlock_node (rn); - break; - } } return; } From 15c713485699fd22dfa5b7ce3ca7c6be049f1033 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Mon, 19 Nov 2012 11:17:31 +0000 Subject: [PATCH 050/482] bgpd: store "no neighbor activate" for IPv4 unicast If a neighbor was in a peer group for any AFI/SAFI, bgpd would never write a "no neighbor activate" line for IPv4 unicast, so a valid setup like following could be configured, but not saved: router bgp 64600 bgp router-id 198.51.100.1 network 198.51.100.0/24 neighbor peers peer-group neighbor 2001:db8::2 remote-as 64601 no neighbor 2001:db8::2 activate ! address-family ipv6 network 2001:db8:1::/48 neighbor peers activate neighbor peers soft-reconfiguration inbound neighbor 2001:db8::2 peer-group peers exit-address-family ! Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- bgpd/bgpd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 908bdd94..0b3f0a8e 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -4915,7 +4915,7 @@ bgp_config_write_peer (struct vty *vty, struct bgp *bgp, vty_out (vty, " neighbor %s strict-capability-match%s", addr, VTY_NEWLINE); - if (! peer_group_active (peer)) + if (! peer->af_group[AFI_IP][SAFI_UNICAST]) { if (bgp_flag_check (bgp, BGP_FLAG_NO_DEFAULT_IPV4)) { From 9499bf2bc6daf0a9b7170d3cf994daef2f1a8920 Mon Sep 17 00:00:00 2001 From: Leonid Rosenboim Date: Thu, 6 Dec 2012 20:17:41 +0000 Subject: [PATCH 051/482] zebra: don't overrun afi/safi array boundaries zebra was not checking afi/safi values. This was leading to crashes where these values were coming directly from some protocol's on-wire fields. Safeguarding them in zebra is a good start. Signed-off-by: David Lamparter --- zebra/zebra_rib.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index a75d7215..4dd8551a 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -155,6 +155,9 @@ vrf_table (afi_t afi, safi_t safi, u_int32_t id) if (! vrf) return NULL; + if( afi >= AFI_MAX || safi >= SAFI_MAX ) + return NULL; + return vrf->table[afi][safi]; } @@ -168,6 +171,9 @@ vrf_static_table (afi_t afi, safi_t safi, u_int32_t id) if (! vrf) return NULL; + if( afi >= AFI_MAX || safi >= SAFI_MAX ) + return NULL; + return vrf->stable[afi][safi]; } From b06b35f0754747f9f178be155a2903b360aa2b6c Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Fri, 7 Dec 2012 14:26:09 +0000 Subject: [PATCH 052/482] bgpd: fix a memleak on "set community none" Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- bgpd/bgp_routemap.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index f2204003..40f20765 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -1347,6 +1347,9 @@ route_set_community (void *rule, struct prefix *prefix, { attr->flag &= ~(ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES)); attr->community = NULL; + /* See the longer comment down below. */ + if (old && old->refcnt == 0) + community_free(old); return RMAP_OKAY; } From a0de1d16cd00694b07b266d4a5dae5985e9072ff Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Fri, 7 Dec 2012 16:35:00 +0000 Subject: [PATCH 053/482] bgpd: fix a bug in bgp_attr_dup Commit 558d1fec11749d3257e improved bgp_attr_dup so it would be possible for the caller to provide attr_extra, allowing to use the stack instead of the heap for operations requiring only a short lived attr. However, this commit introduced a bug where bgp_attr_dup wouldn't copy attr_extra at all (but provide a reference to the original) if the caller provided attr_extra. Cc: Jorge Boncompte [DTI2] Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- bgpd/bgp_attr.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 2cbd7bc3..1dce39bc 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -321,11 +321,24 @@ bgp_attr_dup (struct attr *new, struct attr *orig) struct attr_extra *extra = new->extra; *new = *orig; - if (orig->extra) + /* if caller provided attr_extra space, use it in any case. + * + * This is neccesary even if orig->extra equals NULL, because otherwise + * memory may be later allocated on the heap by bgp_attr_extra_get. + * + * That memory would eventually be leaked, because the caller must not + * call bgp_attr_extra_free if he provided attr_extra on the stack. + */ + if (extra) + { + new->extra = extra; + memset(new->extra, 0, sizeof(struct attr_extra)); + if (orig->extra) + *new->extra = *orig->extra; + } + else if (orig->extra) { - /* if caller provided attr_extra space use it */ - if (! extra) - new->extra = bgp_attr_extra_new(); + new->extra = bgp_attr_extra_new(); *new->extra = *orig->extra; } } From a689e6a9f470d2a72493b907c94ef23516bbbda6 Mon Sep 17 00:00:00 2001 From: Leonid Rosenboim Date: Fri, 7 Dec 2012 21:25:00 +0000 Subject: [PATCH 054/482] bgpd: fix error response to invalid BGP version number BGP4-ANVL 20.1 ANVL tries to open BGP with version 5 and expects correct notification in response. Quagga sends notification, but with incorrect information in it. The data needs to be a 2-byte value, and for now we respond with 0004 for any peer version other than 4. Signed-off-by: David Lamparter --- bgpd/bgp_packet.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index 2d62c8da..968c8e4d 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -1411,14 +1411,16 @@ bgp_open_receive (struct peer *peer, bgp_size_t size) /* Peer BGP version check. */ if (version != BGP_VERSION_4) { - u_int8_t maxver = BGP_VERSION_4; + u_int16_t maxver = htons(BGP_VERSION_4); + /* XXX this reply may not be correct if version < 4 XXX */ if (BGP_DEBUG (normal, NORMAL)) zlog_debug ("%s bad protocol version, remote requested %d, local request %d", peer->host, version, BGP_VERSION_4); + /* Data must be in network byte order here */ bgp_notify_send_with_data (peer, BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_UNSUP_VERSION, - &maxver, 1); + (u_int8_t *) &maxver, 2); return -1; } From 1e0ce7caa622f07c20bb74414a4a5b4cbd732c75 Mon Sep 17 00:00:00 2001 From: Leonid Rosenboim Date: Fri, 7 Dec 2012 21:31:07 +0000 Subject: [PATCH 055/482] bgpd: improve logging of invalid BGP Notifications Invalid BGP Notification messages should be logged locally, cf. RFC4271, Sect. 6.4, p 34, NOTIFICATION Message Error Handling Current notification for invalid Notification code: 2012/10/10 02:17:54 BGP: message index 10 not found in bgp_notify_msg (max is 8) 2012/10/10 02:17:54 BGP: 192.168.1.1 received NOTIFICATION 10/0 ((no item found)) 0 bytes the logging should be a bit more clear. The above logging really doesn't explain much and looks more like a programming error. [rewrote most of it to get in something I can call a shape -David] Signed-off-by: David Lamparter --- bgpd/bgp_debug.c | 28 ++++++++++++++++------------ lib/log.h | 5 +++-- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c index e3e3ec86..726dd862 100644 --- a/bgpd/bgp_debug.c +++ b/bgpd/bgp_debug.c @@ -243,31 +243,37 @@ bgp_notify_print(struct peer *peer, struct bgp_notify *bgp_notify, const char *direct) { const char *subcode_str; + const char *code_str; subcode_str = ""; + code_str = LOOKUP_DEF (bgp_notify_msg, bgp_notify->code, + "Unrecognized Error Code"); - switch (bgp_notify->code) + switch (bgp_notify->code) { case BGP_NOTIFY_HEADER_ERR: - subcode_str = LOOKUP (bgp_notify_head_msg, bgp_notify->subcode); + subcode_str = LOOKUP_DEF (bgp_notify_head_msg, bgp_notify->subcode, + "Unrecognized Error Subcode"); break; case BGP_NOTIFY_OPEN_ERR: - subcode_str = LOOKUP (bgp_notify_open_msg, bgp_notify->subcode); + subcode_str = LOOKUP_DEF (bgp_notify_open_msg, bgp_notify->subcode, + "Unrecognized Error Subcode"); break; case BGP_NOTIFY_UPDATE_ERR: - subcode_str = LOOKUP (bgp_notify_update_msg, bgp_notify->subcode); + subcode_str = LOOKUP_DEF (bgp_notify_update_msg, bgp_notify->subcode, + "Unrecognized Error Subcode"); break; case BGP_NOTIFY_HOLD_ERR: - subcode_str = ""; break; case BGP_NOTIFY_FSM_ERR: - subcode_str = ""; break; case BGP_NOTIFY_CEASE: - subcode_str = LOOKUP (bgp_notify_cease_msg, bgp_notify->subcode); + subcode_str = LOOKUP_DEF (bgp_notify_cease_msg, bgp_notify->subcode, + "Unrecognized Error Subcode"); break; case BGP_NOTIFY_CAPABILITY_ERR: - subcode_str = LOOKUP (bgp_notify_capability_msg, bgp_notify->subcode); + subcode_str = LOOKUP_DEF (bgp_notify_capability_msg, bgp_notify->subcode, + "Unrecognized Error Subcode"); break; } @@ -275,15 +281,13 @@ bgp_notify_print(struct peer *peer, struct bgp_notify *bgp_notify, zlog_info ("%%NOTIFICATION: %s neighbor %s %d/%d (%s%s) %d bytes %s", strcmp (direct, "received") == 0 ? "received from" : "sent to", peer->host, bgp_notify->code, bgp_notify->subcode, - LOOKUP (bgp_notify_msg, bgp_notify->code), - subcode_str, bgp_notify->length, + code_str, subcode_str, bgp_notify->length, bgp_notify->data ? bgp_notify->data : ""); else if (BGP_DEBUG (normal, NORMAL)) plog_debug (peer->log, "%s %s NOTIFICATION %d/%d (%s%s) %d bytes %s", peer ? peer->host : "", direct, bgp_notify->code, bgp_notify->subcode, - LOOKUP (bgp_notify_msg, bgp_notify->code), - subcode_str, bgp_notify->length, + code_str, subcode_str, bgp_notify->length, bgp_notify->data ? bgp_notify->data : ""); } diff --git a/lib/log.h b/lib/log.h index 27f21b31..cf247a83 100644 --- a/lib/log.h +++ b/lib/log.h @@ -146,8 +146,9 @@ extern int zlog_reset_file (struct zlog *zl); /* Rotate log. */ extern int zlog_rotate (struct zlog *); -/* For hackey massage lookup and check */ -#define LOOKUP(x, y) mes_lookup(x, x ## _max, y, "(no item found)", #x) +/* For hackey message lookup and check */ +#define LOOKUP_DEF(x, y, def) mes_lookup(x, x ## _max, y, def, #x) +#define LOOKUP(x, y) LOOKUP_DEF(x, y, "(no item found)") extern const char *lookup (const struct message *, int); extern const char *mes_lookup (const struct message *meslist, From 07ff4dc4d29f6f8db77e0e73da5d59e864ae2e66 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 4 Jan 2013 22:29:20 +0000 Subject: [PATCH 056/482] bgpd: mark route nodes scheduled into work queue The flag bit BGP_NODE_PROCESS_SCHEDULED is checked but never set. This causes route node to be scheduled multiple times under load. Signed-off-by: David Lamparter --- bgpd/bgp_route.c | 1 + 1 file changed, 1 insertion(+) diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 6155bc16..06bd5991 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -1695,6 +1695,7 @@ bgp_process (struct bgp *bgp, struct bgp_node *rn, afi_t afi, safi_t safi) break; } + SET_FLAG (rn->flags, BGP_NODE_PROCESS_SCHEDULED); return; } From 47f6aef02b85c604bbd556574e2c724b7304e9e8 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 4 Jan 2013 22:29:22 +0000 Subject: [PATCH 057/482] configure: allow building without getrusage Measuring the resource usage of threads is moderately expensive since it requires doing an additional system call everytime a thread context switches. Make it possible to disable this with a configuration option. Signed-off-by: David Lamparter --- configure.ac | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/configure.ac b/configure.ac index b69c99d5..ff34f03a 100755 --- a/configure.ac +++ b/configure.ac @@ -266,6 +266,8 @@ AC_ARG_ENABLE(isis_topology, [ --enable-isis-topology enable IS-IS topology generator]) AC_ARG_ENABLE(capabilities, [ --disable-capabilities disable using POSIX capabilities]) +AC_ARG_ENABLE(rusage, +[ --disable-rusage disable using getrusage]) AC_ARG_ENABLE(gcc_ultra_verbose, [ --enable-gcc-ultra-verbose enable ultra verbose GCC warnings]) AC_ARG_ENABLE(linux24_tcp_md5, @@ -1486,12 +1488,13 @@ AC_CHECK_TYPES([struct in_pktinfo], dnl -------------------------------------- dnl checking for getrusage struct and call dnl -------------------------------------- -AC_MSG_CHECKING(whether getrusage is available) -AC_TRY_COMPILE([#include -],[struct rusage ac_x; getrusage (RUSAGE_SELF, &ac_x);], -[AC_MSG_RESULT(yes) - AC_DEFINE(HAVE_RUSAGE,,rusage)], - AC_MSG_RESULT(no)) +if test "${enable_rusage}" != "no"; then + AC_MSG_CHECKING(whether getrusage is available) + AC_TRY_COMPILE([#include ],[struct rusage ac_x; getrusage (RUSAGE_SELF, &ac_x);], + [AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_RUSAGE,,rusage)], + AC_MSG_RESULT(no)) +fi dnl -------------------------------------- dnl checking for clock_time monotonic struct and call From d61c1bbd4bf6ddf717dda88350668a9f1e2da0ac Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 4 Jan 2013 22:29:23 +0000 Subject: [PATCH 058/482] bgpd: use recent monotonic time for readtime The readtime value is for diagnostic, and doesn't have to be highly accurate. This also fixes a problem where the readtime was being measured with system clock, but the peer_uptime() was comparing with bgp_clock. Signed-off-by: David Lamparter --- bgpd/bgp_packet.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index 968c8e4d..02863d75 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -2385,6 +2385,15 @@ bgp_marker_all_one (struct stream *s, int length) return 1; } +/* Recent thread time. + On same clock base as bgp_clock (MONOTONIC) + but can be time of last context switch to bgp_read thread. */ +static time_t +bgp_recent_clock (void) +{ + return recent_relative_time().tv_sec; +} + /* Starting point of packet process function. */ int bgp_read (struct thread *thread) @@ -2513,14 +2522,14 @@ bgp_read (struct thread *thread) bgp_open_receive (peer, size); /* XXX return value ignored! */ break; case BGP_MSG_UPDATE: - peer->readtime = time(NULL); /* Last read timer reset */ + peer->readtime = bgp_recent_clock (); bgp_update_receive (peer, size); break; case BGP_MSG_NOTIFY: bgp_notify_receive (peer, size); break; case BGP_MSG_KEEPALIVE: - peer->readtime = time(NULL); /* Last read timer reset */ + peer->readtime = bgp_recent_clock (); bgp_keepalive_receive (peer, size); break; case BGP_MSG_ROUTE_REFRESH_NEW: From 3a69f74a0a903659e8a5bb930b257d9d09a87626 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 11 Jan 2013 18:27:23 +0000 Subject: [PATCH 059/482] bgpd: uncork after each write Keep data flowing, uncork after each BGP_WRITE_PACKET_MAX. This makes TCP send data sooner, since thread may not be scheduled again for a a longish time because of new UPDATE's coming in. Signed-off-by: Stephen Hemminger Signed-off-by: David Lamparter --- bgpd/bgp_packet.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index 02863d75..b0918fc5 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -677,7 +677,8 @@ bgp_write (struct thread *thread) /* Flush any existing events */ BGP_EVENT_ADD (peer, BGP_Stop); - return 0; + goto done; + case BGP_MSG_KEEPALIVE: peer->keepalive_out++; break; @@ -698,9 +699,9 @@ bgp_write (struct thread *thread) if (bgp_write_proceed (peer)) BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); - else - sockopt_cork (peer->fd, 0); - + + done: + sockopt_cork (peer->fd, 0); return 0; } From 303bb005889a843b0d7e07a9b67b68ee55653d8f Mon Sep 17 00:00:00 2001 From: Hasso Tepper Date: Sun, 13 Jan 2013 17:45:28 +0000 Subject: [PATCH 060/482] build: Remove deprecated AM_CONFIG_HEADER AM_CONFIG_HEADER has been deprecated for many years and is removed completely from automake 1.13. Signed-off-by: David Lamparter --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index ff34f03a..51bc8bd7 100755 --- a/configure.ac +++ b/configure.ac @@ -19,7 +19,7 @@ AC_CANONICAL_HOST() AC_CANONICAL_TARGET() AM_INIT_AUTOMAKE(1.6) -AM_CONFIG_HEADER(config.h) +AC_CONFIG_HEADERS(config.h) AC_PATH_PROG(PERL, perl) AC_CHECK_PROG([GAWK],[gawk],[gawk],[not-in-PATH]) From ea05767770d759d46f97d1dc33423de72ae20ccd Mon Sep 17 00:00:00 2001 From: Hasso Tepper Date: Sun, 13 Jan 2013 17:45:29 +0000 Subject: [PATCH 061/482] build: Fix build on MacOSX 10.8 (Mountain Lion) Newer MacOSX versions have support for both IPv6 advanced socket API RFCs (2292 and 3542) switchable in compile time, but neither of these is default for some strange reason. RFC3542 will be default in future, but for now we have to declare that we want to use the RFC3542 API before including . Signed-off-by: David Lamparter --- configure.ac | 3 +++ lib/zebra.h | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/configure.ac b/configure.ac index 51bc8bd7..92c831ab 100755 --- a/configure.ac +++ b/configure.ac @@ -472,6 +472,9 @@ m4_define([QUAGGA_INCLUDES], #if HAVE_SYS_SOCKET_H # include #endif +#ifdef __APPLE__ +# define __APPLE_USE_RFC_3542 +#endif #if HAVE_NETINET_IN_H # include #endif diff --git a/lib/zebra.h b/lib/zebra.h index 404b832b..ffca7a8f 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -142,6 +142,10 @@ typedef int socklen_t; #include #endif /* HAVE_SYS_SOCKIO_H */ +#ifdef __APPLE__ +#define __APPLE_USE_RFC_3542 +#endif + #ifdef HAVE_NETINET_IN_H #include #endif /* HAVE_NETINET_IN_H */ From f6295c29070631b24a391f79d4d37ab9c0807ecc Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 15 Jan 2013 17:59:08 +0100 Subject: [PATCH 062/482] build: add buildtest.sh script This script compiles Quagga in a variety of configurations and optionally with LLVM and ICC (if those are installed). Signed-off-by: David Lamparter --- buildtest.sh | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100755 buildtest.sh diff --git a/buildtest.sh b/buildtest.sh new file mode 100755 index 00000000..c9dd323f --- /dev/null +++ b/buildtest.sh @@ -0,0 +1,92 @@ +#!/bin/bash +# written 2012-2013 by David Lamparter, placed in Public Domain. +# +# builds some git commit of Quagga in some different configurations +# usage: buildtest.sh [commit [configurations...]] + +basecfg="--prefix=/usr --enable-user=quagga --enable-group=quagga --enable-vty-group=quagga --enable-configfile-mask=0660 --enable-logfile-mask=0640 --enable-vtysh --sysconfdir=/etc/quagga --enable-exampledir=/etc/quagga/samples --localstatedir=/var/run/quagga --libdir=/usr/lib64/quagga --enable-ipv6 --enable-ripngd --enable-ospf6d --enable-rtadv --disable-static --enable-isisd --enable-multipath=0 --enable-babeld" + +configs_base="gcc|$basecfg" + +configs_nov6="gcc|$basecfg" +configs_nov6="${configs_nov6/enable-ipv6/disable-ipv6}" +configs_nov6="${configs_nov6/enable-ospf6d/disable-ospf6d}" +configs_nov6="${configs_nov6/enable-ripngd/disable-ripngd}" +configs_nov6="${configs_nov6/enable-babeld/disable-babeld}" + +configs_ext="gcc|$basecfg --enable-opaque-lsa --enable-ospf-te --enable-ospfclient --enable-isis-topology" +configs_snmp="gcc|$basecfg --enable-opaque-lsa --enable-ospf-te --enable-ospfclient --enable-isis-topology --enable-snmp" +configs_clang="clang|$basecfg --enable-opaque-lsa --enable-ospf-te --enable-ospfclient --enable-isis-topology" +configs_icc="icc|$basecfg --enable-opaque-lsa --enable-ospf-te --enable-ospfclient --enable-isis-topology" + +defconfigs="base nov6 ext" +net-snmp-config --version &> /dev/null && defconfigs="$defconfigs snmp" +clang --version &> /dev/null && defconfigs="$defconfigs clang" +icc --version &> /dev/null && defconfigs="$defconfigs icc" + +echo "enabled configurations: $defconfigs" + +cc_gcc="CC=gcc; export CC" +cc_clang="CC=clang; export CC" +cc_icc="CC=icc; export CC" + +############################### + +errfunc() { + echo "something went wrong! check $TEMP" + exit 1 +} + +set -e +trap errfunc ERR + +COMMITREF="$1" +COMMITISH="`git rev-list --max-count=1 ${COMMITREF:-HEAD}`" +TEMP="`mktemp -t -d quaggabuild.XXXXXX`" +BASE="`pwd`" +CONFIGS="$2" + +echo using temporary directory: $TEMP +echo git commit used: +git --no-pager log -n 1 --pretty=oneline "$COMMITISH" + +cd "$TEMP" +git clone "$BASE" "source" +cd "source" +git checkout -b build "$COMMITISH" +git clean -d -f -x +sh bootstrap.sh + +cd .. + +echo -e "\n\n\n\n\033[33;1mmaking dist tarball\033[m" + +mkdir build_dist +cd build_dist +../source/configure +make distdir=sdist dist-gzip +cd .. +tar zxvf build_dist/sdist.tar.gz + +for cfg in ${CONFIGS:-$defconfigs}; do + echo -e "\n\n\n\n\033[33;1mbuilding configuration $cfg\033[m" + config="\${configs_$cfg}" + eval "config=$config" + + cc="${config%%|*}" + args="${config#*|}" + + ccset="\${cc_$cc}" + eval "ccset=$ccset" + eval "$ccset" + + bdir="build_$cfg" + mkdir "$bdir" + cd "$bdir" + ../sdist/configure $args + make -j5 + make DESTDIR="$TEMP/inst_$cfg" install + cd .. +done + +echo -e "\n\n\n\neverything seems ok. you may now\n\trm -rf $TEMP" From 8d083b9ec5bb0375ebb6d8b2b05c848febd92cb5 Mon Sep 17 00:00:00 2001 From: Leonid Rosenboim Date: Tue, 6 Nov 2012 11:44:04 -0800 Subject: [PATCH 063/482] build: update .gitignore for all test programs --- tests/.gitignore | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/tests/.gitignore b/tests/.gitignore index e6ab706c..93e886c0 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -5,16 +5,25 @@ tags TAGS .deps .nfs* +*~ +*.loT *.lo *.la *.libs -tabletest -testsig .arch-inventory .arch-ids +aspathtest +ecommtest +heavy +heavythread +heavywq +tabletest +testbgpcap +testbgpmpath +testbgpmpattr testbuffer +testchecksum testmemory +testprivs testsig -*~ -*.loT - +teststream From ca3ccd8748434719e4670ce812d1310013fad518 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Wed, 26 Sep 2012 14:52:39 +0200 Subject: [PATCH 064/482] zebra: fix sockaddr_dl length assumptions (BZ#737) Quagga makes bad assumptions about sockaddr_dl (on NetBSD, but possibly on other systems as well). Particularly, sizeof(struct sockaddr_dl) returns a size that does not include the full sdl_data field, leading to not enough data being copied. This breaks IPv6 RAs in particular, as a broken mac address from sockaddr_dl will be included in the packets. From: Matthias-Christian Ott Tested-by: Uwe Toenjes <6bone@6bone.informatik.uni-leipzig.de> [further simplified + more comments] Signed-off-by: David Lamparter --- configure.ac | 1 + lib/if.h | 8 +++++++- lib/zclient.c | 2 +- zebra/kernel_socket.c | 15 ++++++++++++--- zebra/zserv.c | 2 +- 5 files changed, 22 insertions(+), 6 deletions(-) diff --git a/configure.ac b/configure.ac index 92c831ab..937c79ce 100755 --- a/configure.ac +++ b/configure.ac @@ -1469,6 +1469,7 @@ AC_CHECK_TYPES([struct sockaddr, struct sockaddr_in, AC_CHECK_MEMBERS([struct sockaddr.sa_len, struct sockaddr_in.sin_len, struct sockaddr_un.sun_len, struct sockaddr_in6.sin6_scope_id, + struct sockaddr_dl.sdl_len, struct if6_aliasreq.ifra_lifetime, struct nd_opt_adv_interval.nd_opt_ai_type], [], [], QUAGGA_INCLUDES) diff --git a/lib/if.h b/lib/if.h index 841ce51e..2116e12e 100644 --- a/lib/if.h +++ b/lib/if.h @@ -103,7 +103,13 @@ struct interface /* Hardware address. */ #ifdef HAVE_STRUCT_SOCKADDR_DL - struct sockaddr_dl sdl; + union { + /* note that sdl_storage is never accessed, it only exists to make space. + * all actual uses refer to sdl - but use sizeof(sdl_storage)! this fits + * best with C aliasing rules. */ + struct sockaddr_dl sdl; + struct sockaddr_storage sdl_storage; + }; #else unsigned short hw_type; u_char hw_addr[INTERFACE_HWADDR_MAX]; diff --git a/lib/zclient.c b/lib/zclient.c index 61c6f730..d3165962 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -734,7 +734,7 @@ zebra_interface_if_set_value (struct stream *s, struct interface *ifp) ifp->mtu6 = stream_getl (s); ifp->bandwidth = stream_getl (s); #ifdef HAVE_STRUCT_SOCKADDR_DL - stream_get (&ifp->sdl, s, sizeof (ifp->sdl)); + stream_get (&ifp->sdl, s, sizeof (ifp->sdl_storage)); #else ifp->hw_addr_len = stream_getl (s); if (ifp->hw_addr_len) diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index cde36bd0..73fabd4c 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -477,13 +477,22 @@ ifm_read (struct if_msghdr *ifm) /* * XXX sockaddr_dl contents can be larger than the structure - * definition, so the user of the stored structure must be - * careful not to read off the end. - * + * definition. There are 2 big families here: + * - BSD has sdl_len + sdl_data[16] + overruns sdl_data + * we MUST use sdl_len here or we'll truncate data. + * - Solaris has no sdl_len, but sdl_data[244] + * presumably, it's not going to run past that, so sizeof() + * is fine here. * a nonzero ifnlen from RTA_NAME_GET() means sdl is valid */ if (ifnlen) + { +#ifdef HAVE_STRUCT_SOCKADDR_DL_SDL_LEN + memcpy (&ifp->sdl, sdl, sdl->sdl_len); +#else memcpy (&ifp->sdl, sdl, sizeof (struct sockaddr_dl)); +#endif /* HAVE_STRUCT_SOCKADDR_DL_SDL_LEN */ + } if_add_update (ifp); } diff --git a/zebra/zserv.c b/zebra/zserv.c index 9e47f23f..cb8dbcb3 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -153,7 +153,7 @@ zserv_encode_interface (struct stream *s, struct interface *ifp) stream_putl (s, ifp->mtu6); stream_putl (s, ifp->bandwidth); #ifdef HAVE_STRUCT_SOCKADDR_DL - stream_put (s, &ifp->sdl, sizeof (ifp->sdl)); + stream_put (s, &ifp->sdl, sizeof (ifp->sdl_storage)); #else stream_putl (s, ifp->hw_addr_len); if (ifp->hw_addr_len) From a6694fe8a89b957216f548938cc31602df04d495 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Wed, 16 Jan 2013 01:28:36 +0100 Subject: [PATCH 065/482] Revert "bgpd: flock() dump files (BZ#742)" This reverts commit b07458a055493dd37cb955ae90f11ae8bc334d3a. On second thought, the right way to do this is with rename(), not by introducing a lock that can potentially even stall bgpd. Reported-by: Christian Franke Signed-off-by: David Lamparter --- bgpd/bgp_dump.c | 40 +++++----------------------------------- 1 file changed, 5 insertions(+), 35 deletions(-) diff --git a/bgpd/bgp_dump.c b/bgpd/bgp_dump.c index 77548601..edb725a9 100644 --- a/bgpd/bgp_dump.c +++ b/bgpd/bgp_dump.c @@ -95,7 +95,6 @@ static FILE * bgp_dump_open_file (struct bgp_dump *bgp_dump) { int ret; - int fd; time_t clock; struct tm *tm; char fullpath[MAXPATHLEN]; @@ -132,16 +131,6 @@ bgp_dump_open_file (struct bgp_dump *bgp_dump) umask(oldumask); return NULL; } - else - { - fd = fileno(bgp_dump->fp); - ret = flock( fd, LOCK_EX ); - if (ret != 0) - { - zlog_warn ("bgp_dump_open_file: Unable to flock() file %s: %s", realpath, strerror (errno)); - } - } - umask(oldumask); return bgp_dump->fp; @@ -206,7 +195,6 @@ bgp_dump_set_size (struct stream *s, int type) static void bgp_dump_routes_index_table(struct bgp *bgp) { - int ret; struct peer *peer; struct listnode *node; uint16_t peerno = 0; @@ -278,11 +266,7 @@ bgp_dump_routes_index_table(struct bgp *bgp) bgp_dump_set_size(obuf, MSG_TABLE_DUMP_V2); - ret = fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_routes.fp); - if (ret != 1) - { - zlog_warn ("bgp_dump_routes_index_table: fwrite returned %d, expected 1: %s", ret, strerror (errno)); - } + fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_routes.fp); fflush (bgp_dump_routes.fp); } @@ -291,7 +275,6 @@ bgp_dump_routes_index_table(struct bgp *bgp) static unsigned int bgp_dump_routes_func (int afi, int first_run, unsigned int seq) { - int ret; struct stream *obuf; struct bgp_info *info; struct bgp_node *rn; @@ -390,11 +373,8 @@ bgp_dump_routes_func (int afi, int first_run, unsigned int seq) seq++; bgp_dump_set_size(obuf, MSG_TABLE_DUMP_V2); - ret = fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_routes.fp); - if (ret != 1) - { - zlog_warn ("bgp_dump_routes_func: fwrite returned %d, expected 1: %s", ret, strerror (errno)); - } + fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_routes.fp); + } fflush (bgp_dump_routes.fp); @@ -484,7 +464,6 @@ bgp_dump_common (struct stream *obuf, struct peer *peer, int forceas4) void bgp_dump_state (struct peer *peer, int status_old, int status_new) { - int ret; struct stream *obuf; /* If dump file pointer is disabled return immediately. */ @@ -505,11 +484,7 @@ bgp_dump_state (struct peer *peer, int status_old, int status_new) bgp_dump_set_size (obuf, MSG_PROTOCOL_BGP4MP); /* Write to the stream. */ - ret = fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_all.fp); - if (ret != 1) - { - zlog_warn ("bgp_dump_state: fwrite returned %d, expected 1: %s", ret, strerror (errno)); - } + fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_all.fp); fflush (bgp_dump_all.fp); } @@ -517,7 +492,6 @@ static void bgp_dump_packet_func (struct bgp_dump *bgp_dump, struct peer *peer, struct stream *packet) { - int ret; struct stream *obuf; /* If dump file pointer is disabled return immediately. */ @@ -546,11 +520,7 @@ bgp_dump_packet_func (struct bgp_dump *bgp_dump, struct peer *peer, bgp_dump_set_size (obuf, MSG_PROTOCOL_BGP4MP); /* Write to the stream. */ - ret = fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump->fp); - if (ret != 1) - { - zlog_warn ("bgp_dump_packet_func: fwrite returned %d, expected 1: %s", ret, strerror (errno)); - } + fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump->fp); fflush (bgp_dump->fp); } From 86998bc2bc9506841250c8d49dd2df2464660a18 Mon Sep 17 00:00:00 2001 From: Leonid Rosenboim Date: Fri, 14 Dec 2012 19:12:17 +0000 Subject: [PATCH 066/482] bgpd: uncork/nagle socket when sending BGP NOTIFY This pushes out the NOTIFY message before closing a connection. Previously, the TCP_CORK bandwidth optimization code caused NOTIFY messages to disappear prior to when the connection is closed. * bgpd/bgp_packet.c: unset CORK, set NODELAY, and replace writen() by more correct write() Signed-off-by: David Lamparter --- bgpd/bgp_packet.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index b0918fc5..d115353f 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -723,13 +723,21 @@ bgp_write_notify (struct peer *peer) val = fcntl (peer->fd, F_GETFL, 0); fcntl (peer->fd, F_SETFL, val & ~O_NONBLOCK); - ret = writen (peer->fd, STREAM_DATA (s), stream_get_endp (s)); + /* Stop collecting data within the socket */ + sockopt_cork (peer->fd, 0); + + ret = write (peer->fd, STREAM_DATA (s), stream_get_endp (s)); if (ret <= 0) { BGP_EVENT_ADD (peer, TCP_fatal_error); return 0; } + /* Disable Nagle, make NOTIFY packet go out right away */ + val = 1; + (void) setsockopt (peer->fd, IPPROTO_TCP, TCP_NODELAY, + (char *) &val, sizeof (val)); + /* Retrieve BGP packet type. */ stream_set_getp (s, BGP_MARKER_SIZE + 2); type = stream_getc (s); From dcab1bb822161d55795aad59b14c5c5d79b71e1f Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Fri, 7 Dec 2012 16:45:52 +0000 Subject: [PATCH 067/482] bgpd: conditional default-originate using route-map MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Incorporate a patch by Svetozar Mihailov which implements default-originate route-maps to behave as expected, i.e. allowing the default route to be advertised conditionally, depending on a criterion given by the route-map. I am aware that the performance attributes of the following implementation are far from optimal. However, this affects only code paths belonging to a feature that is broken without this patch, therefore, it seems reasonable to me to have this in the mainline for now. Cc: Svetozar Mihailov Reported-by: Sébastien Cramatte Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- NEWS | 5 +++++ bgpd/bgp_nexthop.c | 10 ++++++++++ bgpd/bgp_route.c | 48 ++++++++++++++++++++++++++++++++-------------- 3 files changed, 49 insertions(+), 14 deletions(-) diff --git a/NEWS b/NEWS index d67d6646..fe0d5ad2 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,10 @@ Note: this file lists major user-visible changes only. +- [bgpd] The semantics of default-originate route-map have changed. + The route-map is now used to advertise the default route conditionally. + The old behaviour which allowed to set attributes on the originated + default route is no longer supported. + * Changes in Quagga 0.99.21 - [bgpd] BGP multipath support has been merged diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index 0e56d368..d4692366 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -506,6 +506,16 @@ bgp_scan (afi_t afi, safi_t safi) else if (afi == AFI_IP6) zlog_debug ("scanning IPv6 Unicast routing tables"); } + + /* Reevaluate default-originate route-maps and announce/withdraw + * default route if neccesary. */ + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + if (peer->status == Established + && CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE) + && peer->default_rmap[afi][safi].name) + bgp_default_originate (peer, afi, safi, 0); + } } /* BGP scan thread. This thread check nexthop reachability. */ diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 06bd5991..8bc72d7b 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -2462,8 +2462,9 @@ bgp_default_originate (struct peer *peer, afi_t afi, safi_t safi, int withdraw) struct attr attr; struct aspath *aspath; struct prefix p; - struct bgp_info binfo; struct peer *from; + struct bgp_node *rn; + struct bgp_info *ri; int ret = RMAP_DENYMATCH; if (!(afi == AFI_IP || afi == AFI_IP6)) @@ -2505,21 +2506,37 @@ bgp_default_originate (struct peer *peer, afi_t afi, safi_t safi, int withdraw) if (peer->default_rmap[afi][safi].name) { - binfo.peer = bgp->peer_self; - binfo.attr = &attr; - SET_FLAG (bgp->peer_self->rmap_type, PEER_RMAP_TYPE_DEFAULT); - - ret = route_map_apply (peer->default_rmap[afi][safi].map, &p, - RMAP_BGP, &binfo); - + for (rn = bgp_table_top(bgp->rib[afi][safi]); rn; rn = bgp_route_next(rn)) + { + for (ri = rn->info; ri; ri = ri->next) + { + struct attr dummy_attr; + struct attr_extra dummy_extra; + struct bgp_info info; + + /* Provide dummy so the route-map can't modify the attributes */ + dummy_attr.extra = &dummy_extra; + bgp_attr_dup(&dummy_attr, ri->attr); + info.peer = ri->peer; + info.attr = &dummy_attr; + + ret = route_map_apply(peer->default_rmap[afi][safi].map, &rn->p, + RMAP_BGP, &info); + + /* The route map might have set attributes. If we don't flush them + * here, they will be leaked. */ + bgp_attr_flush(&dummy_attr); + if (ret != RMAP_DENYMATCH) + break; + } + if (ret != RMAP_DENYMATCH) + break; + } bgp->peer_self->rmap_type = 0; if (ret == RMAP_DENYMATCH) - { - bgp_attr_flush (&attr); - withdraw = 1; - } + withdraw = 1; } if (withdraw) @@ -2530,8 +2547,11 @@ bgp_default_originate (struct peer *peer, afi_t afi, safi_t safi, int withdraw) } else { - SET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_DEFAULT_ORIGINATE); - bgp_default_update_send (peer, &attr, afi, safi, from); + if (! CHECK_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_DEFAULT_ORIGINATE)) + { + SET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_DEFAULT_ORIGINATE); + bgp_default_update_send (peer, &attr, afi, safi, from); + } } bgp_attr_extra_free (&attr); From 955be06f8a647d1149d5547e1265fb66f55a9161 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Wed, 16 Jan 2013 01:48:04 +0100 Subject: [PATCH 068/482] build: Quagga 0.99.22-rc1 this is not a full release version, so neither release notes nor documentation are updated yet. Also, signing the tag with my private GPG key instead of the Quagga one. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 937c79ce..9b378e2d 100755 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,7 @@ ## AC_PREREQ(2.53) -AC_INIT(Quagga, 0.99.21, [https://bugzilla.quagga.net]) +AC_INIT(Quagga, 0.99.22-rc1, [https://bugzilla.quagga.net]) AC_CONFIG_SRCDIR(lib/zebra.h) AC_CONFIG_MACRO_DIR([m4]) From f47e5a18b5beb00d6b5b94965e305dadb5aa5bad Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sun, 20 Jan 2013 18:29:28 +0100 Subject: [PATCH 069/482] bgpd: don't try to reconcile AS4_PATH with NULL bgp_attr_munge_as4_attrs would previously try to reintegrate an AS4_PATH with a NULL AS_PATH, leading to a rather nasty SEGV. Let's go by RFC6793 and treat missing AS_PATH as 0-length AS_PATH, which in turn means discarding the AS4_PATH. [NB: we don't actually stick to the actual rule, which is discarding AS4_PATH if it's longer than AS_PATH; indeed we should probably fix that too] Signed-off-by: David Lamparter --- bgpd/bgp_attr.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 1dce39bc..cbf2902d 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -1360,6 +1360,9 @@ bgp_attr_munge_as4_attrs (struct peer *const peer, /* need to reconcile NEW_AS_PATH and AS_PATH */ if (!ignore_as4_path && (attr->flag & (ATTR_FLAG_BIT( BGP_ATTR_AS4_PATH)))) { + if (!attr->aspath) + return BGP_ATTR_PARSE_PROCEED; + newpath = aspath_reconcile_as4 (attr->aspath, as4_path); aspath_unintern (&attr->aspath); attr->aspath = aspath_intern (newpath); From 5e728e929942d39ce5a4ab3d01c33f7b688c4e3f Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Wed, 23 Jan 2013 05:50:24 +0100 Subject: [PATCH 070/482] bgpd: relax ORF capability length handling commit fe9bb64... "bgpd: CVE-2012-1820, DoS in bgp_capability_orf()" made the length test in bgp_capability_orf_entry() stricter and is now causing us to refuse (with CEASE) ORF capabilites carrying any excess data. This does not conform to the robustness principle as laid out by RFC1122 ("be liberal in what you accept"). Even worse, RFC5291 is quite unclear on how to use the ORF capability with multiple AFI/SAFIs. It can be interpreted as either "use one instance, stuff everything in" but also as "use multiple instances". So, if not for applying robustness, we end up clearing sessions from implementations going by the former interpretation. (or if anyone dares add a byte of padding...) Cc: Denis Ovsienko Signed-off-by: David Lamparter --- bgpd/bgp_open.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c index af711cc8..7bf35016 100644 --- a/bgpd/bgp_open.c +++ b/bgpd/bgp_open.c @@ -230,7 +230,7 @@ bgp_capability_orf_entry (struct peer *peer, struct capability_header *hdr) } /* validate number field */ - if (sizeof (struct capability_orf_entry) + (entry.num * 2) != hdr->length) + if (sizeof (struct capability_orf_entry) + (entry.num * 2) > hdr->length) { zlog_info ("%s ORF Capability entry length error," " Cap length %u, num %u", From 1cb9cf062ec3ed7a3f13fb5465eb5fb917ce3329 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 22 Jan 2013 23:39:17 +0100 Subject: [PATCH 071/482] tests: update & extend AS_PATH tests NB: these tests test for current implementation state, not for RFC conformance. In particular, behaviour with confederations in AS4_PATH as well as reconcilation of short AS_PATH + AS4_PATH is currently NOT conforming to RFC 4893/6793. * tests/aspath_test.c: add capability to put both AS4_PATH & AS_PATH, add test for AS4_PATH w/o AS_PATH, update confederation test Signed-off-by: David Lamparter --- tests/aspath_test.c | 40 ++++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/tests/aspath_test.c b/tests/aspath_test.c index 9170455f..7fdb5e22 100644 --- a/tests/aspath_test.c +++ b/tests/aspath_test.c @@ -443,6 +443,7 @@ static struct aspath_tests { const int cap; /* capabilities to set for peer */ const char attrheader [1024]; size_t len; + const struct test_segment *old_segment; } aspath_tests [] = { /* 0 */ @@ -590,10 +591,10 @@ static struct aspath_tests { }, /* 11 */ { - "4b AS_PATH: confed", + "4b AS4_PATH w/o AS_PATH", &test_segments[6], - "8466 3 52737 4096", - AS4_DATA, -1, + NULL, + AS4_DATA, 0, PEER_CAP_AS4_ADV, { BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL, BGP_ATTR_AS4_PATH, @@ -601,6 +602,20 @@ static struct aspath_tests { }, 3, }, + /* 12 */ + { + "4b AS4_PATH: confed", + &test_segments[6], + "8466 3 52737 4096 (123 456 789)", + AS4_DATA, 0, + PEER_CAP_AS4_ADV, + { BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL, + BGP_ATTR_AS4_PATH, + 14, + }, + 3, + &test_segments[0], + }, { NULL, NULL, NULL, 0, 0, 0, { 0 }, 0 }, }; @@ -1212,6 +1227,14 @@ handle_attr_test (struct aspath_tests *t) stream_write (peer.ibuf, t->attrheader, t->len); datalen = aspath_put (peer.ibuf, asp, t->as4 == AS4_DATA); + if (t->old_segment) + { + char dummyaspath[] = { BGP_ATTR_FLAG_TRANS, BGP_ATTR_AS_PATH, + t->old_segment->len }; + stream_write (peer.ibuf, dummyaspath, sizeof (dummyaspath)); + stream_write (peer.ibuf, t->old_segment->asdata, t->old_segment->len); + datalen += sizeof (dummyaspath) + t->old_segment->len; + } ret = bgp_attr_parse (&peer, &attr, t->len + datalen, NULL, NULL); @@ -1224,12 +1247,12 @@ handle_attr_test (struct aspath_tests *t) if (ret != 0) goto out; - if (attr.aspath == NULL) + if (t->shouldbe && attr.aspath == NULL) { - printf ("aspath is NULL!\n"); + printf ("aspath is NULL, but should be: %s\n", t->shouldbe); failed++; } - if (attr.aspath && strcmp (attr.aspath->str, t->shouldbe)) + if (t->shouldbe && attr.aspath && strcmp (attr.aspath->str, t->shouldbe)) { printf ("attr str and 'shouldbe' mismatched!\n" "attr str: %s\n" @@ -1237,6 +1260,11 @@ handle_attr_test (struct aspath_tests *t) attr.aspath->str, t->shouldbe); failed++; } + if (!t->shouldbe && attr.aspath) + { + printf ("aspath should be NULL, but is: %s\n", attr.aspath->str); + failed++; + } out: if (attr.aspath) From aeef13b0d5b50a90f293c93eb5a34c2a099d140b Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Wed, 23 Jan 2013 04:20:37 +0100 Subject: [PATCH 072/482] tests: fix missing init in bgp_mp_attr_test.c turns out, bgp_mp_reach_parse really doesn't like getting garbage attribute input. In particular, attr->extra better be NULL or we merrily go trample random places (like our stack). Signed-off-by: David Lamparter --- tests/bgp_mp_attr_test.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/bgp_mp_attr_test.c b/tests/bgp_mp_attr_test.c index f086740f..177c1ad8 100644 --- a/tests/bgp_mp_attr_test.c +++ b/tests/bgp_mp_attr_test.c @@ -436,8 +436,8 @@ parse_test (struct peer *peer, struct test_segment *t, int type) { int ret; int oldfailed = failed; - struct attr attr; - struct bgp_nlri nlri; + struct attr attr = { }; + struct bgp_nlri nlri = { }; struct bgp_attr_parser_args attr_args = { .peer = peer, .length = t->len, From d53d8fda42e1ce43852d3b4cff914ce79b5c6785 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Mon, 28 Jan 2013 07:14:43 +0100 Subject: [PATCH 073/482] bgpd: fix crash in soft-reconfiguration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 8692c50652 introduced a bug where bgpd would crash on soft-reconfiguration. This happens e.g. when there are filtered unicast routes because rn->info is NULL in that case, which the code did not account for. Reported-by: Paweł Staszewski Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- bgpd/bgp_route.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 8bc72d7b..fb35fab8 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -2643,10 +2643,10 @@ bgp_soft_reconfig_table_rsclient (struct peer *rsclient, afi_t afi, for (ain = rn->adj_in; ain; ain = ain->next) { struct bgp_info *ri = rn->info; + u_char *tag = (ri && ri->extra) ? ri->extra->tag : NULL; bgp_update_rsclient (rsclient, afi, safi, ain->attr, ain->peer, - &rn->p, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, prd, - (bgp_info_extra_get (ri))->tag); + &rn->p, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, prd, tag); } } @@ -2690,10 +2690,11 @@ bgp_soft_reconfig_table (struct peer *peer, afi_t afi, safi_t safi, if (ain->peer == peer) { struct bgp_info *ri = rn->info; + u_char *tag = (ri && ri->extra) ? ri->extra->tag : NULL; ret = bgp_update (peer, &rn->p, ain->attr, afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, - prd, (bgp_info_extra_get (ri))->tag, 1); + prd, tag, 1); if (ret < 0) { From d3c74d218bfbd9b5b9c39ef7d286f1dd007f2a74 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Fri, 18 Jan 2013 18:56:39 +0100 Subject: [PATCH 074/482] doc: update NEWS for 0.99.22 changes --- NEWS | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/NEWS b/NEWS index fe0d5ad2..59e237d6 100644 --- a/NEWS +++ b/NEWS @@ -1,9 +1,21 @@ Note: this file lists major user-visible changes only. +* Changes in Quagga 0.99.22 + - [bgpd] The semantics of default-originate route-map have changed. The route-map is now used to advertise the default route conditionally. The old behaviour which allowed to set attributes on the originated default route is no longer supported. +- [bgpd] There is now a replace-as option to neighbor ... local-as ... + no-prepend. For details, refer to the user documentation. +- [zebra] An FPM interface has been added. This provides an alternate + interface to routing information and is geared at OpenFlow & co. +- [snmp] AgentX is now supported; the old smux backend is considered + deprecated. ospf6d has also had OSPFV3-MIB added. +- [*] several issues with configuration save/load/apply have been fixed, + in particular on ospf "max-metric router-lsa administrative" and + "distribute-list", bgpd "no neighbor activate", isisd "metric-style", +- [*] a lot of bugs have been fixed, please refer to the git log * Changes in Quagga 0.99.21 From b0baf0740e3d6a01980cef7ea0af0a61c10b1bcd Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Fri, 18 Jan 2013 19:11:59 +0100 Subject: [PATCH 075/482] doc: update some introduction paragraphs in particular, - add IS-IS to some listings - list Solaris & OSX as "some work required" - remove OS version numbers. We have no base to specify any of them. - list supported C compilers (gcc, clang, icc) - cut the Quagga 2.0 stuff that promises QoS and firewall functionality --- doc/overview.texi | 54 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/doc/overview.texi b/doc/overview.texi index 7acfc0e9..2301c4b7 100644 --- a/doc/overview.texi +++ b/doc/overview.texi @@ -4,7 +4,7 @@ @uref{http://www.quagga.net,,Quagga} is a routing software package that provides TCP/IP based routing services with routing protocols support such -as RIPv1, RIPv2, RIPng, OSPFv2, OSPFv3, BGP-4, and BGP-4+ (@pxref{Supported +as RIPv1, RIPv2, RIPng, OSPFv2, OSPFv3, IS-IS, BGP-4, and BGP-4+ (@pxref{Supported RFCs}). Quagga also supports special BGP Route Reflector and Route Server behavior. In addition to traditional IPv4 routing protocols, Quagga also supports IPv6 routing protocols. With SNMP daemon which supports SMUX and AgentX @@ -52,7 +52,7 @@ software is very easy. The only thing you have to do is to set up the interfaces and put a few commands about static routes and/or default routes. If the network is rather large, or if the network structure changes frequently, you will want to take advantage of Quagga's dynamic routing -protocol support for protocols such as RIP, OSPF or BGP. +protocol support for protocols such as RIP, OSPF, IS-IS or BGP. Traditionally, UNIX based router configuration is done by @command{ifconfig} and @command{route} commands. Status of routing @@ -63,12 +63,13 @@ mode, the other is enable mode. Normal mode user can only view system status, enable mode user can change system configuration. This UNIX account independent feature will be great help to the router administrator. - Currently, Quagga supports common unicast routing protocols. Multicast -routing protocols such as BGMP, PIM-SM, PIM-DM may be supported in Quagga -2.0. MPLS support is going on. In the future, TCP/IP filtering control, -QoS control, diffserv configuration will be added to Quagga. Quagga -project's final goal is making a productive, quality, free TCP/IP routing -software. + Currently, Quagga supports common unicast routing protocols, that is BGP, +OSPF, RIP and IS-IS. Upcoming for MPLS support, an implementation of LDP is +currently being prepared for merging. Implementations of BFD and PIM-SSM +(IPv4) also exist, but are not actively being worked on. + + The ultimate goal of the Quagga project is making a productive, quality, free +TCP/IP routing software package. @node System Architecture @comment node-name, next, previous, up @@ -139,7 +140,7 @@ events. @cindex Compatibility with other systems @cindex Operating systems that support Quagga -Currently Quagga supports @sc{gnu}/Linux, BSD and Solaris. Porting Quagga +Currently Quagga supports @sc{gnu}/Linux and BSD. Porting Quagga to other platforms is not too difficult as platform dependent code should most be limited to the @command{zebra} daemon. Protocol daemons are mostly platform independent. Please let us know when you find out Quagga runs on a @@ -152,15 +153,40 @@ functionality on further platforms. @sp 1 @itemize @bullet @item -@sc{gnu}/Linux 2.4.x and higher +@sc{gnu}/Linux +@item +FreeBSD @item -FreeBSD 4.x and higher +NetBSD +@item +OpenBSD +@end itemize + +Versions of these platforms that are older than around 2 years from the point +of their original release (in case of @sc{gnu}/Linux, this is since the kernel's +release on kernel.org) may need some work. Similarly, the following platforms +may work with some effort: + +@sp 1 +@itemize @bullet +@item +Solaris +@item +Mac OSX +@end itemize + +Also note that, in particular regarding proprietary platforms, compiler +and C library choice will affect Quagga. Only recent versions of the +following C compilers are well-tested: + +@sp 1 +@itemize @bullet @item -NetBSD 1.6 and higher +@sc{gnu}'s GCC @item -OpenBSD 2.5 and higher +LLVM's clang @item -Solaris 8 and higher +Intel's ICC @end itemize @node Supported RFCs From b2baffe8d255890b85d93aee653bed2c18371128 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sun, 27 Jan 2013 04:46:02 +0100 Subject: [PATCH 076/482] build: fix "pragma weak" mixups Not only was there a minor typo in the "pragma weak" preprocessor checks, but also were the tests not behaving as needed - they only indicated support for the /first/ method of implementing weak aliases, which on Linux is __attribute__ and not #pragma. * m4/ax_sys_weak_alias.m4: set defines for _all_ weak alias methods * zebra/kernel_null.c: fix typo Cc: Doug VanLeuven Signed-off-by: David Lamparter --- m4/ax_sys_weak_alias.m4 | 22 +++++++++------------- zebra/kernel_null.c | 2 +- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/m4/ax_sys_weak_alias.m4 b/m4/ax_sys_weak_alias.m4 index 7c632a0e..27b0f0c4 100644 --- a/m4/ax_sys_weak_alias.m4 +++ b/m4/ax_sys_weak_alias.m4 @@ -167,9 +167,8 @@ void weakf(int c) __attribute__((weak, alias("__weakf")));], ]) # What was the result of the test? - AS_IF([test $ax_sys_weak_alias = no && - test $ax_cv_sys_weak_alias_attribute = yes], [ - ax_sys_weak_alias=attribute + AS_IF([test $ax_cv_sys_weak_alias_attribute = yes], [ + test $ax_sys_weak_alias = no && ax_sys_weak_alias=attribute AC_DEFINE([HAVE_SYS_WEAK_ALIAS_ATTRIBUTE], 1, [Define this if weak aliases may be created with __attribute__]) ]) @@ -192,9 +191,8 @@ void __weakf(int c) {}], ]) # What was the result of the test? - AS_IF([test $ax_sys_weak_alias = no && - test $ax_cv_sys_weak_alias_pragma = yes], [ - ax_sys_weak_alias=pragma + AS_IF([test $ax_cv_sys_weak_alias_pragma = yes], [ + test $ax_sys_weak_alias = no && ax_sys_weak_alias=pragma AC_DEFINE([HAVE_SYS_WEAK_ALIAS_PRAGMA], 1, [Define this if weak aliases may be created with @%:@pragma weak]) ]) @@ -217,9 +215,8 @@ void __weakf(int c) {}], ]) # What was the result of the test? - AS_IF([test $ax_sys_weak_alias = no && - test $ax_cv_sys_weak_alias_hpsecondary = yes], [ - ax_sys_weak_alias=hpsecondary + AS_IF([test $ax_cv_sys_weak_alias_hpsecondary = yes], [ + test $ax_sys_weak_alias = no && ax_sys_weak_alias=hpsecondary AC_DEFINE([HAVE_SYS_WEAK_ALIAS_HPSECONDARY], 1, [Define this if weak aliases may be created with @%:@pragma _HP_SECONDARY_DEF]) ]) @@ -242,9 +239,8 @@ void __weakf(int c) {}], ]) # What was the result of the test? - AS_IF([test $ax_sys_weak_alias = no && - test $ax_cv_sys_weak_alias_criduplicate = yes], [ - ax_sys_weak_alias=criduplicate + AS_IF([test $ax_cv_sys_weak_alias_criduplicate = yes], [ + test $ax_sys_weak_alias = no && ax_sys_weak_alias=criduplicate AC_DEFINE([HAVE_SYS_WEAK_ALIAS_CRIDUPLICATE], 1, [Define this if weak aliases may be created with @%:@pragma _CRI duplicate]) ]) @@ -271,7 +267,6 @@ _ACEOF @%:@ifndef HAVE_SYS_WEAK_ALIAS_ATTRIBUTE extern void weakf(int c); -@%:@endif @%:@if defined(HAVE_SYS_WEAK_ALIAS_PRAGMA) @%:@pragma weak weakf = __weakf @%:@elif defined(HAVE_SYS_WEAK_ALIAS_HPSECONDARY) @@ -279,6 +274,7 @@ extern void weakf(int c); @%:@elif defined(HAVE_SYS_WEAK_ALIAS_CRIDUPLICATE) @%:@pragma _CRI duplicate weakf as __weakf @%:@endif +@%:@endif void __weakf(int c) {} @%:@ifdef HAVE_SYS_WEAK_ALIAS_ATTRIBUTE void weakf(int c) __attribute((weak, alias("__weakf"))); diff --git a/zebra/kernel_null.c b/zebra/kernel_null.c index ec994a6b..cdb6e23f 100644 --- a/zebra/kernel_null.c +++ b/zebra/kernel_null.c @@ -16,7 +16,7 @@ int kernel_delete_ipv4 (struct prefix *a, struct rib *b) { return 0; } #endif int kernel_add_ipv6 (struct prefix *a, struct rib *b) { return 0; } -#ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA_PRAGMA +#ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA #pragma weak kernel_delete_ipv6 = kernel_add_ipv6 #else int kernel_delete_ipv6 (struct prefix *a, struct rib *b) { return 0; } From 66b63aa036c75737c3a8cea9693eff4dec84f903 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sun, 10 Feb 2013 03:08:33 +0100 Subject: [PATCH 077/482] release: 0.99.22 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 9b378e2d..6e59037a 100755 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,7 @@ ## AC_PREREQ(2.53) -AC_INIT(Quagga, 0.99.22-rc1, [https://bugzilla.quagga.net]) +AC_INIT(Quagga, 0.99.22, [https://bugzilla.quagga.net]) AC_CONFIG_SRCDIR(lib/zebra.h) AC_CONFIG_MACRO_DIR([m4]) From f1ef81b2476ea533ac3d2129aa0e89653c427323 Mon Sep 17 00:00:00 2001 From: Ulrich Weber Date: Tue, 22 Jan 2013 10:39:18 +0000 Subject: [PATCH 078/482] zebra: use SO_RCVBUFFORCE for netlink socket so net.core.rmem_max must not be adjusted. Requires linux kernel >= 2.6.14, falls back to SO_RCVBUF on error Signed-off-by: Ulrich Weber Signed-off-by: David Lamparter --- doc/zebra.8 | 2 +- zebra/rt_netlink.c | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/doc/zebra.8 b/doc/zebra.8 index 23703e72..a40909a6 100644 --- a/doc/zebra.8 +++ b/doc/zebra.8 @@ -80,7 +80,7 @@ handle flood of netlink messages from kernel. If you ever see "recvmsg overrun" messages in zebra log, you are in trouble. Solution is to increase receive buffer of netlink socket. Note that kernel -doesn't allow to increase it over maximum value defined in +< 2.6.14 doesn't allow to increase it over maximum value defined in \fI/proc/sys/net/core/rmem_max\fR. If you want to do it, you have to increase maximum before starting zebra. diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index fa446a56..bab17037 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -101,6 +101,10 @@ set_ifindex(struct interface *ifp, unsigned int ifi_index) ifp->ifindex = ifi_index; } +#ifndef SO_RCVBUFFORCE +#define SO_RCVBUFFORCE (33) +#endif + static int netlink_recvbuf (struct nlsock *nl, uint32_t newsize) { @@ -117,8 +121,16 @@ netlink_recvbuf (struct nlsock *nl, uint32_t newsize) return -1; } - ret = setsockopt(nl->sock, SOL_SOCKET, SO_RCVBUF, &nl_rcvbufsize, + /* Try force option (linux >= 2.6.14) and fall back to normal set */ + if ( zserv_privs.change (ZPRIVS_RAISE) ) + zlog_err ("routing_socket: Can't raise privileges"); + ret = setsockopt(nl->sock, SOL_SOCKET, SO_RCVBUFFORCE, &nl_rcvbufsize, sizeof(nl_rcvbufsize)); + if ( zserv_privs.change (ZPRIVS_LOWER) ) + zlog_err ("routing_socket: Can't lower privileges"); + if (ret < 0) + ret = setsockopt(nl->sock, SOL_SOCKET, SO_RCVBUF, &nl_rcvbufsize, + sizeof(nl_rcvbufsize)); if (ret < 0) { zlog (NULL, LOG_ERR, "Can't set %s receive buffer size: %s", nl->name, From 0e4303d3173ecc264b3ec39a863174670bbe2900 Mon Sep 17 00:00:00 2001 From: Roman Hoog Antink Date: Fri, 18 Jan 2013 13:52:03 +0100 Subject: [PATCH 079/482] bgpd: fix lost passwords of grouped neighbors This patch resolves the significance of order of group and password statements. It prevents passwords from being lost in cases where all three conditions apply: 1. the peer is member of a group with or without group password 2. the peer has an individual password set 3. the peer is added to a group within an address-family ipv6 section In addition this patch prevents the same issue in cases, where an IPv4 peer's password is set first and the peer is added to a group afterwards. Adding a peer to a group cancels his individual password. Without ipv6 this is not a problem, because choosing the right order of config statements will do (set password only after adding peer to group). When adding the peer to a group within the address-family section, his password is definitely lost. The same workaround (ie. setting the password after the address-family section) can not be used, because "show run" will print the configuration statements in the wrong order. Signed-off-by: David Lamparter --- bgpd/bgpd.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 0b3f0a8e..c9a04fff 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -1457,13 +1457,8 @@ peer_group2peer_config_copy (struct peer_group *group, struct peer *peer, peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV; /* password apply */ - if (peer->password) - XFREE (MTYPE_PEER_PASSWORD, peer->password); - - if (conf->password) + if (conf->password && !peer->password) peer->password = XSTRDUP (MTYPE_PEER_PASSWORD, conf->password); - else - peer->password = NULL; bgp_md5_set (peer); From 844ee10416c42debbfbc4d470eb2fe328e060977 Mon Sep 17 00:00:00 2001 From: Joakim Tjernlund Date: Sat, 23 Feb 2013 19:38:37 +0100 Subject: [PATCH 080/482] vtysh: fix false lib path matching in extract.pl.in The if ($file =~ /lib/) path matching logic is supposed to match Quagga's lib directory only but will match all path having lib in it such as /var/lib/jenkins/quagga/... Fix by matching both lib and file: lib/keychain.c etc. Signed-off-by: Joakim Tjernlund Signed-off-by: David Lamparter --- vtysh/extract.pl.in | 69 ++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 35 deletions(-) diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in index 37763043..d323cdb0 100755 --- a/vtysh/extract.pl.in +++ b/vtysh/extract.pl.in @@ -90,41 +90,40 @@ foreach (@ARGV) { $cmd =~ s/\s+$//g; # $protocol is VTYSH_PROTO format for redirection of user input - if ($file =~ /lib/) { - if ($file =~ /keychain.c/) { - $protocol = "VTYSH_RIPD"; - } - if ($file =~ /routemap.c/) { - $protocol = "VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ZEBRA"; - } - if ($file =~ /filter.c/) { - $protocol = "VTYSH_ALL"; - } - if ($file =~ /plist.c/) { - if ($defun_array[1] =~ m/ipv6/) { - $protocol = "VTYSH_RIPNGD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ZEBRA"; - } else { - $protocol = "VTYSH_RIPD|VTYSH_OSPFD|VTYSH_BGPD|VTYSH_ZEBRA"; - } - } - if ($file =~ /distribute.c/) { - if ($defun_array[1] =~ m/ipv6/) { - $protocol = "VTYSH_RIPNGD"; - } else { - $protocol = "VTYSH_RIPD"; - } - } - if ($file =~ /if_rmap.c/) { - if ($defun_array[1] =~ m/ipv6/) { - $protocol = "VTYSH_RIPNGD"; - } else { - $protocol = "VTYSH_RIPD"; - } - } - if ($file =~ /vty.c/) { - $protocol = "VTYSH_ALL"; - } - } else { + if ($file =~ /lib\/keychain\.c$/) { + $protocol = "VTYSH_RIPD"; + } + elsif ($file =~ /lib\/routemap\.c$/) { + $protocol = "VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ZEBRA"; + } + elsif ($file =~ /lib\/filter\.c$/) { + $protocol = "VTYSH_ALL"; + } + elsif ($file =~ /lib\/plist\.c$/) { + if ($defun_array[1] =~ m/ipv6/) { + $protocol = "VTYSH_RIPNGD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ZEBRA"; + } else { + $protocol = "VTYSH_RIPD|VTYSH_OSPFD|VTYSH_BGPD|VTYSH_ZEBRA"; + } + } + elsif ($file =~ /lib\/distribute\.c$/) { + if ($defun_array[1] =~ m/ipv6/) { + $protocol = "VTYSH_RIPNGD"; + } else { + $protocol = "VTYSH_RIPD"; + } + } + elsif ($file =~ /lib\/if_rmap\.c$/) { + if ($defun_array[1] =~ m/ipv6/) { + $protocol = "VTYSH_RIPNGD"; + } else { + $protocol = "VTYSH_RIPD"; + } + } + elsif ($file =~ /lib\/vty\.c$/) { + $protocol = "VTYSH_ALL"; + } + else { ($protocol) = ($file =~ /^.*\/([a-z0-9]+)\/[a-zA-Z0-9_\-]+\.c$/); $protocol = "VTYSH_" . uc $protocol; } From fa75585d3cac97616de4ea7c6805d91f709456eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matti-Oskari=20Lepp=C3=A4nen?= Date: Fri, 15 Feb 2013 10:12:55 +0000 Subject: [PATCH 081/482] build: update quagga.spec.in both Quagga and RPM have moved a bit since this was last touched. Should now work again on CentOS 5 and 6. Signed-off-by: David Lamparter --- redhat/quagga.spec.in | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/redhat/quagga.spec.in b/redhat/quagga.spec.in index a8c7fbf3..0ce25ca3 100644 --- a/redhat/quagga.spec.in +++ b/redhat/quagga.spec.in @@ -44,7 +44,7 @@ %{!?dist: %define dist %{default_dist}} # as distros change packages we depend on, our Requires have to change, sadly. -%define quagga_buildreqs texinfo tetex autoconf pam-devel +%define quagga_buildreqs texi2html texinfo tetex autoconf pam-devel %define quagga_buildreqs %{quagga_buildreqs} patch libcap-devel # FC4 and 5 split texi2html out of tetex package. @@ -68,7 +68,7 @@ %define daemon_list zebra ripd ospfd bgpd %if %{with_ipv6} -%define daemonv6_list ripngd ospf6d +%define daemonv6_list ripngd babeld ospf6d %else %define daemonv6_list "" %endif @@ -97,17 +97,17 @@ Source0: http://www.quagga.net/snapshots/cvs/%{name}-%{version}.tar.gz URL: http://www.quagga.net %if %{with_snmp} BuildRequires: net-snmp-devel -Prereq: net-snmp +Requires(pre): net-snmp %endif %if %{with_vtysh} BuildRequires: readline readline-devel ncurses ncurses-devel -Prereq: ncurses +Requires(pre): ncurses %endif BuildRequires: texinfo tetex autoconf pam-devel patch libcap-devel tetex # Initscripts > 5.60 is required for IPv6 support -Prereq: initscripts >= 5.60 -Prereq: ncurses pam -Prereq: /sbin/install-info +Requires(pre): initscripts >= 5.60 +Requires(pre): ncurses pam +Requires(pre): /sbin/install-info Provides: routingdaemon BuildRoot: %{_tmppath}/%{name}-%{version}-root Obsoletes: bird gated mrt zebra @@ -406,6 +406,7 @@ rm -rf $RPM_BUILD_ROOT %if %{with_ipv6} %{_sbindir}/ripngd %{_sbindir}/ospf6d +%{_sbindir}/babeld %endif %if %{with_isisd} %{_sbindir}/isisd From 44a86a0278c1678fd4b8dfa56c4f5f2feb6df3ad Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Fri, 25 Jan 2013 09:14:52 +0100 Subject: [PATCH 082/482] guile: remove --- doc/install.texi | 4 - guile/.gitignore | 10 -- guile/Makefile.am | 12 -- guile/Makefile.in | 299 ------------------------------------------ guile/README | 17 --- guile/guile-bgp.c | 117 ----------------- guile/zebra-guile.c | 71 ---------- guile/zebra-guile.h | 21 --- guile/zebra-support.c | 19 --- 9 files changed, 570 deletions(-) delete mode 100644 guile/.gitignore delete mode 100644 guile/Makefile.am delete mode 100644 guile/Makefile.in delete mode 100644 guile/README delete mode 100644 guile/guile-bgp.c delete mode 100644 guile/zebra-guile.c delete mode 100644 guile/zebra-guile.h delete mode 100644 guile/zebra-support.c diff --git a/doc/install.texi b/doc/install.texi index 1cc65574..0f8f65fa 100644 --- a/doc/install.texi +++ b/doc/install.texi @@ -49,10 +49,6 @@ use to turn off IPv6 support, to disable the compilation of specific daemons, and to enable SNMP support. @table @option -@item --enable-guile -Turn on compilation of the zebra-guile interpreter. You will need the -guile library to make this. zebra-guile implementation is not yet -finished. So this option is only useful for zebra-guile developers. @item --disable-ipv6 Turn off IPv6 related features and daemons. Quagga configure script automatically detects IPv6 stack. But sometimes you might want to diff --git a/guile/.gitignore b/guile/.gitignore deleted file mode 100644 index 5c2e06b6..00000000 --- a/guile/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -Makefile -*.o -zebra-guile -Makefile.in -.nfs* -.arch-inventory -.arch-ids -*~ -*.loT - diff --git a/guile/Makefile.am b/guile/Makefile.am deleted file mode 100644 index 8d7008e9..00000000 --- a/guile/Makefile.am +++ /dev/null @@ -1,12 +0,0 @@ -## Process this file with Automake to create Makefile.in - -INCLUDES = @GUILE_CFLAGS@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib -DEFS = @DEFS@ -I. -I$(srcdir) - -AM_CFLAGS = $(PICFLAGS) -AM_LDFLAGS = $(PILDFLAGS) - -bin_PROGRAMS = zebra-guile -zebra_guile_SOURCES = zebra-guile.c zebra-support.c guile-bgp.c -noinst_HEADERS = zebra-guile.h -zebra_guile_LDADD = @GUILE_LDFLAGS@ ../bgpd/libbgp.a ../lib/libzebra.la diff --git a/guile/Makefile.in b/guile/Makefile.in deleted file mode 100644 index 2773029b..00000000 --- a/guile/Makefile.in +++ /dev/null @@ -1,299 +0,0 @@ -# Makefile.in generated automatically by automake 1.4 from Makefile.am - -# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. -# This Makefile.in is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - - -SHELL = @SHELL@ - -srcdir = @srcdir@ -top_srcdir = @top_srcdir@ -VPATH = @srcdir@ -prefix = @prefix@ -exec_prefix = @exec_prefix@ - -bindir = @bindir@ -sbindir = @sbindir@ -libexecdir = @libexecdir@ -datadir = @datadir@ -sysconfdir = @sysconfdir@ -sharedstatedir = @sharedstatedir@ -localstatedir = @localstatedir@ -libdir = @libdir@ -infodir = @infodir@ -mandir = @mandir@ -includedir = @includedir@ -oldincludedir = /usr/include - -DESTDIR = - -pkgdatadir = $(datadir)/@PACKAGE@ -pkglibdir = $(libdir)/@PACKAGE@ -pkgincludedir = $(includedir)/@PACKAGE@ - -top_builddir = .. - -ACLOCAL = @ACLOCAL@ -AUTOCONF = @AUTOCONF@ -AUTOMAKE = @AUTOMAKE@ -AUTOHEADER = @AUTOHEADER@ - -INSTALL = @INSTALL@ -INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) -INSTALL_DATA = @INSTALL_DATA@ -INSTALL_SCRIPT = @INSTALL_SCRIPT@ -transform = @program_transform_name@ - -NORMAL_INSTALL = : -PRE_INSTALL = : -POST_INSTALL = : -NORMAL_UNINSTALL = : -PRE_UNINSTALL = : -POST_UNINSTALL = : -host_alias = @host_alias@ -host_triplet = @host@ -AR = @AR@ -BGPD = @BGPD@ -CC = @CC@ -CPP = @CPP@ -CURSES = @CURSES@ -IF_METHOD = @IF_METHOD@ -IF_PROC = @IF_PROC@ -IPFORWARD = @IPFORWARD@ -KERNEL_METHOD = @KERNEL_METHOD@ -LIBPAM = @LIBPAM@ -LIB_IPV6 = @LIB_IPV6@ -LIB_REGEX = @LIB_REGEX@ -MAKEINFO = @MAKEINFO@ -MULTIPATH_NUM = @MULTIPATH_NUM@ -OSPF6D = @OSPF6D@ -OSPFD = @OSPFD@ -OTHER_METHOD = @OTHER_METHOD@ -PACKAGE = @PACKAGE@ -RANLIB = @RANLIB@ -RIPD = @RIPD@ -RIPNGD = @RIPNGD@ -RTREAD_METHOD = @RTREAD_METHOD@ -RT_METHOD = @RT_METHOD@ -VERSION = @VERSION@ -VTYSH = @VTYSH@ -ZEBRA = @ZEBRA@ - -INCLUDES = @GUILE_CFLAGS@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -DEFS = @DEFS@ -I. -I$(srcdir) - -bin_PROGRAMS = zebra-guile -zebra_guile_SOURCES = zebra-guile.c zebra-support.c guile-bgp.c -noinst_HEADERS = zebra-guile.h -zebra_guile_LDADD = @GUILE_LDFLAGS@ ../bgpd/libbgp.a ../lib/libzebra.a -mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs -CONFIG_HEADER = ../config.h -CONFIG_CLEAN_FILES = -PROGRAMS = $(bin_PROGRAMS) - -CPPFLAGS = @CPPFLAGS@ -LDFLAGS = @LDFLAGS@ -LIBS = @LIBS@ -zebra_guile_OBJECTS = zebra-guile.o zebra-support.o guile-bgp.o -zebra_guile_DEPENDENCIES = ../bgpd/libbgp.a ../lib/libzebra.a -zebra_guile_LDFLAGS = -CFLAGS = @CFLAGS@ -COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -CCLD = $(CC) -LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ -HEADERS = $(noinst_HEADERS) - -DIST_COMMON = README ChangeLog Makefile.am Makefile.in - - -DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) - -TAR = tar -GZIP_ENV = --best -SOURCES = $(zebra_guile_SOURCES) -OBJECTS = $(zebra_guile_OBJECTS) - -all: all-redirect -.SUFFIXES: -.SUFFIXES: .S .c .o .s -$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) - cd $(top_srcdir) && $(AUTOMAKE) --foreign --include-deps guile/Makefile - -Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status - cd $(top_builddir) \ - && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status - - -mostlyclean-binPROGRAMS: - -clean-binPROGRAMS: - -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) - -distclean-binPROGRAMS: - -maintainer-clean-binPROGRAMS: - -install-binPROGRAMS: $(bin_PROGRAMS) - @$(NORMAL_INSTALL) - $(mkinstalldirs) $(DESTDIR)$(bindir) - @list='$(bin_PROGRAMS)'; for p in $$list; do \ - if test -f $$p; then \ - echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`"; \ - $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \ - else :; fi; \ - done - -uninstall-binPROGRAMS: - @$(NORMAL_UNINSTALL) - list='$(bin_PROGRAMS)'; for p in $$list; do \ - rm -f $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \ - done - -.c.o: - $(COMPILE) -c $< - -.s.o: - $(COMPILE) -c $< - -.S.o: - $(COMPILE) -c $< - -mostlyclean-compile: - -rm -f *.o core *.core - -clean-compile: - -distclean-compile: - -rm -f *.tab.c - -maintainer-clean-compile: - -zebra-guile: $(zebra_guile_OBJECTS) $(zebra_guile_DEPENDENCIES) - @rm -f zebra-guile - $(LINK) $(zebra_guile_LDFLAGS) $(zebra_guile_OBJECTS) $(zebra_guile_LDADD) $(LIBS) - -tags: TAGS - -ID: $(HEADERS) $(SOURCES) $(LISP) - list='$(SOURCES) $(HEADERS)'; \ - unique=`for i in $$list; do echo $$i; done | \ - awk ' { files[$$0] = 1; } \ - END { for (i in files) print i; }'`; \ - here=`pwd` && cd $(srcdir) \ - && mkid -f$$here/ID $$unique $(LISP) - -TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP) - tags=; \ - here=`pwd`; \ - list='$(SOURCES) $(HEADERS)'; \ - unique=`for i in $$list; do echo $$i; done | \ - awk ' { files[$$0] = 1; } \ - END { for (i in files) print i; }'`; \ - test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ - || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS) - -mostlyclean-tags: - -clean-tags: - -distclean-tags: - -rm -f TAGS ID - -maintainer-clean-tags: - -distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) - -subdir = guile - -distdir: $(DISTFILES) - @for file in $(DISTFILES); do \ - d=$(srcdir); \ - if test -d $$d/$$file; then \ - cp -pr $$/$$file $(distdir)/$$file; \ - else \ - test -f $(distdir)/$$file \ - || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ - || cp -p $$d/$$file $(distdir)/$$file || :; \ - fi; \ - done -info-am: -info: info-am -dvi-am: -dvi: dvi-am -check-am: all-am -check: check-am -installcheck-am: -installcheck: installcheck-am -install-exec-am: install-binPROGRAMS -install-exec: install-exec-am - -install-data-am: -install-data: install-data-am - -install-am: all-am - @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am -install: install-am -uninstall-am: uninstall-binPROGRAMS -uninstall: uninstall-am -all-am: Makefile $(PROGRAMS) $(HEADERS) -all-redirect: all-am -install-strip: - $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install -installdirs: - $(mkinstalldirs) $(DESTDIR)$(bindir) - - -mostlyclean-generic: - -clean-generic: - -distclean-generic: - -rm -f Makefile $(CONFIG_CLEAN_FILES) - -rm -f config.cache config.log stamp-h stamp-h[0-9]* - -maintainer-clean-generic: -mostlyclean-am: mostlyclean-binPROGRAMS mostlyclean-compile \ - mostlyclean-tags mostlyclean-generic - -mostlyclean: mostlyclean-am - -clean-am: clean-binPROGRAMS clean-compile clean-tags clean-generic \ - mostlyclean-am - -clean: clean-am - -distclean-am: distclean-binPROGRAMS distclean-compile distclean-tags \ - distclean-generic clean-am - -distclean: distclean-am - -maintainer-clean-am: maintainer-clean-binPROGRAMS \ - maintainer-clean-compile maintainer-clean-tags \ - maintainer-clean-generic distclean-am - @echo "This command is intended for maintainers to use;" - @echo "it deletes files that may require special tools to rebuild." - -maintainer-clean: maintainer-clean-am - -.PHONY: mostlyclean-binPROGRAMS distclean-binPROGRAMS clean-binPROGRAMS \ -maintainer-clean-binPROGRAMS uninstall-binPROGRAMS install-binPROGRAMS \ -mostlyclean-compile distclean-compile clean-compile \ -maintainer-clean-compile tags mostlyclean-tags distclean-tags \ -clean-tags maintainer-clean-tags distdir info-am info dvi-am dvi check \ -check-am installcheck-am installcheck install-exec-am install-exec \ -install-data-am install-data install-am install uninstall-am uninstall \ -all-redirect all-am all installdirs mostlyclean-generic \ -distclean-generic clean-generic maintainer-clean-generic clean \ -mostlyclean distclean maintainer-clean - - -# Tell versions [3.59,3.63) of GNU make to not export all variables. -# Otherwise a system limit (for SysV at least) may be exceeded. -.NOEXPORT: diff --git a/guile/README b/guile/README deleted file mode 100644 index 8e18fae5..00000000 --- a/guile/README +++ /dev/null @@ -1,17 +0,0 @@ - - zebra-guile - - Kunihiro Ishiguro - 1999 - -1. What is zebra-guile - -zebra-guile is GNU Zebra which linked with guile. Almost zebra's -command can be called from guile interpreter. So you can use guile as -a routing scripting language. - -2. How to use it. - -(define bgp (router-bgp 7675)) - -3. diff --git a/guile/guile-bgp.c b/guile/guile-bgp.c deleted file mode 100644 index fbd01ba0..00000000 --- a/guile/guile-bgp.c +++ /dev/null @@ -1,117 +0,0 @@ -/* Guile bgp interface. - Copyright (C) 1999 Kunihiro Ishiguro - -This file is part of GNU Zebra. - -GNU Zebra is free software; you can redistribute it and/or modify it -under the terms of the GNU General Public License as published by the -Free Software Foundation; either version 2, or (at your option) any -later version. - -GNU Zebra is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU Zebra; see the file COPYING. If not, write to the Free -Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA -02111-1307, USA. */ - -#include -#include - -#include "log.h" -#include "bgpd/bgpd.h" - -/* static SCM scm_mark_bgp (SCM obj); */ -static size_t scm_free_bgp (SCM vect); -static int scm_print_bgp (SCM vect, SCM port, scm_print_state *pstate); -static SCM scm_equalp_bgp (SCM a, SCM b); - -/* Tag of scheme type of bgp. */ -long scm_tag_bgp; - -static scm_smobfuns bgp_funs = -{ - scm_mark0, scm_free_bgp, scm_print_bgp, scm_equalp_bgp -}; - -static int -scm_print_bgp (SCM vect, SCM port, scm_print_state *pstate) -{ - unsigned short num; - struct bgp *bgp; - - num = 0; - bgp = (struct bgp *) SCM_CDR (vect); - num = bgp->as; - scm_puts ("#', port); - return 1; -} - -static size_t -scm_free_bgp (SCM obj) -{ - /* dummy function. */ - return 10; -} - -static SCM -scm_equalp_bgp (SCM a, SCM b) -{ - - return SCM_BOOL_F; -} - -/* Make bgp instance. */ -SCM -scm_router_bgp (SCM as_number) -{ - SCM cell; - long num; - struct bgp *bgp; - struct bgp *bgp_create (); - - SCM_ASSERT (SCM_INUMP (as_number), as_number, SCM_ARG1, "router-bgp"); - - SCM_DEFER_INTS; - - num = gh_scm2long (as_number); - - /* Make new bgp object. */ - bgp = bgp_create (); - bgp->as = num; - - SCM_NEWCELL (cell); - SCM_SETCAR (cell, scm_tag_bgp); - SCM_SETCDR (cell, bgp); - - SCM_ALLOW_INTS; - - return cell; -} - -#if 0 -SCM -scm_router_bgp_list () -{ - return NULL; -} -#endif - -void -init_bgp () -{ - void bgp_init (); - - bgp_init (); - - /* Initi types. */ - scm_tag_bgp = scm_newsmob (&bgp_funs); - - gh_new_procedure ("router-bgp", scm_router_bgp, 1, 0, 0); - /* gh_new_procedure ("router-bgp-list", scm_router_bgp_list, 0, 0, 0); */ -} diff --git a/guile/zebra-guile.c b/guile/zebra-guile.c deleted file mode 100644 index f618dbc5..00000000 --- a/guile/zebra-guile.c +++ /dev/null @@ -1,71 +0,0 @@ -/* Zebra guile interface. - Copyright (C) 1998, 99 Kunihiro Ishiguro - -This file is part of GNU Zebra. - -GNU Zebra is free software; you can redistribute it and/or modify it -under the terms of the GNU General Public License as published by the -Free Software Foundation; either version 2, or (at your option) any -later version. - -GNU Zebra is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU Zebra; see the file COPYING. If not, write to the Free -Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA -02111-1307, USA. */ - -#include -#include "zebra-guile.h" - -#include "zebra.h" -#include "thread.h" - -struct thread *master; - -static void -init_libzebra () -{ - void cmd_init(); - void vty_init(); - void memory_init(); - - cmd_init (1); - vty_init (); - memory_init (); -} - -/* Install scheme procudures. */ -void -init_zebra_guile () -{ - init_libzebra (); - - init_bgp (); - -#if 0 - init_zebra (); - init_rip (); - init_ospf (); -#endif /* 0 */ -} - -static void -inner_main (void *closure, int argc, char **argv) -{ - /* Install zebra related scheme procedures. */ - init_zebra_guile (); - - /* Invoke interpreter. */ - scm_shell (argc, argv); -} - -int -main (int argc, char **argv) -{ - scm_boot_guile (argc, argv, inner_main, 0); - return 0; /* Not reached */ -} diff --git a/guile/zebra-guile.h b/guile/zebra-guile.h deleted file mode 100644 index f43e287d..00000000 --- a/guile/zebra-guile.h +++ /dev/null @@ -1,21 +0,0 @@ -/* Zebra guile header. - Copyright (C) 1999 Kunihiro Ishiguro - -This file is part of GNU Zebra. - -GNU Zebra is free software; you can redistribute it and/or modify it -under the terms of the GNU General Public License as published by the -Free Software Foundation; either version 2, or (at your option) any -later version. - -GNU Zebra is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU Zebra; see the file COPYING. If not, write to the Free -Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA -02111-1307, USA. */ - -void init_bgp (); diff --git a/guile/zebra-support.c b/guile/zebra-support.c deleted file mode 100644 index 9a6ef814..00000000 --- a/guile/zebra-support.c +++ /dev/null @@ -1,19 +0,0 @@ -/* Zebra guile interface support. - Copyright (C) 1999 Kunihiro Ishiguro - -This file is part of GNU Zebra. - -GNU Zebra is free software; you can redistribute it and/or modify it -under the terms of the GNU General Public License as published by the -Free Software Foundation; either version 2, or (at your option) any -later version. - -GNU Zebra is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU Zebra; see the file COPYING. If not, write to the Free -Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA -02111-1307, USA. */ From 90645f5598ca8b25cd2692f2ac0d2778a3fd2755 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 4 Jan 2013 22:29:21 +0000 Subject: [PATCH 083/482] hash: force size to be a power of 2 By forcing the hash table size to be a power of 2, a potentially expensive divide can be replaced by a mask operation. Almost all usage of the hash table was using default size of 1024. Only places with different size was thread library (1011) and bgp aspath. Signed-off-by: David Lamparter --- bgpd/bgp_aspath.c | 2 +- lib/hash.c | 5 +++-- lib/hash.h | 2 +- lib/thread.c | 4 ++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c index c37a8897..a8b078ff 100644 --- a/bgpd/bgp_aspath.c +++ b/bgpd/bgp_aspath.c @@ -1856,7 +1856,7 @@ aspath_cmp (const void *arg1, const void *arg2) void aspath_init (void) { - ashash = hash_create_size (32767, aspath_key_make, aspath_cmp); + ashash = hash_create_size (32768, aspath_key_make, aspath_cmp); } void diff --git a/lib/hash.c b/lib/hash.c index 6db79ea7..1e097f28 100644 --- a/lib/hash.c +++ b/lib/hash.c @@ -31,6 +31,7 @@ hash_create_size (unsigned int size, unsigned int (*hash_key) (void *), { struct hash *hash; + assert ((size & (size-1)) == 0); hash = XMALLOC (MTYPE_HASH, sizeof (struct hash)); hash->index = XCALLOC (MTYPE_HASH_INDEX, sizeof (struct hash_backet *) * size); @@ -71,7 +72,7 @@ hash_get (struct hash *hash, void *data, void * (*alloc_func) (void *)) struct hash_backet *backet; key = (*hash->hash_key) (data); - index = key % hash->size; + index = key & (hash->size - 1); for (backet = hash->index[index]; backet != NULL; backet = backet->next) if (backet->key == key && (*hash->hash_cmp) (backet->data, data)) @@ -125,7 +126,7 @@ hash_release (struct hash *hash, void *data) struct hash_backet *pp; key = (*hash->hash_key) (data); - index = key % hash->size; + index = key & (hash->size - 1); for (backet = pp = hash->index[index]; backet; backet = backet->next) { diff --git a/lib/hash.h b/lib/hash.h index 4cb772e5..a6dd5319 100644 --- a/lib/hash.h +++ b/lib/hash.h @@ -41,7 +41,7 @@ struct hash /* Hash backet. */ struct hash_backet **index; - /* Hash table size. */ + /* Hash table size. Must be power of 2 */ unsigned int size; /* Key make function. */ diff --git a/lib/thread.c b/lib/thread.c index 16c92c24..27c29d6c 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -531,8 +531,8 @@ thread_master_create () { if (cpu_record == NULL) cpu_record - = hash_create_size (1011, (unsigned int (*) (void *))cpu_record_hash_key, - (int (*) (const void *, const void *))cpu_record_hash_cmp); + = hash_create ((unsigned int (*) (void *))cpu_record_hash_key, + (int (*) (const void *, const void *))cpu_record_hash_cmp); return (struct thread_master *) XCALLOC (MTYPE_THREAD_MASTER, sizeof (struct thread_master)); From 97c84db00c01b808337bedf69f696a1517e3d8c0 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 11 Jan 2013 18:25:26 +0000 Subject: [PATCH 084/482] hash: dynamically grow hash table Dynamically grow the hash table index if the chains get too long. If expansion doesn't help keep chain length short, then stop expanding, to avoid bad behavior if there is a poor hash function. Not a new idea, based on concepts in uthash. Depends on my previous patch to restrict hash to power of 2. Signed-off-by: Stephen Hemminger [profiling results: sum of cycles spent in hash_get/jhash with RIPE RIS test data (single simple BGP peer) improved to 69% of previously spent] Signed-off-by: David Lamparter --- lib/hash.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++---- lib/hash.h | 6 ++++- 2 files changed, 66 insertions(+), 5 deletions(-) diff --git a/lib/hash.c b/lib/hash.c index 1e097f28..987012a5 100644 --- a/lib/hash.c +++ b/lib/hash.c @@ -48,7 +48,7 @@ struct hash * hash_create (unsigned int (*hash_key) (void *), int (*hash_cmp) (const void *, const void *)) { - return hash_create_size (HASHTABSIZE, hash_key, hash_cmp); + return hash_create_size (HASH_INITIAL_SIZE, hash_key, hash_cmp); } /* Utility function for hash_get(). When this function is specified @@ -60,6 +60,52 @@ hash_alloc_intern (void *arg) return arg; } +/* Expand hash if the chain length exceeds the threshold. */ +static void hash_expand (struct hash *hash) +{ + unsigned int i, new_size, losers; + struct hash_backet *hb, *hbnext, **new_index; + + new_size = hash->size * 2; + new_index = XCALLOC(MTYPE_HASH_INDEX, sizeof(struct hash_backet *) * new_size); + if (new_index == NULL) + return; + + for (i = 0; i < hash->size; i++) + for (hb = hash->index[i]; hb; hb = hbnext) + { + unsigned int h = hb->key & (new_size - 1); + + hbnext = hb->next; + hb->next = new_index[h]; + new_index[h] = hb; + } + + /* Switch to new table */ + XFREE(MTYPE_HASH_INDEX, hash->index); + hash->size = new_size; + hash->index = new_index; + + /* Ideally, new index should have chains half as long as the original. + If expansion didn't help, then not worth expanding again, + the problem is the hash function. */ + losers = 0; + for (i = 0; i < hash->size; i++) + { + unsigned int len = 0; + for (hb = hash->index[i]; hb; hb = hb->next) + { + if (++len > HASH_THRESHOLD/2) + ++losers; + if (len >= HASH_THRESHOLD) + hash->no_expand = 1; + } + } + + if (losers > hash->count / 2) + hash->no_expand = 1; +} + /* Lookup and return hash backet in hash. If there is no corresponding hash backet and alloc_func is specified, create new hash backet. */ @@ -69,14 +115,19 @@ hash_get (struct hash *hash, void *data, void * (*alloc_func) (void *)) unsigned int key; unsigned int index; void *newdata; + unsigned int len; struct hash_backet *backet; key = (*hash->hash_key) (data); index = key & (hash->size - 1); + len = 0; - for (backet = hash->index[index]; backet != NULL; backet = backet->next) - if (backet->key == key && (*hash->hash_cmp) (backet->data, data)) - return backet->data; + for (backet = hash->index[index]; backet != NULL; backet = backet->next) + { + if (backet->key == key && (*hash->hash_cmp) (backet->data, data)) + return backet->data; + ++len; + } if (alloc_func) { @@ -84,6 +135,12 @@ hash_get (struct hash *hash, void *data, void * (*alloc_func) (void *)) if (newdata == NULL) return NULL; + if (len > HASH_THRESHOLD && !hash->no_expand) + { + hash_expand (hash); + index = key & (hash->size - 1); + } + backet = XMALLOC (MTYPE_HASH_BACKET, sizeof (struct hash_backet)); backet->data = newdata; backet->key = key; diff --git a/lib/hash.h b/lib/hash.h index a6dd5319..920c6685 100644 --- a/lib/hash.h +++ b/lib/hash.h @@ -22,7 +22,8 @@ Boston, MA 02111-1307, USA. */ #define _ZEBRA_HASH_H /* Default hash table size. */ -#define HASHTABSIZE 1024 +#define HASH_INITIAL_SIZE 256 /* initial number of backets. */ +#define HASH_THRESHOLD 10 /* expand when backet. */ struct hash_backet { @@ -44,6 +45,9 @@ struct hash /* Hash table size. Must be power of 2 */ unsigned int size; + /* If expansion failed. */ + int no_expand; + /* Key make function. */ unsigned int (*hash_key) (void *); From f05a5595e6b4805bc1a6bd86fe7076c5ffbb7f00 Mon Sep 17 00:00:00 2001 From: Greg Troxel Date: Sun, 3 Mar 2013 11:38:17 -0500 Subject: [PATCH 085/482] build: Update supported versions. INSTALL.quagga.tex: Given the statement that it's viewed as a bug if quagga doesn't build on OS versions on the list, prune the list to the set for which there would be near-universal agreement that it's a bug. Clarify that the response to a system on the list not building might be dropping it from the list. (Time marches on, and these lists are not necessarily maintained. As an example, the comment saying FreeBSD4 support was iffy is now 6 years old.) Delete old discussion of ancient texinfo. Delete discussion of NetBSD versions before 4 (as no longer relevant). --- INSTALL.quagga.txt | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/INSTALL.quagga.txt b/INSTALL.quagga.txt index ec4e799d..ae3d4ec6 100644 --- a/INSTALL.quagga.txt +++ b/INSTALL.quagga.txt @@ -16,23 +16,20 @@ workarounds for POSIX non-compliance are welcome. It is considered a bug if Quagga fails to build and run on any of the following systems (where .x indicates the most recent release), or -such systems "-current" versions. (Note that considering it a bug is +such systems "-current" versions. Or, it might be that this list is +out of date and will be updated. (Note that considering it a bug is not a guarantee of support, merely "we agree that it is broken".) Dragonfly ? - FreeBSD 4.x [In 2007, this is getting tenous.] - FreeBSD 5.x - FreeBSD 6.x + FreeBSD (stable branches currently supported, plus perhaps one) FreeBSD-current Linux [kernel/distribution information needed] - NetBSD 2.x [Note texinfo 4.6 in base system] - NetBSD 3.x NetBSD 4.x + NetBSD 5.x + NetBSD 6.x NetBSD-current OpenBSD ? [info needed on what should work] - Solaris 9 - Solaris 10 - + Solaris (modern/supported versions, including OpenSolaris forks) For further Quagga specific information on 'configure' and build-time configuration of the software, please read the Quagga info @@ -70,7 +67,8 @@ deficient is made. texinfo: 4.7 (released 2004-04-10; 4.8 is not yet common) GNU AWK: 3.1.5 (released 2005-08-12) -Becuase some systems provide texinfo 4.6 (4.7 is new), quagga.info is +[TODO: texinfo 4.6 is now ancient and this should be revisited/fixed] +Because some systems provide texinfo 4.6 (4.7 is new), quagga.info is checked in so that texinfo will generally not be invoked. When texinfo 4.7 is widespread, quagga.info will be removed from CVS and texinfo will become required again. (4.7 has figure support, needed @@ -91,11 +89,12 @@ instructions. Notes on required versions: The general goal is to use a modern baseline of tools, while not -imposing pain on those tracking stable distributions. The notes below -explain what versions are present in various environments. +imposing pain on those tracking supported (or almost supported) stable +distributions. The notes below explain what versions are present in +various environments. -NetBSD 1.6 and 2 provide texinfo 4.6. This is now considered old. -NetBSD 3 and 4 provide texinfo 4.7. +NetBSD 4 provides texinfo 4.7. +NetBSD 5 and 6 provides texinfo 4.8 Fedora Core ? provides autoconf 2.59. From 3d1e5791c53bf67ebd8ce45322779856974c687f Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Fri, 29 Mar 2013 19:31:55 +0100 Subject: [PATCH 086/482] doc: update TODO the TODO was last touched in 2006. This is a first pass at cleaning it up, motivated primarily by the need for an up-to-date idea list for the Google Summer of Code 2013. Signed-off-by: David Lamparter --- TODO | 236 +++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 173 insertions(+), 63 deletions(-) diff --git a/TODO b/TODO index 24941de0..0da35533 100644 --- a/TODO +++ b/TODO @@ -1,78 +1,188 @@ Quagga TODO list - 2004/11/24 + 2013-03-29 -zebra: -o Pointopoint address configuration. -o Multiple (alias) address configuration for the interface when kernel - support it [just starting]. -o improve rtnetlink to handle sequence number tracking and reconciliation - and resyncs. -o Add support for valid and preferred lifetimes to IPv6 addresses -o proper support for (at least) 1-level recursive routes -o Ability to set src on routes, where systems support it. -o Ability to apply route-maps to daemon route updates. +This is the Quagga primary TODO list. It is on git because that way changes +pass through the usual process just like code does, therefore they will have +the same visibility. -bgpd: +If you are working on something beyond a simple fix, to avoid double work it +is a good idea to submit a patch to this TODO list when you are starting, +listing what you're doing. Also, as others may have done just that, check +the list before starting. -o BGP TCP MD5 authentication by password command. -o HUP signal support (reload configuration file). -o BGP multi-path extension -o move FSM state to be per-connection, not per-peer. -o Add support for internal and minimum-metric MED setting +Google Summer of Code 2013 note: this list double-serves as idea list for the +Summer of Code. Ideas considered suitable for students are marked with a star +after the number, like this: "[Q999*] achieve world peace". They will also +have extended descriptions. Nevertheless, if you'd like to do something else, +just write a mail to the mailing list: quagga-dev@lists.quagga.net -ripd: +"GSoC-Mentors:" listings are preliminary at this point. -o Multipath support. + +Overall +======= + +[Q000] improve unit test architecture + +[Q001] kick invalid runtime tests from configure.ac, use list of supported + OSes and their APIs instead. + Priority: low + State: patch half-done 2013-03-29 David Lamparter + +[Q002*] clean up zebra IPC, remove code duplication, align to common API + Priority: high + GSoC-Mentors: David Lamparter, Christian Franke + + Quagga posesses an IPC mechanism to exchange route information among + the different daemons and Zebra, the kernel-interface. This mechanism + is implemented in libzebra, but is currently used in all sorts of + different ways in the individual protocol daemons. Also, in the future + the entire protocol needs to be redone in an extensible way, so we're + able to support MPLS, BFD, Multi-Topology/Instance, VRFs, ... + + This TODO entry only refers to the first-step API cleanup. All the + daemons need to use a single, well-defined libzebra API. Only after + this has been addressed can we look upon changing the protocol itself, + since by then it will be encapsulated inside libzebra. + +[Q003] add multi-instance / multi-topology support to the individual protocols + +[Q004] MPLS support + State: work in progress 2013-03-29 Renato Westphal, Timo Teräs + +[Q005] BFD support + State: two old implementations exist, contact Hasso Tepper + + +library +======= + +[L000] improve route_table speed, eg strided lookups for common prefix depths. + +[L001] ipv6 addresses need concept of valid/preferred + +[L002] implement a generic daemon access/control protocol (eg D-Bus like? + simplified SNMP-a-like? NETCONF?) + +[L003] extend vty command definitions to allow them to be self-documenting + i18n command help strings + +[L004] create a common libspf (for ospfd, ospf6d and possibly isisd and more). + cf. TODO item [O000] for the ospfd/ospf6d specific variant + +[L005] stabilise the API (possibly including symbol/library versioning voodoo) + +[L006] Document the exported API (DocBook/Doxygen?) + +[LE00] incorporate library changes from Euro-IX branch, except threading + +[LE01] incorporate threading library support from Euro-IX branch + + +zebra +===== + +[Z000] Pointopoint address configuration. + Priority: low + State: patch done & tested 2013-03-29 David Lamparter + +[Z001] Add support for valid and preferred lifetimes to IPv6 addresses + +[Z002] proper support for (at least) 1-level recursive routes + Priority: high + +[Z003] Ability to set src on routes, where systems support it. + +[Z004] Ability to apply route-maps to daemon route updates. + + +bgpd +==== + +[B000] HUP signal support (reload configuration file). + +[B001] BGP multi-path extension, relaxed mode + Priority: medium + +[B002] move FSM state to be per-connection, not per-peer. + +[B003] Add support for internal and minimum-metric MED setting + + +ripd +==== + +[R000] Multipath support. + + +ospfd/ospf6d +============ + +[O000] move SPF to common code + +[O001] extend code sharing between ospfd and ospf6d beyond SPF + +[O002*] OSPF testing replay tool + Priority: medium + GSoC-Mentors: Martin Winter, Christian Franke, David Lamparter + + In order to extensively test OSPF implementations, a tool to fake an + OSPF neighbor is immensely useful. This tool needs to be capable of + forming an adjacency and pushing LSAs to the device to be tested. To + maintain the adjacency, some minimal state tracking is useful. + + In total, the tool needs to form an adjacency, read and push LSAs, and + output received LSAs. Additional tools to generate LSAs from + specifications as well as verify received LSA correctness can then be + built on top of that. + + The tool needs to support IPv4 and IPv6, possibly split into 2 tools + with some code sharing. ospfd: -o Rewrite the incremental RT update code. -o Demand circuits. -o Multiple instances. -o OSPF MIB [SNMP get is amost finished]. -o HUP signal treatment. -o Fragment Oversized LSAs -o move SPF to common code -o NSSA priority rules (RFC3101 2.4) -o Type-7 address ranges (RFC3101 2.2) -o Originating Type-7 default into area (RFC3101 2.7) +[O400] Demand circuits. + Priority: very low + +[O401] Multiple instances. + Priority: medium + +[O402] HUP signal treatment. + Priority: medium + State: patch on ML needs review 2012-06-04 Mattias Walström ospf6d: -o move SPF to common code -o add router-id lookups - -isisd: - -o finish SPF -o select nearest L2 when running SPF for L1 -o remove the routes when holding time for nexthop expires -o redistribution -o autosummary - -o Mesh groups (RFC2973) -o Crypto authentication (RFC3567) - -lib: -o improve route_table speed, eg strided lookups for common prefix depths. -o improve hash tables, eg auto-growing hash tables -o move performance sensitive users of hashes over to jhash -o clean up linked lists -o ipv6 addresses need concept of valid/preferred -o implement a generic daemon access/control protocol (eg D-Bus like? - simplified SNMP-a-like?) -o merge SPF code from ospfd and ospf6d into a common libspf -o depends-on(generic A/C protocol) move snmp to seperate daemon -o extend command definitions to allow them to be self-documenting -o i18n command help strings -o Document the exported API (DocBook/Doxygen?) - -vtysh: -o untangle readline specific bits -o add a vtyd with a vty (ie telnet) frontend (as opposed to readline) -o depends-on(generic A/C protocol) use such -o better AAA support than just PAM, eg krb5, SASL, LDAP.. - ----------------------------- +[O600] fix ospf6d in general + Priority: high + State: patches tickling in from Cumulus Networks 2013-03-29 Dinesh Dutt + +isisd +===== + +[I000] reassess isisd TODO + +[I001*] IS-IS testing replay tool + Priority: medium + GSoC-Mentors: Martin Winter, Christian Franke, David Lamparter + + see [O002*]. + +[I002] Mesh groups (RFC2973) + +[I003] Crypto authentication (RFC3567) + + +vtysh +===== + +[V000] untangle readline specific bits + +[V001] add a vtyd with a vty (ie telnet) frontend (as opposed to readline) + +[V002] (=> [L002]) use daemon control protocol + +[V003] better AAA support than just PAM, eg krb5, SASL, LDAP... From 24c6bb86f4d21c74149bc0a7e50f9b95da0b6fd3 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Fri, 29 Mar 2013 19:40:39 +0100 Subject: [PATCH 087/482] doc: add OSPFv3 homenet to TODO The homenet OSPFv3 extensions are not only relevant TODO items, but also suitable for GSoC students. Signed-off-by: David Lamparter --- TODO | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/TODO b/TODO index 0da35533..ccf4d03d 100644 --- a/TODO +++ b/TODO @@ -159,6 +159,21 @@ ospf6d: Priority: high State: patches tickling in from Cumulus Networks 2013-03-29 Dinesh Dutt +[O601*] OSPFv3 autoconfiguration, prefix assignment and sourcedest routing + Priority: medium + State: work in progress 2013-03-29 Edward Seabrook + GSoC-Mentors: David Lamparter + + OSPFv3 application in the homenet is being designed to use several + extensions to the base protocol. In order of dependency, + autoconfiguration, prefix assignment and sourcedest routing should + be implemented. + + This task requires a good level of OSPF understanding plus proper + ability to follow IETF discussion about these points. Also, since work + has already started on this, improvements must obviously build on top + of that. + isisd ===== From b58c90807c9d0bfa9601704c7490a16070906004 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Ter=C3=A4s?= Date: Fri, 22 Mar 2013 08:54:44 +0000 Subject: [PATCH 088/482] doc: fix makeinfo errors and one warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 4afa50b added few lines that are syntactically incorrect with leading plus sign. Cc: Denis Ovsienko Signed-off-by: Timo Teräs Signed-off-by: David Lamparter --- doc/ipv6.texi | 4 ++-- doc/quagga.texi | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/ipv6.texi b/doc/ipv6.texi index b6cc4376..2482c1c0 100644 --- a/doc/ipv6.texi +++ b/doc/ipv6.texi @@ -136,8 +136,8 @@ for the lowest preference possible. Default: 0 @end deffn -+@deffn {Interface Command} {ipv6 nd home-agent-lifetime <0-65520>} {} -+@deffnx {Interface Command} {no ipv6 nd home-agent-lifetime [<0-65520>]} {} +@deffn {Interface Command} {ipv6 nd home-agent-lifetime <0-65520>} {} +@deffnx {Interface Command} {no ipv6 nd home-agent-lifetime [<0-65520>]} {} The value to be placed in Home Agent Option, when Home Agent config flag is set, which indicates to hosts Home Agent Lifetime. The default value of 0 means to place the current Router Lifetime value. diff --git a/doc/quagga.texi b/doc/quagga.texi index ff913aa5..b4105ac9 100644 --- a/doc/quagga.texi +++ b/doc/quagga.texi @@ -1,13 +1,13 @@ \input texinfo @c -*- texinfo -*- +@c Set variables - sourced from defines.texi +@include defines.texi + @c %**start of header @setchapternewpage odd @settitle @uref{http://www.quagga.net,,@value{PACKAGE_NAME}} @setfilename quagga.info @c %**end of header -@c Set variables - sourced from defines.texi -@include defines.texi - @c automake will automatically generate version.texi @c and set EDITION, VERSION, UPDATED and UPDATED-MONTH @include version.texi From 4a014580ff85428aa41b28503554b2ce982805be Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Wed, 27 Feb 2013 11:24:24 +0100 Subject: [PATCH 089/482] build: fix minimal mixup in gitinfo suffix the original version of this had issues with tagless repositories; to fix that I removed the "-g" part from one of the regexes. I then failed to add those 2 characters back, leading to version numbers like "0.99.220123456" instead of "0.99.22-ga123456". Let's put the "-g" back... Signed-off-by: David Lamparter --- lib/gitversion.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gitversion.pl b/lib/gitversion.pl index 448f13d6..8ddd9ffa 100644 --- a/lib/gitversion.pl +++ b/lib/gitversion.pl @@ -6,7 +6,7 @@ my $gitdesc = `git describe --always --dirty || echo -- \"0-gUNKNOWN\"`; chomp $gitdesc; -my $gitsuffix = ($gitdesc =~ /([0-9a-fA-F]{7}(-dirty)?)$/) ? $1 : "-gUNKNOWN"; +my $gitsuffix = ($gitdesc =~ /([0-9a-fA-F]{7}(-dirty)?)$/) ? "-g$1" : "-gUNKNOWN"; printf STDERR "git suffix: %s\n", $gitsuffix; printf "#define GIT_SUFFIX \"%s\"\n", $gitsuffix; From c095185c26f72bff14238ca1209ef6b2d7a8b935 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sat, 23 Feb 2013 22:17:21 +0100 Subject: [PATCH 090/482] tests: make --disable-bgpd kill bgpd tests too bgpd tests don't compile or run with --disable-bgpd, let's catch this in the Makefile. Reported-by: Joachim Nilsson Signed-off-by: David Lamparter --- tests/Makefile.am | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/Makefile.am b/tests/Makefile.am index cd6b2f1b..7bc880b5 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -4,9 +4,15 @@ DEFS = @DEFS@ $(LOCAL_OPTS) -DSYSCONFDIR=\"$(sysconfdir)/\" AM_CFLAGS = $(PICFLAGS) AM_LDFLAGS = $(PILDFLAGS) +if BGPD +TESTS_BGPD = aspathtest testbgpcap ecommtest testbgpmpattr testbgpmpath +else +TESTS_BGPD = +endif + noinst_PROGRAMS = testsig testbuffer testmemory heavy heavywq heavythread \ - aspathtest testprivs teststream testbgpcap ecommtest \ - testbgpmpattr testchecksum testbgpmpath tabletest + testprivs teststream testchecksum tabletest \ + $(TESTS_BGPD) testsig_SOURCES = test-sig.c testbuffer_SOURCES = test-buffer.c From 58952492d2eedd4b7974274a578a1fa9707125bb Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Wed, 20 Feb 2013 10:00:53 +0000 Subject: [PATCH 091/482] ospfd: fix LSA initialization for build without opaque LSA If configured without opaque LSA support, the old code would incorrectly associate type 5 LSAs with an area. Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- ospfd/ospf_packet.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index 9a4587d9..d3f1b563 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -1660,8 +1660,10 @@ ospf_ls_upd_list_lsa (struct ospf_neighbor *nbr, struct stream *s, case OSPF_AS_EXTERNAL_LSA: #ifdef HAVE_OPAQUE_LSA case OSPF_OPAQUE_AS_LSA: +#endif /* HAVE_OPAQUE_LSA */ lsa->area = NULL; break; +#ifdef HAVE_OPAQUE_LSA case OSPF_OPAQUE_LINK_LSA: lsa->oi = oi; /* Remember incoming interface for flooding control. */ /* Fallthrough */ From 8df55970b64984e5071afd510caad5e778569bc1 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Fri, 1 Mar 2013 12:03:58 +0100 Subject: [PATCH 092/482] build: reference libcap from libzebra (BZ#393,626) While the actual build failures have been fixed independently by d1d3ac9 "build: reorder libraries to address linker error", libzebra still does not reference libcap. This will lead to more build failures if someone else tries to use libzebra and doesn't add libcap. Let's just add libcap here and be done with it. I've not added libcap to the _DEPENDENCIES variable above since libcap is a system library. Actually, the whole _DEPENDENCIES thing is rather fishy; automake automatically sets _DEPENDENCIES from _LIBADD. For the sake of not breaking stuff that works (especially since most autotools stuff is arcane magic), I'm leaving it alone... Signed-off-by: David Lamparter --- lib/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Makefile.am b/lib/Makefile.am index d7c31edb..44d95bbe 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -18,7 +18,7 @@ BUILT_SOURCES = memtypes.h route_types.h gitversion.h libzebra_la_DEPENDENCIES = @LIB_REGEX@ -libzebra_la_LIBADD = @LIB_REGEX@ +libzebra_la_LIBADD = @LIB_REGEX@ @LIBCAP@ pkginclude_HEADERS = \ buffer.h checksum.h command.h filter.h getopt.h hash.h \ From b68da446e977f8069fb72bce73402e2234ffc1d9 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Thu, 28 Feb 2013 22:17:00 +0100 Subject: [PATCH 093/482] ripd: correctly redistribute ifindex routes (BZ#664) ripd had a check to restrict metric 0 to only directly connected routes. This check was implemented by checking against Connected as route type. This is, however, incorrect -- all routes that directly use an interface without a nexthop should be treated as directly connected and passed off with metric 0. ripngd does not posess such a check and was not touched. Reported-by: Sean Fulton Signed-off-by: David Lamparter --- ripd/ripd.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ripd/ripd.c b/ripd/ripd.c index db29d54a..55a1a75f 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -482,8 +482,9 @@ rip_rte_process (struct rte *rte, struct sockaddr_in *from, new_dist = rip_distance_apply (&rinfotmp); new_dist = new_dist ? new_dist : ZEBRA_RIP_DISTANCE_DEFAULT; old_dist = rinfo->distance; - /* Only connected routes may have a valid NULL distance */ - if (rinfo->type != ZEBRA_ROUTE_CONNECT) + /* Only routes directly connected to an interface (nexthop == 0) + * may have a valid NULL distance */ + if (rinfo->nexthop.s_addr != 0) old_dist = old_dist ? old_dist : ZEBRA_RIP_DISTANCE_DEFAULT; /* If imported route does not have STRICT precedence, mark it as a ghost */ From 677bcbbf153fe73e57cb44f668977cbd26661fd4 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Wed, 27 Feb 2013 13:47:23 +0000 Subject: [PATCH 094/482] lib/vty: register vtysh socket in server socket vector (BZ#754) Register the vtysh socket in Vvty_serv_thread so it will be correctly closed on vty_reset instead of being leaked. Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- lib/vty.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/vty.c b/lib/vty.c index 70bf5645..0d6345c8 100644 --- a/lib/vty.c +++ b/lib/vty.c @@ -71,7 +71,7 @@ static char *vty_accesslist_name = NULL; static char *vty_ipv6_accesslist_name = NULL; /* VTY server thread. */ -vector Vvty_serv_thread; +static vector Vvty_serv_thread; /* Current directory. */ char *vty_cwd = NULL; @@ -2509,7 +2509,8 @@ vty_event (enum event event, int sock, struct vty *vty) break; #ifdef VTYSH case VTYSH_SERV: - thread_add_read (master, vtysh_accept, vty, sock); + vty_serv_thread = thread_add_read (master, vtysh_accept, vty, sock); + vector_set_index (Vvty_serv_thread, sock, vty_serv_thread); break; case VTYSH_READ: vty->t_read = thread_add_read (master, vtysh_read, vty, sock); From f2b53dac4c72811c06779c596c6162b994eb427a Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Wed, 20 Mar 2013 15:28:46 +0000 Subject: [PATCH 095/482] ospfd: restore nexthop IP for p2p interfaces commit c81ee5c... "ospfd: Optimize and improve SPF nexthop calculation" subtly changed semantics of routes calculated over pointopoint links by removing the nexthop IP address and instead using an ifindex route. This breaks calculation of AS-Ext routes with a forwarding address since in ospf_ase_complete_direct_routes() this will be hit: if (op->nexthop.s_addr == 0) op->nexthop.s_addr = nexthop.s_addr; thus turning the route unusable by having an invalid nexthop. Fix by restoring the nexthop IP on routes over PtP links. This also allows running multi-access (Ethernet) interfaces in PtP mode again. This bug is a regression against 0.99.21 and only present in 0.99.22. Signed-off-by: Christian Franke [patch description and code comments rewritten] Acked-by: Joakim Tjernlund Acked-by: James Li Signed-off-by: David Lamparter --- ospfd/ospf_spf.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/ospfd/ospf_spf.c b/ospfd/ospf_spf.c index a5242b68..bd9564d9 100644 --- a/ospfd/ospf_spf.c +++ b/ospfd/ospf_spf.c @@ -568,8 +568,22 @@ ospf_nexthop_calculation (struct ospf_area *area, struct vertex *v, */ if (oi->type == OSPF_IFTYPE_POINTOPOINT) { - added = 1; - nexthop.s_addr = 0; /* Nexthop not required */ + /* Having nexthop = 0 is tempting, but NOT acceptable. + It breaks AS-External routes with a forwarding address, + since ospf_ase_complete_direct_routes() will mistakenly + assume we've reached the last hop and should place the + forwarding address as nexthop. + Also, users may configure multi-access links in p2p mode, + so we need the IP to ARP the nexthop. + */ + struct ospf_neighbor *nbr_w; + + nbr_w = ospf_nbr_lookup_by_routerid (oi->nbrs, &l->link_id); + if (nbr_w != NULL) + { + added = 1; + nexthop = nbr_w->src; + } } else if (oi->type == OSPF_IFTYPE_POINTOMULTIPOINT) { From f281ab9752393fcc7cbb54c50edb66f25c2a31fb Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 26 Feb 2013 16:21:20 +0100 Subject: [PATCH 096/482] tests: add DejaGNU framework DejaGNU seems to be the 'standard' GNU test framework (which by itself doesn't say much), but it seems relatively usable and the "remote system" capabilities might come in handy for virtualisation-based tests for kernel interactions or something. Signed-off-by: David Lamparter --- buildtest.sh | 1 + configure.ac | 16 ++++++- tests/.gitignore | 5 ++ tests/Makefile.am | 11 +++++ tests/config/unix.exp | 104 ++++++++++++++++++++++++++++++++++++++++++ tests/global-conf.exp | 0 6 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 tests/config/unix.exp create mode 100644 tests/global-conf.exp diff --git a/buildtest.sh b/buildtest.sh index c9dd323f..de638f56 100755 --- a/buildtest.sh +++ b/buildtest.sh @@ -85,6 +85,7 @@ for cfg in ${CONFIGS:-$defconfigs}; do cd "$bdir" ../sdist/configure $args make -j5 + make check make DESTDIR="$TEMP/inst_$cfg" install cd .. done diff --git a/configure.ac b/configure.ac index 6e59037a..582f3572 100755 --- a/configure.ac +++ b/configure.ac @@ -1619,6 +1619,18 @@ fi AC_SUBST(PICFLAGS) AC_SUBST(PILDFLAGS) +dnl ------- +dnl DejaGNU +dnl ------- +if test x"$DEJAGNU" = x +then + DEJAGNU="\$(top_srcdir)/tests/global-conf.exp" +fi +RUNTESTDEFAULTFLAGS="-x --tool \$\$tool" + +AC_SUBST(DEJAGNU) +AC_SUBST(RUNTESTDEFAULTFLAGS) + dnl ------------------------------ dnl set paths for state directory dnl ------------------------------ @@ -1691,8 +1703,8 @@ AC_MSG_RESULT($ac_cv_htonl_works) AC_CONFIG_FILES([Makefile lib/Makefile zebra/Makefile ripd/Makefile ripngd/Makefile bgpd/Makefile ospfd/Makefile watchquagga/Makefile ospf6d/Makefile isisd/Makefile babeld/Makefile vtysh/Makefile - doc/Makefile ospfclient/Makefile tests/Makefile m4/Makefile - redhat/Makefile + doc/Makefile ospfclient/Makefile tests/Makefile m4/Makefile + redhat/Makefile pkgsrc/Makefile redhat/quagga.spec lib/version.h diff --git a/tests/.gitignore b/tests/.gitignore index 93e886c0..8baea0a8 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -10,6 +10,10 @@ TAGS *.lo *.la *.libs +*.bak +*.log +*.sum +*.xml .arch-inventory .arch-ids aspathtest @@ -27,3 +31,4 @@ testmemory testprivs testsig teststream +site.exp diff --git a/tests/Makefile.am b/tests/Makefile.am index 7bc880b5..14f7bef1 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,3 +1,14 @@ +AUTOMAKE_OPTIONS = dejagnu +export DEJAGNU + +SUBDIRS = + +EXTRA_DIST = \ + config/unix.exp \ + global-conf.exp + +DEJATOOL = + INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib DEFS = @DEFS@ $(LOCAL_OPTS) -DSYSCONFDIR=\"$(sysconfdir)/\" diff --git a/tests/config/unix.exp b/tests/config/unix.exp new file mode 100644 index 00000000..25ea97f4 --- /dev/null +++ b/tests/config/unix.exp @@ -0,0 +1,104 @@ + +# every test should always be run and always return some status. +# so, if we lose sync with a multi-test program, aborted will be used +# to flag the remainder of the tests as untested. +#set aborted 0 + +# only match with color codes since "failed" / "OK" might otherwise +# be part of the output... +#set color 1 + +set config_h [open "../config.h" "r"] +set config_h_text [read $config_h] +close $config_h +set i [string first "#define HAVE_IPV6" $config_h_text] +if { $i >= 0 } { + set have_ipv6 1 +} else { + set have_ipv6 0 +} +send_user "IPv6 enabled: $have_ipv6\n" +set xfail 0 + +proc onetest { test_name note start } { + global aborted + global testprefix + global verbose + global color + global xfail + + if { $aborted > 0 } { + untested "$testprefix$test_name" + return + } + + if { $verbose > 0 } { + send_user "$testprefix$test_name$note\n" + } + expect { + "$start" { } + + eof { unresolved "$testprefix$test_name"; set aborted 1; } + timeout { unresolved "$testprefix$test_name"; set aborted 1; } + } + + if { $aborted > 0 } { + send_user "sync failed: $testprefix$test_name$note -- $testprefix aborted!\n" + return + } + + if { $color } { + set pat "(32mOK|31mfailed)" + } else { + set pat "(OK|failed)" + } + expect { + # need this because otherwise expect will skip over a "failed" and + # grab the next "OK" (or the other way around) + -re "$pat" { + if { "$expect_out(0,string)" == "32mOK" || "$expect_out(0,string)" == "OK" } { + pass "$testprefix$test_name" + } else { + if { $xfail } { + xfail "$testprefix$test_name" + } else { + fail "$testprefix$test_name" + } + } + return + } + + eof { unresolved "$testprefix$test_name"; set aborted 1; } + timeout { unresolved "$testprefix$test_name"; set aborted 1; } + } + + if { $aborted > 0 } { + send_user "failed: $testprefix$test_name$note -- $testprefix aborted!\n" + return + } +} + +proc headerline { line } { + global aborted + if { $aborted > 0 } { return; } + expect { + $line { return; } + eof { send_user "numbering mismatch!\n"; set aborted 1; } + timeout { send_user "numbering mismatch!\n"; set aborted 1; } + } +} + +proc simpletest { start } { + onetest "$start" "" "$start" +} + +proc simpletest_nov6 { start } { + global have_ipv6 + global xfail + + set xfail [expr 1-$have_ipv6] + onetest "$start" "" "$start" + set xfail 0 +} + + diff --git a/tests/global-conf.exp b/tests/global-conf.exp new file mode 100644 index 00000000..e69de29b From 5bb7e4d12b3a3015208fad5ee0f3c055b704c8c4 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 26 Feb 2013 17:53:30 +0100 Subject: [PATCH 097/482] tests: DejaGNU bgpd this just wraps the existing test programs in expect wrappers that make their results usable to DejaGNU. Signed-off-by: David Lamparter --- configure.ac | 1 + tests/Makefile.am | 7 +-- tests/bgpd.tests/Makefile.am | 7 +++ tests/bgpd.tests/aspathtest.exp | 76 ++++++++++++++++++++++++++++++ tests/bgpd.tests/ecommtest.exp | 13 +++++ tests/bgpd.tests/testbgpcap.exp | 51 ++++++++++++++++++++ tests/bgpd.tests/testbgpmpath.exp | 12 +++++ tests/bgpd.tests/testbgpmpattr.exp | 31 ++++++++++++ tests/lib/bgpd.exp | 0 9 files changed, 195 insertions(+), 3 deletions(-) create mode 100644 tests/bgpd.tests/Makefile.am create mode 100644 tests/bgpd.tests/aspathtest.exp create mode 100644 tests/bgpd.tests/ecommtest.exp create mode 100644 tests/bgpd.tests/testbgpcap.exp create mode 100644 tests/bgpd.tests/testbgpmpath.exp create mode 100644 tests/bgpd.tests/testbgpmpattr.exp create mode 100644 tests/lib/bgpd.exp diff --git a/configure.ac b/configure.ac index 582f3572..32a59d97 100755 --- a/configure.ac +++ b/configure.ac @@ -1704,6 +1704,7 @@ AC_CONFIG_FILES([Makefile lib/Makefile zebra/Makefile ripd/Makefile ripngd/Makefile bgpd/Makefile ospfd/Makefile watchquagga/Makefile ospf6d/Makefile isisd/Makefile babeld/Makefile vtysh/Makefile doc/Makefile ospfclient/Makefile tests/Makefile m4/Makefile + tests/bgpd.tests/Makefile redhat/Makefile pkgsrc/Makefile redhat/quagga.spec diff --git a/tests/Makefile.am b/tests/Makefile.am index 14f7bef1..153922b9 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,14 +1,14 @@ AUTOMAKE_OPTIONS = dejagnu export DEJAGNU +DEJATOOL = -SUBDIRS = +SUBDIRS = bgpd.tests EXTRA_DIST = \ config/unix.exp \ + lib/bgpd.exp \ global-conf.exp -DEJATOOL = - INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib DEFS = @DEFS@ $(LOCAL_OPTS) -DSYSCONFDIR=\"$(sysconfdir)/\" @@ -17,6 +17,7 @@ AM_LDFLAGS = $(PILDFLAGS) if BGPD TESTS_BGPD = aspathtest testbgpcap ecommtest testbgpmpattr testbgpmpath +DEJATOOL += bgpd else TESTS_BGPD = endif diff --git a/tests/bgpd.tests/Makefile.am b/tests/bgpd.tests/Makefile.am new file mode 100644 index 00000000..5900186c --- /dev/null +++ b/tests/bgpd.tests/Makefile.am @@ -0,0 +1,7 @@ +EXTRA_DIST = \ + aspathtest.exp \ + ecommtest.exp \ + testbgpcap.exp \ + testbgpmpath.exp \ + testbgpmpattr.exp + diff --git a/tests/bgpd.tests/aspathtest.exp b/tests/bgpd.tests/aspathtest.exp new file mode 100644 index 00000000..dfecec78 --- /dev/null +++ b/tests/bgpd.tests/aspathtest.exp @@ -0,0 +1,76 @@ +set timeout 10 +set testprefix "aspathtest " +set aborted 0 +set color 1 + +spawn "./aspathtest" + +# proc onetest { test_name note start } { +# proc headerline { line } { + +set parserno 0 +proc parsertest { test_name } { + global parserno + headerline "test $parserno" + onetest "parse $test_name" " ($parserno)" "$test_name:" + onetest "parse $test_name +empty_prepend" " (#$parserno)" "empty prepend $test_name:" + incr parserno 1 +} +set attrno 0 +proc attrtest { test_name } { + global attrno + headerline "aspath_attr test $attrno" + onetest "attr $test_name" " (#$attrno)" "$test_name" + incr attrno 1 +} + + +parsertest "seq1" +parsertest "seq2" +parsertest "seq3" +parsertest "seqset" +parsertest "seqset2" +parsertest "multi" +parsertest "confed" +parsertest "confed2" +parsertest "confset" +parsertest "confmulti" +parsertest "seq4" +parsertest "tripleseq1" +parsertest "someprivate" +parsertest "allprivate" +parsertest "long" +parsertest "seq1extra" +parsertest "empty" +parsertest "redundantset" +parsertest "reconcile_lead_asp" +parsertest "reconcile_new_asp" +parsertest "reconcile_confed" +parsertest "reconcile_start_trans" +parsertest "reconcile_start_trans4" +parsertest "reconcile_start_trans_error" +parsertest "redundantset2" +parsertest "zero-size overflow" +parsertest "zero-size overflow + valid segment" +parsertest "invalid segment type" + +for {set i 0} {$i < 10} {incr i 1} { onetest "prepend $i" "" "prepend test $i"; } +for {set i 0} {$i < 5} {incr i 1} { onetest "aggregate $i" "" "aggregate test $i"; } +for {set i 0} {$i < 5} {incr i 1} { onetest "reconcile $i" "" "reconcile test $i"; } +for {set i 0} {$i < 22} {incr i 1} { onetest "compare $i" "" "left cmp "; } + +onetest "empty_get" "" "empty_get_test" +attrtest "basic test" +attrtest "length too short" +attrtest "length too long" +attrtest "incorrect flag" +attrtest "as4_path, with as2 format data" +attrtest "as4, with incorrect attr length" +attrtest "basic 4-byte as-path" +attrtest "4b AS_PATH: too short" +attrtest "4b AS_PATH: too long" +attrtest "4b AS_PATH: too long2" +attrtest "4b AS_PATH: bad flags" +attrtest "4b AS4_PATH w/o AS_PATH" +attrtest "4b AS4_PATH: confed" + diff --git a/tests/bgpd.tests/ecommtest.exp b/tests/bgpd.tests/ecommtest.exp new file mode 100644 index 00000000..074952fa --- /dev/null +++ b/tests/bgpd.tests/ecommtest.exp @@ -0,0 +1,13 @@ +set timeout 10 +set testprefix "ecommtest " +set aborted 0 +set color 0 + +spawn "./ecommtest" + +# proc simpletest { start } { + +simpletest "ipaddr" +simpletest "ipaddr-so" +simpletest "asn" +simpletest "asn4" diff --git a/tests/bgpd.tests/testbgpcap.exp b/tests/bgpd.tests/testbgpcap.exp new file mode 100644 index 00000000..1bbdfd12 --- /dev/null +++ b/tests/bgpd.tests/testbgpcap.exp @@ -0,0 +1,51 @@ +set timeout 10 +set testprefix "testbgpcap " +set aborted 0 +set color 1 + +spawn "./testbgpcap" + +# proc simpletest { start } { + +simpletest "MP4: MP IP/Uni" +simpletest_nov6 "MPv6: MP IPv6/Uni" +simpletest "MP2: MP IP/Multicast" +simpletest_nov6 "MP3: MP IP6/MPLS-labeled VPN" +simpletest_nov6 "MP5: MP IP6/MPLS-VPN" +simpletest "MP6: MP IP4/MPLS-laveled VPN" +simpletest "MP8: MP unknown AFI/SAFI" +simpletest "MP-short: MP IP4/Unicast, length too short (< minimum)" +simpletest "MP-overflow: MP IP4/Unicast, length too long" +simpletest "caphdr: capability header, and no more" +simpletest "nodata: header, no data but length says there is" +simpletest "padded: valid, with padding" +simpletest "minsize: violates minsize requirement" +simpletest "ORF: ORF, simple, single entry, single tuple" +simpletest "ORF-many: ORF, multi entry/tuple" +simpletest "ORFlo: ORF, multi entry/tuple, hdr length too short" +simpletest "ORFlu: ORF, multi entry/tuple, length too long" +simpletest "ORFnu: ORF, multi entry/tuple, entry number too long" +simpletest "ORFno: ORF, multi entry/tuple, entry number too short" +simpletest "ORFpad: ORF, multi entry/tuple, padded to align" +simpletest "AS4: AS4 capability" +simpletest "GR: GR capability" +simpletest "GR-short: GR capability, but header length too short" +simpletest "GR-long: GR capability, but header length too long" +simpletest "GR-trunc: GR capability, but truncated" +simpletest "GR-empty: GR capability, but empty." +simpletest "MP-empty: MP capability, but empty." +simpletest "ORF-empty: ORF capability, but empty." +simpletest "AS4-empty: AS4 capability, but empty." +simpletest "dyn-empty: Dynamic capability, but empty." +simpletest "dyn-old: Dynamic capability (deprecated version)" +simpletest "Cap-singlets: One capability per Optional-Param" +simpletest "Cap-series: Series of capability, one Optional-Param" +simpletest "AS4more: AS4 capability after other caps (singlets)" +simpletest "AS4series: AS4 capability, in series of capabilities" +simpletest "AS4real: AS4 capability, in series of capabilities" +simpletest "AS4real2: AS4 capability, in series of capabilities" +simpletest "DynCap: Dynamic Capability Message, IP/Multicast" +simpletest "DynCapLong: Dynamic Capability Message, IP/Multicast, truncated" +simpletest "DynCapPadded: Dynamic Capability Message, IP/Multicast, padded" +simpletest "DynCapMPCpadded: Dynamic Capability Message, IP/Multicast, cap data padded" +simpletest "DynCapMPCoverflow: Dynamic Capability Message, IP/Multicast, cap data != length" diff --git a/tests/bgpd.tests/testbgpmpath.exp b/tests/bgpd.tests/testbgpmpath.exp new file mode 100644 index 00000000..96a51e39 --- /dev/null +++ b/tests/bgpd.tests/testbgpmpath.exp @@ -0,0 +1,12 @@ +set timeout 10 +set testprefix "testbgpmpath " +set aborted 0 +set color 1 + +spawn "./testbgpmpath" + +# proc simpletest { start } { + +simpletest "bgp maximum-paths config" +simpletest "bgp_mp_list" +simpletest "bgp_info_mpath_update" diff --git a/tests/bgpd.tests/testbgpmpattr.exp b/tests/bgpd.tests/testbgpmpattr.exp new file mode 100644 index 00000000..93355ad7 --- /dev/null +++ b/tests/bgpd.tests/testbgpmpattr.exp @@ -0,0 +1,31 @@ +set timeout 10 +set testprefix "testbgpmpattr " +set aborted 0 +set color 1 + +spawn "./testbgpmpattr" + +# proc simpletest { start } { + +simpletest_nov6 "IPv6: IPV6 MP Reach, global nexthop, 1 NLRI" +simpletest_nov6 "IPv6-2: IPV6 MP Reach, global nexthop, 2 NLRIs" +simpletest_nov6 "IPv6-default: IPV6 MP Reach, global nexthop, 2 NLRIs + default" +simpletest_nov6 "IPv6-lnh: IPV6 MP Reach, global+local nexthops, 2 NLRIs + default" +simpletest "IPv6-nhlen: IPV6 MP Reach, inappropriate nexthop length" +simpletest "IPv6-nhlen2: IPV6 MP Reach, invalid nexthop length" +simpletest "IPv6-nhlen3: IPV6 MP Reach, nexthop length overflow" +simpletest "IPv6-nhlen4: IPV6 MP Reach, nexthop length short" +simpletest "IPv6-nlri: IPV6 MP Reach, NLRI bitlen overflow" +simpletest "IPv4: IPv4 MP Reach, 2 NLRIs + default" +simpletest "IPv4-nhlen: IPv4 MP Reach, nexthop lenth overflow" +simpletest "IPv4-nlrilen: IPv4 MP Reach, nlri lenth overflow" +simpletest "IPv4-MLVPN: IPv4/MPLS-labeled VPN MP Reach, RD, Nexthop, 3 NLRIs" +simpletest "IPv6-bug: IPv6, global nexthop, 1 default NLRI" +simpletest_nov6 "IPv6-unreach: IPV6 MP Unreach, 1 NLRI" +simpletest_nov6 "IPv6-unreach2: IPV6 MP Unreach, 2 NLRIs" +simpletest_nov6 "IPv6-unreach-default: IPV6 MP Unreach, 2 NLRIs + default" +simpletest "IPv6-unreach-nlri: IPV6 MP Unreach, NLRI bitlen overflow" +simpletest "IPv4-unreach: IPv4 MP Unreach, 2 NLRIs + default" +simpletest "IPv4-unreach-nlrilen: IPv4 MP Unreach, nlri length overflow" +simpletest "IPv4-unreach-MLVPN: IPv4/MPLS-labeled VPN MP Unreach, RD, 3 NLRIs" + diff --git a/tests/lib/bgpd.exp b/tests/lib/bgpd.exp new file mode 100644 index 00000000..e69de29b From c69905b673812ce6ec2a12960727a27b9b8d6426 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Thu, 24 Jan 2013 01:39:14 +0100 Subject: [PATCH 098/482] tests: DejaGNU libzebra Wrap the few libzebra test programs we have up for DejaGNU. Signed-off-by: David Lamparter --- configure.ac | 1 + tests/Makefile.am | 7 +++++-- tests/config/unix.exp | 18 ++++++++++++++++++ tests/lib/libzebra.exp | 0 tests/libzebra.tests/Makefile.am | 2 ++ tests/libzebra.tests/tabletest.exp | 9 +++++++++ tests/libzebra.tests/teststream.exp | 28 ++++++++++++++++++++++++++++ 7 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 tests/lib/libzebra.exp create mode 100644 tests/libzebra.tests/Makefile.am create mode 100644 tests/libzebra.tests/tabletest.exp create mode 100644 tests/libzebra.tests/teststream.exp diff --git a/configure.ac b/configure.ac index 32a59d97..ff7a4d54 100755 --- a/configure.ac +++ b/configure.ac @@ -1705,6 +1705,7 @@ AC_CONFIG_FILES([Makefile lib/Makefile zebra/Makefile ripd/Makefile ospf6d/Makefile isisd/Makefile babeld/Makefile vtysh/Makefile doc/Makefile ospfclient/Makefile tests/Makefile m4/Makefile tests/bgpd.tests/Makefile + tests/libzebra.tests/Makefile redhat/Makefile pkgsrc/Makefile redhat/quagga.spec diff --git a/tests/Makefile.am b/tests/Makefile.am index 153922b9..93c06b03 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,12 +1,15 @@ AUTOMAKE_OPTIONS = dejagnu export DEJAGNU -DEJATOOL = +DEJATOOL = libzebra -SUBDIRS = bgpd.tests +SUBDIRS = \ + bgpd.tests \ + libzebra.tests EXTRA_DIST = \ config/unix.exp \ lib/bgpd.exp \ + lib/libzebra.exp \ global-conf.exp INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib diff --git a/tests/config/unix.exp b/tests/config/unix.exp index 25ea97f4..b41f072c 100644 --- a/tests/config/unix.exp +++ b/tests/config/unix.exp @@ -20,6 +20,24 @@ if { $i >= 0 } { send_user "IPv6 enabled: $have_ipv6\n" set xfail 0 +proc onesimple { test_name match } { + global verbose + global aborted + global testprefix + if { $aborted > 0 } { + untested "$testprefix$test_name" + return + } + if { $verbose > 0 } { + send_user "$testprefix$test_name$note\n" + } + expect { + "$match" { pass "$testprefix$test_name"; } + eof { fail "$testprefix$test_name"; set aborted 1; } + timeout { unresolved "$testprefix$test_name"; set aborted 1; } + } +} + proc onetest { test_name note start } { global aborted global testprefix diff --git a/tests/lib/libzebra.exp b/tests/lib/libzebra.exp new file mode 100644 index 00000000..e69de29b diff --git a/tests/libzebra.tests/Makefile.am b/tests/libzebra.tests/Makefile.am new file mode 100644 index 00000000..0d29e287 --- /dev/null +++ b/tests/libzebra.tests/Makefile.am @@ -0,0 +1,2 @@ +EXTRA_DIST = \ + tabletest.exp diff --git a/tests/libzebra.tests/tabletest.exp b/tests/libzebra.tests/tabletest.exp new file mode 100644 index 00000000..5838d4fc --- /dev/null +++ b/tests/libzebra.tests/tabletest.exp @@ -0,0 +1,9 @@ +set timeout 10 +set testprefix "tabletest " +set aborted 0 + +spawn "./tabletest" + +for {set i 0} {$i < 6} {incr i 1} { onesimple "cmp $i" "Verifying cmp"; } +for {set i 0} {$i < 11} {incr i 1} { onesimple "succ $i" "Verifying successor"; } +onesimple "pause" "Verified pausing" diff --git a/tests/libzebra.tests/teststream.exp b/tests/libzebra.tests/teststream.exp new file mode 100644 index 00000000..ca602e30 --- /dev/null +++ b/tests/libzebra.tests/teststream.exp @@ -0,0 +1,28 @@ +set timeout 10 +spawn "./teststream" + +expect { + "endp: 15, readable: 15, writeable: 1009" { } + eof { fail "teststream"; exit; } timeout { fail "teststream"; exit; } } +expect { + "0xef 0xbe 0xef 0xde 0xad 0xbe 0xef 0xde 0xad 0xbe 0xef 0xde 0xad 0xbe 0xef" { } + eof { fail "teststream"; exit; } timeout { fail "teststream"; exit; } } +expect { + "endp: 15, readable: 15, writeable: 0" { } + eof { fail "teststream"; exit; } timeout { fail "teststream"; exit; } } +expect { + "0xef 0xbe 0xef 0xde 0xad 0xbe 0xef 0xde 0xad 0xbe 0xef 0xde 0xad 0xbe 0xef" { } + eof { fail "teststream"; exit; } timeout { fail "teststream"; exit; } } +expect { + "c: 0xef" { } + eof { fail "teststream"; exit; } timeout { fail "teststream"; exit; } } +expect { + "w: 0xbeef" { } + eof { fail "teststream"; exit; } timeout { fail "teststream"; exit; } } +expect { + "l: 0xdeadbeef" { } + eof { fail "teststream"; exit; } timeout { fail "teststream"; exit; } } +expect { + "q: 0xdeadbeefdeadbeef" { } + eof { fail "teststream"; exit; } timeout { fail "teststream"; exit; } } +pass "teststream" From 77ef01392f82c27a9892840c61a5e7391fd82415 Mon Sep 17 00:00:00 2001 From: Dinesh G Dutt Date: Fri, 12 Apr 2013 01:37:15 +0000 Subject: [PATCH 099/482] doc: update TODO for ospf6d work & bgp multipath This is work in progress at Cumulus Networks. --- TODO | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/TODO b/TODO index ccf4d03d..246cc257 100644 --- a/TODO +++ b/TODO @@ -103,8 +103,10 @@ bgpd [B000] HUP signal support (reload configuration file). -[B001] BGP multi-path extension, relaxed mode +[B001*] BGP multi-path extension, relaxed mode Priority: medium + Implemented, patch will be sent shortly + Pradosh Mohapatra, Cumulus Networks [B002] move FSM state to be per-connection, not per-peer. @@ -155,9 +157,13 @@ ospfd: ospf6d: -[O600] fix ospf6d in general +[O600*] fix ospf6d in general Priority: high State: patches tickling in from Cumulus Networks 2013-03-29 Dinesh Dutt + Implemented: p2p link support, ABR, Stub area/Totally Stubby area, + SPF throttling, Improving state machine to get performance/scale, + max-metric support, Improving ECMP to be > 4, Various other bug fixes + [O601*] OSPFv3 autoconfiguration, prefix assignment and sourcedest routing Priority: medium From 4de8bf001189d40e086764fd804e59657d2e21e6 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Wed, 20 Feb 2013 10:00:52 +0000 Subject: [PATCH 100/482] ospfd: make ospf_maxage_lsa_remover actually yield ospf_maxage_lsa_remover whould check whether to yield, but run on anyway. Signed-off-by: Christian Franke Signed-off-by: Joachim Nilsson Signed-off-by: David Lamparter --- ospfd/ospf_lsa.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index dfd1a61e..e0e05ab8 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -2843,6 +2843,9 @@ ospf_maxage_lsa_remover (struct thread *thread) continue; } + /* There is at least one neighbor from which we still await an ack + * for that LSA, so we are not allowed to remove it from our lsdb yet + * as per RFC 2328 section 14 para 4 a) */ if (lsa->retransmit_counter > 0) { reschedule = 1; @@ -2851,7 +2854,10 @@ ospf_maxage_lsa_remover (struct thread *thread) /* TODO: maybe convert this function to a work-queue */ if (thread_should_yield (thread)) - OSPF_TIMER_ON (ospf->t_maxage, ospf_maxage_lsa_remover, 0); + { + OSPF_TIMER_ON (ospf->t_maxage, ospf_maxage_lsa_remover, 0); + return 0; + } /* Remove LSA from the LSDB */ if (IS_LSA_SELF (lsa)) From 4c14b7f684510592f2eb46fd84859d8bca57def9 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Wed, 20 Feb 2013 10:00:54 +0000 Subject: [PATCH 101/482] ospfd: fix flooding procedure An ospf router should accept a new maxage LSA into its lsdb if it has any neighbors in state Exchange or Loading. ospfd would however only account for neighbors on the same interface which does not seem to be a valid optimization. Signed-off-by: Christian Franke Signed-off-by: Joachim Nilsson Signed-off-by: David Lamparter --- ospfd/ospf_lsa.c | 2 +- ospfd/ospf_lsa.h | 1 + ospfd/ospf_packet.c | 3 +-- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index e0e05ab8..109a120b 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -2794,7 +2794,7 @@ ospf_lsa_install (struct ospf *ospf, struct ospf_interface *oi, } -static int +int ospf_check_nbr_status (struct ospf *ospf) { struct listnode *node, *nnode; diff --git a/ospfd/ospf_lsa.h b/ospfd/ospf_lsa.h index 9ff2d920..c71877da 100644 --- a/ospfd/ospf_lsa.h +++ b/ospfd/ospf_lsa.h @@ -251,6 +251,7 @@ extern u_int32_t lsa_seqnum_increment (struct ospf_lsa *); extern void lsa_header_set (struct stream *, u_char, u_char, struct in_addr, struct in_addr); extern struct ospf_neighbor *ospf_nbr_lookup_ptop (struct ospf_interface *); +extern int ospf_check_nbr_status (struct ospf *); /* Prototype for LSA primitive. */ extern struct ospf_lsa *ospf_lsa_new (void); diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index d3f1b563..37223fbb 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -1833,8 +1833,7 @@ ospf_ls_upd (struct ip *iph, struct ospf_header *ospfh, then take the following actions: */ if (IS_LSA_MAXAGE (lsa) && !current && - (ospf_nbr_count (oi, NSM_Exchange) + - ospf_nbr_count (oi, NSM_Loading)) == 0) + ospf_check_nbr_status(oi->ospf)) { /* (4a) Response Link State Acknowledgment. */ ospf_ls_ack_send (nbr, lsa); From 1bf0ca9a07358ff13b1390f4462669e9ea4915dc Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Fri, 5 Jul 2013 18:30:56 +0200 Subject: [PATCH 102/482] tests: fix Makefile.am so it works with BSD make The export statement is specific to GNU make and breaks the build with BSD make. I couldn't observe any difference in behaviour between having the export present and absent, therefore, just remove it. Signed-off-by: Christian Franke --- tests/Makefile.am | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Makefile.am b/tests/Makefile.am index 93c06b03..b7a7fe74 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,5 +1,4 @@ AUTOMAKE_OPTIONS = dejagnu -export DEJAGNU DEJATOOL = libzebra SUBDIRS = \ From d77102025a30eef274e8d343bfec75f87899a417 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Fri, 5 Jul 2013 18:30:57 +0200 Subject: [PATCH 103/482] tests: don't build tests unless make check is run Use check_PROGRAMS instead of noinst_PROGRAMS in tests/Makefile.am to build the tests only when make check is actually run. Signed-off-by: Christian Franke --- tests/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Makefile.am b/tests/Makefile.am index b7a7fe74..2ed0e1c5 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -24,7 +24,7 @@ else TESTS_BGPD = endif -noinst_PROGRAMS = testsig testbuffer testmemory heavy heavywq heavythread \ +check_PROGRAMS = testsig testbuffer testmemory heavy heavywq heavythread \ testprivs teststream testchecksum tabletest \ $(TESTS_BGPD) From 78116ab6e1524815910658898620776ae5fd4d18 Mon Sep 17 00:00:00 2001 From: Greg Troxel Date: Mon, 15 Jul 2013 10:15:49 -0400 Subject: [PATCH 104/482] doc: Modernize INSTALL.quagga.txt. Note that list of prereq versions is out of date. Add DejaGnu for testing. Change references to CVS to git. Signed-off-by: Greg Troxel --- INSTALL.quagga.txt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/INSTALL.quagga.txt b/INSTALL.quagga.txt index ae3d4ec6..0d465879 100644 --- a/INSTALL.quagga.txt +++ b/INSTALL.quagga.txt @@ -46,9 +46,9 @@ The Quagga website (http://www.quagga.net) currently has the info files available in various formats. -------------------------------------------------------------------------- -Building Quagga from CVS checkouts: +Building Quagga from git checkouts: -In order to build from CVS, you will need recent versions of several GNU +In order to build from git, you will need recent versions of several GNU tools, particularly autoconf, automake, libtool, GNU awk and texinfo. Note that the CVS snapshots on the Quagga website should not require these tools; everything is already setup ready to run 'configure'. If you have trouble @@ -61,12 +61,17 @@ a bug. Required versions can be moved earlier if no problems, or later after a judgement that a system without a higher version is deficient is made. + [TODO: this list is out of date as of 2013-07] automake: 1.9.6 (released 2005-07-10) autoconf: 2.59 (2.60 on 2006-06-26 is too recent to require) libtool: 1.5.22 (released 2005-12-18) texinfo: 4.7 (released 2004-04-10; 4.8 is not yet common) GNU AWK: 3.1.5 (released 2005-08-12) +For running tests, one also needs: + + DejaGnu: + [TODO: texinfo 4.6 is now ancient and this should be revisited/fixed] Because some systems provide texinfo 4.6 (4.7 is new), quagga.info is checked in so that texinfo will generally not be invoked. When From c51443f4aa6b7f0b0d6ad5409ad7d4b215092443 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Mon, 8 Jul 2013 23:05:28 +0200 Subject: [PATCH 105/482] ospfd: CVE-2013-2236, stack overrun in apiserver the OSPF API-server (exporting the LSDB and allowing announcement of Opaque-LSAs) writes past the end of fixed on-stack buffers. This leads to an exploitable stack overflow. For this condition to occur, the following two conditions must be true: - Quagga is configured with --enable-opaque-lsa - ospfd is started with the "-a" command line option If either of these does not hold, the relevant code is not executed and the issue does not get triggered. Since the issue occurs on receiving large LSAs (larger than 1488 bytes), it is possible for this to happen during normal operation of a network. In particular, if there is an OSPF router with a large number of interfaces, the Router-LSA of that router may exceed 1488 bytes and trigger this, leading to an ospfd crash. For an attacker to exploit this, s/he must be able to inject valid LSAs into the OSPF domain. Any best-practice protection measure (using crypto authentication, restricting OSPF to internal interfaces, packet filtering protocol 89, etc.) will prevent exploitation. On top of that, remote (not on an OSPF-speaking network segment) attackers will have difficulties bringing up the adjacency needed to inject a LSA. This patch only performs minimal changes to remove the possibility of a stack overrun. The OSPF API in general is quite ugly and needs a rewrite. Reported-by: Ricky Charlet Cc: Florian Weimer Signed-off-by: David Lamparter --- ospfd/ospf_api.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/ospfd/ospf_api.c b/ospfd/ospf_api.c index 74a49e3b..fae942ec 100644 --- a/ospfd/ospf_api.c +++ b/ospfd/ospf_api.c @@ -472,6 +472,9 @@ new_msg_register_event (u_int32_t seqnum, struct lsa_filter_type *filter) emsg->filter.typemask = htons (filter->typemask); emsg->filter.origin = filter->origin; emsg->filter.num_areas = filter->num_areas; + if (len > sizeof (buf)) + len = sizeof(buf); + /* API broken - missing memcpy to fill data */ return msg_new (MSG_REGISTER_EVENT, emsg, seqnum, len); } @@ -488,6 +491,9 @@ new_msg_sync_lsdb (u_int32_t seqnum, struct lsa_filter_type *filter) smsg->filter.typemask = htons (filter->typemask); smsg->filter.origin = filter->origin; smsg->filter.num_areas = filter->num_areas; + if (len > sizeof (buf)) + len = sizeof(buf); + /* API broken - missing memcpy to fill data */ return msg_new (MSG_SYNC_LSDB, smsg, seqnum, len); } @@ -501,13 +507,15 @@ new_msg_originate_request (u_int32_t seqnum, int omsglen; char buf[OSPF_API_MAX_MSG_SIZE]; - omsglen = sizeof (struct msg_originate_request) - sizeof (struct lsa_header) - + ntohs (data->length); - omsg = (struct msg_originate_request *) buf; omsg->ifaddr = ifaddr; omsg->area_id = area_id; - memcpy (&omsg->data, data, ntohs (data->length)); + + omsglen = ntohs (data->length); + if (omsglen > sizeof (buf) - offsetof (struct msg_originate_request, data)) + omsglen = sizeof (buf) - offsetof (struct msg_originate_request, data); + memcpy (&omsg->data, data, omsglen); + omsglen += sizeof (struct msg_originate_request) - sizeof (struct lsa_header); return msg_new (MSG_ORIGINATE_REQUEST, omsg, seqnum, omsglen); } @@ -627,13 +635,16 @@ new_msg_lsa_change_notify (u_char msgtype, assert (data); nmsg = (struct msg_lsa_change_notify *) buf; - len = ntohs (data->length) + sizeof (struct msg_lsa_change_notify) - - sizeof (struct lsa_header); nmsg->ifaddr = ifaddr; nmsg->area_id = area_id; nmsg->is_self_originated = is_self_originated; memset (&nmsg->pad, 0, sizeof (nmsg->pad)); - memcpy (&nmsg->data, data, ntohs (data->length)); + + len = ntohs (data->length); + if (len > sizeof (buf) - offsetof (struct msg_lsa_change_notify, data)) + len = sizeof (buf) - offsetof (struct msg_lsa_change_notify, data); + memcpy (&nmsg->data, data, len); + len += sizeof (struct msg_lsa_change_notify) - sizeof (struct lsa_header); return msg_new (msgtype, nmsg, seqnum, len); } From c423d413e464913ee88c1ee700e2c4037e6bdb24 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 30 Jul 2013 15:36:26 +0200 Subject: [PATCH 106/482] lib: unconditionally include stddef.h I've used offsetof() in the previous commit to paper over the security problems in ospf_api.c. This blows the build on FreeBSD 7.0, missing offsetof(). Let's add that to zebra's generally used includes. stddef.h (and offsetof) is defined in C89 section 4.1.5 (and not deprecated/removed by any later standard). If this causes problems, the bug report should go against the host OS/compiler... Signed-off-by: David Lamparter --- lib/zebra.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/zebra.h b/lib/zebra.h index ffca7a8f..780e12eb 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -40,6 +40,7 @@ typedef int socklen_t; #include #include #include +#include #include #include #include @@ -175,7 +176,6 @@ typedef int socklen_t; #include #include #include -#include #else #define RT_TABLE_MAIN 0 #endif /* HAVE_NETLINK */ From 397b5bded5654a31b4bd3b904f091fd3859aecf7 Mon Sep 17 00:00:00 2001 From: Leonid Rosenboim Date: Tue, 30 Jul 2013 20:14:25 +0200 Subject: [PATCH 107/482] bgpd: stricter packet handling in OpenSent Keepalives and updates are not expected in OpenSent, prior to receiving the peer's open message. Terminate the session with the proper notification. From: Leonid Rosenboim [split off FSM changes, some reordering & cleanup. read handling needs to be separately addressed] Signed-off-by: David Lamparter --- bgpd/bgp_fsm.c | 59 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index d9200457..fba94276 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -592,6 +592,32 @@ bgp_stop_with_error (struct peer *peer) return 0; } + +/* something went wrong, send notify and tear down */ +static int +bgp_stop_with_notify (struct peer *peer, u_char code, u_char sub_code) +{ + /* Send notify to remote peer */ + bgp_notify_send (peer, code, sub_code); + + /* Sweep if it is temporary peer. */ + if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) + { + zlog_info ("%s [Event] Accepting BGP peer is deleted", peer->host); + peer_delete (peer); + return -1; + } + + /* Clear start timer value to default. */ + peer->v_start = BGP_INIT_START_TIMER; + + /* bgp_stop needs to be invoked while in Established state */ + bgp_stop(peer); + + return 0; +} + + /* TCP connection open. Next we send open message to remote peer. And add read thread for reading open message. */ static int @@ -740,29 +766,26 @@ bgp_fsm_keepalive_expire (struct peer *peer) return 0; } +/* FSM error, unexpected event. This is error of BGP connection. So cut the + peer and change to Idle status. */ +static int +bgp_fsm_event_error (struct peer *peer) +{ + plog_err (peer->log, "%s [FSM] unexpected packet received in state %s", + peer->host, LOOKUP (bgp_status_msg, peer->status)); + + return bgp_stop_with_notify (peer, BGP_NOTIFY_FSM_ERR, 0); +} + /* Hold timer expire. This is error of BGP connection. So cut the peer and change to Idle status. */ static int bgp_fsm_holdtime_expire (struct peer *peer) { if (BGP_DEBUG (fsm, FSM)) - zlog (peer->log, LOG_DEBUG, "%s [FSM] Hold timer expire", peer->host); + plog_debug (peer->log, "%s [FSM] Hold timer expire", peer->host); - /* Send notify to remote peer. */ - bgp_notify_send (peer, BGP_NOTIFY_HOLD_ERR, 0); - - /* Sweep if it is temporary peer. */ - if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) - { - zlog_info ("%s [Event] Accepting BGP peer is deleted", peer->host); - peer_delete (peer); - return -1; - } - - /* bgp_stop needs to be invoked while in Established state */ - bgp_stop(peer); - - return 0; + return bgp_stop_with_notify (peer, BGP_NOTIFY_HOLD_ERR, 0); } /* Status goes to Established. Send keepalive packet then make first @@ -977,8 +1000,8 @@ static const struct { {bgp_fsm_holdtime_expire, Idle}, /* Hold_Timer_expired */ {bgp_ignore, Idle}, /* KeepAlive_timer_expired */ {bgp_fsm_open, OpenConfirm}, /* Receive_OPEN_message */ - {bgp_ignore, Idle}, /* Receive_KEEPALIVE_message */ - {bgp_ignore, Idle}, /* Receive_UPDATE_message */ + {bgp_fsm_event_error, Idle}, /* Receive_KEEPALIVE_message */ + {bgp_fsm_event_error, Idle}, /* Receive_UPDATE_message */ {bgp_stop_with_error, Idle}, /* Receive_NOTIFICATION_message */ {bgp_ignore, Idle}, /* Clearing_Completed */ }, From 9e47abd862f71847a85f330435c7b3a9b1f76099 Mon Sep 17 00:00:00 2001 From: Rakesh Garimella Date: Mon, 11 Mar 2013 12:38:31 +0000 Subject: [PATCH 108/482] bgpd: prevent double address delete on shutdown bgp_interface_down() and bgp_exit() both proceed to delete the address from bgpd's interface representation, so the second call gets a NULL result from the hash lookup and subsequently crashes. Signed-off-by: Rakesh Garimella [reformatted] Signed-off-by: David Lamparter --- bgpd/bgp_nexthop.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index d4692366..4076fe41 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -604,6 +604,10 @@ bgp_address_del (struct prefix *p) tmp.addr = p->u.prefix4; addr = hash_lookup (bgp_address_hash, &tmp); + /* may have been deleted earlier by bgp_interface_down() */ + if (addr == NULL) + return; + addr->refcnt--; if (addr->refcnt == 0) From 8ff202e2d3fa7ebbd6728fdd230f3ad1a20578cd Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Wed, 31 Jul 2013 14:39:41 +0200 Subject: [PATCH 109/482] bgpd: write NOTIFY non-blockingly switching the socket to blocking may well block the entire bgpd process for some time if our peer is overloaded (which may well be the original reason for the NOTIFY) The error handling is slightly different from the previous ML discussion on this; buffer exhaustion isn't technically a fatal TCP error, and we should probably proceed with FSM actions according to a sent NOTIFY (adjusting timers) even if we didn't manage to get the NOTIFY onto the wire. Acked-by: Leonid Rosenboim Signed-off-by: David Lamparter --- bgpd/bgp_packet.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index d115353f..d71df082 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -719,14 +719,15 @@ bgp_write_notify (struct peer *peer) return 0; assert (stream_get_endp (s) >= BGP_HEADER_SIZE); - /* Put socket in blocking mode. */ - val = fcntl (peer->fd, F_GETFL, 0); - fcntl (peer->fd, F_SETFL, val & ~O_NONBLOCK); - /* Stop collecting data within the socket */ sockopt_cork (peer->fd, 0); + /* socket is in nonblocking mode, if we can't deliver the NOTIFY, well, + * we only care about getting a clean shutdown at this point. */ ret = write (peer->fd, STREAM_DATA (s), stream_get_endp (s)); + + /* only connection reset/close gets counted as TCP_fatal_error, failure + * to write the entire NOTIFY doesn't get different FSM treatment */ if (ret <= 0) { BGP_EVENT_ADD (peer, TCP_fatal_error); From 67e7a2127c05a8c7dfddd7ffc6378edf6b666d55 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Mon, 4 Mar 2013 09:23:30 +0000 Subject: [PATCH 110/482] vtysh: don't append superflous spaces (BZ#750) rl_completion_append_character is reset to space every time the completion function is entered. So we would have to set it to '\0' every time new_completion() is called. We can make this conditional and avoid using rl_pending_input. This code path is most relevant when there are multiple completion matches with the same prefix, e.g. in router bgp context: "neighbor 1.2.3.4 pa" would have been completed to "neighbor 1.2.3.4 pass " instead of "neighbor 1.2.3.4 pass". Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- vtysh/vtysh.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index e3709e07..c575902c 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -677,8 +677,9 @@ new_completion (char *text, int start, int end) if (matches) { rl_point = rl_end; - if (complete_status == CMD_COMPLETE_FULL_MATCH) - rl_pending_input = ' '; + if (complete_status != CMD_COMPLETE_FULL_MATCH) + /* only append a space on full match */ + rl_completion_append_character = '\0'; } return matches; @@ -2214,9 +2215,6 @@ vtysh_readline_init (void) rl_bind_key ('?', (Function *) vtysh_rl_describe); rl_completion_entry_function = vtysh_completion_entry_function; rl_attempted_completion_function = (CPPFunction *)new_completion; - /* do not append space after completion. It will be appended - * in new_completion() function explicitly. */ - rl_completion_append_character = '\0'; } char * From a52070899afed55ae44ed6a140ac090cdf07d141 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Thu, 11 Apr 2013 08:24:29 +0000 Subject: [PATCH 111/482] zebra: don't printf to stdout on ZEBRA_IPV6_NEXTHOP_LOOKUP Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- zebra/zserv.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/zebra/zserv.c b/zebra/zserv.c index cb8dbcb3..8aaaea6f 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -1049,7 +1049,9 @@ zread_ipv6_nexthop_lookup (struct zserv *client, u_short length) char buf[BUFSIZ]; stream_get (&addr, client->ibuf, 16); - printf ("DEBUG %s\n", inet_ntop (AF_INET6, &addr, buf, BUFSIZ)); + if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: looking up %s", __func__, + inet_ntop (AF_INET6, &addr, buf, BUFSIZ)); return zsend_ipv6_nexthop_lookup (client, &addr); } From 4ff3bcad8e81b643f3247317a3949d7867b36f75 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Wed, 20 Mar 2013 10:50:07 +0000 Subject: [PATCH 112/482] isisd, ospf6d: use bug-report information from autoconf Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- isisd/isis_main.c | 2 +- ospf6d/ospf6_main.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/isisd/isis_main.c b/isisd/isis_main.c index dcbc3de9..96816ebb 100644 --- a/isisd/isis_main.c +++ b/isisd/isis_main.c @@ -144,7 +144,7 @@ Daemon which manages IS-IS routing\n\n\ -C, --dryrun Check configuration for validity and exit\n\ -h, --help Display this help and exit\n\ \n\ -Report bugs to http://bugzilla.quagga.net\n", progname); +Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS); } exit (status); diff --git a/ospf6d/ospf6_main.c b/ospf6d/ospf6_main.c index 1bdf52fb..17d7654e 100644 --- a/ospf6d/ospf6_main.c +++ b/ospf6d/ospf6_main.c @@ -125,7 +125,7 @@ Daemon which manages OSPF version 3.\n\n\ -C, --dryrun Check configuration for validity and exit\n\ -h, --help Display this help and exit\n\ \n\ -Report bugs to zebra@zebra.org\n", progname); +Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS); } exit (status); From a0f6ce5b41dcfa059074d72c8fc61896d3e996a9 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Thu, 11 Apr 2013 08:24:30 +0000 Subject: [PATCH 113/482] bgpd: honor PEER_FLAG_DISABLE_CONNECTED_CHECK on bgp_scan When neighbor disable-connected-check was used, bgpd would accept routes with unconnected nexthop as indended, however those routes would be invalidated on the next bgp_scan run as that function did not know about disable-connected-check. Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- bgpd/bgp_nexthop.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index 4076fe41..17586bc8 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -453,7 +453,8 @@ bgp_scan (afi_t afi, safi_t safi) changed = 0; metricchanged = 0; - if (bi->peer->sort == BGP_PEER_EBGP && bi->peer->ttl == 1) + if (bi->peer->sort == BGP_PEER_EBGP && bi->peer->ttl == 1 + && !CHECK_FLAG(bi->peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)) valid = bgp_nexthop_onlink (afi, bi->attr); else valid = bgp_nexthop_lookup (afi, bi->peer, bi, From 5b9f51828db732d56053500b1d257797f7f3401b Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Sat, 25 May 2013 14:01:34 +0000 Subject: [PATCH 114/482] zebra: improve display of NEXTHOP_IPV4_IFINDEX in show ip route Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- zebra/zebra_vty.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index d07b09c8..a672d422 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -621,7 +621,11 @@ vty_show_ip_route_detail (struct vty *vty, struct route_node *rn) { case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV4_IFINDEX: - vty_out (vty, " via %s)", inet_ntoa (nexthop->rgate.ipv4)); + vty_out (vty, " via %s", inet_ntoa (nexthop->rgate.ipv4)); + if (nexthop->rifindex) + vty_out (vty, ", %s", ifindex2ifname (nexthop->rifindex)); + vty_out (vty, ")"); + break; case NEXTHOP_TYPE_IFINDEX: case NEXTHOP_TYPE_IFNAME: @@ -731,7 +735,10 @@ vty_show_ip_route (struct vty *vty, struct route_node *rn, struct rib *rib) { case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV4_IFINDEX: - vty_out (vty, " via %s)", inet_ntoa (nexthop->rgate.ipv4)); + vty_out (vty, " via %s", inet_ntoa (nexthop->rgate.ipv4)); + if (nexthop->rifindex) + vty_out (vty, ", %s", ifindex2ifname (nexthop->rifindex)); + vty_out (vty, ")"); break; case NEXTHOP_TYPE_IFINDEX: case NEXTHOP_TYPE_IFNAME: From bb97e4622ed6f48e2b8e07f1f94edd03162223a1 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Sat, 25 May 2013 14:01:35 +0000 Subject: [PATCH 115/482] bgpd, zebra: Support NEXTHOP_IPV4_IFINDEX in nexthop_lookup api Since commit ba281d3d040, ospfd uses NEXTHOP_IPV4_IFINDEX routes. The API between zebra and bgpd which is used to query nexthops for recursive routes did not support this nexthop type and therefore, ospf changes (or any other IGP changes which use NEXTHOP_IPV4_IFINDEX) would never trigger any recursive route update. Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- bgpd/bgp_nexthop.c | 13 +++++++++++++ zebra/zserv.c | 12 ++++++++++++ 2 files changed, 25 insertions(+) diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index 17586bc8..7d8d8665 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -122,6 +122,11 @@ bgp_nexthop_same (struct nexthop *next1, struct nexthop *next2) if (! IPV4_ADDR_SAME (&next1->gate.ipv4, &next2->gate.ipv4)) return 0; break; + case ZEBRA_NEXTHOP_IPV4_IFINDEX: + if (! IPV4_ADDR_SAME (&next1->gate.ipv4, &next2->gate.ipv4) + || next1->ifindex != next2->ifindex) + return 0; + break; case ZEBRA_NEXTHOP_IFINDEX: case ZEBRA_NEXTHOP_IFNAME: if (next1->ifindex != next2->ifindex) @@ -832,6 +837,10 @@ zlookup_read (void) case ZEBRA_NEXTHOP_IPV4: nexthop->gate.ipv4.s_addr = stream_get_ipv4 (s); break; + case ZEBRA_NEXTHOP_IPV4_IFINDEX: + nexthop->gate.ipv4.s_addr = stream_get_ipv4 (s); + nexthop->ifindex = stream_getl (s); + break; case ZEBRA_NEXTHOP_IFINDEX: case ZEBRA_NEXTHOP_IFNAME: nexthop->ifindex = stream_getl (s); @@ -1304,6 +1313,10 @@ show_ip_bgp_scan_tables (struct vty *vty, const char detail) case NEXTHOP_TYPE_IPV4: vty_out (vty, " gate %s%s", inet_ntop (AF_INET, &bnc->nexthop[i].gate.ipv4, buf, INET6_ADDRSTRLEN), VTY_NEWLINE); break; + case NEXTHOP_TYPE_IPV4_IFINDEX: + vty_out (vty, " gate %s", inet_ntop (AF_INET, &bnc->nexthop[i].gate.ipv4, buf, INET6_ADDRSTRLEN)); + vty_out (vty, " ifidx %u%s", bnc->nexthop[i].ifindex, VTY_NEWLINE); + break; case NEXTHOP_TYPE_IFINDEX: vty_out (vty, " ifidx %u%s", bnc->nexthop[i].ifindex, VTY_NEWLINE); break; diff --git a/zebra/zserv.c b/zebra/zserv.c index 8aaaea6f..f792c838 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -548,6 +548,8 @@ zsend_ipv4_nexthop_lookup (struct zserv *client, struct in_addr addr) if (rib) { + if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: Matching rib entry found.", __func__); stream_putl (s, rib->metric); num = 0; nump = stream_get_endp(s); @@ -561,6 +563,10 @@ zsend_ipv4_nexthop_lookup (struct zserv *client, struct in_addr addr) case ZEBRA_NEXTHOP_IPV4: stream_put_in_addr (s, &nexthop->gate.ipv4); break; + case ZEBRA_NEXTHOP_IPV4_IFINDEX: + stream_put_in_addr (s, &nexthop->gate.ipv4); + stream_putl (s, nexthop->ifindex); + break; case ZEBRA_NEXTHOP_IFINDEX: case ZEBRA_NEXTHOP_IFNAME: stream_putl (s, nexthop->ifindex); @@ -575,6 +581,8 @@ zsend_ipv4_nexthop_lookup (struct zserv *client, struct in_addr addr) } else { + if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: No matching rib entry found.", __func__); stream_putl (s, 0); stream_putc (s, 0); } @@ -890,8 +898,12 @@ static int zread_ipv4_nexthop_lookup (struct zserv *client, u_short length) { struct in_addr addr; + char buf[BUFSIZ]; addr.s_addr = stream_get_ipv4 (client->ibuf); + if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: looking up %s", __func__, + inet_ntop (AF_INET, &addr, buf, BUFSIZ)); return zsend_ipv4_nexthop_lookup (client, addr); } From a12afd5e8e57c95505d4d0166af234c7f19e9fe1 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Sat, 25 May 2013 14:01:36 +0000 Subject: [PATCH 116/482] bgpd, zebra: support NEXTHOP_IPV4_IFINDEX in bgp import check Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- bgpd/bgp_nexthop.c | 16 +++++++++++----- zebra/zserv.c | 4 ++++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index 7d8d8665..e23155c7 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -1093,14 +1093,20 @@ bgp_import_check (struct prefix *p, u_int32_t *igpmetric, { nexthop.s_addr = 0; nexthop_type = stream_getc (s); - if (nexthop_type == ZEBRA_NEXTHOP_IPV4) + switch (nexthop_type) { + case ZEBRA_NEXTHOP_IPV4: nexthop.s_addr = stream_get_ipv4 (s); - if (igpnexthop) - *igpnexthop = nexthop; + break; + case ZEBRA_NEXTHOP_IPV4_IFINDEX: + nexthop.s_addr = stream_get_ipv4 (s); + /* ifindex */ (void)stream_getl (s); + break; + default: + /* do nothing */ + break; } - else - *igpnexthop = nexthop; + *igpnexthop = nexthop; return 1; } diff --git a/zebra/zserv.c b/zebra/zserv.c index f792c838..11829378 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -627,6 +627,10 @@ zsend_ipv4_import_lookup (struct zserv *client, struct prefix_ipv4 *p) case ZEBRA_NEXTHOP_IPV4: stream_put_in_addr (s, &nexthop->gate.ipv4); break; + case ZEBRA_NEXTHOP_IPV4_IFINDEX: + stream_put_in_addr (s, &nexthop->gate.ipv4); + stream_putl (s, nexthop->ifindex); + break; case ZEBRA_NEXTHOP_IFINDEX: case ZEBRA_NEXTHOP_IFNAME: stream_putl (s, nexthop->ifindex); From 23cd8fb7133befdb84b3a918f7b2f6147161ac6e Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Fri, 2 Aug 2013 07:27:53 +0000 Subject: [PATCH 117/482] ospfd: protect vs. VU#229804 (malformed Router-LSA) VU#229804 reports that, by injecting Router LSAs with the Advertising Router ID different from the Link State ID, OSPF implementations can be tricked into retaining and using invalid information. Quagga is not vulnerable to this because it looks up Router LSAs by (Router-ID, LS-ID) pair. The relevant code is in ospf_lsa.c l.3140. Note the double "id" parameter at the end. Still, we can provide an improvement here by discarding such malformed LSAs and providing a warning to the administrator. While we cannot prevent such malformed LSAs from entering the OSPF domain, we can certainly try to limit their distribution. cf. http://www.kb.cert.org/vuls/id/229804 for the vulnerability report. This issue is a specification issue in the OSPF protocol that was discovered by Dr. Gabi Nakibly. Reported-by: CERT Coordination Center Signed-off-by: David Lamparter --- ospfd/ospf_packet.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index 37223fbb..ab68bf0b 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -1823,6 +1823,27 @@ ospf_ls_upd (struct ip *iph, struct ospf_header *ospfh, DISCARD_LSA (lsa,2); } + /* VU229804: Router-LSA Adv-ID must be equal to LS-ID */ + if (lsa->data->type == OSPF_ROUTER_LSA) + if (!IPV4_ADDR_SAME(&lsa->data->id, &lsa->data->adv_router)) + { + char buf1[INET_ADDRSTRLEN]; + char buf2[INET_ADDRSTRLEN]; + char buf3[INET_ADDRSTRLEN]; + + zlog_err("Incoming Router-LSA from %s with " + "Adv-ID[%s] != LS-ID[%s]", + inet_ntop (AF_INET, &ospfh->router_id, + buf1, INET_ADDRSTRLEN), + inet_ntop (AF_INET, &lsa->data->id, + buf2, INET_ADDRSTRLEN), + inet_ntop (AF_INET, &lsa->data->adv_router, + buf3, INET_ADDRSTRLEN)); + zlog_err("OSPF domain compromised by attack or corruption. " + "Verify correct operation of -ALL- OSPF routers."); + DISCARD_LSA (lsa, 0); + } + /* Find the LSA in the current database. */ current = ospf_lsa_lookup_by_header (oi->area, lsa->data); From 599da95527ec8e09cd3d890dc3addc6f2e791a0c Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Thu, 24 Jan 2013 14:04:43 +0000 Subject: [PATCH 118/482] zebra: process information about new addresses (BZ#486) Because of a change of semantics in the Linux kernel, information about changes made by zebra itself was not considered for updates. This change should fix this by accounting for the new semantics. It is based on a patch by lich posted to the bugzilla #486 Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- zebra/rt_netlink.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index bab17037..86e02efb 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -381,8 +381,11 @@ netlink_parse_info (int (*filter) (struct sockaddr_nl *, struct nlmsghdr *), lookup (nlmsg_str, h->nlmsg_type), h->nlmsg_type, h->nlmsg_seq, h->nlmsg_pid); - /* skip unsolicited messages originating from command socket */ - if (nl != &netlink_cmd && h->nlmsg_pid == netlink_cmd.snl.nl_pid) + /* skip unsolicited messages originating from command socket + * linux sets the originators port-id for {NEW|DEL}ADDR messages, + * so this has to be checked here. */ + if (nl != &netlink_cmd && h->nlmsg_pid == netlink_cmd.snl.nl_pid + && (h->nlmsg_type != RTM_NEWADDR && h->nlmsg_type != RTM_DELADDR)) { if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug ("netlink_parse_info: %s packet comes from %s", From 9db047fcb1bedcf1a5a1962e49aa70f48f969b96 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Thu, 24 Jan 2013 14:04:44 +0000 Subject: [PATCH 119/482] zebra: make if_subnet_delete a bit more strict Enhance if_subnet_delete so it will complain about improper use. Also, fix one occurence of improper use where it was called for IPv6 as well. Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- zebra/connected.c | 5 +++-- zebra/interface.c | 17 ++++++++++++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/zebra/connected.c b/zebra/connected.c index 05538c5d..3db1271d 100644 --- a/zebra/connected.c +++ b/zebra/connected.c @@ -49,8 +49,9 @@ connected_withdraw (struct connected *ifc) { zebra_interface_address_delete_update (ifc->ifp, ifc); - if_subnet_delete (ifc->ifp, ifc); - + if (ifc->address->family == AF_INET) + if_subnet_delete (ifc->ifp, ifc); + if (ifc->address->family == AF_INET) connected_down_ipv4 (ifc->ifp, ifc); #ifdef HAVE_IPV6 diff --git a/zebra/interface.c b/zebra/interface.c index 3578b790..baa7ab63 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -161,11 +161,26 @@ if_subnet_delete (struct interface *ifp, struct connected *ifc) /* Get address derived subnet node. */ rn = route_node_lookup (zebra_if->ipv4_subnets, ifc->address); if (! (rn && rn->info)) - return -1; + { + zlog_warn("Trying to remove an address from an unknown subnet." + " (please report this bug)"); + return -1; + } route_unlock_node (rn); /* Untie address from subnet's address list. */ addr_list = rn->info; + + /* Deleting an address that is not registered is a bug. + * In any case, we shouldn't decrement the lock counter if the address + * is unknown. */ + if (!listnode_lookup(addr_list, ifc)) + { + zlog_warn("Trying to remove an address from a subnet where it is not" + " currently registered. (please report this bug)"); + return -1; + } + listnode_delete (addr_list, ifc); route_unlock_node (rn); From 676e1a0142ec8b181d4d7ea1038f461bc67c1ee1 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Thu, 24 Jan 2013 14:04:45 +0000 Subject: [PATCH 120/482] zebra: clear ZEBRA_IFC_CONFIGURED on "no ipv6 addr" To match the semantics of IPv4, the ZEBRA_IFC_CONFIGURED flag should be cleared when an IPv6 connected is uninstalled via vty. Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- zebra/interface.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zebra/interface.c b/zebra/interface.c index baa7ab63..cd78ebbc 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -1499,6 +1499,8 @@ ipv6_address_uninstall (struct vty *vty, struct interface *ifp, if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED)) return CMD_WARNING; + UNSET_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED); + /* This is not real address or interface is not active. */ if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_REAL) || ! CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE)) From d7f5dad6d1ab3078fcabc79e15a88873940c074d Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Thu, 24 Jan 2013 14:04:46 +0000 Subject: [PATCH 121/482] zebra: consolidate connected_implicit_withdraw connected_implicit_withdraw is used at two places and followed by exactly the same code. Move that code into connected_implicit_withdraw and give that function a more descriptive name. Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- zebra/connected.c | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/zebra/connected.c b/zebra/connected.c index 3db1271d..38ab37d5 100644 --- a/zebra/connected.c +++ b/zebra/connected.c @@ -137,14 +137,10 @@ connected_same (struct connected *ifc1, struct connected *ifc2) return 1; } -/* Handle implicit withdrawals of addresses, where a system ADDs an address - * to an interface which already has the same address configured. - * - * Returns the struct connected which must be announced to clients, - * or NULL if nothing to do. - */ -static struct connected * -connected_implicit_withdraw (struct interface *ifp, struct connected *ifc) +/* Handle changes to addresses and send the neccesary announcements + * to clients. */ +static void +connected_update(struct interface *ifp, struct connected *ifc) { struct connected *current; @@ -161,13 +157,17 @@ connected_implicit_withdraw (struct interface *ifp, struct connected *ifc) { /* nothing to do */ connected_free (ifc); - return NULL; + return; } - + + /* Clear the configured flag on the old ifc, so it will be freed by + * connected withdraw. */ UNSET_FLAG(current->conf, ZEBRA_IFC_CONFIGURED); connected_withdraw (current); /* implicit withdraw - freebsd does this */ } - return ifc; + + /* If the connected is new or has changed, announce it */ + connected_announce(ifp, ifc); } /* Called from if_up(). */ @@ -273,11 +273,7 @@ connected_add_ipv4 (struct interface *ifp, int flags, struct in_addr *addr, if (label) ifc->label = XSTRDUP (MTYPE_CONNECTED_LABEL, label); - /* nothing to do? */ - if ((ifc = connected_implicit_withdraw (ifp, ifc)) == NULL) - return; - - connected_announce (ifp, ifc); + connected_update(ifp, ifc); } void @@ -401,11 +397,8 @@ connected_add_ipv6 (struct interface *ifp, int flags, struct in6_addr *addr, /* Label of this address. */ if (label) ifc->label = XSTRDUP (MTYPE_CONNECTED_LABEL, label); - - if ((ifc = connected_implicit_withdraw (ifp, ifc)) == NULL) - return; - - connected_announce (ifp, ifc); + + connected_update(ifp, ifc); } void From c7df92de2dc91d0a934cf892e543728cb1a10849 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Thu, 24 Jan 2013 14:04:47 +0000 Subject: [PATCH 122/482] zebra: warn if advertising connected with _REAL unset The implementation in zebra and the zclient protocol allow to communicate addresses to clients which are not yet in the kernel. This is usually not done and most clients seem to expect an address to be configured in the kernel when they receive it. Therefore, it seems reasonable to issue a warning when advertising an address to the clients that is not yet in the kernel. Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- zebra/redistribute.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/zebra/redistribute.c b/zebra/redistribute.c index 47658248..078c2ad0 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -358,6 +358,9 @@ zebra_interface_address_add_update (struct interface *ifp, p->prefixlen, ifc->ifp->name); } + if (!CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL)) + zlog_warn("WARNING: advertising address to clients that is not yet usable."); + router_id_add_address(ifc); for (ALL_LIST_ELEMENTS (zebrad.client_list, node, nnode, client)) From f7f740fe58fb838fc87e82dc7e1e2d4e5ccf085c Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Thu, 24 Jan 2013 14:04:48 +0000 Subject: [PATCH 123/482] zebra: add ZEBRA_IFC_QUEUED to keep track of kernel state As there are timeframes when we don't get a notification from the kernel about new addresses. (e.g. while Linux performs IPv6 DAD), we need to have some information whether an address has been sent to the kernel or not. One case where this is relevant would be a user adding an IPv6 address, but deleting it before DAD has been complete. With the next patch which removes some (ill assuming) synchronous parts in address setup, ipv6_address_uninstall would not know whether or not it has to actually delete the prefix from the kernel. Resolving these windows where we lack information is what the flag ZEBRA_IFC_QUEUED is intended for. Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- lib/if.h | 7 ++++++- zebra/connected.c | 9 +++++++++ zebra/interface.c | 19 ++++++++++++++----- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/lib/if.h b/lib/if.h index 2116e12e..13cc254e 100644 --- a/lib/if.h +++ b/lib/if.h @@ -151,11 +151,16 @@ struct connected u_char conf; #define ZEBRA_IFC_REAL (1 << 0) #define ZEBRA_IFC_CONFIGURED (1 << 1) +#define ZEBRA_IFC_QUEUED (1 << 2) /* The ZEBRA_IFC_REAL flag should be set if and only if this address - exists in the kernel. + exists in the kernel and is actually usable. (A case where it exists but + is not yet usable would be IPv6 with DAD) The ZEBRA_IFC_CONFIGURED flag should be set if and only if this address was configured by the user from inside quagga. + The ZEBRA_IFC_QUEUED flag should be set if and only if the address exists + in the kernel. It may and should be set although the address might not be + usable yet. (compare with ZEBRA_IFC_REAL) */ /* Flags for connected address. */ diff --git a/zebra/connected.c b/zebra/connected.c index 38ab37d5..d474560c 100644 --- a/zebra/connected.c +++ b/zebra/connected.c @@ -62,6 +62,9 @@ connected_withdraw (struct connected *ifc) UNSET_FLAG (ifc->conf, ZEBRA_IFC_REAL); } + /* The address is not in the kernel anymore, so clear the flag */ + UNSET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED); + if (!CHECK_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED)) { listnode_delete (ifc->ifp->connected, ifc); @@ -211,6 +214,9 @@ connected_add_ipv4 (struct interface *ifp, int flags, struct in_addr *addr, ifc = connected_new (); ifc->ifp = ifp; ifc->flags = flags; + /* If we get a notification from the kernel, + * we can safely assume the address is known to the kernel */ + SET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED); /* Allocate new connected address. */ p = prefix_ipv4_new (); @@ -363,6 +369,9 @@ connected_add_ipv6 (struct interface *ifp, int flags, struct in6_addr *addr, ifc = connected_new (); ifc->ifp = ifp; ifc->flags = flags; + /* If we get a notification from the kernel, + * we can safely assume the address is known to the kernel */ + SET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED); /* Allocate new connected address. */ p = prefix_ipv6_new (); diff --git a/zebra/interface.c b/zebra/interface.c index cd78ebbc..470df0cd 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -289,7 +289,7 @@ if_addr_wakeup (struct interface *ifp) p = ifc->address; if (CHECK_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED) - && ! CHECK_FLAG (ifc->conf, ZEBRA_IFC_REAL)) + && ! CHECK_FLAG (ifc->conf, ZEBRA_IFC_QUEUED)) { /* Address check. */ if (p->family == AF_INET) @@ -321,6 +321,7 @@ if_addr_wakeup (struct interface *ifp) /* Add to subnet chain list. */ if_subnet_add (ifp, ifc); + SET_FLAG (ifc->conf, ZEBRA_IFC_QUEUED); SET_FLAG (ifc->conf, ZEBRA_IFC_REAL); zebra_interface_address_add_update (ifp, ifc); @@ -345,6 +346,7 @@ if_addr_wakeup (struct interface *ifp) safe_strerror(errno)); continue; } + SET_FLAG (ifc->conf, ZEBRA_IFC_QUEUED); SET_FLAG (ifc->conf, ZEBRA_IFC_REAL); zebra_interface_address_add_update (ifp, ifc); @@ -454,6 +456,7 @@ if_delete_update (struct interface *ifp) zebra_interface_address_delete_update (ifp, ifc); UNSET_FLAG (ifc->conf, ZEBRA_IFC_REAL); + UNSET_FLAG (ifc->conf, ZEBRA_IFC_QUEUED); /* Remove from subnet chain. */ list_delete_node (addr_list, anode); @@ -482,6 +485,7 @@ if_delete_update (struct interface *ifp) zebra_interface_address_delete_update (ifp, ifc); UNSET_FLAG (ifc->conf, ZEBRA_IFC_REAL); + UNSET_FLAG (ifc->conf, ZEBRA_IFC_QUEUED); if (CHECK_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED)) last = node; @@ -1229,7 +1233,7 @@ ip_address_install (struct vty *vty, struct interface *ifp, SET_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED); /* In case of this route need to install kernel. */ - if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_REAL) + if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_QUEUED) && CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE)) { /* Some system need to up the interface to set IP address. */ @@ -1251,6 +1255,7 @@ ip_address_install (struct vty *vty, struct interface *ifp, if_subnet_add (ifp, ifc); /* IP address propery set. */ + SET_FLAG (ifc->conf, ZEBRA_IFC_QUEUED); SET_FLAG (ifc->conf, ZEBRA_IFC_REAL); /* Update interface address information to protocol daemon. */ @@ -1296,7 +1301,7 @@ ip_address_uninstall (struct vty *vty, struct interface *ifp, UNSET_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED); /* This is not real address or interface is not active. */ - if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_REAL) + if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_QUEUED) || ! CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE)) { listnode_delete (ifp->connected, ifc); @@ -1321,6 +1326,7 @@ ip_address_uninstall (struct vty *vty, struct interface *ifp, * through the route socket, and we don't want to touch that behaviour * for now. It should work without the #ifdef, but why take the risk... * -- equinox 2012-07-13 */ + UNSET_FLAG (ifc->conf, ZEBRA_IFC_QUEUED); #ifdef HAVE_NETLINK /* Remove connected route. */ @@ -1437,7 +1443,7 @@ ipv6_address_install (struct vty *vty, struct interface *ifp, SET_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED); /* In case of this route need to install kernel. */ - if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_REAL) + if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_QUEUED) && CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE)) { /* Some system need to up the interface to set IP address. */ @@ -1457,6 +1463,7 @@ ipv6_address_install (struct vty *vty, struct interface *ifp, } /* IP address propery set. */ + SET_FLAG (ifc->conf, ZEBRA_IFC_QUEUED); SET_FLAG (ifc->conf, ZEBRA_IFC_REAL); /* Update interface address information to protocol daemon. */ @@ -1502,7 +1509,7 @@ ipv6_address_uninstall (struct vty *vty, struct interface *ifp, UNSET_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED); /* This is not real address or interface is not active. */ - if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_REAL) + if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_QUEUED) || ! CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE)) { listnode_delete (ifp->connected, ifc); @@ -1519,6 +1526,8 @@ ipv6_address_uninstall (struct vty *vty, struct interface *ifp, return CMD_WARNING; } + UNSET_FLAG (ifc->conf, ZEBRA_IFC_QUEUED); + /* Redistribute this information. */ zebra_interface_address_delete_update (ifp, ifc); From 02b4805f3914ef6ba0242c6f4dd1b6759ef97bf2 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Thu, 24 Jan 2013 14:04:49 +0000 Subject: [PATCH 124/482] zebra: don't change connected state from zebra/interface.c Try to avoid changing connected state from zebra/interface.c as this means making assumptions about kernel behaviour which may be or may become wrong. This state should rather be updated by events from the kernel. Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- zebra/connected.c | 49 +++++++++++------- zebra/interface.c | 126 ++++++++++++++-------------------------------- 2 files changed, 69 insertions(+), 106 deletions(-) diff --git a/zebra/connected.c b/zebra/connected.c index d474560c..4802f2ba 100644 --- a/zebra/connected.c +++ b/zebra/connected.c @@ -37,7 +37,7 @@ #include "zebra/connected.h" extern struct zebra_t zebrad; -/* withdraw a connected address */ +/* communicate the withdrawal of a connected address */ static void connected_withdraw (struct connected *ifc) { @@ -81,24 +81,19 @@ connected_announce (struct interface *ifp, struct connected *ifc) listnode_add (ifp->connected, ifc); /* Update interface address information to protocol daemon. */ - if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_REAL)) - { - if (ifc->address->family == AF_INET) - if_subnet_add (ifp, ifc); + if (ifc->address->family == AF_INET) + if_subnet_add (ifp, ifc); - SET_FLAG (ifc->conf, ZEBRA_IFC_REAL); + zebra_interface_address_add_update (ifp, ifc); - zebra_interface_address_add_update (ifp, ifc); - - if (if_is_operative(ifp)) - { - if (ifc->address->family == AF_INET) - connected_up_ipv4 (ifp, ifc); + if (if_is_operative(ifp)) + { + if (ifc->address->family == AF_INET) + connected_up_ipv4 (ifp, ifc); #ifdef HAVE_IPV6 - else - connected_up_ipv6 (ifp, ifc); + else + connected_up_ipv6 (ifp, ifc); #endif - } } } @@ -116,7 +111,7 @@ connected_check (struct interface *ifp, struct prefix *p) return NULL; } -/* Check if two ifc's describe the same address */ +/* Check if two ifc's describe the same address in the same state */ static int connected_same (struct connected *ifc1, struct connected *ifc2) { @@ -136,6 +131,9 @@ connected_same (struct connected *ifc1, struct connected *ifc2) if (ifc1->flags != ifc2->flags) return 0; + + if (ifc1->conf != ifc2->conf) + return 0; return 1; } @@ -156,7 +154,7 @@ connected_update(struct interface *ifp, struct connected *ifc) /* Avoid spurious withdraws, this might be just the kernel 'reflecting' * back an address we have already added. */ - if (connected_same (current, ifc) && CHECK_FLAG(current->conf, ZEBRA_IFC_REAL)) + if (connected_same (current, ifc)) { /* nothing to do */ connected_free (ifc); @@ -169,8 +167,9 @@ connected_update(struct interface *ifp, struct connected *ifc) connected_withdraw (current); /* implicit withdraw - freebsd does this */ } - /* If the connected is new or has changed, announce it */ - connected_announce(ifp, ifc); + /* If the connected is new or has changed, announce it, if it is usable */ + if (CHECK_FLAG(ifc->conf, ZEBRA_IFC_REAL)) + connected_announce(ifp, ifc); } /* Called from if_up(). */ @@ -279,6 +278,10 @@ connected_add_ipv4 (struct interface *ifp, int flags, struct in_addr *addr, if (label) ifc->label = XSTRDUP (MTYPE_CONNECTED_LABEL, label); + /* For all that I know an IPv4 address is always ready when we receive + * the notification. So it should be safe to set the REAL flag here. */ + SET_FLAG(ifc->conf, ZEBRA_IFC_REAL); + connected_update(ifp, ifc); } @@ -407,6 +410,14 @@ connected_add_ipv6 (struct interface *ifp, int flags, struct in6_addr *addr, if (label) ifc->label = XSTRDUP (MTYPE_CONNECTED_LABEL, label); + /* On Linux, we only get here when DAD is complete, therefore we can set + * ZEBRA_IFC_REAL. + * + * On BSD, there currently doesn't seem to be a way to check for completion of + * DAD, so we replicate the old behaviour and set ZEBRA_IFC_REAL, although DAD + * might still be running. + */ + SET_FLAG(ifc->conf, ZEBRA_IFC_REAL); connected_update(ifp, ifc); } diff --git a/zebra/interface.c b/zebra/interface.c index 470df0cd..51798ca6 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -193,6 +193,9 @@ if_subnet_delete (struct interface *ifp, struct connected *ifc) ifc = listgetdata (listhead (addr_list)); zebra_interface_address_delete_update (ifp, ifc); UNSET_FLAG (ifc->flags, ZEBRA_IFA_SECONDARY); + /* XXX: Linux kernel removes all the secondary addresses when the primary + * address is removed. We could try to work around that, though this is + * non-trivial. */ zebra_interface_address_add_update (ifp, ifc); } @@ -296,16 +299,23 @@ if_addr_wakeup (struct interface *ifp) { if (! if_is_up (ifp)) { - /* XXX: WTF is it trying to set flags here? - * caller has just gotten a new interface, has been - * handed the flags already. This code has no business - * trying to override administrative status of the interface. - * The only call path to here which doesn't originate from - * kernel event is irdp - what on earth is it trying to do? - * - * further RUNNING is not a settable flag on any system - * I (paulj) am aware of. - */ + /* Assume zebra is configured like following: + * + * interface gre0 + * ip addr 192.0.2.1/24 + * ! + * + * As soon as zebra becomes first aware that gre0 exists in the + * kernel, it will set gre0 up and configure its addresses. + * + * (This may happen at startup when the interface already exists + * or during runtime when the interface is added to the kernel) + * + * XXX: IRDP code is calling here via if_add_update - this seems + * somewhat weird. + * XXX: RUNNING is not a settable flag on any system + * I (paulj) am aware of. + */ if_set_flags (ifp, IFF_UP | IFF_RUNNING); if_refresh (ifp); } @@ -318,23 +328,17 @@ if_addr_wakeup (struct interface *ifp) continue; } - /* Add to subnet chain list. */ - if_subnet_add (ifp, ifc); - SET_FLAG (ifc->conf, ZEBRA_IFC_QUEUED); - SET_FLAG (ifc->conf, ZEBRA_IFC_REAL); - - zebra_interface_address_add_update (ifp, ifc); - - if (if_is_operative(ifp)) - connected_up_ipv4 (ifp, ifc); + /* The address will be advertised to zebra clients when the notification + * from the kernel has been received. + * It will also be added to the interface's subnet list then. */ } #ifdef HAVE_IPV6 if (p->family == AF_INET6) { if (! if_is_up (ifp)) { - /* XXX: See long comment above */ + /* See long comment above */ if_set_flags (ifp, IFF_UP | IFF_RUNNING); if_refresh (ifp); } @@ -346,13 +350,10 @@ if_addr_wakeup (struct interface *ifp) safe_strerror(errno)); continue; } - SET_FLAG (ifc->conf, ZEBRA_IFC_QUEUED); - SET_FLAG (ifc->conf, ZEBRA_IFC_REAL); - - zebra_interface_address_add_update (ifp, ifc); - if (if_is_operative(ifp)) - connected_up_ipv6 (ifp, ifc); + SET_FLAG (ifc->conf, ZEBRA_IFC_QUEUED); + /* The address will be advertised to zebra clients when the notification + * from the kernel has been received. */ } #endif /* HAVE_IPV6 */ } @@ -450,9 +451,11 @@ if_delete_update (struct interface *ifp) ifc = listgetdata (anode); p = ifc->address; - connected_down_ipv4 (ifp, ifc); + /* XXX: We have to send notifications here explicitly, because we destroy + * the ifc before receiving the notification about the address being deleted. + */ zebra_interface_address_delete_update (ifp, ifc); UNSET_FLAG (ifc->conf, ZEBRA_IFC_REAL); @@ -1251,19 +1254,10 @@ ip_address_install (struct vty *vty, struct interface *ifp, return CMD_WARNING; } - /* Add to subnet chain list (while marking secondary attribute). */ - if_subnet_add (ifp, ifc); - - /* IP address propery set. */ SET_FLAG (ifc->conf, ZEBRA_IFC_QUEUED); - SET_FLAG (ifc->conf, ZEBRA_IFC_REAL); - - /* Update interface address information to protocol daemon. */ - zebra_interface_address_add_update (ifp, ifc); - - /* If interface is up register connected route. */ - if (if_is_operative(ifp)) - connected_up_ipv4 (ifp, ifc); + /* The address will be advertised to zebra clients when the notification + * from the kernel has been received. + * It will also be added to the subnet chain list, then. */ } return CMD_SUCCESS; @@ -1317,35 +1311,9 @@ ip_address_uninstall (struct vty *vty, struct interface *ifp, safe_strerror(errno), VTY_NEWLINE); return CMD_WARNING; } - /* success! call returned that the address deletion went through. - * this is a synchronous operation, so we know it succeeded and can - * now update all internal state. */ - - /* the HAVE_NETLINK check is only here because, on BSD, although the - * call above is still synchronous, we get a second confirmation later - * through the route socket, and we don't want to touch that behaviour - * for now. It should work without the #ifdef, but why take the risk... - * -- equinox 2012-07-13 */ UNSET_FLAG (ifc->conf, ZEBRA_IFC_QUEUED); -#ifdef HAVE_NETLINK - - /* Remove connected route. */ - connected_down_ipv4 (ifp, ifc); - - /* Redistribute this information. */ - zebra_interface_address_delete_update (ifp, ifc); - - /* IP address propery set. */ - UNSET_FLAG (ifc->conf, ZEBRA_IFC_REAL); - - /* remove from interface, remark secondaries */ - if_subnet_delete (ifp, ifc); - - /* Free address information. */ - listnode_delete (ifp->connected, ifc); - connected_free (ifc); -#endif - + /* we will receive a kernel notification about this route being removed. + * this will trigger its removal from the connected list. */ return CMD_SUCCESS; } @@ -1462,16 +1430,9 @@ ipv6_address_install (struct vty *vty, struct interface *ifp, return CMD_WARNING; } - /* IP address propery set. */ SET_FLAG (ifc->conf, ZEBRA_IFC_QUEUED); - SET_FLAG (ifc->conf, ZEBRA_IFC_REAL); - - /* Update interface address information to protocol daemon. */ - zebra_interface_address_add_update (ifp, ifc); - - /* If interface is up register connected route. */ - if (if_is_operative(ifp)) - connected_up_ipv6 (ifp, ifc); + /* The address will be advertised to zebra clients when the notification + * from the kernel has been received. */ } return CMD_SUCCESS; @@ -1527,17 +1488,8 @@ ipv6_address_uninstall (struct vty *vty, struct interface *ifp, } UNSET_FLAG (ifc->conf, ZEBRA_IFC_QUEUED); - - /* Redistribute this information. */ - zebra_interface_address_delete_update (ifp, ifc); - - /* Remove connected route. */ - connected_down_ipv6 (ifp, ifc); - - /* Free address information. */ - listnode_delete (ifp->connected, ifc); - connected_free (ifc); - + /* This information will be propagated to the zclients when the + * kernel notification is received. */ return CMD_SUCCESS; } From bfac8dcd2fe7ed099a679b5c8245599c6d0312ed Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Thu, 24 Jan 2013 14:04:50 +0000 Subject: [PATCH 125/482] zebra: improve interface shutdown behaviour Linux removes IPv6 addresses when the interface is set down. Those addresses need to be readded when the interface is set up again. Also, an interface should not be reactivated from shutdown by configuring an ip address. Finally, remove the three-state logic for the shutdown setting as its sole current use may be mild confusion. Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- zebra/interface.c | 64 +++++++++++++++++++++++++++++++++++------------ zebra/interface.h | 3 +-- 2 files changed, 49 insertions(+), 18 deletions(-) diff --git a/zebra/interface.c b/zebra/interface.c index 51798ca6..155ee029 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -56,7 +56,7 @@ if_zebra_new_hook (struct interface *ifp) zebra_if = XCALLOC (MTYPE_TMP, sizeof (struct zebra_if)); zebra_if->multicast = IF_ZEBRA_MULTICAST_UNSPEC; - zebra_if->shutdown = IF_ZEBRA_SHUTDOWN_UNSPEC; + zebra_if->shutdown = IF_ZEBRA_SHUTDOWN_OFF; #ifdef RTADV { @@ -378,6 +378,14 @@ if_add_update (struct interface *ifp) { SET_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE); + if (if_data && if_data->shutdown == IF_ZEBRA_SHUTDOWN_ON) + { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug ("interface %s index %d is shutdown. Won't wake it up.", + ifp->name, ifp->ifindex); + return; + } + if_addr_wakeup (ifp); if (IS_ZEBRA_DEBUG_KERNEL) @@ -1095,13 +1103,16 @@ DEFUN (shutdown_if, struct zebra_if *if_data; ifp = (struct interface *) vty->index; - ret = if_unset_flags (ifp, IFF_UP); - if (ret < 0) + if (ifp->ifindex != IFINDEX_INTERNAL) { - vty_out (vty, "Can't shutdown interface%s", VTY_NEWLINE); - return CMD_WARNING; + ret = if_unset_flags (ifp, IFF_UP); + if (ret < 0) + { + vty_out (vty, "Can't shutdown interface%s", VTY_NEWLINE); + return CMD_WARNING; + } + if_refresh (ifp); } - if_refresh (ifp); if_data = ifp->info; if_data->shutdown = IF_ZEBRA_SHUTDOWN_ON; @@ -1119,13 +1130,23 @@ DEFUN (no_shutdown_if, struct zebra_if *if_data; ifp = (struct interface *) vty->index; - ret = if_set_flags (ifp, IFF_UP | IFF_RUNNING); - if (ret < 0) + + if (ifp->ifindex != IFINDEX_INTERNAL) { - vty_out (vty, "Can't up interface%s", VTY_NEWLINE); - return CMD_WARNING; + ret = if_set_flags (ifp, IFF_UP | IFF_RUNNING); + if (ret < 0) + { + vty_out (vty, "Can't up interface%s", VTY_NEWLINE); + return CMD_WARNING; + } + if_refresh (ifp); + + /* Some addresses (in particular, IPv6 addresses on Linux) get + * removed when the interface goes down. They need to be readded. + */ + if_addr_wakeup(ifp); } - if_refresh (ifp); + if_data = ifp->info; if_data->shutdown = IF_ZEBRA_SHUTDOWN_OFF; @@ -1191,11 +1212,14 @@ ip_address_install (struct vty *vty, struct interface *ifp, const char *addr_str, const char *peer_str, const char *label) { + struct zebra_if *if_data; struct prefix_ipv4 cp; struct connected *ifc; struct prefix_ipv4 *p; int ret; + if_data = ifp->info; + ret = str2prefix_ipv4 (addr_str, &cp); if (ret <= 0) { @@ -1237,7 +1261,8 @@ ip_address_install (struct vty *vty, struct interface *ifp, /* In case of this route need to install kernel. */ if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_QUEUED) - && CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE)) + && CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE) + && !(if_data && if_data->shutdown == IF_ZEBRA_SHUTDOWN_ON)) { /* Some system need to up the interface to set IP address. */ if (! if_is_up (ifp)) @@ -1371,11 +1396,14 @@ ipv6_address_install (struct vty *vty, struct interface *ifp, const char *addr_str, const char *peer_str, const char *label, int secondary) { + struct zebra_if *if_data; struct prefix_ipv6 cp; struct connected *ifc; struct prefix_ipv6 *p; int ret; + if_data = ifp->info; + ret = str2prefix_ipv6 (addr_str, &cp); if (ret <= 0) { @@ -1412,7 +1440,8 @@ ipv6_address_install (struct vty *vty, struct interface *ifp, /* In case of this route need to install kernel. */ if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_QUEUED) - && CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE)) + && CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE) + && !(if_data && if_data->shutdown == IF_ZEBRA_SHUTDOWN_ON)) { /* Some system need to up the interface to set IP address. */ if (! if_is_up (ifp)) @@ -1533,6 +1562,12 @@ if_config_write (struct vty *vty) vty_out (vty, "interface %s%s", ifp->name, VTY_NEWLINE); + if (if_data) + { + if (if_data->shutdown == IF_ZEBRA_SHUTDOWN_ON) + vty_out (vty, " shutdown%s", VTY_NEWLINE); + } + if (ifp->desc) vty_out (vty, " description %s%s", ifp->desc, VTY_NEWLINE); @@ -1565,9 +1600,6 @@ if_config_write (struct vty *vty) if (if_data) { - if (if_data->shutdown == IF_ZEBRA_SHUTDOWN_ON) - vty_out (vty, " shutdown%s", VTY_NEWLINE); - if (if_data->multicast != IF_ZEBRA_MULTICAST_UNSPEC) vty_out (vty, " %smulticast%s", if_data->multicast == IF_ZEBRA_MULTICAST_ON ? "" : "no ", diff --git a/zebra/interface.h b/zebra/interface.h index 0777a2fa..7a05348c 100644 --- a/zebra/interface.h +++ b/zebra/interface.h @@ -34,9 +34,8 @@ #define IF_ZEBRA_MULTICAST_OFF 2 /* For interface shutdown configuration. */ -#define IF_ZEBRA_SHUTDOWN_UNSPEC 0 +#define IF_ZEBRA_SHUTDOWN_OFF 0 #define IF_ZEBRA_SHUTDOWN_ON 1 -#define IF_ZEBRA_SHUTDOWN_OFF 2 /* Router advertisement feature. */ #if (defined(LINUX_IPV6) && (defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1)) || defined(KAME) From fa713d9ee5ed30dedd0a290be9aaff780a2896be Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Fri, 5 Jul 2013 15:35:37 +0000 Subject: [PATCH 126/482] zebra: rework recursive route resolution Change the datastructure for recursive routes. This brings the following benefits: By using struct nexthop also to store nexthops obtained by recursive resolution, we can get rid of quite a bit of code duplication in the fib management. (rt_netlink, rt_socket, ...) With the new datastructure we can make use of all available paths when recursive routes are resolved with multipath routes. Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- tests/.gitignore | 1 + tests/Makefile.am | 4 +- tests/libzebra.tests/Makefile.am | 3 +- tests/libzebra.tests/testnexthopiter.exp | 8 + tests/prng.c | 82 ++++ tests/prng.h | 34 ++ tests/test-nexthop-iter.c | 291 ++++++++++++ zebra/rib.h | 62 ++- zebra/rt_ioctl.c | 122 ++--- zebra/rt_netlink.c | 548 +++++++++++------------ zebra/rt_socket.c | 98 ++-- zebra/zebra_fpm_netlink.c | 83 +--- zebra/zebra_rib.c | 264 +++++++---- zebra/zebra_routemap.c | 16 +- zebra/zebra_vty.c | 136 ++---- zebra/zserv.c | 12 +- 16 files changed, 1046 insertions(+), 718 deletions(-) create mode 100644 tests/libzebra.tests/testnexthopiter.exp create mode 100644 tests/prng.c create mode 100644 tests/prng.h create mode 100644 tests/test-nexthop-iter.c diff --git a/tests/.gitignore b/tests/.gitignore index 8baea0a8..31fb70a4 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -31,4 +31,5 @@ testmemory testprivs testsig teststream +testnexthopiter site.exp diff --git a/tests/Makefile.am b/tests/Makefile.am index 2ed0e1c5..e5c7fd79 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -25,7 +25,7 @@ TESTS_BGPD = endif check_PROGRAMS = testsig testbuffer testmemory heavy heavywq heavythread \ - testprivs teststream testchecksum tabletest \ + testprivs teststream testchecksum tabletest testnexthopiter \ $(TESTS_BGPD) testsig_SOURCES = test-sig.c @@ -43,6 +43,7 @@ testbgpmpattr_SOURCES = bgp_mp_attr_test.c testchecksum_SOURCES = test-checksum.c testbgpmpath_SOURCES = bgp_mpath_test.c tabletest_SOURCES = table_test.c +testnexthopiter_SOURCES = test-nexthop-iter.c prng.c testsig_LDADD = ../lib/libzebra.la @LIBCAP@ testbuffer_LDADD = ../lib/libzebra.la @LIBCAP@ @@ -59,3 +60,4 @@ testbgpmpattr_LDADD = ../bgpd/libbgp.a ../lib/libzebra.la @LIBCAP@ -lm testchecksum_LDADD = ../lib/libzebra.la @LIBCAP@ testbgpmpath_LDADD = ../bgpd/libbgp.a ../lib/libzebra.la @LIBCAP@ -lm tabletest_LDADD = ../lib/libzebra.la @LIBCAP@ -lm +testnexthopiter_LDADD = ../lib/libzebra.la @LIBCAP@ diff --git a/tests/libzebra.tests/Makefile.am b/tests/libzebra.tests/Makefile.am index 0d29e287..14138a08 100644 --- a/tests/libzebra.tests/Makefile.am +++ b/tests/libzebra.tests/Makefile.am @@ -1,2 +1,3 @@ EXTRA_DIST = \ - tabletest.exp + tabletest.exp \ + testnexthopiter.exp diff --git a/tests/libzebra.tests/testnexthopiter.exp b/tests/libzebra.tests/testnexthopiter.exp new file mode 100644 index 00000000..be35a0a2 --- /dev/null +++ b/tests/libzebra.tests/testnexthopiter.exp @@ -0,0 +1,8 @@ +set timeout 10 +set testprefix "testnexthopiter " +set aborted 0 + +spawn "./testnexthopiter" + +onesimple "simple" "Simple test passed." +onesimple "prng" "PRNG test passed." diff --git a/tests/prng.c b/tests/prng.c new file mode 100644 index 00000000..7b1b4282 --- /dev/null +++ b/tests/prng.c @@ -0,0 +1,82 @@ +/* + * Very simple prng to allow for randomized tests with reproducable + * results. + * + * Copyright (C) 2012 by Open Source Routing. + * Copyright (C) 2012 by Internet Systems Consortium, Inc. ("ISC") + * + * This file is part of Quagga + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include +#include + +#include "prng.h" + +struct prng +{ + unsigned long long state1; + unsigned long long state2; +}; + +static char +prng_bit(struct prng *prng) +{ + prng->state1 *= 2416; + prng->state1 += 374441; + prng->state1 %= 1771875; + + if (prng->state1 % 2) + { + prng->state2 *= 84589; + prng->state2 += 45989; + prng->state2 %= 217728; + } + + return prng->state2 % 2; +} + +struct prng* +prng_new(unsigned long long seed) +{ + struct prng *rv = calloc(sizeof(*rv), 1); + assert(rv); + + rv->state1 = rv->state2 = seed; + + return rv; +} + +unsigned int +prng_rand(struct prng *prng) +{ + unsigned int i, rv = 0; + + for (i = 0; i < 32; i++) + { + rv |= prng_bit(prng); + rv <<= 1; + } + return rv; +} + +void +prng_free(struct prng *prng) +{ + free(prng); +} diff --git a/tests/prng.h b/tests/prng.h new file mode 100644 index 00000000..ed364986 --- /dev/null +++ b/tests/prng.h @@ -0,0 +1,34 @@ +/* + * Very simple prng to allow for randomized tests with reproducable + * results. + * + * Copyright (C) 2012 by Open Source Routing. + * Copyright (C) 2012 by Internet Systems Consortium, Inc. ("ISC") + * + * This file is part of Quagga + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#ifndef _PRNG_H +#define _PRNG_H + +struct prng; + +struct prng* prng_new(unsigned long long seed); +unsigned int prng_rand(struct prng*); +void prng_free(struct prng *); + +#endif diff --git a/tests/test-nexthop-iter.c b/tests/test-nexthop-iter.c new file mode 100644 index 00000000..25037932 --- /dev/null +++ b/tests/test-nexthop-iter.c @@ -0,0 +1,291 @@ +/* + * Recursive Nexthop Iterator test. + * This tests the ALL_NEXTHOPS_RO macro. + * + * Copyright (C) 2012 by Open Source Routing. + * Copyright (C) 2012 by Internet Systems Consortium, Inc. ("ISC") + * + * This file is part of Quagga + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include +#include "zebra/rib.h" +#include "prng.h" + +struct thread_master *master; +static int verbose; + +static void +str_append(char **buf, const char *repr) +{ + if (*buf) + { + *buf = realloc(*buf, strlen(*buf) + strlen(repr) + 1); + assert(*buf); + strncpy((*buf) + strlen(*buf), repr, strlen(repr) + 1); + } + else + { + *buf = strdup(repr); + assert(*buf); + } +} + +static void +str_appendf(char **buf, const char *format, ...) +{ + va_list ap; + int rv; + char *pbuf; + + va_start(ap, format); + rv = vasprintf(&pbuf, format, ap); + va_end(ap); + assert(rv >= 0); + + str_append(buf, pbuf); + free(pbuf); +} + +/* This structure contains a nexthop chain + * and its expected representation */ +struct nexthop_chain +{ + /* Head of the chain */ + struct nexthop *head; + /* Last nexthop in top chain */ + struct nexthop *current_top; + /* Last nexthop in current recursive chain */ + struct nexthop *current_recursive; + /* Expected string representation. */ + char *repr; +}; + +static struct nexthop_chain* +nexthop_chain_new(void) +{ + struct nexthop_chain *rv; + + rv = calloc(sizeof(*rv), 1); + assert(rv); + return rv; +} + +static void +nexthop_chain_add_top(struct nexthop_chain *nc) +{ + struct nexthop *nh; + + nh = calloc(sizeof(*nh), 1); + assert(nh); + + if (nc->head) + { + nc->current_top->next = nh; + nh->prev = nc->current_top; + nc->current_top = nh; + } + else + { + nc->head = nc->current_top = nh; + } + nc->current_recursive = NULL; + str_appendf(&nc->repr, "%p\n", nh); +} + +static void +nexthop_chain_add_recursive(struct nexthop_chain *nc) +{ + struct nexthop *nh; + + nh = calloc(sizeof(*nh), 1); + assert(nh); + + assert(nc->current_top); + if (nc->current_recursive) + { + nc->current_recursive->next = nh; + nh->prev = nc->current_recursive; + nc->current_recursive = nh; + } + else + { + SET_FLAG(nc->current_top->flags, NEXTHOP_FLAG_RECURSIVE); + nc->current_top->resolved = nh; + nc->current_recursive = nh; + } + str_appendf(&nc->repr, " %p\n", nh); +} + +static void +nexthop_chain_clear(struct nexthop_chain *nc) +{ + struct nexthop *tcur, *tnext; + + for (tcur = nc->head; tcur; tcur = tnext) + { + tnext = tcur->next; + if (CHECK_FLAG(tcur->flags, NEXTHOP_FLAG_RECURSIVE)) + { + struct nexthop *rcur, *rnext; + for (rcur = tcur->resolved; rcur; rcur = rnext) + { + rnext = rcur->next; + free(rcur); + } + } + free(tcur); + } + nc->head = nc->current_top = nc->current_recursive = NULL; + free(nc->repr); + nc->repr = NULL; +} + +static void +nexthop_chain_free(struct nexthop_chain *nc) +{ + if (!nc) + return; + nexthop_chain_clear(nc); + free(nc); +} + +/* This function builds a string representation of + * the nexthop chain using the ALL_NEXTHOPS_RO macro. + * It verifies that the ALL_NEXTHOPS_RO macro iterated + * correctly over the nexthop chain by comparing the + * generated representation with the expected representation. + */ +static void +nexthop_chain_verify_iter(struct nexthop_chain *nc) +{ + struct nexthop *nh, *tnh; + int recursing; + char *repr = NULL; + + for (ALL_NEXTHOPS_RO(nc->head, nh, tnh, recursing)) + { + if (recursing) + str_appendf(&repr, " %p\n", nh); + else + str_appendf(&repr, "%p\n", nh); + } + + if (repr && verbose) + printf("===\n%s", repr); + assert((!repr && !nc->repr) || (repr && nc->repr && !strcmp(repr, nc->repr))); + free(repr); +} + +/* This test run builds a simple nexthop chain + * with some recursive nexthops and verifies that + * the iterator works correctly in each stage along + * the way. + */ +static void +test_run_first(void) +{ + struct nexthop_chain *nc; + + nc = nexthop_chain_new(); + nexthop_chain_verify_iter(nc); + + nexthop_chain_add_top(nc); + nexthop_chain_verify_iter(nc); + + nexthop_chain_add_top(nc); + nexthop_chain_verify_iter(nc); + + nexthop_chain_add_recursive(nc); + nexthop_chain_verify_iter(nc); + + nexthop_chain_add_recursive(nc); + nexthop_chain_verify_iter(nc); + + nexthop_chain_add_top(nc); + nexthop_chain_verify_iter(nc); + + nexthop_chain_add_top(nc); + nexthop_chain_verify_iter(nc); + + nexthop_chain_add_top(nc); + nexthop_chain_verify_iter(nc); + + nexthop_chain_add_recursive(nc); + nexthop_chain_verify_iter(nc); + + nexthop_chain_add_recursive(nc); + nexthop_chain_verify_iter(nc); + + nexthop_chain_add_recursive(nc); + nexthop_chain_verify_iter(nc); + + nexthop_chain_free(nc); +} + +/* This test run builds numerous random + * nexthop chain configurations and verifies + * that the iterator correctly progresses + * through each. */ +static void +test_run_prng(void) +{ + struct nexthop_chain *nc; + struct prng *prng; + int i; + + nc = nexthop_chain_new(); + prng = prng_new(0); + + for (i = 0; i < 1000000; i++) + { + switch (prng_rand(prng) % 10) + { + case 0: + nexthop_chain_clear(nc); + break; + case 1: + case 2: + case 3: + case 4: + case 5: + nexthop_chain_add_top(nc); + break; + case 6: + case 7: + case 8: + case 9: + if (nc->current_top) + nexthop_chain_add_recursive(nc); + break; + } + nexthop_chain_verify_iter(nc); + } + nexthop_chain_free(nc); + prng_free(prng); +} + +int main(int argc, char **argv) +{ + if (argc >= 2 && !strcmp("-v", argv[1])) + verbose = 1; + test_run_first(); + printf("Simple test passed.\n"); + test_run_prng(); + printf("PRNG test passed.\n"); +} diff --git a/zebra/rib.h b/zebra/rib.h index e16ce68a..4d98e059 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -254,16 +254,65 @@ struct nexthop #define NEXTHOP_FLAG_FIB (1 << 1) /* FIB nexthop. */ #define NEXTHOP_FLAG_RECURSIVE (1 << 2) /* Recursive nexthop. */ - /* Nexthop address or interface name. */ + /* Nexthop address */ union g_addr gate; - - /* Recursive lookup nexthop. */ - u_char rtype; - unsigned int rifindex; - union g_addr rgate; union g_addr src; + + /* Nexthops obtained by recursive resolution. + * + * If the nexthop struct needs to be resolved recursively, + * NEXTHOP_FLAG_RECURSIVE will be set in flags and the nexthops + * obtained by recursive resolution will be added to `resolved'. + * Only one level of recursive resolution is currently supported. */ + struct nexthop *resolved; }; +/* The following for loop allows to iterate over the nexthop + * structure of routes. + * + * We have to maintain quite a bit of state: + * + * nexthop: The pointer to the current nexthop, either in the + * top-level chain or in the resolved chain of ni. + * tnexthop: The pointer to the current nexthop in the top-level + * nexthop chain. + * recursing: Information if nh currently is in the top-level chain + * (0) or in a resolved chain (1). + * + * Initialization: Set `nexthop' and `tnexthop' to the head of the + * top-level chain. As nexthop is in the top level chain, set recursing + * to 0. + * + * Iteration check: Check that the `nexthop' pointer is not NULL. + * + * Iteration step: This is the tricky part. Check if `nexthop' has + * NEXTHOP_FLAG_RECURSIVE set. If yes, this implies that `nexthop' is in + * the top level chain and has at least one nexthop attached to + * `nexthop->resolved'. As we want to descend into `nexthop->resolved', + * set `recursing' to 1 and set `nexthop' to `nexthop->resolved'. + * `tnexthop' is left alone in that case so we can remember which nexthop + * in the top level chain we are currently handling. + * + * If NEXTHOP_FLAG_RECURSIVE is not set, `nexthop' will progress in its + * current chain. If we are recursing, `nexthop' will be set to + * `nexthop->next' and `tnexthop' will be left alone. If we are not + * recursing, both `tnexthop' and `nexthop' will be set to `nexthop->next' + * as we are progressing in the top level chain. + * If we encounter `nexthop->next == NULL', we will clear the `recursing' + * flag as we arived either at the end of the resolved chain or at the end + * of the top level chain. In both cases, we set `tnexthop' and `nexthop' + * to `tnexthop->next', progressing to the next position in the top-level + * chain and possibly to its end marked by NULL. + */ +#define ALL_NEXTHOPS_RO(head, nexthop, tnexthop, recursing) \ + (tnexthop) = (nexthop) = (head), (recursing) = 0; \ + (nexthop); \ + (nexthop) = CHECK_FLAG((nexthop)->flags, NEXTHOP_FLAG_RECURSIVE) \ + ? (((recursing) = 1), (nexthop)->resolved) \ + : ((nexthop)->next ? ((recursing) ? (nexthop)->next \ + : ((tnexthop) = (nexthop)->next)) \ + : (((recursing) = 0),((tnexthop) = (tnexthop)->next))) + /* Routing table instance. */ struct vrf { @@ -333,6 +382,7 @@ extern struct nexthop *nexthop_ipv4_ifindex_add (struct rib *, struct in_addr *, struct in_addr *, unsigned int); +extern int nexthop_has_fib_child(struct nexthop *); extern void rib_lookup_and_dump (struct prefix_ipv4 *); extern void rib_lookup_and_pushup (struct prefix_ipv4 *); extern void rib_dump (const char *, const struct prefix_ipv4 *, const struct rib *); diff --git a/zebra/rt_ioctl.c b/zebra/rt_ioctl.c index a5d588c7..404a7c69 100644 --- a/zebra/rt_ioctl.c +++ b/zebra/rt_ioctl.c @@ -169,7 +169,8 @@ kernel_ioctl_ipv4 (u_long cmd, struct prefix *p, struct rib *rib, int family) int sock; struct rtentry rtentry; struct sockaddr_in sin_dest, sin_mask, sin_gate; - struct nexthop *nexthop; + struct nexthop *nexthop, *tnexthop; + int recursing; int nexthop_num = 0; struct interface *ifp; @@ -188,65 +189,49 @@ kernel_ioctl_ipv4 (u_long cmd, struct prefix *p, struct rib *rib, int family) SET_FLAG (rtentry.rt_flags, RTF_REJECT); if (cmd == SIOCADDRT) - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) - SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); - + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) + { + /* We shouldn't encounter recursive nexthops on discard routes, + * but it is probably better to handle that case correctly anyway. + */ + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); + } goto skip; } memset (&sin_gate, 0, sizeof (struct sockaddr_in)); /* Make gateway. */ - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; + if ((cmd == SIOCADDRT && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) || (cmd == SIOCDELRT && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))) { - if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + if (nexthop->type == NEXTHOP_TYPE_IPV4 || + nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) { - if (nexthop->rtype == NEXTHOP_TYPE_IPV4 || - nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX) - { - sin_gate.sin_family = AF_INET; + sin_gate.sin_family = AF_INET; #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - sin_gate.sin_len = sizeof (struct sockaddr_in); + sin_gate.sin_len = sizeof (struct sockaddr_in); #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ - sin_gate.sin_addr = nexthop->rgate.ipv4; - rtentry.rt_flags |= RTF_GATEWAY; - } - if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX - || nexthop->rtype == NEXTHOP_TYPE_IFNAME) - { - ifp = if_lookup_by_index (nexthop->rifindex); - if (ifp) - rtentry.rt_dev = ifp->name; - else - return -1; - } + sin_gate.sin_addr = nexthop->gate.ipv4; + rtentry.rt_flags |= RTF_GATEWAY; } - else + if (nexthop->type == NEXTHOP_TYPE_IFINDEX + || nexthop->type == NEXTHOP_TYPE_IFNAME) { - if (nexthop->type == NEXTHOP_TYPE_IPV4 || - nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) - { - sin_gate.sin_family = AF_INET; -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - sin_gate.sin_len = sizeof (struct sockaddr_in); -#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ - sin_gate.sin_addr = nexthop->gate.ipv4; - rtentry.rt_flags |= RTF_GATEWAY; - } - if (nexthop->type == NEXTHOP_TYPE_IFINDEX - || nexthop->type == NEXTHOP_TYPE_IFNAME) - { - ifp = if_lookup_by_index (nexthop->ifindex); - if (ifp) - rtentry.rt_dev = ifp->name; - else - return -1; - } + ifp = if_lookup_by_index (nexthop->ifindex); + if (ifp) + rtentry.rt_dev = ifp->name; + else + return -1; } if (cmd == SIOCADDRT) @@ -430,7 +415,8 @@ kernel_ioctl_ipv6_multipath (u_long cmd, struct prefix *p, struct rib *rib, int ret; int sock; struct in6_rtmsg rtm; - struct nexthop *nexthop; + struct nexthop *nexthop, *tnexthop; + int recursing; int nexthop_num = 0; memset (&rtm, 0, sizeof (struct in6_rtmsg)); @@ -456,48 +442,30 @@ kernel_ioctl_ipv6_multipath (u_long cmd, struct prefix *p, struct rib *rib, /* rtm.rtmsg_flags |= RTF_DYNAMIC; */ /* Make gateway. */ - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; + if ((cmd == SIOCADDRT && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) || (cmd == SIOCDELRT && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))) { - if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + if (nexthop->type == NEXTHOP_TYPE_IPV6 + || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME + || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) { - if (nexthop->rtype == NEXTHOP_TYPE_IPV6 - || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME - || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX) - { - memcpy (&rtm.rtmsg_gateway, &nexthop->rgate.ipv6, - sizeof (struct in6_addr)); - } - if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX - || nexthop->rtype == NEXTHOP_TYPE_IFNAME - || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME - || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX) - rtm.rtmsg_ifindex = nexthop->rifindex; - else - rtm.rtmsg_ifindex = 0; - + memcpy (&rtm.rtmsg_gateway, &nexthop->gate.ipv6, + sizeof (struct in6_addr)); } + if (nexthop->type == NEXTHOP_TYPE_IFINDEX + || nexthop->type == NEXTHOP_TYPE_IFNAME + || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME + || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) + rtm.rtmsg_ifindex = nexthop->ifindex; else - { - if (nexthop->type == NEXTHOP_TYPE_IPV6 - || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME - || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) - { - memcpy (&rtm.rtmsg_gateway, &nexthop->gate.ipv6, - sizeof (struct in6_addr)); - } - if (nexthop->type == NEXTHOP_TYPE_IFINDEX - || nexthop->type == NEXTHOP_TYPE_IFNAME - || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME - || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) - rtm.rtmsg_ifindex = nexthop->ifindex; - else - rtm.rtmsg_ifindex = 0; - } + rtm.rtmsg_ifindex = 0; if (cmd == SIOCADDRT) SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 86e02efb..b0ade058 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -1426,6 +1426,207 @@ netlink_route (int cmd, int family, void *dest, int length, void *gate, return 0; } +/* This function takes a nexthop as argument and adds + * the appropriate netlink attributes to an existing + * netlink message. + * + * @param routedesc: Human readable description of route type + * (direct/recursive, single-/multipath) + * @param bytelen: Length of addresses in bytes. + * @param nexthop: Nexthop information + * @param nlmsg: nlmsghdr structure to fill in. + * @param req_size: The size allocated for the message. + */ +static void +_netlink_route_build_singlepath( + const char *routedesc, + int bytelen, + struct nexthop *nexthop, + struct nlmsghdr *nlmsg, + size_t req_size) +{ + if (nexthop->type == NEXTHOP_TYPE_IPV4 + || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) + { + addattr_l (nlmsg, req_size, RTA_GATEWAY, + &nexthop->gate.ipv4, bytelen); + if (nexthop->src.ipv4.s_addr) + addattr_l (nlmsg, req_size, RTA_PREFSRC, + &nexthop->src.ipv4, bytelen); + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("netlink_route_multipath() (%s): " + "nexthop via %s if %u", + routedesc, + inet_ntoa (nexthop->gate.ipv4), + nexthop->ifindex); + } +#ifdef HAVE_IPV6 + if (nexthop->type == NEXTHOP_TYPE_IPV6 + || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME + || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) + { + addattr_l (nlmsg, req_size, RTA_GATEWAY, + &nexthop->gate.ipv6, bytelen); + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("netlink_route_multipath() (%s): " + "nexthop via %s if %u", + routedesc, + inet6_ntoa (nexthop->gate.ipv6), + nexthop->ifindex); + } +#endif /* HAVE_IPV6 */ + if (nexthop->type == NEXTHOP_TYPE_IFINDEX + || nexthop->type == NEXTHOP_TYPE_IFNAME + || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) + { + addattr32 (nlmsg, req_size, RTA_OIF, nexthop->ifindex); + + if (nexthop->src.ipv4.s_addr) + addattr_l (nlmsg, req_size, RTA_PREFSRC, + &nexthop->src.ipv4, bytelen); + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("netlink_route_multipath() (%s): " + "nexthop via if %u", routedesc, nexthop->ifindex); + } + + if (nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX + || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME) + { + addattr32 (nlmsg, req_size, RTA_OIF, nexthop->ifindex); + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("netlink_route_multipath() (%s): " + "nexthop via if %u", routedesc, nexthop->ifindex); + } +} + +/* This function takes a nexthop as argument and + * appends to the given rtattr/rtnexthop pair the + * representation of the nexthop. If the nexthop + * defines a preferred source, the src parameter + * will be modified to point to that src, otherwise + * it will be kept unmodified. + * + * @param routedesc: Human readable description of route type + * (direct/recursive, single-/multipath) + * @param bytelen: Length of addresses in bytes. + * @param nexthop: Nexthop information + * @param rta: rtnetlink attribute structure + * @param rtnh: pointer to an rtnetlink nexthop structure + * @param src: pointer pointing to a location where + * the prefsrc should be stored. + */ +static void +_netlink_route_build_multipath( + const char *routedesc, + int bytelen, + struct nexthop *nexthop, + struct rtattr *rta, + struct rtnexthop *rtnh, + union g_addr **src + ) +{ + rtnh->rtnh_len = sizeof (*rtnh); + rtnh->rtnh_flags = 0; + rtnh->rtnh_hops = 0; + rta->rta_len += rtnh->rtnh_len; + + if (nexthop->type == NEXTHOP_TYPE_IPV4 + || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) + { + rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_GATEWAY, + &nexthop->gate.ipv4, bytelen); + rtnh->rtnh_len += sizeof (struct rtattr) + 4; + + if (nexthop->src.ipv4.s_addr) + *src = &nexthop->src; + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("netlink_route_multipath() (%s): " + "nexthop via %s if %u", + routedesc, + inet_ntoa (nexthop->gate.ipv4), + nexthop->ifindex); + } +#ifdef HAVE_IPV6 + if (nexthop->type == NEXTHOP_TYPE_IPV6 + || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME + || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) + { + rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_GATEWAY, + &nexthop->gate.ipv6, bytelen); + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("netlink_route_multipath() (%s): " + "nexthop via %s if %u", + routedesc, + inet6_ntoa (nexthop->gate.ipv6), + nexthop->ifindex); + } +#endif /* HAVE_IPV6 */ + /* ifindex */ + if (nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX + || nexthop->type == NEXTHOP_TYPE_IFINDEX + || nexthop->type == NEXTHOP_TYPE_IFNAME) + { + rtnh->rtnh_ifindex = nexthop->ifindex; + if (nexthop->src.ipv4.s_addr) + *src = &nexthop->src; + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("netlink_route_multipath() (%s): " + "nexthop via if %u", routedesc, nexthop->ifindex); + } + else if (nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME + || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) + { + rtnh->rtnh_ifindex = nexthop->ifindex; + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("netlink_route_multipath() (%s): " + "nexthop via if %u", routedesc, nexthop->ifindex); + } + else + { + rtnh->rtnh_ifindex = 0; + } +} + +/* Log debug information for netlink_route_multipath + * if debug logging is enabled. + * + * @param cmd: Netlink command which is to be processed + * @param p: Prefix for which the change is due + * @param nexthop: Nexthop which is currently processed + * @param routedesc: Semantic annotation for nexthop + * (recursive, multipath, etc.) + * @param family: Address family which the change concerns + */ +static void +_netlink_route_debug( + int cmd, + struct prefix *p, + struct nexthop *nexthop, + const char *routedesc, + int family) +{ + if (IS_ZEBRA_DEBUG_KERNEL) + { + zlog_debug ("netlink_route_multipath() (%s): %s %s/%d type %s", + routedesc, + lookup (nlmsg_str, cmd), +#ifdef HAVE_IPV6 + (family == AF_INET) ? inet_ntoa (p->u.prefix4) : + inet6_ntoa (p->u.prefix6), +#else + inet_ntoa (p->u.prefix4), +#endif /* HAVE_IPV6 */ + p->prefixlen, nexthop_type_to_str (nexthop->type)); + } +} + /* Routing table change via netlink interface. */ static int netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib, @@ -1433,9 +1634,11 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib, { int bytelen; struct sockaddr_nl snl; - struct nexthop *nexthop = NULL; - int nexthop_num = 0; + struct nexthop *nexthop = NULL, *tnexthop; + int recursing; + int nexthop_num; int discard; + const char *routedesc; struct { @@ -1485,159 +1688,52 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib, if (discard) { if (cmd == RTM_NEWROUTE) - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) - SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) + { + /* We shouldn't encounter recursive nexthops on discard routes, + * but it is probably better to handle that case correctly anyway. + */ + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); + } goto skip; } - /* Multipath case. */ - if (rib->nexthop_active_num == 1 || MULTIPATH_NUM == 1) + /* Count overall nexthops so we can decide whether to use singlepath + * or multipath case. */ + nexthop_num = 0; + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) + { + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; + if (cmd == RTM_NEWROUTE && !CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + continue; + if (cmd == RTM_DELROUTE && !CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) + continue; + + nexthop_num++; + } + + /* Singlepath case. */ + if (nexthop_num == 1 || MULTIPATH_NUM == 1) { - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) + nexthop_num = 0; + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) { + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; if ((cmd == RTM_NEWROUTE && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) || (cmd == RTM_DELROUTE && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))) { + routedesc = recursing ? "recursive, 1 hop" : "single hop"; - if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - { - if (IS_ZEBRA_DEBUG_KERNEL) - { - zlog_debug - ("netlink_route_multipath() (recursive, 1 hop): " - "%s %s/%d, type %s", lookup (nlmsg_str, cmd), -#ifdef HAVE_IPV6 - (family == AF_INET) ? inet_ntoa (p->u.prefix4) : - inet6_ntoa (p->u.prefix6), -#else - inet_ntoa (p->u.prefix4), -#endif /* HAVE_IPV6 */ - - p->prefixlen, nexthop_type_to_str (nexthop->rtype)); - } - - if (nexthop->rtype == NEXTHOP_TYPE_IPV4 - || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX) - { - addattr_l (&req.n, sizeof req, RTA_GATEWAY, - &nexthop->rgate.ipv4, bytelen); - if (nexthop->src.ipv4.s_addr) - addattr_l(&req.n, sizeof req, RTA_PREFSRC, - &nexthop->src.ipv4, bytelen); - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("netlink_route_multipath() (recursive, " - "1 hop): nexthop via %s if %u", - inet_ntoa (nexthop->rgate.ipv4), - nexthop->rifindex); - } -#ifdef HAVE_IPV6 - if (nexthop->rtype == NEXTHOP_TYPE_IPV6 - || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX - || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME) - { - addattr_l (&req.n, sizeof req, RTA_GATEWAY, - &nexthop->rgate.ipv6, bytelen); - - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("netlink_route_multipath() (recursive, " - "1 hop): nexthop via %s if %u", - inet6_ntoa (nexthop->rgate.ipv6), - nexthop->rifindex); - } -#endif /* HAVE_IPV6 */ - if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX - || nexthop->rtype == NEXTHOP_TYPE_IFNAME - || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX - || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX - || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME) - { - addattr32 (&req.n, sizeof req, RTA_OIF, - nexthop->rifindex); - if ((nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX - || nexthop->rtype == NEXTHOP_TYPE_IFINDEX) - && nexthop->src.ipv4.s_addr) - addattr_l (&req.n, sizeof req, RTA_PREFSRC, - &nexthop->src.ipv4, bytelen); - - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("netlink_route_multipath() (recursive, " - "1 hop): nexthop via if %u", - nexthop->rifindex); - } - } - else - { - if (IS_ZEBRA_DEBUG_KERNEL) - { - zlog_debug - ("netlink_route_multipath() (single hop): " - "%s %s/%d, type %s", lookup (nlmsg_str, cmd), -#ifdef HAVE_IPV6 - (family == AF_INET) ? inet_ntoa (p->u.prefix4) : - inet6_ntoa (p->u.prefix6), -#else - inet_ntoa (p->u.prefix4), -#endif /* HAVE_IPV6 */ - p->prefixlen, nexthop_type_to_str (nexthop->type)); - } - - if (nexthop->type == NEXTHOP_TYPE_IPV4 - || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) - { - addattr_l (&req.n, sizeof req, RTA_GATEWAY, - &nexthop->gate.ipv4, bytelen); - if (nexthop->src.ipv4.s_addr) - addattr_l (&req.n, sizeof req, RTA_PREFSRC, - &nexthop->src.ipv4, bytelen); - - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("netlink_route_multipath() (single hop): " - "nexthop via %s if %u", - inet_ntoa (nexthop->gate.ipv4), - nexthop->ifindex); - } -#ifdef HAVE_IPV6 - if (nexthop->type == NEXTHOP_TYPE_IPV6 - || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME - || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) - { - addattr_l (&req.n, sizeof req, RTA_GATEWAY, - &nexthop->gate.ipv6, bytelen); - - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("netlink_route_multipath() (single hop): " - "nexthop via %s if %u", - inet6_ntoa (nexthop->gate.ipv6), - nexthop->ifindex); - } -#endif /* HAVE_IPV6 */ - if (nexthop->type == NEXTHOP_TYPE_IFINDEX - || nexthop->type == NEXTHOP_TYPE_IFNAME - || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) - { - addattr32 (&req.n, sizeof req, RTA_OIF, nexthop->ifindex); - - if (nexthop->src.ipv4.s_addr) - addattr_l (&req.n, sizeof req, RTA_PREFSRC, - &nexthop->src.ipv4, bytelen); - - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("netlink_route_multipath() (single hop): " - "nexthop via if %u", nexthop->ifindex); - } - else if (nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX - || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME) - { - addattr32 (&req.n, sizeof req, RTA_OIF, nexthop->ifindex); - - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("netlink_route_multipath() (single hop): " - "nexthop via if %u", nexthop->ifindex); - } - } + _netlink_route_debug(cmd, p, nexthop, routedesc, family); + _netlink_route_build_singlepath(routedesc, bytelen, + nexthop, &req.n, sizeof req); if (cmd == RTM_NEWROUTE) SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); @@ -1659,168 +1755,26 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib, rtnh = RTA_DATA (rta); nexthop_num = 0; - for (nexthop = rib->nexthop; - nexthop && (MULTIPATH_NUM == 0 || nexthop_num < MULTIPATH_NUM); - nexthop = nexthop->next) + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) { + if (MULTIPATH_NUM != 0 && nexthop_num >= MULTIPATH_NUM) + break; + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; + if ((cmd == RTM_NEWROUTE && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) || (cmd == RTM_DELROUTE && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))) { + routedesc = recursing ? "recursive, multihop" : "multihop"; nexthop_num++; - rtnh->rtnh_len = sizeof (*rtnh); - rtnh->rtnh_flags = 0; - rtnh->rtnh_hops = 0; - rta->rta_len += rtnh->rtnh_len; - - if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - { - if (IS_ZEBRA_DEBUG_KERNEL) - { - zlog_debug ("netlink_route_multipath() " - "(recursive, multihop): %s %s/%d type %s", - lookup (nlmsg_str, cmd), -#ifdef HAVE_IPV6 - (family == AF_INET) ? inet_ntoa (p->u.prefix4) : - inet6_ntoa (p->u.prefix6), -#else - inet_ntoa (p->u.prefix4), -#endif /* HAVE_IPV6 */ - p->prefixlen, nexthop_type_to_str (nexthop->rtype)); - } - if (nexthop->rtype == NEXTHOP_TYPE_IPV4 - || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX) - { - rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_GATEWAY, - &nexthop->rgate.ipv4, bytelen); - rtnh->rtnh_len += sizeof (struct rtattr) + 4; - - if (nexthop->src.ipv4.s_addr) - src = &nexthop->src; - - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("netlink_route_multipath() (recursive, " - "multihop): nexthop via %s if %u", - inet_ntoa (nexthop->rgate.ipv4), - nexthop->rifindex); - } -#ifdef HAVE_IPV6 - if (nexthop->rtype == NEXTHOP_TYPE_IPV6 - || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME - || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX) - { - rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_GATEWAY, - &nexthop->rgate.ipv6, bytelen); - - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("netlink_route_multipath() (recursive, " - "multihop): nexthop via %s if %u", - inet6_ntoa (nexthop->rgate.ipv6), - nexthop->rifindex); - } -#endif /* HAVE_IPV6 */ - /* ifindex */ - if (nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX - || nexthop->rtype == NEXTHOP_TYPE_IFINDEX - || nexthop->rtype == NEXTHOP_TYPE_IFNAME) - { - rtnh->rtnh_ifindex = nexthop->rifindex; - if (nexthop->src.ipv4.s_addr) - src = &nexthop->src; - - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("netlink_route_multipath() (recursive, " - "multihop): nexthop via if %u", - nexthop->rifindex); - } - else if (nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX - || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME) - { - rtnh->rtnh_ifindex = nexthop->rifindex; - - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("netlink_route_multipath() (recursive, " - "multihop): nexthop via if %u", - nexthop->rifindex); - } - else - { - rtnh->rtnh_ifindex = 0; - } - } - else - { - if (IS_ZEBRA_DEBUG_KERNEL) - { - zlog_debug ("netlink_route_multipath() (multihop): " - "%s %s/%d, type %s", lookup (nlmsg_str, cmd), -#ifdef HAVE_IPV6 - (family == AF_INET) ? inet_ntoa (p->u.prefix4) : - inet6_ntoa (p->u.prefix6), -#else - inet_ntoa (p->u.prefix4), -#endif /* HAVE_IPV6 */ - p->prefixlen, nexthop_type_to_str (nexthop->type)); - } - if (nexthop->type == NEXTHOP_TYPE_IPV4 - || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) - { - rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_GATEWAY, - &nexthop->gate.ipv4, bytelen); - rtnh->rtnh_len += sizeof (struct rtattr) + 4; - - if (nexthop->src.ipv4.s_addr) - src = &nexthop->src; - - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("netlink_route_multipath() (multihop): " - "nexthop via %s if %u", - inet_ntoa (nexthop->gate.ipv4), - nexthop->ifindex); - } -#ifdef HAVE_IPV6 - if (nexthop->type == NEXTHOP_TYPE_IPV6 - || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME - || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) - { - rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_GATEWAY, - &nexthop->gate.ipv6, bytelen); - - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("netlink_route_multipath() (multihop): " - "nexthop via %s if %u", - inet6_ntoa (nexthop->gate.ipv6), - nexthop->ifindex); - } -#endif /* HAVE_IPV6 */ - /* ifindex */ - if (nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX - || nexthop->type == NEXTHOP_TYPE_IFINDEX - || nexthop->type == NEXTHOP_TYPE_IFNAME) - { - rtnh->rtnh_ifindex = nexthop->ifindex; - if (nexthop->src.ipv4.s_addr) - src = &nexthop->src; - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("netlink_route_multipath() (multihop): " - "nexthop via if %u", nexthop->ifindex); - } - else if (nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME - || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) - { - rtnh->rtnh_ifindex = nexthop->ifindex; - - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("netlink_route_multipath() (multihop): " - "nexthop via if %u", nexthop->ifindex); - } - else - { - rtnh->rtnh_ifindex = 0; - } - } + _netlink_route_debug(cmd, p, nexthop, + routedesc, family); + _netlink_route_build_multipath(routedesc, bytelen, + nexthop, rta, rtnh, &src); rtnh = RTNH_NEXT (rtnh); if (cmd == RTM_NEWROUTE) diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index 1b8ded7e..5d175d84 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -71,7 +71,8 @@ kernel_rtm_ipv4 (int cmd, struct prefix *p, struct rib *rib, int family) { struct sockaddr_in *mask = NULL; struct sockaddr_in sin_dest, sin_mask, sin_gate; - struct nexthop *nexthop; + struct nexthop *nexthop, *tnexthop; + int recursing; int nexthop_num = 0; unsigned int ifindex = 0; int gate = 0; @@ -96,8 +97,11 @@ kernel_rtm_ipv4 (int cmd, struct prefix *p, struct rib *rib, int family) #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ /* Make gateway. */ - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) { + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; + gate = 0; char gate_buf[INET_ADDRSTRLEN] = "NULL"; @@ -112,38 +116,22 @@ kernel_rtm_ipv4 (int cmd, struct prefix *p, struct rib *rib, int family) && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) )) { - if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + if (nexthop->type == NEXTHOP_TYPE_IPV4 || + nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) { - if (nexthop->rtype == NEXTHOP_TYPE_IPV4 || - nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX) - { - sin_gate.sin_addr = nexthop->rgate.ipv4; - gate = 1; - } - if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX - || nexthop->rtype == NEXTHOP_TYPE_IFNAME - || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX) - ifindex = nexthop->rifindex; + sin_gate.sin_addr = nexthop->gate.ipv4; + gate = 1; } - else + if (nexthop->type == NEXTHOP_TYPE_IFINDEX + || nexthop->type == NEXTHOP_TYPE_IFNAME + || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) + ifindex = nexthop->ifindex; + if (nexthop->type == NEXTHOP_TYPE_BLACKHOLE) { - if (nexthop->type == NEXTHOP_TYPE_IPV4 || - nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) - { - sin_gate.sin_addr = nexthop->gate.ipv4; - gate = 1; - } - if (nexthop->type == NEXTHOP_TYPE_IFINDEX - || nexthop->type == NEXTHOP_TYPE_IFNAME - || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) - ifindex = nexthop->ifindex; - if (nexthop->type == NEXTHOP_TYPE_BLACKHOLE) - { - struct in_addr loopback; - loopback.s_addr = htonl (INADDR_LOOPBACK); - sin_gate.sin_addr = loopback; - gate = 1; - } + struct in_addr loopback; + loopback.s_addr = htonl (INADDR_LOOPBACK); + sin_gate.sin_addr = loopback; + gate = 1; } if (gate && p->prefixlen == 32) @@ -219,7 +207,7 @@ kernel_rtm_ipv4 (int cmd, struct prefix *p, struct rib *rib, int family) if (IS_ZEBRA_DEBUG_RIB) zlog_debug ("%s: odd command %s for flags %d", __func__, lookup (rtm_type_str, cmd), nexthop->flags); - } /* for (nexthop = ... */ + } /* for (ALL_NEXTHOPS_RO(...))*/ /* If there was no useful nexthop, then complain. */ if (nexthop_num == 0 && IS_ZEBRA_DEBUG_KERNEL) @@ -354,7 +342,8 @@ kernel_rtm_ipv6_multipath (int cmd, struct prefix *p, struct rib *rib, { struct sockaddr_in6 *mask; struct sockaddr_in6 sin_dest, sin_mask, sin_gate; - struct nexthop *nexthop; + struct nexthop *nexthop, *tnexthop; + int recursing; int nexthop_num = 0; unsigned int ifindex = 0; int gate = 0; @@ -376,8 +365,11 @@ kernel_rtm_ipv6_multipath (int cmd, struct prefix *p, struct rib *rib, #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ /* Make gateway. */ - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) { + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; + gate = 0; if ((cmd == RTM_ADD @@ -388,36 +380,18 @@ kernel_rtm_ipv6_multipath (int cmd, struct prefix *p, struct rib *rib, #endif )) { - if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - { - if (nexthop->rtype == NEXTHOP_TYPE_IPV6 - || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME - || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX) - { - sin_gate.sin6_addr = nexthop->rgate.ipv6; - gate = 1; - } - if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX - || nexthop->rtype == NEXTHOP_TYPE_IFNAME - || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME - || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX) - ifindex = nexthop->rifindex; - } - else + if (nexthop->type == NEXTHOP_TYPE_IPV6 + || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME + || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) { - if (nexthop->type == NEXTHOP_TYPE_IPV6 - || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME - || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) - { - sin_gate.sin6_addr = nexthop->gate.ipv6; - gate = 1; - } - if (nexthop->type == NEXTHOP_TYPE_IFINDEX - || nexthop->type == NEXTHOP_TYPE_IFNAME - || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME - || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) - ifindex = nexthop->ifindex; + sin_gate.sin6_addr = nexthop->gate.ipv6; + gate = 1; } + if (nexthop->type == NEXTHOP_TYPE_IFINDEX + || nexthop->type == NEXTHOP_TYPE_IFNAME + || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME + || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) + ifindex = nexthop->ifindex; if (cmd == RTM_ADD) SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c index 67bcf0a1..b5f2b760 100644 --- a/zebra/zebra_fpm_netlink.c +++ b/zebra/zebra_fpm_netlink.c @@ -152,7 +152,8 @@ typedef struct netlink_route_info_t_ * Returns TRUE if a nexthop was added, FALSE otherwise. */ static int -netlink_route_info_add_nh (netlink_route_info_t *ri, struct nexthop *nexthop) +netlink_route_info_add_nh (netlink_route_info_t *ri, struct nexthop *nexthop, + int recursive) { netlink_nh_info_t nhi; union g_addr *src; @@ -163,40 +164,7 @@ netlink_route_info_add_nh (netlink_route_info_t *ri, struct nexthop *nexthop) if (ri->num_nhs >= (int) ZEBRA_NUM_OF (ri->nhs)) return 0; - if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - { - nhi.recursive = 1; - nhi.type = nexthop->rtype; - nhi.if_index = nexthop->rifindex; - - if (nexthop->rtype == NEXTHOP_TYPE_IPV4 - || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX) - { - nhi.gateway = &nexthop->rgate; - if (nexthop->src.ipv4.s_addr) - src = &nexthop->src; - } - -#ifdef HAVE_IPV6 - if (nexthop->rtype == NEXTHOP_TYPE_IPV6 - || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX - || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME) - { - nhi.gateway = &nexthop->rgate; - } -#endif /* HAVE_IPV6 */ - - if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX - || nexthop->rtype == NEXTHOP_TYPE_IFNAME) - { - if (nexthop->src.ipv4.s_addr) - src = &nexthop->src; - } - - goto done; - } - - nhi.recursive = 0; + nhi.recursive = recursive; nhi.type = nexthop->type; nhi.if_index = nexthop->ifindex; @@ -224,11 +192,6 @@ netlink_route_info_add_nh (netlink_route_info_t *ri, struct nexthop *nexthop) src = &nexthop->src; } - /* - * Fall through... - */ - - done: if (!nhi.gateway && nhi.if_index == 0) return 0; @@ -272,7 +235,8 @@ static int netlink_route_info_fill (netlink_route_info_t *ri, int cmd, rib_dest_t *dest, struct rib *rib) { - struct nexthop *nexthop = NULL; + struct nexthop *nexthop, *tnexthop; + int recursing; int discard; memset (ri, 0, sizeof (*ri)); @@ -321,35 +285,20 @@ netlink_route_info_fill (netlink_route_info_t *ri, int cmd, goto skip; } - /* Multipath case. */ - if (rib->nexthop_active_num == 1 || MULTIPATH_NUM == 1) + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) { - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) - { + if (MULTIPATH_NUM != 0 && ri->num_nhs >= MULTIPATH_NUM) + break; - if ((cmd == RTM_NEWROUTE - && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) - || (cmd == RTM_DELROUTE - && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))) - { - netlink_route_info_add_nh (ri, nexthop); - break; - } - } - } - else - { - for (nexthop = rib->nexthop; - nexthop && (MULTIPATH_NUM == 0 || ri->num_nhs < MULTIPATH_NUM); - nexthop = nexthop->next) + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; + + if ((cmd == RTM_NEWROUTE + && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + || (cmd == RTM_DELROUTE + && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))) { - if ((cmd == RTM_NEWROUTE - && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) - || (cmd == RTM_DELROUTE - && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))) - { - netlink_route_info_add_nh (ri, nexthop); - } + netlink_route_info_add_nh (ri, nexthop, recursing); } } diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 4dd8551a..e39976ee 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -202,20 +202,26 @@ nexthop_type_to_str (enum nexthop_types_t nh_type) return desc[nh_type]; } -/* Add nexthop to the end of the list. */ +/* Add nexthop to the end of a nexthop list. */ static void -nexthop_add (struct rib *rib, struct nexthop *nexthop) +_nexthop_add (struct nexthop **target, struct nexthop *nexthop) { struct nexthop *last; - for (last = rib->nexthop; last && last->next; last = last->next) + for (last = *target; last && last->next; last = last->next) ; if (last) last->next = nexthop; else - rib->nexthop = nexthop; + *target = nexthop; nexthop->prev = last; +} +/* Add nexthop to the end of a rib node's nexthop list */ +static void +nexthop_add (struct rib *rib, struct nexthop *nexthop) +{ + _nexthop_add(&rib->nexthop, nexthop); rib->nexthop_num++; } @@ -232,15 +238,32 @@ nexthop_delete (struct rib *rib, struct nexthop *nexthop) rib->nexthop_num--; } +static void nexthops_free(struct nexthop *nexthop); + /* Free nexthop. */ static void nexthop_free (struct nexthop *nexthop) { if (nexthop->ifname) XFREE (0, nexthop->ifname); + if (nexthop->resolved) + nexthops_free(nexthop->resolved); XFREE (MTYPE_NEXTHOP, nexthop); } +/* Frees a list of nexthops */ +static void +nexthops_free (struct nexthop *nexthop) +{ + struct nexthop *nh, *next; + + for (nh = nexthop; nh; nh = next) + { + next = nh->next; + nexthop_free (nh); + } +} + struct nexthop * nexthop_ifindex_add (struct rib *rib, unsigned int ifindex) { @@ -365,6 +388,24 @@ nexthop_blackhole_add (struct rib *rib) return nexthop; } +/* This method checks whether a recursive nexthop has at + * least one resolved nexthop in the fib. + */ +int +nexthop_has_fib_child(struct nexthop *nexthop) +{ + struct nexthop *nh; + + if (! CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + return 0; + + for (nh = nexthop->resolved; nh; nh = nh->next) + if (CHECK_FLAG (nh->flags, NEXTHOP_FLAG_FIB)) + return 1; + + return 0; +} + /* If force flag is not set, do not modify falgs at all for uninstall the route from FIB. */ static int @@ -375,13 +416,19 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, struct route_table *table; struct route_node *rn; struct rib *match; + int resolved; struct nexthop *newhop; + struct nexthop *resolved_hop; if (nexthop->type == NEXTHOP_TYPE_IPV4) nexthop->ifindex = 0; if (set) - UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE); + { + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE); + nexthops_free(nexthop->resolved); + nexthop->resolved = NULL; + } /* Make lookup prefix. */ memset (&p, 0, sizeof (struct prefix_ipv4)); @@ -436,6 +483,7 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, } else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL)) { + resolved = 0; for (newhop = match->nexthop; newhop; newhop = newhop->next) if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB) && ! CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_RECURSIVE)) @@ -443,18 +491,25 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, if (set) { SET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE); - nexthop->rtype = newhop->type; + + resolved_hop = XCALLOC(MTYPE_NEXTHOP, sizeof (struct nexthop)); + SET_FLAG (resolved_hop->flags, NEXTHOP_FLAG_ACTIVE); + + resolved_hop->type = newhop->type; if (newhop->type == NEXTHOP_TYPE_IPV4 || newhop->type == NEXTHOP_TYPE_IPV4_IFINDEX) - nexthop->rgate.ipv4 = newhop->gate.ipv4; + resolved_hop->gate.ipv4 = newhop->gate.ipv4; + if (newhop->type == NEXTHOP_TYPE_IFINDEX || newhop->type == NEXTHOP_TYPE_IFNAME || newhop->type == NEXTHOP_TYPE_IPV4_IFINDEX) - nexthop->rifindex = newhop->ifindex; + resolved_hop->ifindex = newhop->ifindex; + + _nexthop_add(&nexthop->resolved, resolved_hop); } - return 1; + resolved = 1; } - return 0; + return resolved; } else { @@ -476,13 +531,19 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, struct route_table *table; struct route_node *rn; struct rib *match; + int resolved; struct nexthop *newhop; + struct nexthop *resolved_hop; if (nexthop->type == NEXTHOP_TYPE_IPV6) nexthop->ifindex = 0; if (set) - UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE); + { + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE); + nexthops_free(nexthop->resolved); + nexthop->resolved = NULL; + } /* Make lookup prefix. */ memset (&p, 0, sizeof (struct prefix_ipv6)); @@ -538,6 +599,7 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, } else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL)) { + resolved = 0; for (newhop = match->nexthop; newhop; newhop = newhop->next) if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB) && ! CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_RECURSIVE)) @@ -545,20 +607,27 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, if (set) { SET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE); - nexthop->rtype = newhop->type; + + resolved_hop = XCALLOC(MTYPE_NEXTHOP, sizeof (struct nexthop)); + SET_FLAG (resolved_hop->flags, NEXTHOP_FLAG_ACTIVE); + + resolved_hop->type = newhop->type; if (newhop->type == NEXTHOP_TYPE_IPV6 || newhop->type == NEXTHOP_TYPE_IPV6_IFINDEX || newhop->type == NEXTHOP_TYPE_IPV6_IFNAME) - nexthop->rgate.ipv6 = newhop->gate.ipv6; + resolved_hop->gate.ipv6 = newhop->gate.ipv6; + if (newhop->type == NEXTHOP_TYPE_IFINDEX || newhop->type == NEXTHOP_TYPE_IFNAME || newhop->type == NEXTHOP_TYPE_IPV6_IFINDEX || newhop->type == NEXTHOP_TYPE_IPV6_IFNAME) - nexthop->rifindex = newhop->ifindex; + resolved_hop->ifindex = newhop->ifindex; + + _nexthop_add(&nexthop->resolved, resolved_hop); } - return 1; + resolved = 1; } - return 0; + return resolved; } else { @@ -577,7 +646,8 @@ rib_match_ipv4 (struct in_addr addr) struct route_table *table; struct route_node *rn; struct rib *match; - struct nexthop *newhop; + struct nexthop *newhop, *tnewhop; + int recursing; /* Lookup table. */ table = vrf_table (AFI_IP, SAFI_UNICAST, 0); @@ -622,7 +692,7 @@ rib_match_ipv4 (struct in_addr addr) return match; else { - for (newhop = match->nexthop; newhop; newhop = newhop->next) + for (ALL_NEXTHOPS_RO(match->nexthop, newhop, tnewhop, recursing)) if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB)) return match; return NULL; @@ -638,7 +708,8 @@ rib_lookup_ipv4 (struct prefix_ipv4 *p) struct route_table *table; struct route_node *rn; struct rib *match; - struct nexthop *nexthop; + struct nexthop *nexthop, *tnexthop; + int recursing; /* Lookup table. */ table = vrf_table (AFI_IP, SAFI_UNICAST, 0); @@ -668,7 +739,7 @@ rib_lookup_ipv4 (struct prefix_ipv4 *p) if (match->type == ZEBRA_ROUTE_CONNECT) return match; - for (nexthop = match->nexthop; nexthop; nexthop = nexthop->next) + for (ALL_NEXTHOPS_RO(match->nexthop, nexthop, tnexthop, recursing)) if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) return match; @@ -693,7 +764,9 @@ rib_lookup_ipv4_route (struct prefix_ipv4 *p, union sockunion * qgate) struct route_table *table; struct route_node *rn; struct rib *match; - struct nexthop *nexthop; + struct nexthop *nexthop, *tnexthop; + int recursing; + int nexthops_active; /* Lookup table. */ table = vrf_table (AFI_IP, SAFI_UNICAST, 0); @@ -727,26 +800,25 @@ rib_lookup_ipv4_route (struct prefix_ipv4 *p, union sockunion * qgate) return ZEBRA_RIB_FOUND_CONNECTED; /* Ok, we have a cood candidate, let's check it's nexthop list... */ - for (nexthop = match->nexthop; nexthop; nexthop = nexthop->next) + nexthops_active = 0; + for (ALL_NEXTHOPS_RO(match->nexthop, nexthop, tnexthop, recursing)) if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) - { - /* We are happy with either direct or recursive hexthop */ - if (nexthop->gate.ipv4.s_addr == sockunion2ip (qgate) || - nexthop->rgate.ipv4.s_addr == sockunion2ip (qgate)) - return ZEBRA_RIB_FOUND_EXACT; - else { + nexthops_active = 1; + if (nexthop->gate.ipv4.s_addr == sockunion2ip (qgate)) + return ZEBRA_RIB_FOUND_EXACT; if (IS_ZEBRA_DEBUG_RIB) - { - char gate_buf[INET_ADDRSTRLEN], rgate_buf[INET_ADDRSTRLEN], qgate_buf[INET_ADDRSTRLEN]; - inet_ntop (AF_INET, &nexthop->gate.ipv4.s_addr, gate_buf, INET_ADDRSTRLEN); - inet_ntop (AF_INET, &nexthop->rgate.ipv4.s_addr, rgate_buf, INET_ADDRSTRLEN); - inet_ntop (AF_INET, &sockunion2ip (qgate), qgate_buf, INET_ADDRSTRLEN); - zlog_debug ("%s: qgate == %s, gate == %s, rgate == %s", __func__, qgate_buf, gate_buf, rgate_buf); - } - return ZEBRA_RIB_FOUND_NOGATE; + { + char gate_buf[INET_ADDRSTRLEN], qgate_buf[INET_ADDRSTRLEN]; + inet_ntop (AF_INET, &nexthop->gate.ipv4.s_addr, gate_buf, INET_ADDRSTRLEN); + inet_ntop (AF_INET, &sockunion2ip(qgate), qgate_buf, INET_ADDRSTRLEN); + zlog_debug ("%s: qgate == %s, %s == %s", __func__, + qgate_buf, recursing ? "rgate" : "gate", gate_buf); + } } - } + + if (nexthops_active) + return ZEBRA_RIB_FOUND_NOGATE; return ZEBRA_RIB_NOTFOUND; } @@ -759,7 +831,8 @@ rib_match_ipv6 (struct in6_addr *addr) struct route_table *table; struct route_node *rn; struct rib *match; - struct nexthop *newhop; + struct nexthop *newhop, *tnewhop; + int recursing; /* Lookup table. */ table = vrf_table (AFI_IP6, SAFI_UNICAST, 0); @@ -804,7 +877,7 @@ rib_match_ipv6 (struct in6_addr *addr) return match; else { - for (newhop = match->nexthop; newhop; newhop = newhop->next) + for (ALL_NEXTHOPS_RO(match->nexthop, newhop, tnewhop, recursing)) if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB)) return match; return NULL; @@ -966,7 +1039,8 @@ static void rib_install_kernel (struct route_node *rn, struct rib *rib) { int ret = 0; - struct nexthop *nexthop; + struct nexthop *nexthop, *tnexthop; + int recursing; /* * Make sure we update the FPM any time we send new information to @@ -988,7 +1062,7 @@ rib_install_kernel (struct route_node *rn, struct rib *rib) /* This condition is never met, if we are using rt_socket.c */ if (ret < 0) { - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); } } @@ -998,7 +1072,8 @@ static int rib_uninstall_kernel (struct route_node *rn, struct rib *rib) { int ret = 0; - struct nexthop *nexthop; + struct nexthop *nexthop, *tnexthop; + int recursing; /* * Make sure we update the FPM any time we send new information to @@ -1018,7 +1093,7 @@ rib_uninstall_kernel (struct route_node *rn, struct rib *rib) #endif /* HAVE_IPV6 */ } - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); return ret; @@ -1114,7 +1189,8 @@ rib_process (struct route_node *rn) struct rib *select = NULL; struct rib *del = NULL; int installed = 0; - struct nexthop *nexthop = NULL; + struct nexthop *nexthop = NULL, *tnexthop; + int recursing; char buf[INET6_ADDRSTRLEN]; assert (rn); @@ -1237,7 +1313,7 @@ rib_process (struct route_node *rn) This makes sure the routes are IN the kernel. */ - for (nexthop = select->nexthop; nexthop; nexthop = nexthop->next) + for (ALL_NEXTHOPS_RO(select->nexthop, nexthop, tnexthop, recursing)) if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) { installed = 1; @@ -1626,7 +1702,6 @@ rib_addnode (struct route_node *rn, struct rib *rib) static void rib_unlink (struct route_node *rn, struct rib *rib) { - struct nexthop *nexthop, *next; char buf[INET6_ADDRSTRLEN]; rib_dest_t *dest; @@ -1652,11 +1727,7 @@ rib_unlink (struct route_node *rn, struct rib *rib) } /* free RIB and nexthops */ - for (nexthop = rib->nexthop; nexthop; nexthop = next) - { - next = nexthop->next; - nexthop_free (nexthop); - } + nexthops_free(rib->nexthop); XFREE (MTYPE_RIB, rib); } @@ -1786,11 +1857,12 @@ rib_add_ipv4 (int type, int flags, struct prefix_ipv4 *p, void rib_dump (const char * func, const struct prefix_ipv4 * p, const struct rib * rib) { - char straddr1[INET_ADDRSTRLEN], straddr2[INET_ADDRSTRLEN]; - struct nexthop *nexthop; + char straddr[INET_ADDRSTRLEN]; + struct nexthop *nexthop, *tnexthop; + int recursing; - inet_ntop (AF_INET, &p->prefix, straddr1, INET_ADDRSTRLEN); - zlog_debug ("%s: dumping RIB entry %p for %s/%d", func, rib, straddr1, p->prefixlen); + inet_ntop (AF_INET, &p->prefix, straddr, INET_ADDRSTRLEN); + zlog_debug ("%s: dumping RIB entry %p for %s/%d", func, rib, straddr, p->prefixlen); zlog_debug ( "%s: refcnt == %lu, uptime == %lu, type == %u, table == %d", @@ -1817,21 +1889,20 @@ void rib_dump (const char * func, const struct prefix_ipv4 * p, const struct rib rib->nexthop_active_num, rib->nexthop_fib_num ); - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) - { - inet_ntop (AF_INET, &nexthop->gate.ipv4.s_addr, straddr1, INET_ADDRSTRLEN); - inet_ntop (AF_INET, &nexthop->rgate.ipv4.s_addr, straddr2, INET_ADDRSTRLEN); - zlog_debug - ( - "%s: NH %s (%s) with flags %s%s%s", - func, - straddr1, - straddr2, - (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE) ? "ACTIVE " : ""), - (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? "FIB " : ""), - (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE) ? "RECURSIVE" : "") - ); - } + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) + { + inet_ntop (AF_INET, &nexthop->gate.ipv4.s_addr, straddr, INET_ADDRSTRLEN); + zlog_debug + ( + "%s: %s %s with flags %s%s%s", + func, + (recursing ? " NH" : "NH"), + straddr, + (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE) ? "ACTIVE " : ""), + (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? "FIB " : ""), + (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE) ? "RECURSIVE" : "") + ); + } zlog_debug ("%s: dump complete", func); } @@ -2018,7 +2089,8 @@ rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p, struct rib *rib; struct rib *fib = NULL; struct rib *same = NULL; - struct nexthop *nexthop; + struct nexthop *nexthop, *tnexthop; + int recursing; char buf1[INET_ADDRSTRLEN]; char buf2[INET_ADDRSTRLEN]; @@ -2085,16 +2157,23 @@ rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p, break; } /* Make sure that the route found has the same gateway. */ - else if (gate == NULL || - ((nexthop = rib->nexthop) && - (IPV4_ADDR_SAME (&nexthop->gate.ipv4, gate) || - IPV4_ADDR_SAME (&nexthop->rgate.ipv4, gate)))) + else { - same = rib; - break; - } + if (gate == NULL) + { + same = rib; + break; + } + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) + if (IPV4_ADDR_SAME (&nexthop->gate.ipv4, gate)) + { + same = rib; + break; + } + if (same) + break; + } } - /* If same type of route can't be found and this message is from kernel. */ if (! same) @@ -2576,7 +2655,8 @@ rib_delete_ipv6 (int type, int flags, struct prefix_ipv6 *p, struct rib *rib; struct rib *fib = NULL; struct rib *same = NULL; - struct nexthop *nexthop; + struct nexthop *nexthop, *tnexthop; + int recursing; char buf1[INET6_ADDRSTRLEN]; char buf2[INET6_ADDRSTRLEN]; @@ -2636,14 +2716,22 @@ rib_delete_ipv6 (int type, int flags, struct prefix_ipv6 *p, break; } /* Make sure that the route found has the same gateway. */ - else if (gate == NULL || - ((nexthop = rib->nexthop) && - (IPV6_ADDR_SAME (&nexthop->gate.ipv6, gate) || - IPV6_ADDR_SAME (&nexthop->rgate.ipv6, gate)))) - { - same = rib; - break; - } + else + { + if (gate == NULL) + { + same = rib; + break; + } + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) + if (IPV6_ADDR_SAME (&nexthop->gate.ipv6, gate)) + { + same = rib; + break; + } + if (same) + break; + } } /* If same type of route can't be found and this message is from diff --git a/zebra/zebra_routemap.c b/zebra/zebra_routemap.c index b3111b8e..39c7e1bf 100644 --- a/zebra/zebra_routemap.c +++ b/zebra/zebra_routemap.c @@ -422,14 +422,10 @@ route_match_ip_next_hop (void *rule, struct prefix *prefix, switch (nexthop->type) { case NEXTHOP_TYPE_IFINDEX: case NEXTHOP_TYPE_IFNAME: + /* Interface routes can't match ip next-hop */ + return RMAP_NOMATCH; case NEXTHOP_TYPE_IPV4_IFINDEX: case NEXTHOP_TYPE_IPV4_IFNAME: - if (nexthop->rtype != NEXTHOP_TYPE_IPV4) - return RMAP_NOMATCH; - p.family = AF_INET; - p.prefix = nexthop->rgate.ipv4; - p.prefixlen = IPV4_MAX_BITLEN; - break; case NEXTHOP_TYPE_IPV4: p.family = AF_INET; p.prefix = nexthop->gate.ipv4; @@ -488,14 +484,10 @@ route_match_ip_next_hop_prefix_list (void *rule, struct prefix *prefix, switch (nexthop->type) { case NEXTHOP_TYPE_IFINDEX: case NEXTHOP_TYPE_IFNAME: + /* Interface routes can't match ip next-hop */ + return RMAP_NOMATCH; case NEXTHOP_TYPE_IPV4_IFINDEX: case NEXTHOP_TYPE_IPV4_IFNAME: - if (nexthop->rtype != NEXTHOP_TYPE_IPV4) - return RMAP_NOMATCH; - p.family = AF_INET; - p.prefix = nexthop->rgate.ipv4; - p.prefixlen = IPV4_MAX_BITLEN; - break; case NEXTHOP_TYPE_IPV4: p.family = AF_INET; p.prefix = nexthop->gate.ipv4; diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index a672d422..e1da7df8 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -533,7 +533,8 @@ static void vty_show_ip_route_detail (struct vty *vty, struct route_node *rn) { struct rib *rib; - struct nexthop *nexthop; + struct nexthop *nexthop, *tnexthop; + int recursing; RNODE_FOREACH_RIB (rn, rib) { @@ -582,12 +583,13 @@ vty_show_ip_route_detail (struct vty *vty, struct route_node *rn) vty_out (vty, " ago%s", VTY_NEWLINE); } - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) { char addrstr[32]; - vty_out (vty, " %c", - CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? '*' : ' '); + vty_out (vty, " %c%s", + CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? '*' : ' ', + recursing ? " " : ""); switch (nexthop->type) { @@ -614,28 +616,8 @@ vty_show_ip_route_detail (struct vty *vty, struct route_node *rn) vty_out (vty, " inactive"); if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - { - vty_out (vty, " (recursive"); - - switch (nexthop->rtype) - { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - vty_out (vty, " via %s", inet_ntoa (nexthop->rgate.ipv4)); - if (nexthop->rifindex) - vty_out (vty, ", %s", ifindex2ifname (nexthop->rifindex)); - vty_out (vty, ")"); - - break; - case NEXTHOP_TYPE_IFINDEX: - case NEXTHOP_TYPE_IFNAME: - vty_out (vty, " is directly connected, %s)", - ifindex2ifname (nexthop->rifindex)); - break; - default: - break; - } - } + vty_out (vty, " (recursive)"); + switch (nexthop->type) { case NEXTHOP_TYPE_IPV4: @@ -672,12 +654,13 @@ vty_show_ip_route_detail (struct vty *vty, struct route_node *rn) static void vty_show_ip_route (struct vty *vty, struct route_node *rn, struct rib *rib) { - struct nexthop *nexthop; + struct nexthop *nexthop, *tnexthop; + int recursing; int len = 0; char buf[BUFSIZ]; /* Nexthop information. */ - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) { if (nexthop == rib->nexthop) { @@ -701,7 +684,7 @@ vty_show_ip_route (struct vty *vty, struct route_node *rn, struct rib *rib) vty_out (vty, " %c%*c", CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? '*' : ' ', - len - 3, ' '); + len - 3 + (2 * recursing), ' '); switch (nexthop->type) { @@ -728,27 +711,8 @@ vty_show_ip_route (struct vty *vty, struct route_node *rn, struct rib *rib) vty_out (vty, " inactive"); if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - { - vty_out (vty, " (recursive"); - - switch (nexthop->rtype) - { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - vty_out (vty, " via %s", inet_ntoa (nexthop->rgate.ipv4)); - if (nexthop->rifindex) - vty_out (vty, ", %s", ifindex2ifname (nexthop->rifindex)); - vty_out (vty, ")"); - break; - case NEXTHOP_TYPE_IFINDEX: - case NEXTHOP_TYPE_IFNAME: - vty_out (vty, " is directly connected, %s)", - ifindex2ifname (nexthop->rifindex)); - break; - default: - break; - } - } + vty_out (vty, " (recursive)"); + switch (nexthop->type) { case NEXTHOP_TYPE_IPV4: @@ -1058,7 +1022,8 @@ vty_show_ip_route_summary (struct vty *vty, struct route_table *table) { rib_cnt[ZEBRA_ROUTE_TOTAL]++; rib_cnt[rib->type]++; - if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) + || nexthop_has_fib_child(nexthop)) { fib_cnt[ZEBRA_ROUTE_TOTAL]++; fib_cnt[rib->type]++; @@ -1067,7 +1032,8 @@ vty_show_ip_route_summary (struct vty *vty, struct route_table *table) CHECK_FLAG (rib->flags, ZEBRA_FLAG_IBGP)) { rib_cnt[ZEBRA_ROUTE_IBGP]++; - if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) + || nexthop_has_fib_child(nexthop)) fib_cnt[ZEBRA_ROUTE_IBGP]++; } } @@ -1550,7 +1516,8 @@ static void vty_show_ipv6_route_detail (struct vty *vty, struct route_node *rn) { struct rib *rib; - struct nexthop *nexthop; + struct nexthop *nexthop, *tnexthop; + int recursing; char buf[BUFSIZ]; RNODE_FOREACH_RIB (rn, rib) @@ -1601,10 +1568,11 @@ vty_show_ipv6_route_detail (struct vty *vty, struct route_node *rn) vty_out (vty, " ago%s", VTY_NEWLINE); } - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) { - vty_out (vty, " %c", - CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? '*' : ' '); + vty_out (vty, " %c%s", + CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? '*' : ' ', + recursing ? " " : ""); switch (nexthop->type) { @@ -1633,29 +1601,8 @@ vty_show_ipv6_route_detail (struct vty *vty, struct route_node *rn) vty_out (vty, " inactive"); if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - { - vty_out (vty, " (recursive"); - - switch (nexthop->rtype) - { - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - case NEXTHOP_TYPE_IPV6_IFNAME: - vty_out (vty, " via %s)", - inet_ntop (AF_INET6, &nexthop->rgate.ipv6, - buf, BUFSIZ)); - if (nexthop->rifindex) - vty_out (vty, ", %s", ifindex2ifname (nexthop->rifindex)); - break; - case NEXTHOP_TYPE_IFINDEX: - case NEXTHOP_TYPE_IFNAME: - vty_out (vty, " is directly connected, %s)", - ifindex2ifname (nexthop->rifindex)); - break; - default: - break; - } - } + vty_out (vty, " (recursive)"); + vty_out (vty, "%s", VTY_NEWLINE); } vty_out (vty, "%s", VTY_NEWLINE); @@ -1666,12 +1613,13 @@ static void vty_show_ipv6_route (struct vty *vty, struct route_node *rn, struct rib *rib) { - struct nexthop *nexthop; + struct nexthop *nexthop, *tnexthop; + int recursing; int len = 0; char buf[BUFSIZ]; /* Nexthop information. */ - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) { if (nexthop == rib->nexthop) { @@ -1695,7 +1643,7 @@ vty_show_ipv6_route (struct vty *vty, struct route_node *rn, vty_out (vty, " %c%*c", CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? '*' : ' ', - len - 3, ' '); + len - 3 + (2 * recursing), ' '); switch (nexthop->type) { @@ -1724,29 +1672,7 @@ vty_show_ipv6_route (struct vty *vty, struct route_node *rn, vty_out (vty, " inactive"); if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - { - vty_out (vty, " (recursive"); - - switch (nexthop->rtype) - { - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - case NEXTHOP_TYPE_IPV6_IFNAME: - vty_out (vty, " via %s)", - inet_ntop (AF_INET6, &nexthop->rgate.ipv6, - buf, BUFSIZ)); - if (nexthop->rifindex) - vty_out (vty, ", %s", ifindex2ifname (nexthop->rifindex)); - break; - case NEXTHOP_TYPE_IFINDEX: - case NEXTHOP_TYPE_IFNAME: - vty_out (vty, " is directly connected, %s)", - ifindex2ifname (nexthop->rifindex)); - break; - default: - break; - } - } + vty_out (vty, " (recursive)"); if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_BLACKHOLE)) vty_out (vty, ", bh"); diff --git a/zebra/zserv.c b/zebra/zserv.c index 11829378..5df521b0 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -389,7 +389,8 @@ zsend_route_multipath (int cmd, struct zserv *client, struct prefix *p, for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) { - if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB) + || nexthop_has_fib_child(nexthop)) { SET_FLAG (zapi_flags, ZAPI_MESSAGE_NEXTHOP); SET_FLAG (zapi_flags, ZAPI_MESSAGE_IFINDEX); @@ -488,6 +489,9 @@ zsend_ipv6_nexthop_lookup (struct zserv *client, struct in6_addr *addr) num = 0; nump = stream_get_endp(s); stream_putc (s, 0); + /* Only non-recursive routes are elegible to resolve nexthop we + * are looking up. Therefore, we will just iterate over the top + * chain of nexthops. */ for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) { @@ -554,6 +558,9 @@ zsend_ipv4_nexthop_lookup (struct zserv *client, struct in_addr addr) num = 0; nump = stream_get_endp(s); stream_putc (s, 0); + /* Only non-recursive routes are elegible to resolve the nexthop we + * are looking up. Therefore, we will just iterate over the top + * chain of nexthops. */ for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) { @@ -619,7 +626,8 @@ zsend_ipv4_import_lookup (struct zserv *client, struct prefix_ipv4 *p) nump = stream_get_endp(s); stream_putc (s, 0); for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) - if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB) + || nexthop_has_fib_child(nexthop)) { stream_putc (s, nexthop->type); switch (nexthop->type) From 48a53dc71cb422e619859b79d5069e02fcd867d0 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Fri, 5 Jul 2013 15:35:38 +0000 Subject: [PATCH 127/482] zebra: handle blackholes encountered in recursive resolution Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- zebra/zebra_rib.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index e39976ee..301e0cc1 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -472,6 +472,12 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, } else { + /* If the longest prefix match for the nexthop yields + * a blackhole, mark it as inactive. */ + if (CHECK_FLAG (match->flags, ZEBRA_FLAG_BLACKHOLE) + || CHECK_FLAG (match->flags, ZEBRA_FLAG_REJECT)) + return 0; + if (match->type == ZEBRA_ROUTE_CONNECT) { /* Directly point connected route. */ @@ -587,6 +593,12 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, } else { + /* If the longest prefix match for the nexthop yields + * a blackhole, mark it as inactive. */ + if (CHECK_FLAG (match->flags, ZEBRA_FLAG_BLACKHOLE) + || CHECK_FLAG (match->flags, ZEBRA_FLAG_REJECT)) + return 0; + if (match->type == ZEBRA_ROUTE_CONNECT) { /* Directly point connected route. */ From e8d3d2991f72613edb76dea244a8c8e4684873dd Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Fri, 5 Jul 2013 15:35:39 +0000 Subject: [PATCH 128/482] zebra: implement NEXTHOP_FLAG_ONLINK On Linux, the kernel will only allow for a route to be installed when its gateway is directly attached according the kernel fib. There are cases when this restriction by the kernel is too strong, in those cases, we deploy the RTNH_F_ONLINK netlink flag. Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- zebra/rib.h | 1 + zebra/rt_netlink.c | 9 ++++++++- zebra/zebra_vty.c | 9 +++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/zebra/rib.h b/zebra/rib.h index 4d98e059..1c548795 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -253,6 +253,7 @@ struct nexthop #define NEXTHOP_FLAG_ACTIVE (1 << 0) /* This nexthop is alive. */ #define NEXTHOP_FLAG_FIB (1 << 1) /* FIB nexthop. */ #define NEXTHOP_FLAG_RECURSIVE (1 << 2) /* Recursive nexthop. */ +#define NEXTHOP_FLAG_ONLINK (1 << 3) /* Nexthop should be installed onlink. */ /* Nexthop address */ union g_addr gate; diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index b0ade058..7a820bfd 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -1443,8 +1443,11 @@ _netlink_route_build_singlepath( int bytelen, struct nexthop *nexthop, struct nlmsghdr *nlmsg, + struct rtmsg *rtmsg, size_t req_size) { + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ONLINK)) + rtmsg->rtm_flags |= RTNH_F_ONLINK; if (nexthop->type == NEXTHOP_TYPE_IPV4 || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) { @@ -1534,6 +1537,9 @@ _netlink_route_build_multipath( rtnh->rtnh_hops = 0; rta->rta_len += rtnh->rtnh_len; + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ONLINK)) + rtnh->rtnh_flags |= RTNH_F_ONLINK; + if (nexthop->type == NEXTHOP_TYPE_IPV4 || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) { @@ -1733,7 +1739,8 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib, _netlink_route_debug(cmd, p, nexthop, routedesc, family); _netlink_route_build_singlepath(routedesc, bytelen, - nexthop, &req.n, sizeof req); + nexthop, &req.n, &req.r, + sizeof req); if (cmd == RTM_NEWROUTE) SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index e1da7df8..45928e93 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -615,6 +615,9 @@ vty_show_ip_route_detail (struct vty *vty, struct route_node *rn) if (! CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) vty_out (vty, " inactive"); + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ONLINK)) + vty_out (vty, " onlink"); + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) vty_out (vty, " (recursive)"); @@ -710,6 +713,9 @@ vty_show_ip_route (struct vty *vty, struct route_node *rn, struct rib *rib) if (! CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) vty_out (vty, " inactive"); + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ONLINK)) + vty_out (vty, " onlink"); + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) vty_out (vty, " (recursive)"); @@ -1600,6 +1606,9 @@ vty_show_ipv6_route_detail (struct vty *vty, struct route_node *rn) if (! CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) vty_out (vty, " inactive"); + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ONLINK)) + vty_out (vty, " onlink"); + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) vty_out (vty, " (recursive)"); From c3e6b595160cd3aa601ae7e1887e695710cde15d Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Fri, 5 Jul 2013 15:35:40 +0000 Subject: [PATCH 129/482] zebra: fix recursive-routes via ifindex routes Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- zebra/zebra_rib.c | 65 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 16 deletions(-) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 301e0cc1..3106523d 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -500,16 +500,37 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, resolved_hop = XCALLOC(MTYPE_NEXTHOP, sizeof (struct nexthop)); SET_FLAG (resolved_hop->flags, NEXTHOP_FLAG_ACTIVE); - - resolved_hop->type = newhop->type; - if (newhop->type == NEXTHOP_TYPE_IPV4 || - newhop->type == NEXTHOP_TYPE_IPV4_IFINDEX) - resolved_hop->gate.ipv4 = newhop->gate.ipv4; - + /* If the resolving route specifies a gateway, use it */ + if (newhop->type == NEXTHOP_TYPE_IPV4 + || newhop->type == NEXTHOP_TYPE_IPV4_IFINDEX + || newhop->type == NEXTHOP_TYPE_IPV4_IFNAME) + { + resolved_hop->type = newhop->type; + resolved_hop->gate.ipv4 = newhop->gate.ipv4; + + if (newhop->ifindex) + { + resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX; + resolved_hop->ifindex = newhop->ifindex; + } + } + + /* If the resolving route is an interface route, + * it means the gateway we are looking up is connected + * to that interface. (The actual network is _not_ onlink). + * Therefore, the resolved route should have the original + * gateway as nexthop as it is directly connected. + * + * On Linux, we have to set the onlink netlink flag because + * otherwise, the kernel won't accept the route. */ if (newhop->type == NEXTHOP_TYPE_IFINDEX - || newhop->type == NEXTHOP_TYPE_IFNAME - || newhop->type == NEXTHOP_TYPE_IPV4_IFINDEX) - resolved_hop->ifindex = newhop->ifindex; + || newhop->type == NEXTHOP_TYPE_IFNAME) + { + resolved_hop->flags |= NEXTHOP_FLAG_ONLINK; + resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX; + resolved_hop->gate.ipv4 = nexthop->gate.ipv4; + resolved_hop->ifindex = newhop->ifindex; + } _nexthop_add(&nexthop->resolved, resolved_hop); } @@ -622,18 +643,30 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, resolved_hop = XCALLOC(MTYPE_NEXTHOP, sizeof (struct nexthop)); SET_FLAG (resolved_hop->flags, NEXTHOP_FLAG_ACTIVE); - - resolved_hop->type = newhop->type; + /* See nexthop_active_ipv4 for a description how the + * resolved nexthop is constructed. */ if (newhop->type == NEXTHOP_TYPE_IPV6 || newhop->type == NEXTHOP_TYPE_IPV6_IFINDEX || newhop->type == NEXTHOP_TYPE_IPV6_IFNAME) - resolved_hop->gate.ipv6 = newhop->gate.ipv6; + { + resolved_hop->type = newhop->type; + resolved_hop->gate.ipv6 = newhop->gate.ipv6; + + if (newhop->ifindex) + { + resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX; + resolved_hop->ifindex = newhop->ifindex; + } + } if (newhop->type == NEXTHOP_TYPE_IFINDEX - || newhop->type == NEXTHOP_TYPE_IFNAME - || newhop->type == NEXTHOP_TYPE_IPV6_IFINDEX - || newhop->type == NEXTHOP_TYPE_IPV6_IFNAME) - resolved_hop->ifindex = newhop->ifindex; + || newhop->type == NEXTHOP_TYPE_IFNAME) + { + resolved_hop->flags |= NEXTHOP_FLAG_ONLINK; + resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX; + resolved_hop->gate.ipv6 = nexthop->gate.ipv6; + resolved_hop->ifindex = newhop->ifindex; + } _nexthop_add(&nexthop->resolved, resolved_hop); } From f3a1732eb3bb41c094ec558d2aeee2766878a91d Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Fri, 5 Jul 2013 15:35:41 +0000 Subject: [PATCH 130/482] zebra: apply route-maps for interface routes Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- zebra/zebra_rib.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 3106523d..ace69cac 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -950,6 +950,7 @@ static unsigned nexthop_active_check (struct route_node *rn, struct rib *rib, struct nexthop *nexthop, int set) { + rib_table_info_t *info = rn->table->info; struct interface *ifp; route_map_result_t ret = RMAP_MATCH; extern char *proto_rm[AFI_MAX][ZEBRA_ROUTE_MAX+1]; @@ -1027,11 +1028,22 @@ nexthop_active_check (struct route_node *rn, struct rib *rib, if (! CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) return 0; + /* XXX: What exactly do those checks do? Do we support + * e.g. IPv4 routes with IPv6 nexthops or vice versa? */ if (RIB_SYSTEM_ROUTE(rib) || (family == AFI_IP && rn->p.family != AF_INET) || (family == AFI_IP6 && rn->p.family != AF_INET6)) return CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + /* The original code didn't determine the family correctly + * e.g. for NEXTHOP_TYPE_IFINDEX. Retrieve the correct afi + * from the rib_table_info in those cases. + * Possibly it may be better to use only the rib_table_info + * in every case. + */ + if (!family) + family = info->afi; + rmap = 0; if (rib->type >= 0 && rib->type < ZEBRA_ROUTE_MAX && proto_rm[family][rib->type]) From fed643f4093abc0ed5e796aab9047768f7036ed6 Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Tue, 23 Oct 2012 16:00:42 +0000 Subject: [PATCH 131/482] zebra: make rib_dump() compatible with IPv6 RIB [DL: resolved conflicts in zebra_rib.c] [DL: fix usage with --disable-ipv6] Signed-off-by: David Lamparter --- zebra/rib.h | 2 +- zebra/zebra_rib.c | 31 +++++++++++++++++++++++-------- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/zebra/rib.h b/zebra/rib.h index 1c548795..97a20af3 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -386,7 +386,7 @@ extern struct nexthop *nexthop_ipv4_ifindex_add (struct rib *, extern int nexthop_has_fib_child(struct nexthop *); extern void rib_lookup_and_dump (struct prefix_ipv4 *); extern void rib_lookup_and_pushup (struct prefix_ipv4 *); -extern void rib_dump (const char *, const struct prefix_ipv4 *, const struct rib *); +extern void rib_dump (const char *, const struct prefix *, const struct rib *); extern int rib_lookup_ipv4_route (struct prefix_ipv4 *, union sockunion *); #define ZEBRA_RIB_LOOKUP_ERROR -1 #define ZEBRA_RIB_FOUND_EXACT 0 diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index ace69cac..77c0d8ca 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -1912,13 +1912,13 @@ rib_add_ipv4 (int type, int flags, struct prefix_ipv4 *p, * question are passed as 1st and 2nd arguments. */ -void rib_dump (const char * func, const struct prefix_ipv4 * p, const struct rib * rib) +void rib_dump (const char * func, const struct prefix * p, const struct rib * rib) { - char straddr[INET_ADDRSTRLEN]; + char straddr[INET6_ADDRSTRLEN]; struct nexthop *nexthop, *tnexthop; int recursing; - inet_ntop (AF_INET, &p->prefix, straddr, INET_ADDRSTRLEN); + inet_ntop (p->family, &p->u.prefix, straddr, INET6_ADDRSTRLEN); zlog_debug ("%s: dumping RIB entry %p for %s/%d", func, rib, straddr, p->prefixlen); zlog_debug ( @@ -1946,9 +1946,10 @@ void rib_dump (const char * func, const struct prefix_ipv4 * p, const struct rib rib->nexthop_active_num, rib->nexthop_fib_num ); + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) { - inet_ntop (AF_INET, &nexthop->gate.ipv4.s_addr, straddr, INET_ADDRSTRLEN); + inet_ntop (p->family, &nexthop->gate, straddr, INET6_ADDRSTRLEN); zlog_debug ( "%s: %s %s with flags %s%s%s", @@ -2008,7 +2009,7 @@ void rib_lookup_and_dump (struct prefix_ipv4 * p) (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED) ? "removed" : "NOT removed"), (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED) ? "selected" : "NOT selected") ); - rib_dump (__func__, p, rib); + rib_dump (__func__, (struct prefix *) p, rib); } } @@ -2055,7 +2056,7 @@ void rib_lookup_and_pushup (struct prefix_ipv4 * p) char buf[INET_ADDRSTRLEN]; inet_ntop (rn->p.family, &p->prefix, buf, INET_ADDRSTRLEN); zlog_debug ("%s: freeing way for connected prefix %s/%d", __func__, buf, p->prefixlen); - rib_dump (__func__, (struct prefix_ipv4 *)&rn->p, rib); + rib_dump (__func__, &rn->p, rib); } rib_uninstall (rn, rib); } @@ -2117,7 +2118,7 @@ rib_add_ipv4_multipath (struct prefix_ipv4 *p, struct rib *rib, safi_t safi) { zlog_debug ("%s: called rib_addnode (%p, %p) on new RIB entry", __func__, rn, rib); - rib_dump (__func__, p, rib); + rib_dump (__func__, (struct prefix *) p, rib); } /* Free implicit route.*/ @@ -2127,7 +2128,7 @@ rib_add_ipv4_multipath (struct prefix_ipv4 *p, struct rib *rib, safi_t safi) { zlog_debug ("%s: calling rib_delnode (%p, %p) on existing RIB entry", __func__, rn, same); - rib_dump (__func__, p, same); + rib_dump (__func__, (struct prefix *) p, same); } rib_delnode (rn, same); } @@ -2693,10 +2694,24 @@ rib_add_ipv6 (int type, int flags, struct prefix_ipv6 *p, /* Link new rib to node.*/ rib_addnode (rn, rib); + if (IS_ZEBRA_DEBUG_RIB) + { + zlog_debug ("%s: called rib_addnode (%p, %p) on new RIB entry", + __func__, rn, rib); + rib_dump (__func__, (struct prefix *) p, rib); + } /* Free implicit route.*/ if (same) + { + if (IS_ZEBRA_DEBUG_RIB) + { + zlog_debug ("%s: calling rib_delnode (%p, %p) on existing RIB entry", + __func__, rn, same); + rib_dump (__func__, (struct prefix *) p, same); + } rib_delnode (rn, same); + } route_unlock_node (rn); return 0; From a83a1e9c2f035d3152451dcfc97ab13b4ac427b9 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 22 Oct 2013 11:35:48 -0700 Subject: [PATCH 132/482] build: fix tests/prng.h missing from dist broken by fa713d9... "zebra: rework recursive route resolution", and I forgot to squash the fix into that. Signed-off-by: David Lamparter --- tests/Makefile.am | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/Makefile.am b/tests/Makefile.am index e5c7fd79..9260a900 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -28,6 +28,8 @@ check_PROGRAMS = testsig testbuffer testmemory heavy heavywq heavythread \ testprivs teststream testchecksum tabletest testnexthopiter \ $(TESTS_BGPD) +noinst_HEADERS = prng.h + testsig_SOURCES = test-sig.c testbuffer_SOURCES = test-buffer.c testmemory_SOURCES = test-memory.c From 8551e6dadce41fb87a61767af723cb25ae611a04 Mon Sep 17 00:00:00 2001 From: Dinesh Dutt Date: Tue, 22 Oct 2013 17:42:18 -0700 Subject: [PATCH 133/482] ospf6d/ospfd: refactor some common defines Rearranging common defs and structures for use betweeen OSPFv2 and OSPFv3. Created a new file called libospf.h under lib directory to hold defines that are common between OSPFv2 and OSPFv3 code bases. [DL: split of defines refactor from timer refactor] Signed-off-by: David Lamparter --- lib/Makefile.am | 2 +- lib/libospf.h | 82 ++++++++++++++++++++++++++++++++++++++++ ospf6d/ospf6_abr.c | 6 +-- ospf6d/ospf6_area.c | 1 + ospf6d/ospf6_area.h | 2 - ospf6d/ospf6_asbr.c | 4 +- ospf6d/ospf6_flood.c | 8 ++-- ospf6d/ospf6_interface.c | 6 +-- ospf6d/ospf6_lsa.c | 32 ++++++++-------- ospf6d/ospf6_lsa.h | 2 +- ospf6d/ospf6_lsdb.c | 2 +- ospf6d/ospf6_message.c | 2 +- ospf6d/ospf6_network.c | 1 + ospf6d/ospf6_proto.h | 21 ---------- ospf6d/ospf6d.h | 3 ++ ospfd/ospfd.h | 53 +------------------------- 16 files changed, 120 insertions(+), 107 deletions(-) create mode 100644 lib/libospf.h diff --git a/lib/Makefile.am b/lib/Makefile.am index 44d95bbe..bd210929 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -27,7 +27,7 @@ pkginclude_HEADERS = \ str.h stream.h table.h thread.h vector.h version.h vty.h zebra.h \ plist.h zclient.h sockopt.h smux.h md5.h if_rmap.h keychain.h \ privs.h sigevent.h pqueue.h jhash.h zassert.h memtypes.h \ - workqueue.h route_types.h + workqueue.h route_types.h libospf.h EXTRA_DIST = \ regex.c regex-gnu.h \ diff --git a/lib/libospf.h b/lib/libospf.h new file mode 100644 index 00000000..2282c072 --- /dev/null +++ b/lib/libospf.h @@ -0,0 +1,82 @@ +/* + * Defines and structures common to OSPFv2 and OSPFv3 + * Copyright (C) 1998, 99, 2000 Kunihiro Ishiguro, Toshiaki Takada + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _LIBOSPFD_H +#define _LIBOSPFD_H + +/* IP precedence. */ +#ifndef IPTOS_PREC_INTERNETCONTROL +#define IPTOS_PREC_INTERNETCONTROL 0xC0 +#endif /* IPTOS_PREC_INTERNETCONTROL */ + +/* Default protocol, port number. */ +#ifndef IPPROTO_OSPFIGP +#define IPPROTO_OSPFIGP 89 +#endif /* IPPROTO_OSPFIGP */ + +/* Architectual Constants */ +#ifdef DEBUG +#define OSPF_LS_REFRESH_TIME 60 +#else +#define OSPF_LS_REFRESH_TIME 1800 +#endif +#define OSPF_MIN_LS_INTERVAL 5 +#define OSPF_MIN_LS_ARRIVAL 1 +#define OSPF_LSA_INITIAL_AGE 0 /* useful for debug */ +#define OSPF_LSA_MAXAGE 3600 +#define OSPF_CHECK_AGE 300 +#define OSPF_LSA_MAXAGE_DIFF 900 +#define OSPF_LS_INFINITY 0xffffff +#define OSPF_DEFAULT_DESTINATION 0x00000000 /* 0.0.0.0 */ +#define OSPF_INITIAL_SEQUENCE_NUMBER 0x80000001 +#define OSPF_MAX_SEQUENCE_NUMBER 0x7fffffff + +/* OSPF interface default values. */ +#define OSPF_OUTPUT_COST_DEFAULT 10 +#define OSPF_OUTPUT_COST_INFINITE UINT16_MAX +#define OSPF_ROUTER_DEAD_INTERVAL_DEFAULT 40 +#define OSPF_ROUTER_DEAD_INTERVAL_MINIMAL 1 +#define OSPF_HELLO_INTERVAL_DEFAULT 10 +#define OSPF_ROUTER_PRIORITY_DEFAULT 1 +#define OSPF_RETRANSMIT_INTERVAL_DEFAULT 5 +#define OSPF_TRANSMIT_DELAY_DEFAULT 1 +#define OSPF_DEFAULT_BANDWIDTH 10000 /* Kbps */ + +#define OSPF_DEFAULT_REF_BANDWIDTH 100000 /* Kbps */ + +#define OSPF_POLL_INTERVAL_DEFAULT 60 +#define OSPF_NEIGHBOR_PRIORITY_DEFAULT 0 + +#define OSPF_MTU_IGNORE_DEFAULT 0 +#define OSPF_FAST_HELLO_DEFAULT 0 + +#define OSPF_AREA_BACKBONE 0x00000000 /* 0.0.0.0 */ + +/* SPF Throttling timer values. */ +#define OSPF_SPF_DELAY_DEFAULT 200 +#define OSPF_SPF_HOLDTIME_DEFAULT 1000 +#define OSPF_SPF_MAX_HOLDTIME_DEFAULT 10000 + +#define OSPF_LSA_MAXAGE_CHECK_INTERVAL 30 +#define OSFP_LSA_MAXAGE_REMOVE_DELAY_DEFAULT 60 + +#endif /* _LIBOSPFD_H */ diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c index c3a63fe6..3277e7d8 100644 --- a/ospf6d/ospf6_abr.c +++ b/ospf6d/ospf6_abr.c @@ -253,7 +253,7 @@ ospf6_abr_originate_summary_to_area (struct ospf6_route *route, } /* do not generate if the route cost is greater or equal to LSInfinity */ - if (route->path.cost >= LS_INFINITY) + if (route->path.cost >= OSPF_LS_INFINITY) { if (is_debug) zlog_debug ("The cost exceeds LSInfinity, withdraw"); @@ -296,7 +296,7 @@ ospf6_abr_originate_summary_to_area (struct ospf6_route *route, /* ranges are ignored when originate backbone routes to transit area. Otherwise, if ranges are configured, the route is suppressed. */ if (range && ! CHECK_FLAG (range->flag, OSPF6_ROUTE_REMOVE) && - (route->path.area_id != BACKBONE_AREA_ID || + (route->path.area_id != OSPF_AREA_BACKBONE || ! IS_AREA_TRANSIT (area))) { if (is_debug) @@ -604,7 +604,7 @@ ospf6_abr_examin_summary (struct ospf6_lsa *lsa, struct ospf6_area *oa) } /* (1) if cost == LSInfinity or if the LSA is MaxAge */ - if (cost == LS_INFINITY) + if (cost == OSPF_LS_INFINITY) { if (is_debug) zlog_debug ("cost is LS_INFINITY, ignore"); diff --git a/ospf6d/ospf6_area.c b/ospf6d/ospf6_area.c index 9934e6b9..2d20e625 100644 --- a/ospf6d/ospf6_area.c +++ b/ospf6d/ospf6_area.c @@ -401,6 +401,7 @@ DEFUN (no_area_range, } ospf6_route_remove (range, oa->range_table); + return CMD_SUCCESS; } diff --git a/ospf6d/ospf6_area.h b/ospf6d/ospf6_area.h index e8a4fbd8..655967a9 100644 --- a/ospf6d/ospf6_area.h +++ b/ospf6d/ospf6_area.h @@ -105,8 +105,6 @@ struct ospf6_area #define OSPF6_AREA_TRANSIT 0x04 /* TransitCapability */ #define OSPF6_AREA_STUB 0x08 -#define BACKBONE_AREA_ID (htonl (0)) -#define IS_AREA_BACKBONE(oa) ((oa)->area_id == BACKBONE_AREA_ID) #define IS_AREA_ENABLED(oa) (CHECK_FLAG ((oa)->flag, OSPF6_AREA_ENABLE)) #define IS_AREA_ACTIVE(oa) (CHECK_FLAG ((oa)->flag, OSPF6_AREA_ACTIVE)) #define IS_AREA_TRANSIT(oa) (CHECK_FLAG ((oa)->flag, OSPF6_AREA_TRANSIT)) diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index ae0a286d..1a0634ed 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -174,7 +174,7 @@ ospf6_asbr_lsa_add (struct ospf6_lsa *lsa) return; } - if (OSPF6_ASBR_METRIC (external) == LS_INFINITY) + if (OSPF6_ASBR_METRIC (external) == OSPF_LS_INFINITY) { if (IS_OSPF6_DEBUG_EXAMIN (AS_EXTERNAL)) zlog_debug ("Ignore LSA with LSInfinity Metric"); @@ -890,7 +890,7 @@ ospf6_routemap_rule_set_metric_compile (const char *arg) u_int32_t metric; char *endp; metric = strtoul (arg, &endp, 0); - if (metric > LS_INFINITY || *endp != '\0') + if (metric > OSPF_LS_INFINITY || *endp != '\0') return NULL; return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); } diff --git a/ospf6d/ospf6_flood.c b/ospf6d/ospf6_flood.c index b8159729..3a9af01d 100644 --- a/ospf6d/ospf6_flood.c +++ b/ospf6d/ospf6_flood.c @@ -113,7 +113,7 @@ ospf6_lsa_originate (struct ospf6_lsa *lsa) ospf6_lsdb_add (ospf6_lsa_copy (lsa), lsdb_self); lsa->refresh = thread_add_timer (master, ospf6_lsa_refresh, lsa, - LS_REFRESH_TIME); + OSPF_LS_REFRESH_TIME); if (IS_OSPF6_DEBUG_LSA_TYPE (lsa->header->type) || IS_OSPF6_DEBUG_ORIGINATE_TYPE (lsa->header->type)) @@ -228,7 +228,7 @@ ospf6_install_lsa (struct ospf6_lsa *lsa) quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); if (! OSPF6_LSA_IS_MAXAGE (lsa)) lsa->expire = thread_add_timer (master, ospf6_lsa_expire, lsa, - MAXAGE + lsa->birth.tv_sec - now.tv_sec); + OSPF_LSA_MAXAGE + lsa->birth.tv_sec - now.tv_sec); else lsa->expire = NULL; @@ -837,7 +837,7 @@ ospf6_receive_lsa (struct ospf6_neighbor *from, struct timeval now, res; quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); timersub (&now, &old->installed, &res); - if (res.tv_sec < MIN_LS_ARRIVAL) + if (res.tv_sec < OSPF_MIN_LS_ARRIVAL) { if (is_debug) zlog_debug ("LSA can't be updated within MinLSArrival, discard"); @@ -944,7 +944,7 @@ ospf6_receive_lsa (struct ospf6_neighbor *from, /* If database copy is in 'Seqnumber Wrapping', simply discard the received LSA */ if (OSPF6_LSA_IS_MAXAGE (old) && - old->header->seqnum == htonl (MAX_SEQUENCE_NUMBER)) + old->header->seqnum == htonl (OSPF_MAX_SEQUENCE_NUMBER)) { if (is_debug) { diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index 40cda246..467479b1 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -112,9 +112,9 @@ ospf6_interface_create (struct interface *ifp) oi->transdelay = OSPF6_INTERFACE_TRANSDELAY; oi->priority = OSPF6_INTERFACE_PRIORITY; - oi->hello_interval = OSPF6_INTERFACE_HELLO_INTERVAL; - oi->dead_interval = OSPF6_INTERFACE_DEAD_INTERVAL; - oi->rxmt_interval = OSPF6_INTERFACE_RXMT_INTERVAL; + oi->hello_interval = OSPF_HELLO_INTERVAL_DEFAULT; + oi->dead_interval = OSPF_ROUTER_DEAD_INTERVAL_DEFAULT; + oi->rxmt_interval = OSPF_RETRANSMIT_INTERVAL_DEFAULT; oi->cost = OSPF6_INTERFACE_COST; oi->state = OSPF6_INTERFACE_DOWN; oi->flag = 0; diff --git a/ospf6d/ospf6_lsa.c b/ospf6d/ospf6_lsa.c index ff061dfb..7dbd303f 100644 --- a/ospf6d/ospf6_lsa.c +++ b/ospf6d/ospf6_lsa.c @@ -139,11 +139,11 @@ ospf6_lsa_is_differ (struct ospf6_lsa *lsa1, ospf6_lsa_age_current (lsa1); ospf6_lsa_age_current (lsa2); - if (ntohs (lsa1->header->age) == MAXAGE && - ntohs (lsa2->header->age) != MAXAGE) + if (ntohs (lsa1->header->age) == OSPF_LSA_MAXAGE && + ntohs (lsa2->header->age) != OSPF_LSA_MAXAGE) return 1; - if (ntohs (lsa1->header->age) != MAXAGE && - ntohs (lsa2->header->age) == MAXAGE) + if (ntohs (lsa1->header->age) != OSPF_LSA_MAXAGE && + ntohs (lsa2->header->age) == OSPF_LSA_MAXAGE) return 1; /* compare body */ @@ -218,19 +218,19 @@ ospf6_lsa_age_current (struct ospf6_lsa *lsa) zlog_warn ("LSA: quagga_gettime failed, may fail LSA AGEs: %s", safe_strerror (errno)); - if (ntohs (lsa->header->age) >= MAXAGE) + if (ntohs (lsa->header->age) >= OSPF_LSA_MAXAGE) { /* ospf6_lsa_premature_aging () sets age to MAXAGE; when using relative time, we cannot compare against lsa birth time, so we catch this special case here. */ - lsa->header->age = htons (MAXAGE); - return MAXAGE; + lsa->header->age = htons (OSPF_LSA_MAXAGE); + return OSPF_LSA_MAXAGE; } /* calculate age */ ulage = now.tv_sec - lsa->birth.tv_sec; /* if over MAXAGE, set to it */ - age = (ulage > MAXAGE ? MAXAGE : ulage); + age = (ulage > OSPF_LSA_MAXAGE ? OSPF_LSA_MAXAGE : ulage); lsa->header->age = htons (age); return age; @@ -243,8 +243,8 @@ ospf6_lsa_age_update_to_send (struct ospf6_lsa *lsa, u_int32_t transdelay) unsigned short age; age = ospf6_lsa_age_current (lsa) + transdelay; - if (age > MAXAGE) - age = MAXAGE; + if (age > OSPF_LSA_MAXAGE) + age = OSPF_LSA_MAXAGE; lsa->header->age = htons (age); } @@ -258,7 +258,7 @@ ospf6_lsa_premature_aging (struct ospf6_lsa *lsa) THREAD_OFF (lsa->expire); THREAD_OFF (lsa->refresh); - lsa->header->age = htons (MAXAGE); + lsa->header->age = htons (OSPF_LSA_MAXAGE); thread_execute (master, ospf6_lsa_expire, lsa, 0); } @@ -297,15 +297,15 @@ ospf6_lsa_compare (struct ospf6_lsa *a, struct ospf6_lsa *b) ageb = ospf6_lsa_age_current (b); /* MaxAge check */ - if (agea == MAXAGE && ageb != MAXAGE) + if (agea == OSPF_LSA_MAXAGE && ageb != OSPF_LSA_MAXAGE) return -1; - else if (agea != MAXAGE && ageb == MAXAGE) + else if (agea != OSPF_LSA_MAXAGE && ageb == OSPF_LSA_MAXAGE) return 1; /* Age check */ - if (agea > ageb && agea - ageb >= MAX_AGE_DIFF) + if (agea > ageb && agea - ageb >= OSPF_LSA_MAXAGE_DIFF) return 1; - else if (agea < ageb && ageb - agea >= MAX_AGE_DIFF) + else if (agea < ageb && ageb - agea >= OSPF_LSA_MAXAGE_DIFF) return -1; /* neither recent */ @@ -653,7 +653,7 @@ ospf6_lsa_refresh (struct thread *thread) new = ospf6_lsa_create (self->header); new->lsdb = old->lsdb; new->refresh = thread_add_timer (master, ospf6_lsa_refresh, new, - LS_REFRESH_TIME); + OSPF_LS_REFRESH_TIME); /* store it in the LSDB for self-originated LSAs */ ospf6_lsdb_add (ospf6_lsa_copy (new), lsdb_self); diff --git a/ospf6d/ospf6_lsa.h b/ospf6d/ospf6_lsa.h index 263411fc..f10ee671 100644 --- a/ospf6d/ospf6_lsa.h +++ b/ospf6d/ospf6_lsa.h @@ -107,7 +107,7 @@ struct ospf6_lsa_header ((L)->header->adv_router == (a) && (L)->header->id == (i) && \ (L)->header->type == (t)) #define OSPF6_LSA_IS_DIFFER(L1, L2) ospf6_lsa_is_differ (L1, L2) -#define OSPF6_LSA_IS_MAXAGE(L) (ospf6_lsa_age_current (L) == MAXAGE) +#define OSPF6_LSA_IS_MAXAGE(L) (ospf6_lsa_age_current (L) == OSPF_LSA_MAXAGE) #define OSPF6_LSA_IS_CHANGED(L1, L2) ospf6_lsa_is_changed (L1, L2) struct ospf6_lsa diff --git a/ospf6d/ospf6_lsdb.c b/ospf6d/ospf6_lsdb.c index 280bdf95..7455d835 100644 --- a/ospf6d/ospf6_lsdb.c +++ b/ospf6d/ospf6_lsdb.c @@ -572,7 +572,7 @@ ospf6_new_ls_seqnum (u_int16_t type, u_int32_t id, u_int32_t adv_router, /* if current database copy not found, return InitialSequenceNumber */ lsa = ospf6_lsdb_lookup (type, id, adv_router, lsdb); if (lsa == NULL) - seqnum = INITIAL_SEQUENCE_NUMBER; + seqnum = OSPF_INITIAL_SEQUENCE_NUMBER; else seqnum = (signed long) ntohl (lsa->header->seqnum) + 1; diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c index 5edb70ce..b35aa1ac 100644 --- a/ospf6d/ospf6_message.c +++ b/ospf6d/ospf6_message.c @@ -1282,7 +1282,7 @@ ospf6_rxpacket_examin (struct ospf6_interface *oi, struct ospf6_header *oh, cons { if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) { - if (oh->area_id == BACKBONE_AREA_ID) + if (oh->area_id == OSPF_AREA_BACKBONE) zlog_debug ("%s: Message may be via Virtual Link: not supported", __func__); else zlog_debug diff --git a/ospf6d/ospf6_network.c b/ospf6d/ospf6_network.c index e5a1436c..eed7f9d6 100644 --- a/ospf6d/ospf6_network.c +++ b/ospf6d/ospf6_network.c @@ -27,6 +27,7 @@ #include "sockopt.h" #include "privs.h" +#include "libospf.h" #include "ospf6_proto.h" #include "ospf6_network.h" diff --git a/ospf6d/ospf6_proto.h b/ospf6d/ospf6_proto.h index 64625004..af60eb92 100644 --- a/ospf6d/ospf6_proto.h +++ b/ospf6d/ospf6_proto.h @@ -26,33 +26,12 @@ /* OSPF protocol version */ #define OSPFV3_VERSION 3 -/* OSPF protocol number. */ -#ifndef IPPROTO_OSPFIGP -#define IPPROTO_OSPFIGP 89 -#endif - /* TOS field normaly null */ #define DEFAULT_TOS_VALUE 0x0 -/* Architectural Constants */ -#define LS_REFRESH_TIME 1800 /* 30 min */ -#define MIN_LS_INTERVAL 5 -#define MIN_LS_ARRIVAL 1 -#define MAXAGE 3600 /* 1 hour */ -#define CHECK_AGE 300 /* 5 min */ -#define MAX_AGE_DIFF 900 /* 15 min */ -#define LS_INFINITY 0xffffff /* 24-bit binary value */ -#define INITIAL_SEQUENCE_NUMBER 0x80000001 /* signed 32-bit integer */ -#define MAX_SEQUENCE_NUMBER 0x7fffffff /* signed 32-bit integer */ - #define ALLSPFROUTERS6 "ff02::5" #define ALLDROUTERS6 "ff02::6" -/* Configurable Constants */ - -#define DEFAULT_HELLO_INTERVAL 10 -#define DEFAULT_ROUTER_DEAD_INTERVAL 40 - #define OSPF6_ROUTER_BIT_W (1 << 3) #define OSPF6_ROUTER_BIT_V (1 << 2) #define OSPF6_ROUTER_BIT_E (1 << 1) diff --git a/ospf6d/ospf6d.h b/ospf6d/ospf6d.h index 2ac6300e..13699d61 100644 --- a/ospf6d/ospf6d.h +++ b/ospf6d/ospf6d.h @@ -24,6 +24,9 @@ #define OSPF6_DAEMON_VERSION "0.9.7r" +#include "libospf.h" +#include "thread.h" + /* global variables */ extern struct thread_master *master; diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h index fb57bf51..fe9d77e9 100644 --- a/ospfd/ospfd.h +++ b/ospfd/ospfd.h @@ -24,22 +24,13 @@ #define _ZEBRA_OSPFD_H #include +#include "libospf.h" #include "filter.h" #include "log.h" #define OSPF_VERSION 2 -/* Default protocol, port number. */ -#ifndef IPPROTO_OSPFIGP -#define IPPROTO_OSPFIGP 89 -#endif /* IPPROTO_OSPFIGP */ - -/* IP precedence. */ -#ifndef IPTOS_PREC_INTERNETCONTROL -#define IPTOS_PREC_INTERNETCONTROL 0xC0 -#endif /* IPTOS_PREC_INTERNETCONTROL */ - /* VTY port number. */ #define OSPF_VTY_PORT 2604 @@ -50,29 +41,11 @@ /* Default configuration file name for ospfd. */ #define OSPF_DEFAULT_CONFIG "ospfd.conf" -/* Architectual Constants */ -#ifdef DEBUG -#define OSPF_LS_REFRESH_TIME 60 -#else -#define OSPF_LS_REFRESH_TIME 1800 -#endif -#define OSPF_MIN_LS_INTERVAL 5 -#define OSPF_MIN_LS_ARRIVAL 1 -#define OSPF_LSA_INITIAL_AGE 0 /* useful for debug */ -#define OSPF_LSA_MAXAGE 3600 -#define OSPF_CHECK_AGE 300 -#define OSPF_LSA_MAXAGE_DIFF 900 -#define OSPF_LS_INFINITY 0xffffff -#define OSPF_DEFAULT_DESTINATION 0x00000000 /* 0.0.0.0 */ -#define OSPF_INITIAL_SEQUENCE_NUMBER 0x80000001 -#define OSPF_MAX_SEQUENCE_NUMBER 0x7fffffff - #define OSPF_NSSA_TRANS_STABLE_DEFAULT 40 #define OSPF_ALLSPFROUTERS 0xe0000005 /* 224.0.0.5 */ #define OSPF_ALLDROUTERS 0xe0000006 /* 224.0.0.6 */ -#define OSPF_AREA_BACKBONE 0x00000000 /* 0.0.0.0 */ /* OSPF Authentication Type. */ #define OSPF_AUTH_NULL 0 @@ -85,30 +58,6 @@ been given or not in VLink command handlers */ #define OSPF_AUTH_CMD_NOTSEEN -2 -/* OSPF SPF timer values. */ -#define OSPF_SPF_DELAY_DEFAULT 200 -#define OSPF_SPF_HOLDTIME_DEFAULT 1000 -#define OSPF_SPF_MAX_HOLDTIME_DEFAULT 10000 - -/* OSPF interface default values. */ -#define OSPF_OUTPUT_COST_DEFAULT 10 -#define OSPF_OUTPUT_COST_INFINITE UINT16_MAX -#define OSPF_ROUTER_DEAD_INTERVAL_DEFAULT 40 -#define OSPF_ROUTER_DEAD_INTERVAL_MINIMAL 1 -#define OSPF_HELLO_INTERVAL_DEFAULT 10 -#define OSPF_ROUTER_PRIORITY_DEFAULT 1 -#define OSPF_RETRANSMIT_INTERVAL_DEFAULT 5 -#define OSPF_TRANSMIT_DELAY_DEFAULT 1 -#define OSPF_DEFAULT_BANDWIDTH 10000 /* Kbps */ - -#define OSPF_DEFAULT_REF_BANDWIDTH 100000 /* Kbps */ - -#define OSPF_POLL_INTERVAL_DEFAULT 60 -#define OSPF_NEIGHBOR_PRIORITY_DEFAULT 0 - -#define OSPF_MTU_IGNORE_DEFAULT 0 -#define OSPF_FAST_HELLO_DEFAULT 0 - /* OSPF options. */ #define OSPF_OPTION_T 0x01 /* TOS. */ #define OSPF_OPTION_E 0x02 From 3810e06eebe14f75c66fb17a88574384573e95fa Mon Sep 17 00:00:00 2001 From: Dinesh Dutt Date: Sat, 24 Aug 2013 07:54:09 +0000 Subject: [PATCH 134/482] ospf6d: schedule SPF to run on events rather than directly on each event. OSPV3 SPF triggers on every SPF-able event instead of using timers the way OSPFv2 does. This patch makes SPF be triggered/throttled similar to OSPFv2. It adds a command to quagga identical to the OSPFv2 equivalent to configure these timers. Summary: Signed-off-by: Dinesh Dutt Reviewed-by: Scott Feldman [DL: removed reference to oa->ts_spf for rebase] [DL: killed timeval_subtract] Signed-off-by: David Lamparter --- doc/ospf6d.texi | 38 ++++++++ ospf6d/ospf6_area.c | 4 +- ospf6d/ospf6_interface.c | 12 ++- ospf6d/ospf6_spf.c | 189 +++++++++++++++++++++++++++++++++++---- ospf6d/ospf6_spf.h | 5 +- ospf6d/ospf6_top.c | 7 ++ ospf6d/ospf6_top.h | 13 +++ 7 files changed, 245 insertions(+), 23 deletions(-) diff --git a/doc/ospf6d.texi b/doc/ospf6d.texi index 6667221c..c01c0510 100644 --- a/doc/ospf6d.texi +++ b/doc/ospf6d.texi @@ -28,6 +28,44 @@ Bind interface to specified area, and start sending OSPF packets. @var{area} ca be specified as 0. @end deffn +@deffn {OSPF6 Command} {timers throttle spf @var{delay} @var{initial-holdtime} @var{max-holdtime}} {} +@deffnx {OSPF6 Command} {no timers throttle spf} {} +This command sets the initial @var{delay}, the @var{initial-holdtime} +and the @var{maximum-holdtime} between when SPF is calculated and the +event which triggered the calculation. The times are specified in +milliseconds and must be in the range of 0 to 600000 milliseconds. + +The @var{delay} specifies the minimum amount of time to delay SPF +calculation (hence it affects how long SPF calculation is delayed after +an event which occurs outside of the holdtime of any previous SPF +calculation, and also serves as a minimum holdtime). + +Consecutive SPF calculations will always be seperated by at least +'hold-time' milliseconds. The hold-time is adaptive and initially is +set to the @var{initial-holdtime} configured with the above command. +Events which occur within the holdtime of the previous SPF calculation +will cause the holdtime to be increased by @var{initial-holdtime}, bounded +by the @var{maximum-holdtime} configured with this command. If the adaptive +hold-time elapses without any SPF-triggering event occuring then +the current holdtime is reset to the @var{initial-holdtime}. + +@example +@group +router ospf6 + timers throttle spf 200 400 10000 +@end group +@end example + +In this example, the @var{delay} is set to 200ms, the @var{initial +holdtime} is set to 400ms and the @var{maximum holdtime} to 10s. Hence +there will always be at least 200ms between an event which requires SPF +calculation and the actual SPF calculation. Further consecutive SPF +calculations will always be seperated by between 400ms to 10s, the +hold-time increasing by 400ms each time an SPF-triggering event occurs +within the hold-time of the previous SPF calculation. + +@end deffn + @node OSPF6 area @section OSPF6 area diff --git a/ospf6d/ospf6_area.c b/ospf6d/ospf6_area.c index 2d20e625..1e07d857 100644 --- a/ospf6d/ospf6_area.c +++ b/ospf6d/ospf6_area.c @@ -67,7 +67,7 @@ ospf6_area_lsdb_hook_add (struct ospf6_lsa *lsa) zlog_debug ("Schedule SPF Calculation for %s", OSPF6_AREA (lsa->lsdb->data)->name); } - ospf6_spf_schedule (OSPF6_AREA (lsa->lsdb->data)); + ospf6_spf_schedule (OSPF6_PROCESS(OSPF6_AREA (lsa->lsdb->data)->ospf6)); break; case OSPF6_LSTYPE_INTRA_PREFIX: @@ -97,7 +97,7 @@ ospf6_area_lsdb_hook_remove (struct ospf6_lsa *lsa) zlog_debug ("Schedule SPF Calculation for %s", OSPF6_AREA (lsa->lsdb->data)->name); } - ospf6_spf_schedule (OSPF6_AREA (lsa->lsdb->data)); + ospf6_spf_schedule (OSPF6_PROCESS(OSPF6_AREA (lsa->lsdb->data)->ospf6)); break; case OSPF6_LSTYPE_INTRA_PREFIX: diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index 467479b1..7c45fe46 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -75,12 +75,18 @@ ospf6_interface_lookup_by_ifindex (int ifindex) static void ospf6_interface_lsdb_hook (struct ospf6_lsa *lsa) { + struct ospf6_interface *oi; + + if (lsa == NULL) + return; + + oi = lsa->lsdb->data; switch (ntohs (lsa->header->type)) { case OSPF6_LSTYPE_LINK: - if (OSPF6_INTERFACE (lsa->lsdb->data)->state == OSPF6_INTERFACE_DR) - OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT (OSPF6_INTERFACE (lsa->lsdb->data)); - ospf6_spf_schedule (OSPF6_INTERFACE (lsa->lsdb->data)->area); + if (oi->state == OSPF6_INTERFACE_DR) + OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT (oi); + ospf6_spf_schedule (oi->area->ospf6); break; default: diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c index da0ee131..e4c424db 100644 --- a/ospf6d/ospf6_spf.c +++ b/ospf6d/ospf6_spf.c @@ -506,39 +506,128 @@ static int ospf6_spf_calculation_thread (struct thread *t) { struct ospf6_area *oa; + struct ospf6 *ospf6; struct timeval start, end, runtime; + struct listnode *node; + struct ospf6_route *route; - oa = (struct ospf6_area *) THREAD_ARG (t); - oa->thread_spf_calculation = NULL; - - if (IS_OSPF6_DEBUG_SPF (PROCESS)) - zlog_debug ("SPF calculation for Area %s", oa->name); - if (IS_OSPF6_DEBUG_SPF (DATABASE)) - ospf6_spf_log_database (oa); + ospf6 = (struct ospf6 *)THREAD_ARG (t); + ospf6->t_spf_calc = NULL; /* execute SPF calculation */ quagga_gettime (QUAGGA_CLK_MONOTONIC, &start); - ospf6_spf_calculation (oa->ospf6->router_id, oa->spf_table, oa); + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa)) + { + + if (oa == ospf6->backbone) + continue; + + if (IS_OSPF6_DEBUG_SPF (PROCESS)) + zlog_debug ("SPF calculation for Area %s", oa->name); + if (IS_OSPF6_DEBUG_SPF (DATABASE)) + ospf6_spf_log_database (oa); + + ospf6_spf_calculation (ospf6->router_id, oa->spf_table, oa); + ospf6_intra_route_calculation (oa); + ospf6_intra_brouter_calculation (oa); + } + + if (ospf6->backbone) + { + if (IS_OSPF6_DEBUG_SPF (PROCESS)) + zlog_debug ("SPF calculation for Backbone area %s", + ospf6->backbone->name); + if (IS_OSPF6_DEBUG_SPF (DATABASE)) + ospf6_spf_log_database(ospf6->backbone); + + ospf6_spf_calculation(ospf6->router_id, ospf6->backbone->spf_table, + ospf6->backbone); + ospf6_intra_route_calculation(ospf6->backbone); + ospf6_intra_brouter_calculation(ospf6->backbone); + } + + /* Redo summaries if required */ + for (route = ospf6_route_head (ospf6->route_table); route; + route = ospf6_route_next (route)) + ospf6_abr_originate_summary(route); + quagga_gettime (QUAGGA_CLK_MONOTONIC, &end); timersub (&end, &start, &runtime); + ospf6->ts_spf_duration = runtime; + if (IS_OSPF6_DEBUG_SPF (PROCESS) || IS_OSPF6_DEBUG_SPF (TIME)) zlog_debug ("SPF runtime: %ld sec %ld usec", runtime.tv_sec, runtime.tv_usec); - ospf6_intra_route_calculation (oa); - ospf6_intra_brouter_calculation (oa); - return 0; } +/* Add schedule for SPF calculation. To avoid frequenst SPF calc, we + set timer for SPF calc. */ void -ospf6_spf_schedule (struct ospf6_area *oa) +ospf6_spf_schedule (struct ospf6 *ospf6) { - if (oa->thread_spf_calculation) + unsigned long delay, elapsed, ht; + struct timeval now, result; + + if (IS_OSPF6_DEBUG_SPF(PROCESS) || IS_OSPF6_DEBUG_SPF (TIME)) + zlog_debug ("SPF: calculation timer scheduled"); + + /* OSPF instance does not exist. */ + if (ospf6 == NULL) return; - oa->thread_spf_calculation = - thread_add_event (master, ospf6_spf_calculation_thread, oa, 0); + + /* SPF calculation timer is already scheduled. */ + if (ospf6->t_spf_calc) + { + if (IS_OSPF6_DEBUG_SPF(PROCESS) || IS_OSPF6_DEBUG_SPF (TIME)) + zlog_debug ("SPF: calculation timer is already scheduled: %p", + ospf6->t_spf_calc); + return; + } + + /* XXX Monotic timers: we only care about relative time here. */ + now = recent_relative_time (); + timersub (&now, &ospf6->ts_spf, &result); + + elapsed = (result.tv_sec * 1000) + (result.tv_usec / 1000); + ht = ospf6->spf_holdtime * ospf6->spf_hold_multiplier; + + if (ht > ospf6->spf_max_holdtime) + ht = ospf6->spf_max_holdtime; + + /* Get SPF calculation delay time. */ + if (elapsed < ht) + { + /* Got an event within the hold time of last SPF. We need to + * increase the hold_multiplier, if it's not already at/past + * maximum value, and wasn't already increased.. + */ + if (ht < ospf6->spf_max_holdtime) + ospf6->spf_hold_multiplier++; + + /* always honour the SPF initial delay */ + if ( (ht - elapsed) < ospf6->spf_delay) + delay = ospf6->spf_delay; + else + delay = ht - elapsed; + } + else + { + /* Event is past required hold-time of last SPF */ + delay = ospf6->spf_delay; + ospf6->spf_hold_multiplier = 1; + } + + if (IS_OSPF6_DEBUG_SPF(PROCESS) || IS_OSPF6_DEBUG_SPF (TIME)) + zlog_debug ("SPF: calculation timer delay = %ld", delay); + + zlog_info ("SPF: Scheduled in %ld msec", delay); + + ospf6->t_spf_calc = + thread_add_timer_msec (master, ospf6_spf_calculation_thread, ospf6, delay); } void @@ -666,6 +755,59 @@ DEFUN (no_debug_ospf6_spf_database, return CMD_SUCCESS; } +static int +ospf6_timers_spf_set (struct vty *vty, unsigned int delay, + unsigned int hold, + unsigned int max) +{ + struct ospf6 *ospf = vty->index; + + ospf->spf_delay = delay; + ospf->spf_holdtime = hold; + ospf->spf_max_holdtime = max; + + return CMD_SUCCESS; +} + +DEFUN (ospf6_timers_throttle_spf, + ospf6_timers_throttle_spf_cmd, + "timers throttle spf <0-600000> <0-600000> <0-600000>", + "Adjust routing timers\n" + "Throttling adaptive timer\n" + "OSPF6 SPF timers\n" + "Delay (msec) from first change received till SPF calculation\n" + "Initial hold time (msec) between consecutive SPF calculations\n" + "Maximum hold time (msec)\n") +{ + unsigned int delay, hold, max; + + if (argc != 3) + { + vty_out (vty, "Insufficient arguments%s", VTY_NEWLINE); + return CMD_WARNING; + } + + VTY_GET_INTEGER_RANGE ("SPF delay timer", delay, argv[0], 0, 600000); + VTY_GET_INTEGER_RANGE ("SPF hold timer", hold, argv[1], 0, 600000); + VTY_GET_INTEGER_RANGE ("SPF max-hold timer", max, argv[2], 0, 600000); + + return ospf6_timers_spf_set (vty, delay, hold, max); +} + +DEFUN (no_ospf6_timers_throttle_spf, + no_ospf6_timers_throttle_spf_cmd, + "no timers throttle spf", + NO_STR + "Adjust routing timers\n" + "Throttling adaptive timer\n" + "OSPF6 SPF timers\n") +{ + return ospf6_timers_spf_set (vty, + OSPF_SPF_DELAY_DEFAULT, + OSPF_SPF_HOLDTIME_DEFAULT, + OSPF_SPF_MAX_HOLDTIME_DEFAULT); +} + int config_write_ospf6_debug_spf (struct vty *vty) { @@ -678,6 +820,19 @@ config_write_ospf6_debug_spf (struct vty *vty) return 0; } +void +ospf6_spf_config_write (struct vty *vty) +{ + + if (ospf6->spf_delay != OSPF_SPF_DELAY_DEFAULT || + ospf6->spf_holdtime != OSPF_SPF_HOLDTIME_DEFAULT || + ospf6->spf_max_holdtime != OSPF_SPF_MAX_HOLDTIME_DEFAULT) + vty_out (vty, " timers throttle spf %d %d %d%s", + ospf6->spf_delay, ospf6->spf_holdtime, + ospf6->spf_max_holdtime, VTY_NEWLINE); + +} + void install_element_ospf6_debug_spf (void) { @@ -698,6 +853,6 @@ install_element_ospf6_debug_spf (void) void ospf6_spf_init (void) { + install_element (OSPF6_NODE, &ospf6_timers_throttle_spf_cmd); + install_element (OSPF6_NODE, &no_ospf6_timers_throttle_spf_cmd); } - - diff --git a/ospf6d/ospf6_spf.h b/ospf6d/ospf6_spf.h index c7069c25..6c40424f 100644 --- a/ospf6d/ospf6_spf.h +++ b/ospf6d/ospf6_spf.h @@ -22,6 +22,8 @@ #ifndef OSPF6_SPF_H #define OSPF6_SPF_H +#include "ospf6_top.h" + /* Debug option */ extern unsigned char conf_debug_ospf6_spf; #define OSPF6_DEBUG_SPF_PROCESS 0x01 @@ -81,11 +83,12 @@ extern void ospf6_spf_table_finish (struct ospf6_route_table *result_table); extern void ospf6_spf_calculation (u_int32_t router_id, struct ospf6_route_table *result_table, struct ospf6_area *oa); -extern void ospf6_spf_schedule (struct ospf6_area *oa); +extern void ospf6_spf_schedule (struct ospf6 *ospf); extern void ospf6_spf_display_subtree (struct vty *vty, const char *prefix, int rest, struct ospf6_vertex *v); +extern void ospf6_spf_config_write (struct vty *vty); extern int config_write_ospf6_debug_spf (struct vty *vty); extern void install_element_ospf6_debug_spf (void); extern void ospf6_spf_init (void); diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c index df856b4c..540ef382 100644 --- a/ospf6d/ospf6_top.c +++ b/ospf6d/ospf6_top.c @@ -46,6 +46,7 @@ #include "ospf6_asbr.h" #include "ospf6_abr.h" #include "ospf6_intra.h" +#include "ospf6_spf.h" #include "ospf6d.h" /* global ospf6d variable */ @@ -127,6 +128,11 @@ ospf6_create (void) o->lsdb->hook_add = ospf6_top_lsdb_hook_add; o->lsdb->hook_remove = ospf6_top_lsdb_hook_remove; + o->spf_delay = OSPF_SPF_DELAY_DEFAULT; + o->spf_holdtime = OSPF_SPF_HOLDTIME_DEFAULT; + o->spf_max_holdtime = OSPF_SPF_MAX_HOLDTIME_DEFAULT; + o->spf_hold_multiplier = 1; + o->route_table = OSPF6_ROUTE_TABLE_CREATE (GLOBAL, ROUTES); o->route_table->scope = o; o->route_table->hook_add = ospf6_top_route_hook_add; @@ -650,6 +656,7 @@ config_write_ospf6 (struct vty *vty) ospf6_redistribute_config_write (vty); ospf6_area_config_write (vty); + ospf6_spf_config_write (vty); for (ALL_LIST_ELEMENTS_RO (ospf6->area_list, j, oa)) { diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h index 4b2d2c3e..27eb18cd 100644 --- a/ospf6d/ospf6_top.h +++ b/ospf6d/ospf6_top.h @@ -38,6 +38,7 @@ struct ospf6 /* list of areas */ struct list *area_list; + struct ospf6_area *backbone; /* AS scope link state database */ struct ospf6_lsdb *lsdb; @@ -59,6 +60,18 @@ struct ospf6 u_char flag; + /* SPF parameters */ + unsigned int spf_delay; /* SPF delay time. */ + unsigned int spf_holdtime; /* SPF hold time. */ + unsigned int spf_max_holdtime; /* SPF maximum-holdtime */ + unsigned int spf_hold_multiplier; /* Adaptive multiplier for hold time */ + + struct timeval ts_spf; /* SPF calculation time stamp. */ + struct timeval ts_spf_duration; /* Execution time of last SPF */ + + /* Threads */ + struct thread *t_spf_calc; /* SPF calculation timer. */ + struct thread *t_ase_calc; /* ASE calculation timer. */ struct thread *maxage_remover; }; From 2449fcd64ae95d5fbfd95d93468fc57003def57d Mon Sep 17 00:00:00 2001 From: Dinesh Dutt Date: Sat, 24 Aug 2013 07:54:17 +0000 Subject: [PATCH 135/482] ospf6d: reinvoke MaxAge remover thread if not all MaxAge LSAs were flushed. MaxAge LSAs are being flushed out only on an event, unlike OSPFv2 where they're flushed out periodically. This causes certain LSAs to hang around forever, never getting flushed out. This patch makes flushing out MaxAge LSAs periodic, retriggered after a certain period if not all MaxAge LSAs were flushed out. Signed-off-by: Dinesh G Dutt Reviewed-by: Scott Feldman Signed-off-by: David Lamparter --- lib/libospf.h | 2 +- ospf6d/ospf6_lsdb.c | 23 +++++++++++++++++++++++ ospf6d/ospf6_lsdb.h | 15 --------------- ospf6d/ospf6_top.c | 32 ++++++++++++++++++++++++++------ ospfd/ospfd.c | 2 +- ospfd/ospfd.h | 2 -- 6 files changed, 51 insertions(+), 25 deletions(-) diff --git a/lib/libospf.h b/lib/libospf.h index 2282c072..9a60ce9e 100644 --- a/lib/libospf.h +++ b/lib/libospf.h @@ -77,6 +77,6 @@ #define OSPF_SPF_MAX_HOLDTIME_DEFAULT 10000 #define OSPF_LSA_MAXAGE_CHECK_INTERVAL 30 -#define OSFP_LSA_MAXAGE_REMOVE_DELAY_DEFAULT 60 +#define OSPF_LSA_MAXAGE_REMOVE_DELAY_DEFAULT 60 #endif /* _LIBOSPFD_H */ diff --git a/ospf6d/ospf6_lsdb.c b/ospf6d/ospf6_lsdb.c index 7455d835..0edc7a34 100644 --- a/ospf6d/ospf6_lsdb.c +++ b/ospf6d/ospf6_lsdb.c @@ -481,6 +481,29 @@ ospf6_lsdb_remove_all (struct ospf6_lsdb *lsdb) ospf6_lsdb_remove (lsa, lsdb); } +int +ospf6_lsdb_maxage_remover (struct ospf6_lsdb *lsdb) +{ + int reschedule = 0; + struct ospf6_lsa *lsa; + + for (lsa = ospf6_lsdb_head (lsdb); lsa; lsa = ospf6_lsdb_next (lsa)) + { + if (! OSPF6_LSA_IS_MAXAGE (lsa)) + continue; + if (lsa->retrans_count != 0) + { + reschedule = 1; + continue; + } + if (IS_OSPF6_DEBUG_LSA_TYPE (lsa->header->type)) + zlog_debug ("Remove MaxAge %s", lsa->name); + ospf6_lsdb_remove (lsa, lsdb); + } + + return (reschedule); +} + void ospf6_lsdb_show (struct vty *vty, int level, u_int16_t *type, u_int32_t *id, u_int32_t *adv_router, diff --git a/ospf6d/ospf6_lsdb.h b/ospf6d/ospf6_lsdb.h index 71297dae..2974ffb1 100644 --- a/ospf6d/ospf6_lsdb.h +++ b/ospf6d/ospf6_lsdb.h @@ -34,21 +34,6 @@ struct ospf6_lsdb void (*hook_remove) (struct ospf6_lsa *); }; -#define OSPF6_LSDB_MAXAGE_REMOVER(lsdb) \ - do { \ - struct ospf6_lsa *lsa; \ - for (lsa = ospf6_lsdb_head (lsdb); lsa; lsa = ospf6_lsdb_next (lsa)) \ - { \ - if (! OSPF6_LSA_IS_MAXAGE (lsa)) \ - continue; \ - if (lsa->retrans_count != 0) \ - continue; \ - if (IS_OSPF6_DEBUG_LSA_TYPE (lsa->header->type)) \ - zlog_debug ("Remove MaxAge %s", lsa->name); \ - ospf6_lsdb_remove (lsa, lsdb); \ - } \ - } while (0) - /* Function Prototypes */ extern struct ospf6_lsdb *ospf6_lsdb_create (void *data); extern void ospf6_lsdb_delete (struct ospf6_lsdb *lsdb); diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c index 540ef382..e9fe7a4e 100644 --- a/ospf6d/ospf6_top.c +++ b/ospf6d/ospf6_top.c @@ -208,7 +208,7 @@ ospf6_disable (struct ospf6 *o) } } -static int +int ospf6_maxage_remover (struct thread *thread) { struct ospf6 *o = (struct ospf6 *) THREAD_ARG (thread); @@ -216,6 +216,7 @@ ospf6_maxage_remover (struct thread *thread) struct ospf6_interface *oi; struct ospf6_neighbor *on; struct listnode *i, *j, *k; + int reschedule = 0; o->maxage_remover = (struct thread *) NULL; @@ -227,8 +228,9 @@ ospf6_maxage_remover (struct thread *thread) { if (on->state != OSPF6_NEIGHBOR_EXCHANGE && on->state != OSPF6_NEIGHBOR_LOADING) - continue; + continue; + ospf6_maxage_remove (o); return 0; } } @@ -237,11 +239,28 @@ ospf6_maxage_remover (struct thread *thread) for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa)) { for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi)) - OSPF6_LSDB_MAXAGE_REMOVER (oi->lsdb); + { + if (ospf6_lsdb_maxage_remover (oi->lsdb)) + { + reschedule = 1; + } + } - OSPF6_LSDB_MAXAGE_REMOVER (oa->lsdb); + if (ospf6_lsdb_maxage_remover (oa->lsdb)) + { + reschedule = 1; + } + } + + if (ospf6_lsdb_maxage_remover (o->lsdb)) + { + reschedule = 1; + } + + if (reschedule) + { + ospf6_maxage_remove (o); } - OSPF6_LSDB_MAXAGE_REMOVER (o->lsdb); return 0; } @@ -250,7 +269,8 @@ void ospf6_maxage_remove (struct ospf6 *o) { if (o && ! o->maxage_remover) - o->maxage_remover = thread_add_event (master, ospf6_maxage_remover, o, 0); + o->maxage_remover = thread_add_timer (master, ospf6_maxage_remover, o, + OSPF_LSA_MAXAGE_REMOVE_DELAY_DEFAULT); } /* start ospf6 */ diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index 3e2b2348..538bc094 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -200,7 +200,7 @@ ospf_new (void) new->spf_hold_multiplier = 1; /* MaxAge init. */ - new->maxage_delay = OSFP_LSA_MAXAGE_REMOVE_DELAY_DEFAULT; + new->maxage_delay = OSPF_LSA_MAXAGE_REMOVE_DELAY_DEFAULT; new->maxage_lsa = route_table_init(); new->t_maxage_walker = thread_add_timer (master, ospf_lsa_maxage_walker, diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h index fe9d77e9..4242aa01 100644 --- a/ospfd/ospfd.h +++ b/ospfd/ospfd.h @@ -211,10 +211,8 @@ struct ospf struct thread *t_opaque_lsa_self; /* Type-11 Opaque-LSAs origin event. */ #endif /* HAVE_OPAQUE_LSA */ -#define OSFP_LSA_MAXAGE_REMOVE_DELAY_DEFAULT 60 unsigned int maxage_delay; /* Delay on Maxage remover timer, sec */ struct thread *t_maxage; /* MaxAge LSA remover timer. */ -#define OSPF_LSA_MAXAGE_CHECK_INTERVAL 30 struct thread *t_maxage_walker; /* MaxAge LSA checking timer. */ struct thread *t_deferred_shutdown; /* deferred/stub-router shutdown timer*/ From ac58e143f7278d5faaad7a8672e48328b03cffa5 Mon Sep 17 00:00:00 2001 From: Dinesh Dutt Date: Sat, 24 Aug 2013 07:54:24 +0000 Subject: [PATCH 136/482] ospf6d: remove older version of LSA from neigbor retx list before prematurely aging it. See comment in code for very detailed issue and fix. Signed-off-by: Dinesh G Dutt Reviewed-by: Scott Feldman Signed-off-by: David Lamparter --- ospf6d/ospf6_lsa.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/ospf6d/ospf6_lsa.c b/ospf6d/ospf6_lsa.c index 7dbd303f..18398800 100644 --- a/ospf6d/ospf6_lsa.c +++ b/ospf6d/ospf6_lsa.c @@ -258,6 +258,29 @@ ospf6_lsa_premature_aging (struct ospf6_lsa *lsa) THREAD_OFF (lsa->expire); THREAD_OFF (lsa->refresh); + /* + * We clear the LSA from the neighbor retx lists now because it + * will not get deleted later. Essentially, changing the age to + * MaxAge will prevent this LSA from being matched with its + * existing entries in the retx list thereby causing those entries + * to be silently replaced with its MaxAged version, but with ever + * increasing retx count causing this LSA to remain forever and + * for the MaxAge remover thread to be called forever too. + * + * The reason the previous entry silently disappears is that when + * entry is added to a neighbor's retx list, it replaces the existing + * entry. But since the ospf6_lsdb_add() routine is generic and not aware + * of the special semantics of retx count, the retx count is not + * decremented when its replaced. Attempting to add the incr and decr + * retx count routines as the hook_add and hook_remove for the retx lists + * have a problem because the hook_remove routine is called for MaxAge + * entries (as will be the case in a traditional LSDB, unlike in this case + * where an LSDB is used as an efficient tree structure to store all kinds + * of data) that are added instead of calling the hook_add routine. + */ + + ospf6_flood_clear (lsa); + lsa->header->age = htons (OSPF_LSA_MAXAGE); thread_execute (master, ospf6_lsa_expire, lsa, 0); } From 09df4574b95dbb5880eb2d3c3c3c900687a9a3ef Mon Sep 17 00:00:00 2001 From: Dinesh Dutt Date: Sat, 24 Aug 2013 07:54:31 +0000 Subject: [PATCH 137/482] ospf6d: enable the commands to support detailed debugging of LSAs. The code for the commands exists, but it hasn't been defined in the definition of the command itself. This patch fixes that. Signed-off-by: Dinesh G Dutt Reviewed-by: Scott Feldman Signed-off-by: David Lamparter --- ospf6d/ospf6_lsa.c | 177 ++++++++------------------------------------- 1 file changed, 31 insertions(+), 146 deletions(-) diff --git a/ospf6d/ospf6_lsa.c b/ospf6d/ospf6_lsa.c index 18398800..3be08ef6 100644 --- a/ospf6d/ospf6_lsa.c +++ b/ospf6d/ospf6_lsa.c @@ -74,7 +74,7 @@ ospf6_unknown_lsa_show (struct vty *vty, struct ospf6_lsa *lsa) struct ospf6_lsa_handler unknown_handler = { OSPF6_LSTYPE_UNKNOWN, - "Unknown", + "unknown", ospf6_unknown_lsa_show, OSPF6_LSA_DEBUG, }; @@ -748,7 +748,7 @@ ospf6_lsa_handler_name (struct ospf6_lsa_handler *h) unsigned int i; unsigned int size = strlen (h->name); - if (!strcmp(h->name, "Unknown") && + if (!strcmp(h->name, "unknown") && h->type != OSPF6_LSTYPE_UNKNOWN) { snprintf (buf, sizeof (buf), "%#04hx", h->type); @@ -768,7 +768,7 @@ ospf6_lsa_handler_name (struct ospf6_lsa_handler *h) DEFUN (debug_ospf6_lsa_type, debug_ospf6_lsa_hex_cmd, - "debug ospf6 lsa XXXX/0xXXXX", + "debug ospf6 lsa (router|network|inter-prefix|inter-router|as-ext|grp-mbr|type7|link|intra-prefix|unknown)", DEBUG_STR OSPF6_STR "Debug Link State Advertisements (LSAs)\n" @@ -777,44 +777,21 @@ DEFUN (debug_ospf6_lsa_type, { unsigned int i; struct ospf6_lsa_handler *handler = NULL; - unsigned long val; - char *endptr = NULL; - u_int16_t type = 0; assert (argc); - if ((strlen (argv[0]) == 6 && ! strncmp (argv[0], "0x", 2)) || - (strlen (argv[0]) == 4)) - { - val = strtoul (argv[0], &endptr, 16); - if (*endptr == '\0') - type = val; - } - for (i = 0; i < vector_active (ospf6_lsa_handler_vector); i++) { handler = vector_slot (ospf6_lsa_handler_vector, i); if (handler == NULL) continue; - if (type && handler->type == type) + if (strncmp (argv[0], ospf6_lsa_handler_name(handler), strlen(argv[0])) == 0) break; if (! strcasecmp (argv[0], handler->name)) break; handler = NULL; } - if (type && handler == NULL) - { - handler = (struct ospf6_lsa_handler *) - malloc (sizeof (struct ospf6_lsa_handler)); - memset (handler, 0, sizeof (struct ospf6_lsa_handler)); - handler->type = type; - handler->name = "Unknown"; - handler->show = ospf6_unknown_lsa_show; - vector_set_index (ospf6_lsa_handler_vector, - handler->type & OSPF6_LSTYPE_FCODE_MASK, handler); - } - if (handler == NULL) handler = &unknown_handler; @@ -822,7 +799,7 @@ DEFUN (debug_ospf6_lsa_type, { if (! strcmp (argv[1], "originate")) SET_FLAG (handler->debug, OSPF6_LSA_DEBUG_ORIGINATE); - if (! strcmp (argv[1], "examin")) + if (! strcmp (argv[1], "examine")) SET_FLAG (handler->debug, OSPF6_LSA_DEBUG_EXAMIN); if (! strcmp (argv[1], "flooding")) SET_FLAG (handler->debug, OSPF6_LSA_DEBUG_FLOOD); @@ -833,9 +810,18 @@ DEFUN (debug_ospf6_lsa_type, return CMD_SUCCESS; } +ALIAS (debug_ospf6_lsa_type, + debug_ospf6_lsa_hex_detail_cmd, + "debug ospf6 lsa (router|network|inter-prefix|inter-router|as-ext|grp-mbr|type7|link|intra-prefix|unknown) (originate|examine|flooding)", + DEBUG_STR + OSPF6_STR + "Debug Link State Advertisements (LSAs)\n" + "Specify LS type as Hexadecimal\n" + ) + DEFUN (no_debug_ospf6_lsa_type, no_debug_ospf6_lsa_hex_cmd, - "no debug ospf6 lsa XXXX/0xXXXX", + "no debug ospf6 lsa (router|network|inter-prefix|inter-router|as-ext|grp-mbr|type7|link|intra-prefix|unknown)", NO_STR DEBUG_STR OSPF6_STR @@ -845,26 +831,15 @@ DEFUN (no_debug_ospf6_lsa_type, { u_int i; struct ospf6_lsa_handler *handler = NULL; - unsigned long val; - char *endptr = NULL; - u_int16_t type = 0; assert (argc); - if ((strlen (argv[0]) == 6 && ! strncmp (argv[0], "0x", 2)) || - (strlen (argv[0]) == 4)) - { - val = strtoul (argv[0], &endptr, 16); - if (*endptr == '\0') - type = val; - } - for (i = 0; i < vector_active (ospf6_lsa_handler_vector); i++) { handler = vector_slot (ospf6_lsa_handler_vector, i); if (handler == NULL) continue; - if (type && handler->type == type) + if (strncmp (argv[0], ospf6_lsa_handler_name(handler), strlen(argv[0])) == 0) break; if (! strcasecmp (argv[0], handler->name)) break; @@ -877,7 +852,7 @@ DEFUN (no_debug_ospf6_lsa_type, { if (! strcmp (argv[1], "originate")) UNSET_FLAG (handler->debug, OSPF6_LSA_DEBUG_ORIGINATE); - if (! strcmp (argv[1], "examin")) + if (! strcmp (argv[1], "examine")) UNSET_FLAG (handler->debug, OSPF6_LSA_DEBUG_EXAMIN); if (! strcmp (argv[1], "flooding")) UNSET_FLAG (handler->debug, OSPF6_LSA_DEBUG_FLOOD); @@ -885,120 +860,30 @@ DEFUN (no_debug_ospf6_lsa_type, else UNSET_FLAG (handler->debug, OSPF6_LSA_DEBUG); - if (handler->debug == 0 && - !strcmp(handler->name, "Unknown") && type != OSPF6_LSTYPE_UNKNOWN) - { - free (handler); - vector_slot (ospf6_lsa_handler_vector, i) = NULL; - } - return CMD_SUCCESS; } -struct cmd_element debug_ospf6_lsa_type_cmd; -struct cmd_element debug_ospf6_lsa_type_detail_cmd; -struct cmd_element no_debug_ospf6_lsa_type_cmd; -struct cmd_element no_debug_ospf6_lsa_type_detail_cmd; +ALIAS (no_debug_ospf6_lsa_type, + no_debug_ospf6_lsa_hex_detail_cmd, + "no debug ospf6 lsa (router|network|inter-prefix|inter-router|as-ext|grp-mbr|type7|link|intra-prefix) (originate|examine|flooding)", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug Link State Advertisements (LSAs)\n" + "Specify LS type as Hexadecimal\n" + ) void install_element_ospf6_debug_lsa (void) { - u_int i; - struct ospf6_lsa_handler *handler; -#define STRSIZE 256 -#define DOCSIZE 1024 - static char strbuf[STRSIZE]; - static char docbuf[DOCSIZE]; - static char detail_strbuf[STRSIZE]; - static char detail_docbuf[DOCSIZE]; - char *str, *no_str; - char *doc, *no_doc; - - strbuf[0] = '\0'; - no_str = &strbuf[strlen (strbuf)]; - strncat (strbuf, "no ", STRSIZE - strlen (strbuf)); - str = &strbuf[strlen (strbuf)]; - - strncat (strbuf, "debug ospf6 lsa (", STRSIZE - strlen (strbuf)); - for (i = 0; i < vector_active (ospf6_lsa_handler_vector); i++) - { - handler = vector_slot (ospf6_lsa_handler_vector, i); - if (handler == NULL) - continue; - strncat (strbuf, ospf6_lsa_handler_name (handler), - STRSIZE - strlen (strbuf)); - strncat (strbuf, "|", STRSIZE - strlen (strbuf)); - } - strbuf[strlen (strbuf) - 1] = ')'; - strbuf[strlen (strbuf)] = '\0'; - - docbuf[0] = '\0'; - no_doc = &docbuf[strlen (docbuf)]; - strncat (docbuf, NO_STR, DOCSIZE - strlen (docbuf)); - doc = &docbuf[strlen (docbuf)]; - - strncat (docbuf, DEBUG_STR, DOCSIZE - strlen (docbuf)); - strncat (docbuf, OSPF6_STR, DOCSIZE - strlen (docbuf)); - strncat (docbuf, "Debug Link State Advertisements (LSAs)\n", - DOCSIZE - strlen (docbuf)); - - for (i = 0; i < vector_active (ospf6_lsa_handler_vector); i++) - { - handler = vector_slot (ospf6_lsa_handler_vector, i); - if (handler == NULL) - continue; - strncat (docbuf, "Debug ", DOCSIZE - strlen (docbuf)); - strncat (docbuf, handler->name, DOCSIZE - strlen (docbuf)); - strncat (docbuf, "-LSA\n", DOCSIZE - strlen (docbuf)); - } - docbuf[strlen (docbuf)] = '\0'; - - debug_ospf6_lsa_type_cmd.string = str; - debug_ospf6_lsa_type_cmd.func = debug_ospf6_lsa_type; - debug_ospf6_lsa_type_cmd.doc = doc; - - no_debug_ospf6_lsa_type_cmd.string = no_str; - no_debug_ospf6_lsa_type_cmd.func = no_debug_ospf6_lsa_type; - no_debug_ospf6_lsa_type_cmd.doc = no_doc; - - strncpy (detail_strbuf, strbuf, STRSIZE); - strncat (detail_strbuf, " (originate|examin|flooding)", - STRSIZE - strlen (detail_strbuf)); - detail_strbuf[strlen (detail_strbuf)] = '\0'; - no_str = &detail_strbuf[0]; - str = &detail_strbuf[strlen ("no ")]; - - strncpy (detail_docbuf, docbuf, DOCSIZE); - strncat (detail_docbuf, "Debug Originating LSA\n", - DOCSIZE - strlen (detail_docbuf)); - strncat (detail_docbuf, "Debug Examining LSA\n", - DOCSIZE - strlen (detail_docbuf)); - strncat (detail_docbuf, "Debug Flooding LSA\n", - DOCSIZE - strlen (detail_docbuf)); - detail_docbuf[strlen (detail_docbuf)] = '\0'; - no_doc = &detail_docbuf[0]; - doc = &detail_docbuf[strlen (NO_STR)]; - - debug_ospf6_lsa_type_detail_cmd.string = str; - debug_ospf6_lsa_type_detail_cmd.func = debug_ospf6_lsa_type; - debug_ospf6_lsa_type_detail_cmd.doc = doc; - - no_debug_ospf6_lsa_type_detail_cmd.string = no_str; - no_debug_ospf6_lsa_type_detail_cmd.func = no_debug_ospf6_lsa_type; - no_debug_ospf6_lsa_type_detail_cmd.doc = no_doc; - install_element (ENABLE_NODE, &debug_ospf6_lsa_hex_cmd); - install_element (ENABLE_NODE, &debug_ospf6_lsa_type_cmd); - install_element (ENABLE_NODE, &debug_ospf6_lsa_type_detail_cmd); + install_element (ENABLE_NODE, &debug_ospf6_lsa_hex_detail_cmd); install_element (ENABLE_NODE, &no_debug_ospf6_lsa_hex_cmd); - install_element (ENABLE_NODE, &no_debug_ospf6_lsa_type_cmd); - install_element (ENABLE_NODE, &no_debug_ospf6_lsa_type_detail_cmd); + install_element (ENABLE_NODE, &no_debug_ospf6_lsa_hex_detail_cmd); install_element (CONFIG_NODE, &debug_ospf6_lsa_hex_cmd); - install_element (CONFIG_NODE, &debug_ospf6_lsa_type_cmd); - install_element (CONFIG_NODE, &debug_ospf6_lsa_type_detail_cmd); + install_element (CONFIG_NODE, &debug_ospf6_lsa_hex_detail_cmd); install_element (CONFIG_NODE, &no_debug_ospf6_lsa_hex_cmd); - install_element (CONFIG_NODE, &no_debug_ospf6_lsa_type_cmd); - install_element (CONFIG_NODE, &no_debug_ospf6_lsa_type_detail_cmd); + install_element (CONFIG_NODE, &no_debug_ospf6_lsa_hex_detail_cmd); } int @@ -1019,7 +904,7 @@ config_write_ospf6_debug_lsa (struct vty *vty) vty_out (vty, "debug ospf6 lsa %s originate%s", ospf6_lsa_handler_name (handler), VNL); if (CHECK_FLAG (handler->debug, OSPF6_LSA_DEBUG_EXAMIN)) - vty_out (vty, "debug ospf6 lsa %s examin%s", + vty_out (vty, "debug ospf6 lsa %s examine%s", ospf6_lsa_handler_name (handler), VNL); if (CHECK_FLAG (handler->debug, OSPF6_LSA_DEBUG_FLOOD)) vty_out (vty, "debug ospf6 lsa %s flooding%s", From 8ae454e779d5620576990f4bf5da3a35f913cc4d Mon Sep 17 00:00:00 2001 From: Dinesh Dutt Date: Sat, 24 Aug 2013 07:54:41 +0000 Subject: [PATCH 138/482] ospf6d: add more details to show ipv6 ospf6 data Specifically, it displays the flags, lock and retransmission count fields. Signed-off-by: Dinesh G Dutt Reviewed-by: JR Rivers Reviewed-by: Scott Feldman Signed-off-by: David Lamparter --- ospf6d/ospf6_lsa.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ospf6d/ospf6_lsa.c b/ospf6d/ospf6_lsa.c index 3be08ef6..64f929d9 100644 --- a/ospf6d/ospf6_lsa.c +++ b/ospf6d/ospf6_lsa.c @@ -452,6 +452,11 @@ ospf6_lsa_show_internal (struct vty *vty, struct ospf6_lsa *lsa) ntohs (lsa->header->length), VNL); vty_out (vty, " Prev: %p This: %p Next: %p%s", lsa->prev, lsa, lsa->next, VNL); + vty_out (vty, "Flag: %x %s", lsa->flag, VNL); + vty_out (vty, "Lock: %d %s", lsa->lock, VNL); + vty_out (vty, "ReTx Count: %d%s", lsa->retrans_count, VNL); + vty_out (vty, "Threads: Expire: %x, Refresh: %x %s", + lsa->expire, lsa->refresh, VNL); vty_out (vty, "%s", VNL); return; } From bf986da797e7d2a0aaae313fea626ba433581f03 Mon Sep 17 00:00:00 2001 From: Dinesh Dutt Date: Sat, 24 Aug 2013 07:54:50 +0000 Subject: [PATCH 139/482] ospf6d: fix various bugs in installing and flooding LSAs Signed-off-by: Dinesh G Dutt Reviewed-by: Pradosh Mohapatra Reviewed-by: Scott Feldman Signed-off-by: David Lamparter --- ospf6d/ospf6_flood.c | 14 ++++++-------- ospf6d/ospf6_lsa.c | 9 ++++----- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/ospf6d/ospf6_flood.c b/ospf6d/ospf6_flood.c index 3a9af01d..ba770092 100644 --- a/ospf6d/ospf6_flood.c +++ b/ospf6d/ospf6_flood.c @@ -122,10 +122,8 @@ ospf6_lsa_originate (struct ospf6_lsa *lsa) ospf6_lsa_header_print (lsa); } - if (old) - ospf6_flood_clear (old); - ospf6_flood (NULL, lsa); ospf6_install_lsa (lsa); + ospf6_flood (NULL, lsa); } void @@ -849,7 +847,11 @@ ospf6_receive_lsa (struct ospf6_neighbor *from, quagga_gettime (QUAGGA_CLK_MONOTONIC, &new->received); if (is_debug) - zlog_debug ("Flood, Install, Possibly acknowledge the received LSA"); + zlog_debug ("Install, Flood, Possibly acknowledge the received LSA"); + + /* Remove older copies of this LSA from retx lists */ + if (old) + ospf6_flood_clear (old); /* (b) immediately flood and (c) remove from all retrans-list */ /* Prevent self-originated LSA to be flooded. this is to make @@ -858,10 +860,6 @@ ospf6_receive_lsa (struct ospf6_neighbor *from, if (new->header->adv_router != from->ospf6_if->area->ospf6->router_id) ospf6_flood (from, new); - /* (c) Remove the current database copy from all neighbors' Link - state retransmission lists. */ - /* XXX, flood_clear ? */ - /* (d), installing lsdb, which may cause routing table calculation (replacing database copy) */ ospf6_install_lsa (new); diff --git a/ospf6d/ospf6_lsa.c b/ospf6d/ospf6_lsa.c index 64f929d9..db14731f 100644 --- a/ospf6d/ospf6_lsa.c +++ b/ospf6d/ospf6_lsa.c @@ -636,12 +636,12 @@ ospf6_lsa_expire (struct thread *thread) if (CHECK_FLAG (lsa->flag, OSPF6_LSA_HEADERONLY)) return 0; /* dbexchange will do something ... */ - /* reflood lsa */ - ospf6_flood (NULL, lsa); - /* reinstall lsa */ ospf6_install_lsa (lsa); + /* reflood lsa */ + ospf6_flood (NULL, lsa); + /* schedule maxage remover */ ospf6_maxage_remove (ospf6); @@ -692,9 +692,8 @@ ospf6_lsa_refresh (struct thread *thread) ospf6_lsa_header_print (new); } - ospf6_flood_clear (old); - ospf6_flood (NULL, new); ospf6_install_lsa (new); + ospf6_flood (NULL, new); return 0; } From c5926a92235874574b20e426a3752c1451a90ec7 Mon Sep 17 00:00:00 2001 From: Dinesh Dutt Date: Sat, 24 Aug 2013 07:55:00 +0000 Subject: [PATCH 140/482] ospf6d: add p2p interface support Signed-off-by: Dinesh G Dutt Signed-off-by: Ayan Banerjee Reviewed-by: Scott Feldman Reviewed-by: James Li Signed-off-by: David Lamparter --- doc/ospf6d.texi | 4 + lib/libospf.h | 10 ++ ospf6d/ospf6_flood.c | 3 +- ospf6d/ospf6_interface.c | 105 +++++++++++++++++- ospf6d/ospf6_interface.h | 3 + ospf6d/ospf6_intra.c | 8 +- ospf6d/ospf6_message.c | 229 ++++++++++++++++++++++++--------------- ospfd/ospf_interface.h | 8 -- 8 files changed, 270 insertions(+), 100 deletions(-) diff --git a/doc/ospf6d.texi b/doc/ospf6d.texi index c01c0510..d981820c 100644 --- a/doc/ospf6d.texi +++ b/doc/ospf6d.texi @@ -98,6 +98,10 @@ Sets interface's Router Priority. Default value is 1. Sets interface's Inf-Trans-Delay. Default value is 1. @end deffn +@deffn {Interface Command} {ipv6 ospf6 network (broadcast|point-to-point)} {} +Set explicitly network type for specifed interface. +@end deffn + @node Redistribute routes to OSPF6 @section Redistribute routes to OSPF6 diff --git a/lib/libospf.h b/lib/libospf.h index 9a60ce9e..856c76df 100644 --- a/lib/libospf.h +++ b/lib/libospf.h @@ -50,6 +50,16 @@ #define OSPF_INITIAL_SEQUENCE_NUMBER 0x80000001 #define OSPF_MAX_SEQUENCE_NUMBER 0x7fffffff +/* OSPF Interface Types */ +#define OSPF_IFTYPE_NONE 0 +#define OSPF_IFTYPE_POINTOPOINT 1 +#define OSPF_IFTYPE_BROADCAST 2 +#define OSPF_IFTYPE_NBMA 3 +#define OSPF_IFTYPE_POINTOMULTIPOINT 4 +#define OSPF_IFTYPE_VIRTUALLINK 5 +#define OSPF_IFTYPE_LOOPBACK 6 +#define OSPF_IFTYPE_MAX 7 + /* OSPF interface default values. */ #define OSPF_OUTPUT_COST_DEFAULT 10 #define OSPF_OUTPUT_COST_INFINITE UINT16_MAX diff --git a/ospf6d/ospf6_flood.c b/ospf6d/ospf6_flood.c index ba770092..beae6992 100644 --- a/ospf6d/ospf6_flood.c +++ b/ospf6d/ospf6_flood.c @@ -366,7 +366,8 @@ ospf6_flood_interface (struct ospf6_neighbor *from, /* (5) flood the LSA out the interface. */ if (is_debug) zlog_debug ("Schedule flooding for the interface"); - if (if_is_broadcast (oi->interface)) + if ((oi->type == OSPF_IFTYPE_BROADCAST) || + (oi->type == OSPF_IFTYPE_POINTOPOINT)) { ospf6_lsdb_add (ospf6_lsa_copy (lsa), oi->lsupdate_list); if (oi->thread_send_lsupdate == NULL) diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index 7c45fe46..94b599be 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -94,6 +94,17 @@ ospf6_interface_lsdb_hook (struct ospf6_lsa *lsa) } } +static u_char +ospf6_default_iftype(struct interface *ifp) +{ + if (if_is_pointopoint (ifp)) + return OSPF_IFTYPE_POINTOPOINT; + else if (if_is_loopback (ifp)) + return OSPF_IFTYPE_LOOPBACK; + else + return OSPF_IFTYPE_BROADCAST; +} + /* Create new ospf6 interface structure */ struct ospf6_interface * ospf6_interface_create (struct interface *ifp) @@ -122,6 +133,7 @@ ospf6_interface_create (struct interface *ifp) oi->dead_interval = OSPF_ROUTER_DEAD_INTERVAL_DEFAULT; oi->rxmt_interval = OSPF_RETRANSMIT_INTERVAL_DEFAULT; oi->cost = OSPF6_INTERFACE_COST; + oi->type = ospf6_default_iftype (ifp); oi->state = OSPF6_INTERFACE_DOWN; oi->flag = 0; oi->mtu_ignore = 0; @@ -407,6 +419,7 @@ ospf6_interface_state_change (u_char next_state, struct ospf6_interface *oi) (next_state != OSPF6_INTERFACE_DR && next_state != OSPF6_INTERFACE_BDR)) ospf6_sso (oi->interface->ifindex, &alldrouters6, IPV6_LEAVE_GROUP); + if ((prev_state != OSPF6_INTERFACE_DR && prev_state != OSPF6_INTERFACE_BDR) && (next_state == OSPF6_INTERFACE_DR || @@ -640,8 +653,10 @@ interface_up (struct thread *thread) thread_add_event (master, ospf6_hello_send, oi, 0); /* decide next interface state */ - if (if_is_pointopoint (oi->interface)) + if ((if_is_pointopoint (oi->interface)) || + (oi->type == OSPF_IFTYPE_POINTOPOINT)) { ospf6_interface_state_change (OSPF6_INTERFACE_POINTTOPOINT, oi); + } else if (oi->priority == 0) ospf6_interface_state_change (OSPF6_INTERFACE_DROTHER, oi); else @@ -1522,6 +1537,86 @@ DEFUN (no_ipv6_ospf6_advertise_prefix_list, return CMD_SUCCESS; } +DEFUN (ipv6_ospf6_network, + ipv6_ospf6_network_cmd, + "ipv6 ospf6 network (broadcast|point-to-point)", + IP6_STR + OSPF6_STR + "Network Type\n" + "Specify OSPFv6 broadcast network\n" + "Specify OSPF6 point-to-point network\n" + ) +{ + struct ospf6_interface *oi; + struct interface *ifp; + + ifp = (struct interface *) vty->index; + assert (ifp); + + oi = (struct ospf6_interface *) ifp->info; + if (oi == NULL) { + oi = ospf6_interface_create (ifp); + } + assert (oi); + + if (strncmp (argv[0], "b", 1) == 0) + { + if (oi->type == OSPF_IFTYPE_BROADCAST) + return CMD_SUCCESS; + + oi->type = OSPF_IFTYPE_BROADCAST; + } + else if (strncmp (argv[0], "point-to-p", 10) == 0) + { + if (oi->type == OSPF_IFTYPE_POINTOPOINT) { + return CMD_SUCCESS; + } + oi->type = OSPF_IFTYPE_POINTOPOINT; + } + + /* Reset the interface */ + thread_add_event (master, interface_down, oi, 0); + thread_add_event (master, interface_up, oi, 0); + + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_ospf6_network, + no_ipv6_ospf6_network_cmd, + "no ipv6 ospf6 network", + NO_STR + IP6_STR + OSPF6_STR + "Network Type\n" + "Default to whatever interface type system specifies" + ) +{ + struct ospf6_interface *oi; + struct interface *ifp; + int type; + + ifp = (struct interface *) vty->index; + assert (ifp); + + oi = (struct ospf6_interface *) ifp->info; + if (oi == NULL) { + return CMD_SUCCESS; + } + + type = ospf6_default_iftype (ifp); + if (oi->type == type) + { + return CMD_SUCCESS; + } + oi->type = type; + + /* Reset the interface */ + thread_add_event (master, interface_down, oi, 0); + thread_add_event (master, interface_up, oi, 0); + + return CMD_SUCCESS; +} + static int config_write_ospf6_interface (struct vty *vty) { @@ -1581,6 +1676,11 @@ config_write_ospf6_interface (struct vty *vty) if (oi->mtu_ignore) vty_out (vty, " ipv6 ospf6 mtu-ignore%s", VNL); + if (oi->type == OSPF_IFTYPE_POINTOPOINT) + vty_out (vty, " ipv6 ospf6 network point-to-point%s", VNL); + else if (oi->type == OSPF_IFTYPE_BROADCAST) + vty_out (vty, " ipv6 ospf6 network broadcast%s", VNL); + vty_out (vty, "!%s", VNL); } return 0; @@ -1638,6 +1738,9 @@ ospf6_interface_init (void) install_element (INTERFACE_NODE, &ipv6_ospf6_advertise_prefix_list_cmd); install_element (INTERFACE_NODE, &no_ipv6_ospf6_advertise_prefix_list_cmd); + + install_element (INTERFACE_NODE, &ipv6_ospf6_network_cmd); + install_element (INTERFACE_NODE, &no_ipv6_ospf6_network_cmd); } DEFUN (debug_ospf6_interface, diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h index d80b0730..808f0968 100644 --- a/ospf6d/ospf6_interface.h +++ b/ospf6d/ospf6_interface.h @@ -56,6 +56,9 @@ struct ospf6_interface /* I/F transmission delay */ u_int32_t transdelay; + /* Network Type */ + u_char type; + /* Router Priority */ u_char priority; diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c index 9bc603b3..86f41460 100644 --- a/ospf6d/ospf6_intra.c +++ b/ospf6d/ospf6_intra.c @@ -215,7 +215,7 @@ ospf6_router_lsa_originate (struct thread *thread) } /* Point-to-Point interfaces */ - if (if_is_pointopoint (oi->interface)) + if (oi->type == OSPF_IFTYPE_POINTOPOINT) { for (ALL_LIST_ELEMENTS_RO (oi->neighbor_list, j, on)) { @@ -233,7 +233,7 @@ ospf6_router_lsa_originate (struct thread *thread) } /* Broadcast and NBMA interfaces */ - if (if_is_broadcast (oi->interface)) + else if (oi->type == OSPF_IFTYPE_BROADCAST) { /* If this router is not DR, and If this router not fully adjacent with DR, @@ -261,6 +261,10 @@ ospf6_router_lsa_originate (struct thread *thread) lsdesc++; } + else + { + assert (0); /* Unknown interface type */ + } /* Virtual links */ /* xxx */ diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c index b35aa1ac..31db9a4b 100644 --- a/ospf6d/ospf6_message.c +++ b/ospf6d/ospf6_message.c @@ -1785,6 +1785,7 @@ ospf6_dbdesc_send (struct thread *thread) struct ospf6_dbdesc *dbdesc; u_char *p; struct ospf6_lsa *lsa; + struct in6_addr *dst; on = (struct ospf6_neighbor *) THREAD_ARG (thread); on->thread_send_dbdesc = (struct thread *) NULL; @@ -1848,8 +1849,14 @@ ospf6_dbdesc_send (struct thread *thread) oh->type = OSPF6_MESSAGE_TYPE_DBDESC; oh->length = htons (p - sendbuf); - ospf6_send (on->ospf6_if->linklocal_addr, &on->linklocal_addr, - on->ospf6_if, oh); + + if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT) + dst = &allspfrouters6; + else + dst = &on->linklocal_addr; + + ospf6_send (on->ospf6_if->linklocal_addr, dst, on->ospf6_if, oh); + return 0; } @@ -1952,8 +1959,13 @@ ospf6_lsreq_send (struct thread *thread) oh->type = OSPF6_MESSAGE_TYPE_LSREQ; oh->length = htons (p - sendbuf); - ospf6_send (on->ospf6_if->linklocal_addr, &on->linklocal_addr, + if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT) + ospf6_send (on->ospf6_if->linklocal_addr, &allspfrouters6, on->ospf6_if, oh); + else + ospf6_send (on->ospf6_if->linklocal_addr, &on->linklocal_addr, + on->ospf6_if, oh); + return 0; } @@ -1964,7 +1976,7 @@ ospf6_lsupdate_send_neighbor (struct thread *thread) struct ospf6_header *oh; struct ospf6_lsupdate *lsupdate; u_char *p; - int num; + int lsa_cnt; struct ospf6_lsa *lsa; on = (struct ospf6_neighbor *) THREAD_ARG (thread); @@ -1981,22 +1993,13 @@ ospf6_lsupdate_send_neighbor (struct thread *thread) return 0; } - /* if we have nothing to send, return */ - if (on->lsupdate_list->count == 0 && - on->retrans_list->count == 0) - { - if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_LSUPDATE, SEND)) - zlog_debug ("Quit to send (nothing to send)"); - return 0; - } - memset (sendbuf, 0, iobuflen); oh = (struct ospf6_header *) sendbuf; lsupdate = (struct ospf6_lsupdate *) ((caddr_t) oh + sizeof (struct ospf6_header)); p = (u_char *)((caddr_t) lsupdate + sizeof (struct ospf6_lsupdate)); - num = 0; + lsa_cnt = 0; /* lsupdate_list lists those LSA which doesn't need to be retransmitted. remove those from the list */ @@ -2005,58 +2008,85 @@ ospf6_lsupdate_send_neighbor (struct thread *thread) { /* MTU check */ if ( (p - sendbuf + (unsigned int)OSPF6_LSA_SIZE (lsa->header)) - > ospf6_packet_max(on->ospf6_if)) - { - ospf6_lsa_unlock (lsa); - break; - } + > ospf6_packet_max(on->ospf6_if)) + { + ospf6_lsa_unlock (lsa); + break; + } ospf6_lsa_age_update_to_send (lsa, on->ospf6_if->transdelay); memcpy (p, lsa->header, OSPF6_LSA_SIZE (lsa->header)); p += OSPF6_LSA_SIZE (lsa->header); - num++; + lsa_cnt++; assert (lsa->lock == 2); ospf6_lsdb_remove (lsa, on->lsupdate_list); } + if (lsa_cnt) + { + oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE; + oh->length = htons (p - sendbuf); + lsupdate->lsa_number = htonl (lsa_cnt); + + if ((on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT) || + (on->ospf6_if->state == OSPF6_INTERFACE_DR) || + (on->ospf6_if->state == OSPF6_INTERFACE_BDR)) + ospf6_send (on->ospf6_if->linklocal_addr, &allspfrouters6, + on->ospf6_if, oh); + else + ospf6_send (on->ospf6_if->linklocal_addr, &on->linklocal_addr, + on->ospf6_if, oh); + } + + /* The addresses used for retransmissions are different from those sent the + first time and so we need to separate them here. + */ + memset (sendbuf, 0, iobuflen); + oh = (struct ospf6_header *) sendbuf; + lsupdate = (struct ospf6_lsupdate *) + ((caddr_t) oh + sizeof (struct ospf6_header)); + p = (u_char *)((caddr_t) lsupdate + sizeof (struct ospf6_lsupdate)); + lsa_cnt = 0; + for (lsa = ospf6_lsdb_head (on->retrans_list); lsa; lsa = ospf6_lsdb_next (lsa)) { /* MTU check */ if ( (p - sendbuf + (unsigned int)OSPF6_LSA_SIZE (lsa->header)) - > ospf6_packet_max(on->ospf6_if)) - { - ospf6_lsa_unlock (lsa); - break; - } + > ospf6_packet_max(on->ospf6_if)) + { + ospf6_lsa_unlock (lsa); + break; + } ospf6_lsa_age_update_to_send (lsa, on->ospf6_if->transdelay); memcpy (p, lsa->header, OSPF6_LSA_SIZE (lsa->header)); p += OSPF6_LSA_SIZE (lsa->header); - num++; + lsa_cnt++; } - lsupdate->lsa_number = htonl (num); - - oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE; - oh->length = htons (p - sendbuf); - - ospf6_send (on->ospf6_if->linklocal_addr, &on->linklocal_addr, - on->ospf6_if, oh); - - if (on->lsupdate_list->count != 0 || - on->retrans_list->count != 0) + if (lsa_cnt) { - if (on->lsupdate_list->count != 0) - on->thread_send_lsupdate = - thread_add_event (master, ospf6_lsupdate_send_neighbor, on, 0); + oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE; + oh->length = htons (p - sendbuf); + lsupdate->lsa_number = htonl (lsa_cnt); + + if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT) + ospf6_send (on->ospf6_if->linklocal_addr, &allspfrouters6, + on->ospf6_if, oh); else - on->thread_send_lsupdate = - thread_add_timer (master, ospf6_lsupdate_send_neighbor, on, - on->ospf6_if->rxmt_interval); + ospf6_send (on->ospf6_if->linklocal_addr, &on->linklocal_addr, + on->ospf6_if, oh); } + if (on->lsupdate_list->count != 0) + on->thread_send_lsupdate = + thread_add_event (master, ospf6_lsupdate_send_neighbor, on, 0); + else if (on->retrans_list->count != 0) + on->thread_send_lsupdate = + thread_add_timer (master, ospf6_lsupdate_send_neighbor, on, + on->ospf6_if->rxmt_interval); return 0; } @@ -2067,7 +2097,7 @@ ospf6_lsupdate_send_interface (struct thread *thread) struct ospf6_header *oh; struct ospf6_lsupdate *lsupdate; u_char *p; - int num; + int lsa_cnt; struct ospf6_lsa *lsa; oi = (struct ospf6_interface *) THREAD_ARG (thread); @@ -2088,41 +2118,46 @@ ospf6_lsupdate_send_interface (struct thread *thread) memset (sendbuf, 0, iobuflen); oh = (struct ospf6_header *) sendbuf; lsupdate = (struct ospf6_lsupdate *)((caddr_t) oh + - sizeof (struct ospf6_header)); + sizeof (struct ospf6_header)); p = (u_char *)((caddr_t) lsupdate + sizeof (struct ospf6_lsupdate)); - num = 0; + lsa_cnt = 0; for (lsa = ospf6_lsdb_head (oi->lsupdate_list); lsa; lsa = ospf6_lsdb_next (lsa)) { /* MTU check */ if ( (p - sendbuf + ((unsigned int)OSPF6_LSA_SIZE (lsa->header))) - > ospf6_packet_max(oi)) - { - ospf6_lsa_unlock (lsa); - break; - } + > ospf6_packet_max(oi)) + { + ospf6_lsa_unlock (lsa); + break; + } ospf6_lsa_age_update_to_send (lsa, oi->transdelay); memcpy (p, lsa->header, OSPF6_LSA_SIZE (lsa->header)); p += OSPF6_LSA_SIZE (lsa->header); - num++; + lsa_cnt++; assert (lsa->lock == 2); ospf6_lsdb_remove (lsa, oi->lsupdate_list); } - lsupdate->lsa_number = htonl (num); + if (lsa_cnt) + { + lsupdate->lsa_number = htonl (lsa_cnt); - oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE; - oh->length = htons (p - sendbuf); + oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE; + oh->length = htons (p - sendbuf); - if (oi->state == OSPF6_INTERFACE_DR || - oi->state == OSPF6_INTERFACE_BDR) - ospf6_send (oi->linklocal_addr, &allspfrouters6, oi, oh); - else - ospf6_send (oi->linklocal_addr, &alldrouters6, oi, oh); + if ((oi->state == OSPF6_INTERFACE_POINTTOPOINT) || + (oi->state == OSPF6_INTERFACE_DR) || + (oi->state == OSPF6_INTERFACE_BDR)) + ospf6_send (oi->linklocal_addr, &allspfrouters6, oi, oh); + else + ospf6_send (oi->linklocal_addr, &alldrouters6, oi, oh); + + } if (oi->lsupdate_list->count > 0) { @@ -2140,6 +2175,7 @@ ospf6_lsack_send_neighbor (struct thread *thread) struct ospf6_header *oh; u_char *p; struct ospf6_lsa *lsa; + int lsa_cnt = 0; on = (struct ospf6_neighbor *) THREAD_ARG (thread); on->thread_send_lsack = (struct thread *) NULL; @@ -2166,16 +2202,16 @@ ospf6_lsack_send_neighbor (struct thread *thread) { /* MTU check */ if (p - sendbuf + sizeof (struct ospf6_lsa_header) > ospf6_packet_max(on->ospf6_if)) - { - /* if we run out of packet size/space here, - better to try again soon. */ - THREAD_OFF (on->thread_send_lsack); - on->thread_send_lsack = - thread_add_event (master, ospf6_lsack_send_neighbor, on, 0); + { + /* if we run out of packet size/space here, + better to try again soon. */ + THREAD_OFF (on->thread_send_lsack); + on->thread_send_lsack = + thread_add_event (master, ospf6_lsack_send_neighbor, on, 0); - ospf6_lsa_unlock (lsa); - break; - } + ospf6_lsa_unlock (lsa); + break; + } ospf6_lsa_age_update_to_send (lsa, on->ospf6_if->transdelay); memcpy (p, lsa->header, sizeof (struct ospf6_lsa_header)); @@ -2183,13 +2219,24 @@ ospf6_lsack_send_neighbor (struct thread *thread) assert (lsa->lock == 2); ospf6_lsdb_remove (lsa, on->lsack_list); + lsa_cnt++; } - oh->type = OSPF6_MESSAGE_TYPE_LSACK; - oh->length = htons (p - sendbuf); + if (lsa_cnt) + { + oh->type = OSPF6_MESSAGE_TYPE_LSACK; + oh->length = htons (p - sendbuf); + + ospf6_send (on->ospf6_if->linklocal_addr, &on->linklocal_addr, + on->ospf6_if, oh); + } + + if (on->thread_send_lsack == NULL && on->lsack_list->count > 0) + { + on->thread_send_lsack = + thread_add_event (master, ospf6_lsack_send_neighbor, on, 0); + } - ospf6_send (on->ospf6_if->linklocal_addr, &on->linklocal_addr, - on->ospf6_if, oh); return 0; } @@ -2200,6 +2247,7 @@ ospf6_lsack_send_interface (struct thread *thread) struct ospf6_header *oh; u_char *p; struct ospf6_lsa *lsa; + int lsa_cnt = 0; oi = (struct ospf6_interface *) THREAD_ARG (thread); oi->thread_send_lsack = (struct thread *) NULL; @@ -2226,16 +2274,16 @@ ospf6_lsack_send_interface (struct thread *thread) { /* MTU check */ if (p - sendbuf + sizeof (struct ospf6_lsa_header) > ospf6_packet_max(oi)) - { - /* if we run out of packet size/space here, - better to try again soon. */ - THREAD_OFF (oi->thread_send_lsack); - oi->thread_send_lsack = - thread_add_event (master, ospf6_lsack_send_interface, oi, 0); + { + /* if we run out of packet size/space here, + better to try again soon. */ + THREAD_OFF (oi->thread_send_lsack); + oi->thread_send_lsack = + thread_add_event (master, ospf6_lsack_send_interface, oi, 0); - ospf6_lsa_unlock (lsa); - break; - } + ospf6_lsa_unlock (lsa); + break; + } ospf6_lsa_age_update_to_send (lsa, oi->transdelay); memcpy (p, lsa->header, sizeof (struct ospf6_lsa_header)); @@ -2243,16 +2291,21 @@ ospf6_lsack_send_interface (struct thread *thread) assert (lsa->lock == 2); ospf6_lsdb_remove (lsa, oi->lsack_list); + lsa_cnt++; } - oh->type = OSPF6_MESSAGE_TYPE_LSACK; - oh->length = htons (p - sendbuf); + if (lsa_cnt) + { + oh->type = OSPF6_MESSAGE_TYPE_LSACK; + oh->length = htons (p - sendbuf); - if (oi->state == OSPF6_INTERFACE_DR || - oi->state == OSPF6_INTERFACE_BDR) - ospf6_send (oi->linklocal_addr, &allspfrouters6, oi, oh); - else - ospf6_send (oi->linklocal_addr, &alldrouters6, oi, oh); + if ((oi->state == OSPF6_INTERFACE_POINTTOPOINT) || + (oi->state == OSPF6_INTERFACE_DR) || + (oi->state == OSPF6_INTERFACE_BDR)) + ospf6_send (oi->linklocal_addr, &allspfrouters6, oi, oh); + else + ospf6_send (oi->linklocal_addr, &alldrouters6, oi, oh); + } if (oi->thread_send_lsack == NULL && oi->lsack_list->count > 0) { diff --git a/ospfd/ospf_interface.h b/ospfd/ospf_interface.h index 9de65507..ed698c87 100644 --- a/ospfd/ospf_interface.h +++ b/ospfd/ospf_interface.h @@ -140,14 +140,6 @@ struct ospf_interface /* OSPF Network Type. */ u_char type; -#define OSPF_IFTYPE_NONE 0 -#define OSPF_IFTYPE_POINTOPOINT 1 -#define OSPF_IFTYPE_BROADCAST 2 -#define OSPF_IFTYPE_NBMA 3 -#define OSPF_IFTYPE_POINTOMULTIPOINT 4 -#define OSPF_IFTYPE_VIRTUALLINK 5 -#define OSPF_IFTYPE_LOOPBACK 6 -#define OSPF_IFTYPE_MAX 7 /* State of Interface State Machine. */ u_char state; From eb82e9ee81792f86548d4937486b5dcbbd411a98 Mon Sep 17 00:00:00 2001 From: Dinesh Dutt Date: Sat, 24 Aug 2013 07:55:07 +0000 Subject: [PATCH 141/482] ospf6d: fix neighbor state machine (faster lsdb sync, RFC compliance) The OSPFv3 code doesn't do the following things right as part of an adjacency bringup: - Transmit DbDesc frames appropriately to ensure faster state transition to Loading state - Transmit LsReq frames when switching to exchange state and on receipt of an LS update in Loading state - Requesting LSAs multiple times in LsReq. It currently uses retransmit timer expiry to send the LsReq and DbDesc frames which significantly slows down large lsdb syncs. Signed-off-by: Dinesh G Dutt Reviewed-by: Scott Feldman Signed-off-by: David Lamparter --- ospf6d/ospf6_flood.c | 34 +++++++++++++++++-------- ospf6d/ospf6_message.c | 56 +++++++++++++++++++++-------------------- ospf6d/ospf6_neighbor.c | 39 +++++++++++++++++++++++----- ospf6d/ospf6_neighbor.h | 3 +++ 4 files changed, 88 insertions(+), 44 deletions(-) diff --git a/ospf6d/ospf6_flood.c b/ospf6d/ospf6_flood.c index beae6992..e02a432f 100644 --- a/ospf6d/ospf6_flood.c +++ b/ospf6d/ospf6_flood.c @@ -206,8 +206,8 @@ ospf6_decrement_retrans_count (struct ospf6_lsa *lsa) void ospf6_install_lsa (struct ospf6_lsa *lsa) { - struct ospf6_lsa *old; struct timeval now; + struct ospf6_lsa *old; if (IS_OSPF6_DEBUG_LSA_TYPE (lsa->header->type) || IS_OSPF6_DEBUG_EXAMIN_TYPE (lsa->header->type)) @@ -290,7 +290,7 @@ ospf6_flood_interface (struct ospf6_neighbor *from, if (ospf6_lsa_compare (lsa, req) > 0) { if (is_debug) - zlog_debug ("Requesting is newer, next neighbor"); + zlog_debug ("Requesting is older, next neighbor"); continue; } @@ -298,18 +298,30 @@ ospf6_flood_interface (struct ospf6_neighbor *from, examin next neighbor */ if (ospf6_lsa_compare (lsa, req) == 0) { - if (is_debug) - zlog_debug ("Requesting the same, remove it, next neighbor"); + if (is_debug) + zlog_debug ("Requesting the same, remove it, next neighbor"); + if (req == on->last_ls_req) + { + ospf6_lsa_unlock (req); + on->last_ls_req = NULL; + } ospf6_lsdb_remove (req, on->request_list); + ospf6_check_nbr_loading (on); continue; } /* If the new LSA is more recent, delete from request-list */ if (ospf6_lsa_compare (lsa, req) < 0) { - if (is_debug) - zlog_debug ("Received is newer, remove requesting"); + if (is_debug) + zlog_debug ("Received is newer, remove requesting"); + if (req == on->last_ls_req) + { + ospf6_lsa_unlock (req); + on->last_ls_req = NULL; + } ospf6_lsdb_remove (req, on->request_list); + ospf6_check_nbr_loading (on); /* fall through */ } } @@ -796,7 +808,7 @@ ospf6_receive_lsa (struct ospf6_neighbor *from, { /* log */ if (is_debug) - zlog_debug ("Drop MaxAge LSA with direct acknowledgement."); + zlog_debug ("Drop MaxAge LSA with direct acknowledgement."); /* a) Acknowledge back to neighbor (Direct acknowledgement, 13.5) */ ospf6_lsdb_add (ospf6_lsa_copy (new), from->lsack_list); @@ -950,8 +962,8 @@ ospf6_receive_lsa (struct ospf6_neighbor *from, zlog_debug ("The LSA is in Seqnumber Wrapping"); zlog_debug ("MaxAge & MaxSeqNum, discard"); } - ospf6_lsa_delete (new); - return; + ospf6_lsa_delete (new); + return; } /* Otherwise, Send database copy of this LSA to this neighbor */ @@ -968,8 +980,8 @@ ospf6_receive_lsa (struct ospf6_neighbor *from, if (from->thread_send_lsupdate == NULL) from->thread_send_lsupdate = thread_add_event (master, ospf6_lsupdate_send_neighbor, from, 0); - ospf6_lsa_delete (new); - return; + ospf6_lsa_delete (new); + return; } return; } diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c index 31db9a4b..dcbb36bf 100644 --- a/ospf6d/ospf6_message.c +++ b/ospf6d/ospf6_message.c @@ -517,20 +517,20 @@ ospf6_dbdesc_recv_master (struct ospf6_header *oh, { if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) zlog_debug ("Add request (No database copy)"); - ospf6_lsdb_add (his, on->request_list); + ospf6_lsdb_add (ospf6_lsa_copy(his), on->request_list); } else if (ospf6_lsa_compare (his, mine) < 0) { if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) zlog_debug ("Add request (Received MoreRecent)"); - ospf6_lsdb_add (his, on->request_list); + ospf6_lsdb_add (ospf6_lsa_copy(his), on->request_list); } else { if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) zlog_debug ("Discard (Existing MoreRecent)"); - ospf6_lsa_delete (his); } + ospf6_lsa_delete (his); } assert (p == OSPF6_MESSAGE_END (oh)); @@ -539,7 +539,7 @@ ospf6_dbdesc_recv_master (struct ospf6_header *oh, on->dbdesc_seqnum ++; /* schedule send lsreq */ - if (on->thread_send_lsreq == NULL) + if (on->request_list->count && (on->thread_send_lsreq == NULL)) on->thread_send_lsreq = thread_add_event (master, ospf6_lsreq_send, on, 0); @@ -735,10 +735,9 @@ ospf6_dbdesc_recv_slave (struct ospf6_header *oh, { if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) zlog_debug ("Add request-list: %s", his->name); - ospf6_lsdb_add (his, on->request_list); + ospf6_lsdb_add (ospf6_lsa_copy(his), on->request_list); } - else - ospf6_lsa_delete (his); + ospf6_lsa_delete (his); } assert (p == OSPF6_MESSAGE_END (oh)); @@ -747,7 +746,8 @@ ospf6_dbdesc_recv_slave (struct ospf6_header *oh, on->dbdesc_seqnum = ntohl (dbdesc->seqnum); /* schedule send lsreq */ - if (on->thread_send_lsreq == NULL) + if ((on->thread_send_lsreq == NULL) && + (on->request_list->count)) on->thread_send_lsreq = thread_add_event (master, ospf6_lsreq_send, on, 0); @@ -1351,19 +1351,6 @@ ospf6_lsupdate_recv (struct in6_addr *src, struct in6_addr *dst, assert (p == OSPF6_MESSAGE_END (oh)); - /* RFC2328 Section 10.9: When the neighbor responds to these requests - with the proper Link State Update packet(s), the Link state request - list is truncated and a new Link State Request packet is sent. */ - /* send new Link State Request packet if this LS Update packet - can be recognized as a response to our previous LS Request */ - if (! IN6_IS_ADDR_MULTICAST (dst) && - (on->state == OSPF6_NEIGHBOR_EXCHANGE || - on->state == OSPF6_NEIGHBOR_LOADING)) - { - THREAD_OFF (on->thread_send_lsreq); - on->thread_send_lsreq = - thread_add_event (master, ospf6_lsreq_send, on, 0); - } } static void @@ -1907,7 +1894,7 @@ ospf6_lsreq_send (struct thread *thread) struct ospf6_header *oh; struct ospf6_lsreq_entry *e; u_char *p; - struct ospf6_lsa *lsa; + struct ospf6_lsa *lsa, *last_req; on = (struct ospf6_neighbor *) THREAD_ARG (thread); on->thread_send_lsreq = (struct thread *) NULL; @@ -1929,13 +1916,9 @@ ospf6_lsreq_send (struct thread *thread) return 0; } - /* set next thread */ - on->thread_send_lsreq = - thread_add_timer (master, ospf6_lsreq_send, on, - on->ospf6_if->rxmt_interval); - memset (sendbuf, 0, iobuflen); oh = (struct ospf6_header *) sendbuf; + last_req = NULL; /* set Request entries in lsreq */ p = (u_char *)((caddr_t) oh + sizeof (struct ospf6_header)); @@ -1954,6 +1937,17 @@ ospf6_lsreq_send (struct thread *thread) e->id = lsa->header->id; e->adv_router = lsa->header->adv_router; p += sizeof (struct ospf6_lsreq_entry); + last_req = lsa; + } + + if (last_req != NULL) + { + if (on->last_ls_req != NULL) + { + ospf6_lsa_unlock (on->last_ls_req); + } + ospf6_lsa_lock (last_req); + on->last_ls_req = last_req; } oh->type = OSPF6_MESSAGE_TYPE_LSREQ; @@ -1966,6 +1960,14 @@ ospf6_lsreq_send (struct thread *thread) ospf6_send (on->ospf6_if->linklocal_addr, &on->linklocal_addr, on->ospf6_if, oh); + /* set next thread */ + if (on->request_list->count != 0) + { + on->thread_send_lsreq = + thread_add_timer (master, ospf6_lsreq_send, on, + on->ospf6_if->rxmt_interval); + } + return 0; } diff --git a/ospf6d/ospf6_neighbor.c b/ospf6d/ospf6_neighbor.c index 806767dd..84f0b002 100644 --- a/ospf6d/ospf6_neighbor.c +++ b/ospf6d/ospf6_neighbor.c @@ -98,7 +98,6 @@ ospf6_neighbor_create (u_int32_t router_id, struct ospf6_interface *oi) on->retrans_list = ospf6_lsdb_create (on); on->dbdesc_list = ospf6_lsdb_create (on); - on->lsreq_list = ospf6_lsdb_create (on); on->lsupdate_list = ospf6_lsdb_create (on); on->lsack_list = ospf6_lsdb_create (on); @@ -121,7 +120,6 @@ ospf6_neighbor_delete (struct ospf6_neighbor *on) } ospf6_lsdb_remove_all (on->dbdesc_list); - ospf6_lsdb_remove_all (on->lsreq_list); ospf6_lsdb_remove_all (on->lsupdate_list); ospf6_lsdb_remove_all (on->lsack_list); @@ -130,7 +128,6 @@ ospf6_neighbor_delete (struct ospf6_neighbor *on) ospf6_lsdb_delete (on->retrans_list); ospf6_lsdb_delete (on->dbdesc_list); - ospf6_lsdb_delete (on->lsreq_list); ospf6_lsdb_delete (on->lsupdate_list); ospf6_lsdb_delete (on->lsack_list); @@ -360,11 +357,41 @@ exchange_done (struct thread *thread) if (on->request_list->count == 0) ospf6_neighbor_state_change (OSPF6_NEIGHBOR_FULL, on); else - ospf6_neighbor_state_change (OSPF6_NEIGHBOR_LOADING, on); + { + ospf6_neighbor_state_change (OSPF6_NEIGHBOR_LOADING, on); + + if (on->thread_send_lsreq == NULL) + on->thread_send_lsreq = + thread_add_event (master, ospf6_lsreq_send, on, 0); + } return 0; } +/* Check loading state. */ +void +ospf6_check_nbr_loading (struct ospf6_neighbor *on) +{ + + /* RFC2328 Section 10.9: When the neighbor responds to these requests + with the proper Link State Update packet(s), the Link state request + list is truncated and a new Link State Request packet is sent. + */ + if ((on->state == OSPF6_NEIGHBOR_LOADING) || + (on->state == OSPF6_NEIGHBOR_EXCHANGE)) + { + if (on->request_list->count == 0) + thread_add_event (master, loading_done, on, 0); + else if (on->last_ls_req == NULL) + { + if (on->thread_send_lsreq != NULL) + THREAD_OFF (on->thread_send_lsreq); + on->thread_send_lsreq = + thread_add_event (master, ospf6_lsreq_send, on, 0); + } + } +} + int loading_done (struct thread *thread) { @@ -727,10 +754,10 @@ ospf6_neighbor_show_detail (struct vty *vty, struct ospf6_neighbor *on) timersub (&on->thread_send_lsreq->u.sands, &now, &res); timerstring (&res, duration, sizeof (duration)); vty_out (vty, " %d Pending LSAs for LSReq in Time %s [thread %s]%s", - on->lsreq_list->count, duration, + on->request_list->count, duration, (on->thread_send_lsreq ? "on" : "off"), VNL); - for (lsa = ospf6_lsdb_head (on->lsreq_list); lsa; + for (lsa = ospf6_lsdb_head (on->request_list); lsa; lsa = ospf6_lsdb_next (lsa)) vty_out (vty, " %s%s", lsa->name, VNL); diff --git a/ospf6d/ospf6_neighbor.h b/ospf6d/ospf6_neighbor.h index 5f46c6f3..750e1b24 100644 --- a/ospf6d/ospf6_neighbor.h +++ b/ospf6d/ospf6_neighbor.h @@ -86,6 +86,8 @@ struct ospf6_neighbor struct ospf6_lsdb *lsupdate_list; struct ospf6_lsdb *lsack_list; + struct ospf6_lsa *last_ls_req; + /* Inactivity timer */ struct thread *inactivity_timer; @@ -130,6 +132,7 @@ extern int seqnumber_mismatch (struct thread *); extern int bad_lsreq (struct thread *); extern int oneway_received (struct thread *); extern int inactivity_timer (struct thread *); +extern void ospf6_check_nbr_loading (struct ospf6_neighbor *); extern void ospf6_neighbor_init (void); extern int config_write_ospf6_debug_neighbor (struct vty *vty); From a765eb9383c53c68523a67b36cea92eadf6f3439 Mon Sep 17 00:00:00 2001 From: Dinesh Dutt Date: Sat, 24 Aug 2013 07:55:14 +0000 Subject: [PATCH 142/482] ospf6d: convert LSDB to use route_node, improve performance the performance in the presence of a large number of LSAs. I also verified that the performance improvements stayed in the presence of a large number of peers (I tested upto 128). Signed-off-by: Dinesh G Dutt Reviewed-by: Scott Feldman Summary: Reviewed-by: James Li Signed-off-by: David Lamparter --- ospf6d/ospf6_lsa.c | 3 +- ospf6d/ospf6_lsa.h | 3 +- ospf6d/ospf6_lsdb.c | 165 +++++++++++++++++------------------------ ospf6d/ospf6_lsdb.h | 2 + ospf6d/ospf6_message.c | 16 ++-- 5 files changed, 78 insertions(+), 111 deletions(-) diff --git a/ospf6d/ospf6_lsa.c b/ospf6d/ospf6_lsa.c index db14731f..592aad9b 100644 --- a/ospf6d/ospf6_lsa.c +++ b/ospf6d/ospf6_lsa.c @@ -450,8 +450,6 @@ ospf6_lsa_show_internal (struct vty *vty, struct ospf6_lsa *lsa) vty_out (vty, "CheckSum: %#06hx Length: %hu%s", ntohs (lsa->header->checksum), ntohs (lsa->header->length), VNL); - vty_out (vty, " Prev: %p This: %p Next: %p%s", - lsa->prev, lsa, lsa->next, VNL); vty_out (vty, "Flag: %x %s", lsa->flag, VNL); vty_out (vty, "Lock: %d %s", lsa->lock, VNL); vty_out (vty, "ReTx Count: %d%s", lsa->retrans_count, VNL); @@ -586,6 +584,7 @@ ospf6_lsa_copy (struct ospf6_lsa *lsa) copy->received = lsa->received; copy->installed = lsa->installed; copy->lsdb = lsa->lsdb; + copy->rn = NULL; return copy; } diff --git a/ospf6d/ospf6_lsa.h b/ospf6d/ospf6_lsa.h index f10ee671..998599ba 100644 --- a/ospf6d/ospf6_lsa.h +++ b/ospf6d/ospf6_lsa.h @@ -114,8 +114,7 @@ struct ospf6_lsa { char name[64]; /* dump string */ - struct ospf6_lsa *prev; - struct ospf6_lsa *next; + struct route_node *rn; unsigned char lock; /* reference counter */ unsigned char flag; /* special meaning (e.g. floodback) */ diff --git a/ospf6d/ospf6_lsdb.c b/ospf6d/ospf6_lsdb.c index 0edc7a34..657a5799 100644 --- a/ospf6d/ospf6_lsdb.c +++ b/ospf6d/ospf6_lsdb.c @@ -54,9 +54,12 @@ ospf6_lsdb_create (void *data) void ospf6_lsdb_delete (struct ospf6_lsdb *lsdb) { - ospf6_lsdb_remove_all (lsdb); - route_table_finish (lsdb->table); - XFREE (MTYPE_OSPF6_LSDB, lsdb); + if (lsdb != NULL) + { + ospf6_lsdb_remove_all (lsdb); + route_table_finish (lsdb->table); + XFREE (MTYPE_OSPF6_LSDB, lsdb); + } } static void @@ -102,8 +105,8 @@ void ospf6_lsdb_add (struct ospf6_lsa *lsa, struct ospf6_lsdb *lsdb) { struct prefix_ipv6 key; - struct route_node *current, *nextnode, *prevnode; - struct ospf6_lsa *next, *prev, *old = NULL; + struct route_node *current; + struct ospf6_lsa *old = NULL; memset (&key, 0, sizeof (key)); ospf6_lsdb_set_key (&key, &lsa->header->type, sizeof (lsa->header->type)); @@ -114,55 +117,25 @@ ospf6_lsdb_add (struct ospf6_lsa *lsa, struct ospf6_lsdb *lsdb) current = route_node_get (lsdb->table, (struct prefix *) &key); old = current->info; current->info = lsa; + lsa->rn = current; ospf6_lsa_lock (lsa); - if (old) - { - if (old->prev) - old->prev->next = lsa; - if (old->next) - old->next->prev = lsa; - lsa->next = old->next; - lsa->prev = old->prev; - } - else + if (!old) { - /* next link */ - nextnode = current; - route_lock_node (nextnode); - do { - nextnode = route_next (nextnode); - } while (nextnode && nextnode->info == NULL); - if (nextnode == NULL) - lsa->next = NULL; - else - { - next = nextnode->info; - lsa->next = next; - next->prev = lsa; - route_unlock_node (nextnode); - } + lsdb->count++; - /* prev link */ - prevnode = current; - route_lock_node (prevnode); - do { - prevnode = route_prev (prevnode); - } while (prevnode && prevnode->info == NULL); - if (prevnode == NULL) - lsa->prev = NULL; + if (OSPF6_LSA_IS_MAXAGE (lsa)) + { + if (lsdb->hook_remove) + (*lsdb->hook_remove) (lsa); + } else - { - prev = prevnode->info; - lsa->prev = prev; - prev->next = lsa; - route_unlock_node (prevnode); - } - - lsdb->count++; + { + if (lsdb->hook_add) + (*lsdb->hook_add) (lsa); + } } - - if (old) + else { if (OSPF6_LSA_IS_CHANGED (old, lsa)) { @@ -187,20 +160,8 @@ ospf6_lsdb_add (struct ospf6_lsa *lsa, struct ospf6_lsdb *lsdb) (*lsdb->hook_add) (lsa); } } + ospf6_lsa_unlock (old); } - else if (OSPF6_LSA_IS_MAXAGE (lsa)) - { - if (lsdb->hook_remove) - (*lsdb->hook_remove) (lsa); - } - else - { - if (lsdb->hook_add) - (*lsdb->hook_add) (lsa); - } - - if (old) - ospf6_lsa_unlock (old); ospf6_lsdb_count_assert (lsdb); } @@ -220,19 +181,15 @@ ospf6_lsdb_remove (struct ospf6_lsa *lsa, struct ospf6_lsdb *lsdb) node = route_node_lookup (lsdb->table, (struct prefix *) &key); assert (node && node->info == lsa); - if (lsa->prev) - lsa->prev->next = lsa->next; - if (lsa->next) - lsa->next->prev = lsa->prev; - node->info = NULL; lsdb->count--; if (lsdb->hook_remove) (*lsdb->hook_remove) (lsa); + route_unlock_node (node); /* to free the lookup lock */ + route_unlock_node (node); /* to free the original lock */ ospf6_lsa_unlock (lsa); - route_unlock_node (node); ospf6_lsdb_count_assert (lsdb); } @@ -255,6 +212,8 @@ ospf6_lsdb_lookup (u_int16_t type, u_int32_t id, u_int32_t adv_router, node = route_node_lookup (lsdb->table, (struct prefix *) &key); if (node == NULL || node->info == NULL) return NULL; + + route_unlock_node (node); return (struct ospf6_lsa *) node->info; } @@ -306,21 +265,9 @@ ospf6_lsdb_lookup_next (u_int16_t type, u_int32_t id, u_int32_t adv_router, if (prefix_same (&node->p, p)) { - struct route_node *prev = node; - struct ospf6_lsa *lsa_prev; - struct ospf6_lsa *lsa_next; - node = route_next (node); while (node && node->info == NULL) node = route_next (node); - - lsa_prev = prev->info; - lsa_next = (node ? node->info : NULL); - assert (lsa_prev); - assert (lsa_prev->next == lsa_next); - if (lsa_next) - assert (lsa_next->prev == lsa_prev); - zlog_debug ("lsdb_lookup_next: assert OK with previous LSA"); } if (! node) @@ -346,7 +293,6 @@ ospf6_lsdb_head (struct ospf6_lsdb *lsdb) if (node == NULL) return NULL; - route_unlock_node (node); if (node->info) ospf6_lsa_lock ((struct ospf6_lsa *) node->info); return (struct ospf6_lsa *) node->info; @@ -355,12 +301,20 @@ ospf6_lsdb_head (struct ospf6_lsdb *lsdb) struct ospf6_lsa * ospf6_lsdb_next (struct ospf6_lsa *lsa) { - struct ospf6_lsa *next = lsa->next; + struct route_node *node = lsa->rn; + struct ospf6_lsa *next = NULL; - ospf6_lsa_unlock (lsa); - if (next) - ospf6_lsa_lock (next); + do { + node = route_next (node); + } while (node && node->info == NULL); + + if ((node != NULL) && (node->info != NULL)) + { + next = node->info; + ospf6_lsa_lock (next); + } + ospf6_lsa_unlock (lsa); return next; } @@ -390,8 +344,6 @@ ospf6_lsdb_type_router_head (u_int16_t type, u_int32_t adv_router, if (node == NULL) return NULL; - else - route_unlock_node (node); if (! prefix_match ((struct prefix *) &key, &node->p)) return NULL; @@ -406,18 +358,19 @@ struct ospf6_lsa * ospf6_lsdb_type_router_next (u_int16_t type, u_int32_t adv_router, struct ospf6_lsa *lsa) { - struct ospf6_lsa *next = lsa->next; + struct ospf6_lsa *next = ospf6_lsdb_next(lsa); if (next) { if (next->header->type != type || next->header->adv_router != adv_router) - next = NULL; + { + route_unlock_node (next->rn); + ospf6_lsa_unlock (next); + next = NULL; + } } - if (next) - ospf6_lsa_lock (next); - ospf6_lsa_unlock (lsa); return next; } @@ -444,8 +397,6 @@ ospf6_lsdb_type_head (u_int16_t type, struct ospf6_lsdb *lsdb) if (node == NULL) return NULL; - else - route_unlock_node (node); if (! prefix_match ((struct prefix *) &key, &node->p)) return NULL; @@ -459,17 +410,18 @@ ospf6_lsdb_type_head (u_int16_t type, struct ospf6_lsdb *lsdb) struct ospf6_lsa * ospf6_lsdb_type_next (u_int16_t type, struct ospf6_lsa *lsa) { - struct ospf6_lsa *next = lsa->next; + struct ospf6_lsa *next = ospf6_lsdb_next (lsa); if (next) { if (next->header->type != type) - next = NULL; + { + route_unlock_node (next->rn); + ospf6_lsa_unlock (next); + next = NULL; + } } - if (next) - ospf6_lsa_lock (next); - ospf6_lsa_unlock (lsa); return next; } @@ -477,10 +429,25 @@ void ospf6_lsdb_remove_all (struct ospf6_lsdb *lsdb) { struct ospf6_lsa *lsa; + + if (lsdb == NULL) + return; + for (lsa = ospf6_lsdb_head (lsdb); lsa; lsa = ospf6_lsdb_next (lsa)) ospf6_lsdb_remove (lsa, lsdb); } +void +ospf6_lsdb_lsa_unlock (struct ospf6_lsa *lsa) +{ + if (lsa != NULL) + { + if (lsa->rn != NULL) + route_unlock_node (lsa->rn); + ospf6_lsa_unlock (lsa); + } +} + int ospf6_lsdb_maxage_remover (struct ospf6_lsdb *lsdb) { @@ -574,7 +541,7 @@ ospf6_new_ls_id (u_int16_t type, u_int32_t adv_router, continue; if (ntohl (lsa->header->id) > id) { - ospf6_lsa_unlock (lsa); + ospf6_lsdb_lsa_unlock (lsa); break; } id++; diff --git a/ospf6d/ospf6_lsdb.h b/ospf6d/ospf6_lsdb.h index 2974ffb1..a124adbf 100644 --- a/ospf6d/ospf6_lsdb.h +++ b/ospf6d/ospf6_lsdb.h @@ -64,6 +64,7 @@ extern struct ospf6_lsa *ospf6_lsdb_type_next (u_int16_t type, struct ospf6_lsa *lsa); extern void ospf6_lsdb_remove_all (struct ospf6_lsdb *lsdb); +extern void ospf6_lsdb_lsa_unlock (struct ospf6_lsa *lsa); #define OSPF6_LSDB_SHOW_LEVEL_NORMAL 0 #define OSPF6_LSDB_SHOW_LEVEL_DETAIL 1 @@ -79,5 +80,6 @@ extern u_int32_t ospf6_new_ls_id (u_int16_t type, u_int32_t adv_router, extern u_int32_t ospf6_new_ls_seqnum (u_int16_t type, u_int32_t id, u_int32_t adv_router, struct ospf6_lsdb *lsdb); +extern int ospf6_lsdb_maxage_remover (struct ospf6_lsdb *lsdb); #endif /* OSPF6_LSDB_H */ diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c index dcbb36bf..82d2d340 100644 --- a/ospf6d/ospf6_message.c +++ b/ospf6d/ospf6_message.c @@ -1825,7 +1825,7 @@ ospf6_dbdesc_send (struct thread *thread) if (p - sendbuf + sizeof (struct ospf6_lsa_header) > ospf6_packet_max(on->ospf6_if)) { - ospf6_lsa_unlock (lsa); + ospf6_lsdb_lsa_unlock (lsa); break; } memcpy (p, lsa->header, sizeof (struct ospf6_lsa_header)); @@ -1865,7 +1865,7 @@ ospf6_dbdesc_send_newone (struct thread *thread) { if (size + sizeof (struct ospf6_lsa_header) > ospf6_packet_max(on->ospf6_if)) { - ospf6_lsa_unlock (lsa); + ospf6_lsdb_lsa_unlock (lsa); break; } @@ -1928,7 +1928,7 @@ ospf6_lsreq_send (struct thread *thread) /* MTU check */ if (p - sendbuf + sizeof (struct ospf6_lsreq_entry) > ospf6_packet_max(on->ospf6_if)) { - ospf6_lsa_unlock (lsa); + ospf6_lsdb_lsa_unlock (lsa); break; } @@ -2012,7 +2012,7 @@ ospf6_lsupdate_send_neighbor (struct thread *thread) if ( (p - sendbuf + (unsigned int)OSPF6_LSA_SIZE (lsa->header)) > ospf6_packet_max(on->ospf6_if)) { - ospf6_lsa_unlock (lsa); + ospf6_lsdb_lsa_unlock (lsa); break; } @@ -2058,7 +2058,7 @@ ospf6_lsupdate_send_neighbor (struct thread *thread) if ( (p - sendbuf + (unsigned int)OSPF6_LSA_SIZE (lsa->header)) > ospf6_packet_max(on->ospf6_if)) { - ospf6_lsa_unlock (lsa); + ospf6_lsdb_lsa_unlock (lsa); break; } @@ -2132,7 +2132,7 @@ ospf6_lsupdate_send_interface (struct thread *thread) if ( (p - sendbuf + ((unsigned int)OSPF6_LSA_SIZE (lsa->header))) > ospf6_packet_max(oi)) { - ospf6_lsa_unlock (lsa); + ospf6_lsdb_lsa_unlock (lsa); break; } @@ -2211,7 +2211,7 @@ ospf6_lsack_send_neighbor (struct thread *thread) on->thread_send_lsack = thread_add_event (master, ospf6_lsack_send_neighbor, on, 0); - ospf6_lsa_unlock (lsa); + ospf6_lsdb_lsa_unlock (lsa); break; } @@ -2283,7 +2283,7 @@ ospf6_lsack_send_interface (struct thread *thread) oi->thread_send_lsack = thread_add_event (master, ospf6_lsack_send_interface, oi, 0); - ospf6_lsa_unlock (lsa); + ospf6_lsdb_lsa_unlock (lsa); break; } From e39d05384d4563f04edf339bbd67a117dd18e533 Mon Sep 17 00:00:00 2001 From: Dinesh Dutt Date: Sat, 24 Aug 2013 07:55:36 +0000 Subject: [PATCH 143/482] ospf6d: turn off expensive debugging OSPF6 has very expensive LSDB and route debug on by default. This needs to be turned off for scaled performance. Signed-off-by: James Li Reviewed-by: Dinesh G Dutt Summary: Signed-off-by: David Lamparter --- ospf6d/ospf6_lsdb.c | 6 +++--- ospf6d/ospf6_route.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ospf6d/ospf6_lsdb.c b/ospf6d/ospf6_lsdb.c index 657a5799..b13ae9b1 100644 --- a/ospf6d/ospf6_lsdb.c +++ b/ospf6d/ospf6_lsdb.c @@ -73,7 +73,7 @@ ospf6_lsdb_set_key (struct prefix_ipv6 *key, void *value, int len) key->prefixlen += len * 8; } -#ifndef NDEBUG +#ifdef DEBUG static void _lsdb_count_assert (struct ospf6_lsdb *lsdb) { @@ -97,9 +97,9 @@ _lsdb_count_assert (struct ospf6_lsdb *lsdb) assert (num == lsdb->count); } #define ospf6_lsdb_count_assert(t) (_lsdb_count_assert (t)) -#else /*NDEBUG*/ +#else /*DEBUG*/ #define ospf6_lsdb_count_assert(t) ((void) 0) -#endif /*NDEBUG*/ +#endif /*DEBUG*/ void ospf6_lsdb_add (struct ospf6_lsa *lsa, struct ospf6_lsdb *lsdb) diff --git a/ospf6d/ospf6_route.c b/ospf6d/ospf6_route.c index 398acfa8..5f1869ac 100644 --- a/ospf6d/ospf6_route.c +++ b/ospf6d/ospf6_route.c @@ -304,7 +304,7 @@ ospf6_route_lookup_bestmatch (struct prefix *prefix, return route; } -#ifndef NDEBUG +#ifdef DEBUG static void route_table_assert (struct ospf6_route_table *table) { @@ -350,7 +350,7 @@ route_table_assert (struct ospf6_route_table *table) #define ospf6_route_table_assert(t) (route_table_assert (t)) #else #define ospf6_route_table_assert(t) ((void) 0) -#endif /*NDEBUG*/ +#endif /*DEBUG*/ struct ospf6_route * ospf6_route_add (struct ospf6_route *route, From 17d003da963d6294e5ab95c690de285eccf0bac0 Mon Sep 17 00:00:00 2001 From: Dinesh Dutt Date: Sat, 24 Aug 2013 07:55:43 +0000 Subject: [PATCH 144/482] ospf6d: don't suppress empty router LSA Currently in OSPFv3 implementation, if all the interfaces are down/loopback or are without any full adjacencies, the router LSA is suppressed. So for a router with only stub networks, no router LSA is generated. However, intra-prefix LSAs are generated for the stub networks and these intra-prefix LSAs will reference the router LSA. So the router LSA really should not be suppressed. It needs to be generated to be the starting vertex for SPF w.r.t the stub networks. Signed-off-by: James Li Reviewed-by: Dinesh G Dutt Signed-off-by: David Lamparter --- ospf6d/ospf6_intra.c | 49 ++++++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c index 86f41460..4cb751f0 100644 --- a/ospf6d/ospf6_intra.c +++ b/ospf6d/ospf6_intra.c @@ -272,35 +272,26 @@ ospf6_router_lsa_originate (struct thread *thread) /* xxx */ } - if ((caddr_t) lsdesc != (caddr_t) router_lsa + - sizeof (struct ospf6_router_lsa)) - { - /* Fill LSA Header */ - lsa_header->age = 0; - lsa_header->type = htons (OSPF6_LSTYPE_ROUTER); - lsa_header->id = htonl (link_state_id); - lsa_header->adv_router = oa->ospf6->router_id; - lsa_header->seqnum = - ospf6_new_ls_seqnum (lsa_header->type, lsa_header->id, - lsa_header->adv_router, oa->lsdb); - lsa_header->length = htons ((caddr_t) lsdesc - (caddr_t) buffer); - - /* LSA checksum */ - ospf6_lsa_checksum (lsa_header); - - /* create LSA */ - lsa = ospf6_lsa_create (lsa_header); - - /* Originate */ - ospf6_lsa_originate_area (lsa, oa); - - link_state_id ++; - } - else - { - if (IS_OSPF6_DEBUG_ORIGINATE (ROUTER)) - zlog_debug ("Nothing to describe in Router-LSA, suppress"); - } + /* Fill LSA Header */ + lsa_header->age = 0; + lsa_header->type = htons (OSPF6_LSTYPE_ROUTER); + lsa_header->id = htonl (link_state_id); + lsa_header->adv_router = oa->ospf6->router_id; + lsa_header->seqnum = + ospf6_new_ls_seqnum (lsa_header->type, lsa_header->id, + lsa_header->adv_router, oa->lsdb); + lsa_header->length = htons ((caddr_t) lsdesc - (caddr_t) buffer); + + /* LSA checksum */ + ospf6_lsa_checksum (lsa_header); + + /* create LSA */ + lsa = ospf6_lsa_create (lsa_header); + + /* Originate */ + ospf6_lsa_originate_area (lsa, oa); + + link_state_id ++; /* Do premature-aging of rest, undesired Router-LSAs */ type = ntohs (OSPF6_LSTYPE_ROUTER); From b81e97a8a7f85ecc7489dc8a7c7b9d403d9c4bc6 Mon Sep 17 00:00:00 2001 From: Dinesh Dutt Date: Sat, 24 Aug 2013 07:55:50 +0000 Subject: [PATCH 145/482] ospf6d: correct nexthop through directly connected networks This is implementing this part of RFC 2328: This is the "first case", see below, 16.1.1. The next hop calculation ... If there is at least one intervening router in the current shortest path between the destination and the root, the destination simply inherits the set of next hops from the parent. Otherwise, there are two cases. In the first case, the parent vertex is the root (the calculating router itself). This means that the destination is either a directly connected network or directly connected router. The outgoing interface in this case is simply the OSPF interface connecting to the destination network/router. ... The current Quagga code always tries to inherit the nexthop from a parent vertex, but does not cover the case that the destination is directly connected to the root vertex. This patch adds support for that case. Signed-off-by: James Li Reviewed-by: Dinesh G Dutt Signed-off-by: David Lamparter --- lib/if.c | 24 ++++++++++++++++++++++++ lib/if.h | 1 + ospf6d/ospf6_intra.c | 23 ++++++++++++++++++++--- 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/lib/if.c b/lib/if.c index e26aa046..6348403b 100644 --- a/lib/if.c +++ b/lib/if.c @@ -304,6 +304,30 @@ if_lookup_address (struct in_addr src) return match; } +/* Lookup interface by prefix */ +struct interface * +if_lookup_prefix (struct prefix *prefix) +{ + struct listnode *node; + struct prefix addr; + int bestlen = 0; + struct listnode *cnode; + struct interface *ifp; + struct connected *c; + + for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) + { + for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, c)) + { + if (prefix_cmp(c->address, prefix) == 0) + { + return ifp; + } + } + } + return NULL; +} + /* Get interface by name if given name interface doesn't exist create one. */ struct interface * diff --git a/lib/if.h b/lib/if.h index 13cc254e..8081be87 100644 --- a/lib/if.h +++ b/lib/if.h @@ -245,6 +245,7 @@ extern struct interface *if_create (const char *name, int namelen); extern struct interface *if_lookup_by_index (unsigned int); extern struct interface *if_lookup_exact_address (struct in_addr); extern struct interface *if_lookup_address (struct in_addr); +extern struct interface *if_lookup_prefix (struct prefix *prefix); /* These 2 functions are to be used when the ifname argument is terminated by a '\0' character: */ diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c index 4cb751f0..c08a6ae6 100644 --- a/ospf6d/ospf6_intra.c +++ b/ospf6d/ospf6_intra.c @@ -1024,6 +1024,8 @@ ospf6_intra_prefix_lsa_add (struct ospf6_lsa *lsa) struct ospf6_prefix *op; char *start, *current, *end; char buf[64]; + struct interface *ifp; + int direct_connect = 0; if (OSPF6_LSA_IS_MAXAGE (lsa)) return; @@ -1060,6 +1062,12 @@ ospf6_intra_prefix_lsa_add (struct ospf6_lsa *lsa) return; } + if (intra_prefix_lsa->ref_adv_router == oa->ospf6->router_id) + { + /* the intra-prefix are directly connected */ + direct_connect = 1; + } + prefix_num = ntohs (intra_prefix_lsa->prefix_num); start = (caddr_t) intra_prefix_lsa + sizeof (struct ospf6_intra_prefix_lsa); @@ -1090,9 +1098,18 @@ ospf6_intra_prefix_lsa_add (struct ospf6_lsa *lsa) route->path.cost = ls_entry->path.cost + ntohs (op->prefix_metric); - for (i = 0; ospf6_nexthop_is_set (&ls_entry->nexthop[i]) && - i < OSPF6_MULTI_PATH_LIMIT; i++) - ospf6_nexthop_copy (&route->nexthop[i], &ls_entry->nexthop[i]); + if (direct_connect) + { + ifp = if_lookup_prefix(&route->prefix); + if (ifp) + route->nexthop[0].ifindex = ifp->ifindex; + } + else + { + for (i = 0; ospf6_nexthop_is_set (&ls_entry->nexthop[i]) && + i < OSPF6_MULTI_PATH_LIMIT; i++) + ospf6_nexthop_copy (&route->nexthop[i], &ls_entry->nexthop[i]); + } if (IS_OSPF6_DEBUG_EXAMIN (INTRA_PREFIX)) { From e7ad6b20d7a7c63ed7c640ab9f61c853e77508c5 Mon Sep 17 00:00:00 2001 From: Dinesh Dutt Date: Sat, 24 Aug 2013 07:55:57 +0000 Subject: [PATCH 146/482] ospf6d: fix linkdown handling Ensure OSPFv3 handles link down even correctly. OSPFv3 checks only the administrative status of a link instead of its operative status. This prevents it up from detecting a real link down event and reacting appropriately. Only protocol timer timeouts make it detect a link down eventually. This patch makes it look for the operative status of a link instead of admin status. Signed-off-by: Dinesh G Dutt Reviewed-by: James Li Signed-off-by: David Lamparter --- ospf6d/ospf6_interface.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index 94b599be..d1f5cba1 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -316,7 +316,7 @@ ospf6_interface_state_update (struct interface *ifp) if (oi->area == NULL) return; - if (if_is_up (ifp)) + if (if_is_operative (ifp)) thread_add_event (master, interface_up, oi, 0); else thread_add_event (master, interface_down, oi, 0); @@ -625,7 +625,7 @@ interface_up (struct thread *thread) oi->interface->name); /* check physical interface is up */ - if (! if_is_up (oi->interface)) + if (! if_is_operative (oi->interface)) { if (IS_OSPF6_DEBUG_INTERFACE) zlog_debug ("Interface %s is down, can't execute [InterfaceUp]", @@ -779,7 +779,7 @@ ospf6_interface_show (struct vty *vty, struct interface *ifp) type = "UNKNOWN"; vty_out (vty, "%s is %s, type %s%s", - ifp->name, updown[if_is_up (ifp)], type, + ifp->name, updown[if_is_operative (ifp)], type, VNL); vty_out (vty, " Interface ID: %d%s", ifp->ifindex, VNL); From f41b4a021659dd48d62b1a7aac4b28e3663dbdaa Mon Sep 17 00:00:00 2001 From: Dinesh Dutt Date: Sat, 24 Aug 2013 08:00:37 +0000 Subject: [PATCH 147/482] ospf6d: add overload support OSPFv3: Support setting/clearing overload bit on router It is sometimes necessary for a router to gracefully remove itself from the SPF tree i.e. it cannot act as a transit router. It does this by setting the overload bit in the router LSA. This patch adds support for enabling/disabling the overload bit. Signed-off-by: Dinesh G Dutt Reviewed-by: Pradosh Mohapatra [DL: patch applied with fuzz] Signed-off-by: David Lamparter --- ospf6d/ospf6_area.c | 13 ++++- ospf6d/ospf6_intra.c | 23 +++++++++ ospf6d/ospf6_intra.h | 8 +++ ospf6d/ospf6_spf.c | 5 ++ ospf6d/ospf6_top.c | 117 +++++++++++++++++++++++++++++++++++++++++++ ospf6d/ospf6_top.h | 1 + 6 files changed, 165 insertions(+), 2 deletions(-) diff --git a/ospf6d/ospf6_area.c b/ospf6d/ospf6_area.c index 1e07d857..37e5c004 100644 --- a/ospf6d/ospf6_area.c +++ b/ospf6d/ospf6_area.c @@ -164,9 +164,18 @@ ospf6_area_create (u_int32_t area_id, struct ospf6 *o) oa->summary_router->scope = oa; /* set default options */ - OSPF6_OPT_SET (oa->options, OSPF6_OPT_V6); + if (CHECK_FLAG (o->flag, OSPF6_STUB_ROUTER)) + { + OSPF6_OPT_CLEAR (oa->options, OSPF6_OPT_V6); + OSPF6_OPT_CLEAR (oa->options, OSPF6_OPT_R); + } + else + { + OSPF6_OPT_SET (oa->options, OSPF6_OPT_V6); + OSPF6_OPT_SET (oa->options, OSPF6_OPT_R); + } + OSPF6_OPT_SET (oa->options, OSPF6_OPT_E); - OSPF6_OPT_SET (oa->options, OSPF6_OPT_R); oa->ospf6 = o; listnode_add_sort (o->area_list, oa); diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c index c08a6ae6..025a7710 100644 --- a/ospf6d/ospf6_intra.c +++ b/ospf6d/ospf6_intra.c @@ -104,6 +104,29 @@ ospf6_router_lsa_show (struct vty *vty, struct ospf6_lsa *lsa) return 0; } +int +ospf6_router_is_stub_router (struct ospf6_lsa *lsa) +{ + struct ospf6_router_lsa *rtr_lsa; + + if (lsa != NULL && OSPF6_LSA_IS_TYPE (ROUTER, lsa)) + { + rtr_lsa = (struct ospf6_router_lsa *) + ((caddr_t) lsa->header + sizeof (struct ospf6_lsa_header)); + + if (!OSPF6_OPT_ISSET (rtr_lsa->options, OSPF6_OPT_R)) + { + return (OSPF6_IS_STUB_ROUTER); + } + else if (!OSPF6_OPT_ISSET (rtr_lsa->options, OSPF6_OPT_V6)) + { + return (OSPF6_IS_STUB_ROUTER_V6); + } + } + + return (OSPF6_NOT_STUB_ROUTER); +} + int ospf6_router_lsa_originate (struct thread *thread) { diff --git a/ospf6d/ospf6_intra.h b/ospf6d/ospf6_intra.h index 3810174e..a25efa12 100644 --- a/ospf6d/ospf6_intra.h +++ b/ospf6d/ospf6_intra.h @@ -94,6 +94,13 @@ struct ospf6_router_lsdesc #define OSPF6_ROUTER_LSDESC_STUB_NETWORK 3 #define OSPF6_ROUTER_LSDESC_VIRTUAL_LINK 4 +enum stub_router_mode + { + OSPF6_NOT_STUB_ROUTER, + OSPF6_IS_STUB_ROUTER, + OSPF6_IS_STUB_ROUTER_V6, + }; + #define ROUTER_LSDESC_IS_TYPE(t,x) \ ((((struct ospf6_router_lsdesc *)(x))->type == \ OSPF6_ROUTER_LSDESC_ ## t) ? 1 : 0) @@ -200,6 +207,7 @@ extern char *ospf6_router_lsdesc_lookup (u_char type, u_int32_t interface_id, extern char *ospf6_network_lsdesc_lookup (u_int32_t router_id, struct ospf6_lsa *lsa); +extern int ospf6_router_is_stub_router (struct ospf6_lsa *lsa); extern int ospf6_router_lsa_originate (struct thread *); extern int ospf6_network_lsa_originate (struct thread *); extern int ospf6_link_lsa_originate (struct thread *); diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c index e4c424db..845e206c 100644 --- a/ospf6d/ospf6_spf.c +++ b/ospf6d/ospf6_spf.c @@ -424,6 +424,11 @@ ospf6_spf_calculation (u_int32_t router_id, if (ospf6_spf_install (v, result_table) < 0) continue; + /* Skip overloaded routers */ + if ((OSPF6_LSA_IS_TYPE (ROUTER, v->lsa) && + ospf6_router_is_stub_router (v->lsa))) + continue; + /* For each LS description in the just-added vertex V's LSA */ size = (VERTEX_IS_TYPE (ROUTER, v) ? sizeof (struct ospf6_router_lsdesc) : diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c index e9fe7a4e..dec7096c 100644 --- a/ospf6d/ospf6_top.c +++ b/ospf6d/ospf6_top.c @@ -464,6 +464,101 @@ DEFUN (no_ospf6_interface_area, return CMD_SUCCESS; } +DEFUN (ospf6_stub_router_admin, + ospf6_stub_router_admin_cmd, + "stub-router administrative", + "Make router a stub router\n" + "Advertise inability to be a transit router\n" + "Administratively applied, for an indefinite period\n") +{ + struct listnode *node; + struct ospf6_area *oa; + + if (!CHECK_FLAG (ospf6->flag, OSPF6_STUB_ROUTER)) + { + for (ALL_LIST_ELEMENTS_RO (ospf6->area_list, node, oa)) + { + OSPF6_OPT_CLEAR (oa->options, OSPF6_OPT_V6); + OSPF6_OPT_CLEAR (oa->options, OSPF6_OPT_R); + OSPF6_ROUTER_LSA_SCHEDULE (oa); + } + SET_FLAG (ospf6->flag, OSPF6_STUB_ROUTER); + } + + return CMD_SUCCESS; +} + +DEFUN (no_ospf6_stub_router_admin, + no_ospf6_stub_router_admin_cmd, + "no stub-router administrative", + NO_STR + "Make router a stub router\n" + "Advertise ability to be a transit router\n" + "Administratively applied, for an indefinite period\n") +{ + struct listnode *node; + struct ospf6_area *oa; + + if (CHECK_FLAG (ospf6->flag, OSPF6_STUB_ROUTER)) + { + for (ALL_LIST_ELEMENTS_RO (ospf6->area_list, node, oa)) + { + OSPF6_OPT_SET (oa->options, OSPF6_OPT_V6); + OSPF6_OPT_SET (oa->options, OSPF6_OPT_R); + OSPF6_ROUTER_LSA_SCHEDULE (oa); + } + UNSET_FLAG (ospf6->flag, OSPF6_STUB_ROUTER); + } + + return CMD_SUCCESS; +} + +DEFUN (ospf6_stub_router_startup, + ospf6_stub_router_startup_cmd, + "stub-router on-startup <5-86400>", + "Make router a stub router\n" + "Advertise inability to be a transit router\n" + "Automatically advertise as stub-router on startup of OSPF6\n" + "Time (seconds) to advertise self as stub-router\n") +{ + return CMD_SUCCESS; +} + +DEFUN (no_ospf6_stub_router_startup, + no_ospf6_stub_router_startup_cmd, + "no stub-router on-startup", + NO_STR + "Make router a stub router\n" + "Advertise inability to be a transit router\n" + "Automatically advertise as stub-router on startup of OSPF6\n" + "Time (seconds) to advertise self as stub-router\n") +{ + return CMD_SUCCESS; +} + +DEFUN (ospf6_stub_router_shutdown, + ospf6_stub_router_shutdown_cmd, + "stub-router on-shutdown <5-86400>", + "Make router a stub router\n" + "Advertise inability to be a transit router\n" + "Automatically advertise as stub-router before shutdown\n" + "Time (seconds) to advertise self as stub-router\n") +{ + return CMD_SUCCESS; +} + +DEFUN (no_ospf6_stub_router_shutdown, + no_ospf6_stub_router_shutdown_cmd, + "no stub-router on-shutdown", + NO_STR + "Make router a stub router\n" + "Advertise inability to be a transit router\n" + "Automatically advertise as stub-router before shutdown\n" + "Time (seconds) to advertise self as stub-router\n") +{ + return CMD_SUCCESS; +} + static void ospf6_show (struct vty *vty, struct ospf6 *o) { @@ -486,6 +581,9 @@ ospf6_show (struct vty *vty, struct ospf6 *o) /* Redistribute configuration */ /* XXX */ + if (CHECK_FLAG (o->flag, OSPF6_STUB_ROUTER)) + vty_out (vty, " Router Is Stub Router%s", VNL); + /* LSAs */ vty_out (vty, " Number of AS scoped LSAs is %u%s", o->lsdb->count, VNL); @@ -654,6 +752,16 @@ DEFUN (show_ipv6_ospf6_route_type_detail, return CMD_SUCCESS; } +static void +ospf6_stub_router_config_write (struct vty *vty) +{ + if (CHECK_FLAG (ospf6->flag, OSPF6_STUB_ROUTER)) + { + vty_out (vty, " stub-router administrative%s", VNL); + } + return; +} + /* OSPF configuration write function. */ static int config_write_ospf6 (struct vty *vty) @@ -674,6 +782,7 @@ config_write_ospf6 (struct vty *vty) if (ospf6->router_id_static != 0) vty_out (vty, " router-id %s%s", router_id, VNL); + ospf6_stub_router_config_write (vty); ospf6_redistribute_config_write (vty); ospf6_area_config_write (vty); ospf6_spf_config_write (vty); @@ -729,6 +838,14 @@ ospf6_top_init (void) install_element (OSPF6_NODE, &ospf6_router_id_cmd); install_element (OSPF6_NODE, &ospf6_interface_area_cmd); install_element (OSPF6_NODE, &no_ospf6_interface_area_cmd); + install_element (OSPF6_NODE, &ospf6_stub_router_admin_cmd); + install_element (OSPF6_NODE, &no_ospf6_stub_router_admin_cmd); + /* For a later time + install_element (OSPF6_NODE, &ospf6_stub_router_startup_cmd); + install_element (OSPF6_NODE, &no_ospf6_stub_router_startup_cmd); + install_element (OSPF6_NODE, &ospf6_stub_router_shutdown_cmd); + install_element (OSPF6_NODE, &no_ospf6_stub_router_shutdown_cmd); + */ } diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h index 27eb18cd..02eb9154 100644 --- a/ospf6d/ospf6_top.h +++ b/ospf6d/ospf6_top.h @@ -76,6 +76,7 @@ struct ospf6 }; #define OSPF6_DISABLED 0x01 +#define OSPF6_STUB_ROUTER 0x02 /* global pointer for OSPF top data structure */ extern struct ospf6 *ospf6; From 3b220289a4d0da4539d965ca71e9479d68c87b11 Mon Sep 17 00:00:00 2001 From: Dinesh Dutt Date: Sat, 24 Aug 2013 08:00:44 +0000 Subject: [PATCH 148/482] ospf6d: handle seqnum wrapping Signed-off-by: Shrijeet Mukherjee Reviewed-by: Dinesh G Dutt [DL: mechanical adjust to rebase] [DL: adjust to removal of timerwheel code] Signed-off-by: David Lamparter --- ospf6d/ospf6_flood.c | 19 +++++++++++++++++++ ospf6d/ospf6_lsa.h | 3 +++ ospf6d/ospf6_lsdb.c | 13 ++++++++++++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/ospf6d/ospf6_flood.c b/ospf6d/ospf6_flood.c index e02a432f..7f6b2850 100644 --- a/ospf6d/ospf6_flood.c +++ b/ospf6d/ospf6_flood.c @@ -230,6 +230,25 @@ ospf6_install_lsa (struct ospf6_lsa *lsa) else lsa->expire = NULL; + if (OSPF6_LSA_IS_SEQWRAP(lsa) && + ! (CHECK_FLAG(lsa->flag,OSPF6_LSA_SEQWRAPPED) && + lsa->header->seqnum == htonl(OSPF_MAX_SEQUENCE_NUMBER))) + { + if (IS_OSPF6_DEBUG_EXAMIN_TYPE (lsa->header->type)) + zlog_debug("lsa install wrapping: sequence 0x%x", + ntohl(lsa->header->seqnum)); + SET_FLAG(lsa->flag, OSPF6_LSA_SEQWRAPPED); + /* in lieu of premature_aging, since we do not want to recreate this lsa + * and/or mess with timers etc, we just want to wrap the sequence number + * and reflood the lsa before continuing. + * NOTE: Flood needs to be called right after this function call, by the + * caller + */ + lsa->header->seqnum = htonl (OSPF_MAX_SEQUENCE_NUMBER); + lsa->header->age = htons (OSPF_LSA_MAXAGE); + ospf6_lsa_checksum (lsa->header); + } + /* actually install */ lsa->installed = now; ospf6_lsdb_add (lsa, lsa->lsdb); diff --git a/ospf6d/ospf6_lsa.h b/ospf6d/ospf6_lsa.h index 998599ba..ffd6ae0a 100644 --- a/ospf6d/ospf6_lsa.h +++ b/ospf6d/ospf6_lsa.h @@ -109,6 +109,8 @@ struct ospf6_lsa_header #define OSPF6_LSA_IS_DIFFER(L1, L2) ospf6_lsa_is_differ (L1, L2) #define OSPF6_LSA_IS_MAXAGE(L) (ospf6_lsa_age_current (L) == OSPF_LSA_MAXAGE) #define OSPF6_LSA_IS_CHANGED(L1, L2) ospf6_lsa_is_changed (L1, L2) +#define OSPF6_LSA_IS_SEQWRAP(L) ((L)->header->seqnum == htonl(OSPF_MAX_SEQUENCE_NUMBER + 1)) + struct ospf6_lsa { @@ -139,6 +141,7 @@ struct ospf6_lsa #define OSPF6_LSA_FLOODBACK 0x02 #define OSPF6_LSA_DUPLICATE 0x04 #define OSPF6_LSA_IMPLIEDACK 0x08 +#define OSPF6_LSA_SEQWRAPPED 0x20 struct ospf6_lsa_handler { diff --git a/ospf6d/ospf6_lsdb.c b/ospf6d/ospf6_lsdb.c index b13ae9b1..5138d1c1 100644 --- a/ospf6d/ospf6_lsdb.c +++ b/ospf6d/ospf6_lsdb.c @@ -465,7 +465,18 @@ ospf6_lsdb_maxage_remover (struct ospf6_lsdb *lsdb) } if (IS_OSPF6_DEBUG_LSA_TYPE (lsa->header->type)) zlog_debug ("Remove MaxAge %s", lsa->name); - ospf6_lsdb_remove (lsa, lsdb); + if (CHECK_FLAG(lsa->flag, OSPF6_LSA_SEQWRAPPED)) + { + UNSET_FLAG(lsa->flag, OSPF6_LSA_SEQWRAPPED); + /* + * lsa->header->age = 0; + */ + lsa->header->seqnum = htonl(OSPF_MAX_SEQUENCE_NUMBER + 1); + ospf6_lsa_checksum (lsa->header); + thread_execute (master, ospf6_lsa_refresh, lsa, 0); + } else { + ospf6_lsdb_remove (lsa, lsdb); + } } return (reschedule); From 7a10a359e9740710c1e39c8be0f761f506795480 Mon Sep 17 00:00:00 2001 From: Dinesh Dutt Date: Sun, 25 Aug 2013 03:03:07 +0000 Subject: [PATCH 149/482] ospf6d: don't change SeqNum on initial DbDesc message The code was setting the DbDesc seqnum to the current seconds value of time if this was the initial DbDesc. However, the same code was getting invoked if the initial DbDesc was retransmitted. Caused ANVL test XX.XX to fail. Signed-off-by: Dinesh G Dutt Signed-off-by: David Lamparter --- ospf6d/ospf6_message.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c index 82d2d340..caebf5d6 100644 --- a/ospf6d/ospf6_message.c +++ b/ospf6d/ospf6_message.c @@ -1797,7 +1797,8 @@ ospf6_dbdesc_send (struct thread *thread) sizeof (struct ospf6_header)); /* if this is initial one, initialize sequence number for DbDesc */ - if (CHECK_FLAG (on->dbdesc_bits, OSPF6_DBDESC_IBIT)) + if (CHECK_FLAG (on->dbdesc_bits, OSPF6_DBDESC_IBIT) && + (on->dbdesc_seqnum == 0)) { struct timeval tv; if (quagga_gettime (QUAGGA_CLK_MONOTONIC, &tv) < 0) From 931b1b8c9a612665391ed43653c970fcb38bbbf0 Mon Sep 17 00:00:00 2001 From: Dinesh Dutt Date: Sun, 25 Aug 2013 03:03:15 +0000 Subject: [PATCH 150/482] ospf6d: increment dbdesc seqnum on SeqNumberMismatch/BadLsReq event As per RFC 2328, section 10.3, if the neighbor state machine reaches SeqNumberMismatch state when the NSM is in state Exchange or greater, "router increments the DD sequence number in the neighbor data structure, declares itself master (sets the master/slave bit to master), and starts sending Database Description Packets, with the initialize (I), more (M) and master (MS) bits set.". The existing code doesn't increment the DD SeqNum. This patch fixes that. Signed-off-by: Dinesh G Dutt Signed-off-by: David Lamparter --- ospf6d/ospf6_neighbor.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ospf6d/ospf6_neighbor.c b/ospf6d/ospf6_neighbor.c index 84f0b002..b7d2e40d 100644 --- a/ospf6d/ospf6_neighbor.c +++ b/ospf6d/ospf6_neighbor.c @@ -484,6 +484,8 @@ seqnumber_mismatch (struct thread *thread) } THREAD_OFF (on->thread_send_dbdesc); + on->dbdesc_seqnum++; /* Incr seqnum as per RFC2328, sec 10.3 */ + on->thread_send_dbdesc = thread_add_event (master, ospf6_dbdesc_send, on, 0); @@ -520,6 +522,8 @@ bad_lsreq (struct thread *thread) } THREAD_OFF (on->thread_send_dbdesc); + on->dbdesc_seqnum++; /* Incr seqnum as per RFC2328, sec 10.3 */ + on->thread_send_dbdesc = thread_add_event (master, ospf6_dbdesc_send, on, 0); From e68a67672ccfabefadac36c66e88af997fb572b2 Mon Sep 17 00:00:00 2001 From: Dinesh Dutt Date: Sun, 25 Aug 2013 03:03:23 +0000 Subject: [PATCH 151/482] ospf6d: add LSA payload to show summary output Unlike OSPFv2, the LSID of an LSA isn't sufficient to know what the contents of the LSA are. Its useful for debugging and basic eyeball tests to see the contents of the LSA in the simple tabular format of "show ipv6 ospf6 database". This patch adds that output to the command. It replaces the existing fields of "duration, Chksum and Length" with a single field called Payload which is dependent on the LSA type. For Inter-Area Prefix, Intra-Area Prefix and AS-External LSAs, this will be the advertised prefix/prefix length, for Router LSAs, it is RtrID/IfID etc. Signed-off-by: Dinesh G Dutt Reviewed-by: Pradosh Mohapatra Reviewed-by: Scott Feldman [DL: rebase fix, line disappeared in ospf6_abr_originate_summary_to_area] Signed-off-by: David Lamparter --- ospf6d/ospf6_abr.c | 61 +++++++++++-- ospf6d/ospf6_asbr.c | 52 ++++++++--- ospf6d/ospf6_intra.c | 201 ++++++++++++++++++++++++++++++++++++++++++- ospf6d/ospf6_lsa.c | 77 +++++++++++++---- ospf6d/ospf6_lsa.h | 3 + 5 files changed, 356 insertions(+), 38 deletions(-) diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c index 3277e7d8..379a62ed 100644 --- a/ospf6d/ospf6_abr.c +++ b/ospf6d/ospf6_abr.c @@ -764,12 +764,34 @@ ospf6_abr_reimport (struct ospf6_area *oa) /* Display functions */ +static char * +ospf6_inter_area_prefix_lsa_get_prefix_str (struct ospf6_lsa *lsa, char *buf, + int buflen, int pos) +{ + struct ospf6_inter_prefix_lsa *prefix_lsa; + struct in6_addr in6; + + if (lsa != NULL) + { + prefix_lsa = (struct ospf6_inter_prefix_lsa *) + OSPF6_LSA_HEADER_END (lsa->header); + + ospf6_prefix_in6_addr (&in6, &prefix_lsa->prefix); + if (buf) + { + inet_ntop (AF_INET6, &in6, buf, buflen); + sprintf (&buf[strlen(buf)], "/%d", prefix_lsa->prefix.prefix_length); + } + } + + return (buf); +} + static int ospf6_inter_area_prefix_lsa_show (struct vty *vty, struct ospf6_lsa *lsa) { struct ospf6_inter_prefix_lsa *prefix_lsa; - struct in6_addr in6; - char buf[64]; + char buf[INET6_ADDRSTRLEN]; prefix_lsa = (struct ospf6_inter_prefix_lsa *) OSPF6_LSA_HEADER_END (lsa->header); @@ -781,14 +803,32 @@ ospf6_inter_area_prefix_lsa_show (struct vty *vty, struct ospf6_lsa *lsa) buf, sizeof (buf)); vty_out (vty, " Prefix Options: %s%s", buf, VNL); - ospf6_prefix_in6_addr (&in6, &prefix_lsa->prefix); - inet_ntop (AF_INET6, &in6, buf, sizeof (buf)); - vty_out (vty, " Prefix: %s/%d%s", buf, - prefix_lsa->prefix.prefix_length, VNL); + vty_out (vty, " Prefix: %s%s", + ospf6_inter_area_prefix_lsa_get_prefix_str (lsa, buf, sizeof(buf), + 0), VNL); return 0; } +static char * +ospf6_inter_area_router_lsa_get_prefix_str (struct ospf6_lsa *lsa, char *buf, + int buflen, int pos) +{ + struct ospf6_inter_router_lsa *router_lsa; + + if (lsa != NULL) + { + router_lsa = (struct ospf6_inter_router_lsa *) + OSPF6_LSA_HEADER_END (lsa->header); + + + if (buf) + inet_ntop (AF_INET, &router_lsa->router_id, buf, buflen); + } + + return (buf); +} + static int ospf6_inter_area_router_lsa_show (struct vty *vty, struct ospf6_lsa *lsa) { @@ -802,6 +842,7 @@ ospf6_inter_area_router_lsa_show (struct vty *vty, struct ospf6_lsa *lsa) vty_out (vty, " Options: %s%s", buf, VNL); vty_out (vty, " Metric: %lu%s", (u_long) OSPF6_ABR_SUMMARY_METRIC (router_lsa), VNL); + inet_ntop (AF_INET, &router_lsa->router_id, buf, sizeof (buf)); vty_out (vty, " Destination Router ID: %s%s", buf, VNL); @@ -855,14 +896,18 @@ struct ospf6_lsa_handler inter_prefix_handler = { OSPF6_LSTYPE_INTER_PREFIX, "Inter-Prefix", - ospf6_inter_area_prefix_lsa_show + "IAP", + ospf6_inter_area_prefix_lsa_show, + ospf6_inter_area_prefix_lsa_get_prefix_str, }; struct ospf6_lsa_handler inter_router_handler = { OSPF6_LSTYPE_INTER_ROUTER, "Inter-Router", - ospf6_inter_area_router_lsa_show + "IAR", + ospf6_inter_area_router_lsa_show, + ospf6_inter_area_router_lsa_get_prefix_str, }; void diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index 1a0634ed..27726c67 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -1161,12 +1161,44 @@ ospf6_routemap_init (void) /* Display functions */ +static char * +ospf6_as_external_lsa_get_prefix_str (struct ospf6_lsa *lsa, char *buf, + int buflen, int pos) +{ + struct ospf6_as_external_lsa *external; + struct in6_addr in6; + int prefix_length = 0; + + if (lsa) + { + external = (struct ospf6_as_external_lsa *) + OSPF6_LSA_HEADER_END (lsa->header); + + if (pos == 0) + { + ospf6_prefix_in6_addr (&in6, &external->prefix); + prefix_length = external->prefix.prefix_length; + } + else { + in6 = *((struct in6_addr *) + ((caddr_t) external + sizeof (struct ospf6_as_external_lsa) + + OSPF6_PREFIX_SPACE (external->prefix.prefix_length))); + } + if (buf) + { + inet_ntop (AF_INET6, &in6, buf, buflen); + if (prefix_length) + sprintf (&buf[strlen(buf)], "/%d", prefix_length); + } + } + return (buf); +} + static int ospf6_as_external_lsa_show (struct vty *vty, struct ospf6_lsa *lsa) { struct ospf6_as_external_lsa *external; char buf[64]; - struct in6_addr in6, *forwarding; assert (lsa->header); external = (struct ospf6_as_external_lsa *) @@ -1191,19 +1223,15 @@ ospf6_as_external_lsa_show (struct vty *vty, struct ospf6_lsa *lsa) ntohs (external->prefix.prefix_refer_lstype), VNL); - ospf6_prefix_in6_addr (&in6, &external->prefix); - inet_ntop (AF_INET6, &in6, buf, sizeof (buf)); - vty_out (vty, " Prefix: %s/%d%s", buf, - external->prefix.prefix_length, VNL); + vty_out (vty, " Prefix: %s%s", + ospf6_as_external_lsa_get_prefix_str (lsa, buf, sizeof(buf), 0), VNL); /* Forwarding-Address */ if (CHECK_FLAG (external->bits_metric, OSPF6_ASBR_BIT_F)) { - forwarding = (struct in6_addr *) - ((caddr_t) external + sizeof (struct ospf6_as_external_lsa) + - OSPF6_PREFIX_SPACE (external->prefix.prefix_length)); - inet_ntop (AF_INET6, forwarding, buf, sizeof (buf)); - vty_out (vty, " Forwarding-Address: %s%s", buf, VNL); + vty_out (vty, " Forwarding-Address: %s%s", + ospf6_as_external_lsa_get_prefix_str (lsa, buf, sizeof(buf), 1), + VNL); } return 0; @@ -1257,7 +1285,9 @@ struct ospf6_lsa_handler as_external_handler = { OSPF6_LSTYPE_AS_EXTERNAL, "AS-External", - ospf6_as_external_lsa_show + "ASE", + ospf6_as_external_lsa_show, + ospf6_as_external_lsa_get_prefix_str }; void diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c index 025a7710..9c84e657 100644 --- a/ospf6d/ospf6_intra.c +++ b/ospf6d/ospf6_intra.c @@ -56,6 +56,42 @@ u_int32_t conf_debug_ospf6_brouter_specific_area_id; /* RFC2740 3.4.3.1 Router-LSA */ /******************************/ +static char * +ospf6_router_lsa_get_nbr_id (struct ospf6_lsa *lsa, char *buf, int buflen, + int pos) +{ + struct ospf6_router_lsa *router_lsa; + struct ospf6_router_lsdesc *lsdesc; + char *start, *end; + char buf1[INET_ADDRSTRLEN], buf2[INET_ADDRSTRLEN]; + + if (lsa) + { + router_lsa = (struct ospf6_router_lsa *) + ((char *) lsa->header + sizeof (struct ospf6_lsa_header)); + start = (char *) router_lsa + sizeof (struct ospf6_router_lsa); + end = (char *) lsa->header + ntohs (lsa->header->length); + + lsdesc = (struct ospf6_router_lsdesc *) + (start + pos*(sizeof (struct ospf6_router_lsdesc))); + if ((char *)lsdesc < end) + { + if (buf && (buflen > INET_ADDRSTRLEN*2)) + { + inet_ntop (AF_INET, &lsdesc->neighbor_interface_id, + buf1, sizeof(buf1)); + inet_ntop (AF_INET, &lsdesc->neighbor_router_id, + buf2, sizeof(buf2)); + sprintf (buf, "%s/%s", buf2, buf1); + } + } + else + return NULL; + } + + return buf; +} + static int ospf6_router_lsa_show (struct vty *vty, struct ospf6_lsa *lsa) { @@ -334,6 +370,36 @@ ospf6_router_lsa_originate (struct thread *thread) /* RFC2740 3.4.3.2 Network-LSA */ /*******************************/ +static char * +ospf6_network_lsa_get_ar_id (struct ospf6_lsa *lsa, char *buf, int buflen, + int pos) +{ + char *start, *end, *current; + struct ospf6_network_lsa *network_lsa; + struct ospf6_network_lsdesc *lsdesc; + + if (lsa) + { + network_lsa = (struct ospf6_network_lsa *) + ((caddr_t) lsa->header + sizeof (struct ospf6_lsa_header)); + + start = (char *) network_lsa + sizeof (struct ospf6_network_lsa); + end = (char *) lsa->header + ntohs (lsa->header->length); + current = start + pos*(sizeof (struct ospf6_network_lsdesc)); + + if ((current + sizeof(struct ospf6_network_lsdesc)) <= end) + { + lsdesc = (struct ospf6_network_lsdesc *)current; + if (buf) + inet_ntop (AF_INET, &lsdesc->router_id, buf, buflen); + } + else + return NULL; + } + + return (buf); +} + static int ospf6_network_lsa_show (struct vty *vty, struct ospf6_lsa *lsa) { @@ -480,6 +546,61 @@ ospf6_network_lsa_originate (struct thread *thread) /* RFC2740 3.4.3.6 Link-LSA */ /****************************/ +static char * +ospf6_link_lsa_get_prefix_str (struct ospf6_lsa *lsa, char *buf, int buflen, + int pos) +{ + char *start, *end, *current; + struct ospf6_link_lsa *link_lsa; + struct in6_addr in6; + struct ospf6_prefix *prefix; + int cnt = 0, prefixnum; + + if (lsa) + { + link_lsa = (struct ospf6_link_lsa *) + ((caddr_t) lsa->header + sizeof (struct ospf6_lsa_header)); + + if (pos == 0) { + inet_ntop (AF_INET6, &link_lsa->linklocal_addr, buf, buflen); + return (buf); + } + + prefixnum = ntohl (link_lsa->prefix_num); + if (pos > prefixnum) + return (NULL); + + start = (char *) link_lsa + sizeof (struct ospf6_link_lsa); + end = (char *) lsa->header + ntohs (lsa->header->length); + current = start; + + do + { + prefix = (struct ospf6_prefix *) current; + if (prefix->prefix_length == 0 || + current + OSPF6_PREFIX_SIZE (prefix) > end) + { + return (NULL); + } + + if (cnt < pos) + { + current = start + pos*OSPF6_PREFIX_SIZE(prefix); + cnt++; + } + else + { + memset (&in6, 0, sizeof (in6)); + memcpy (&in6, OSPF6_PREFIX_BODY (prefix), + OSPF6_PREFIX_SPACE (prefix->prefix_length)); + inet_ntop (AF_INET6, &in6, buf, buflen); + return (buf); + } + } while (current <= end); + } + return (NULL); +} + static int ospf6_link_lsa_show (struct vty *vty, struct ospf6_lsa *lsa) { @@ -632,6 +753,56 @@ ospf6_link_lsa_originate (struct thread *thread) /*****************************************/ /* RFC2740 3.4.3.7 Intra-Area-Prefix-LSA */ /*****************************************/ +static char * +ospf6_intra_prefix_lsa_get_prefix_str (struct ospf6_lsa *lsa, char *buf, + int buflen, int pos) +{ + char *start, *end, *current; + struct ospf6_intra_prefix_lsa *intra_prefix_lsa; + struct in6_addr in6; + int prefixnum, cnt = 0; + struct ospf6_prefix *prefix; + + if (lsa) + { + intra_prefix_lsa = (struct ospf6_intra_prefix_lsa *) + ((caddr_t) lsa->header + sizeof (struct ospf6_lsa_header)); + + prefixnum = ntohs (intra_prefix_lsa->prefix_num); + if (pos > prefixnum) + return (NULL); + + start = (char *) intra_prefix_lsa + sizeof (struct ospf6_intra_prefix_lsa); + end = (char *) lsa->header + ntohs (lsa->header->length); + current = start; + + do + { + prefix = (struct ospf6_prefix *) current; + if (prefix->prefix_length == 0 || + current + OSPF6_PREFIX_SIZE (prefix) > end) + { + return NULL; + } + + if (cnt < pos) + { + current = start + pos*OSPF6_PREFIX_SIZE(prefix); + cnt++; + } + else + { + memset (&in6, 0, sizeof (in6)); + memcpy (&in6, OSPF6_PREFIX_BODY (prefix), + OSPF6_PREFIX_SPACE (prefix->prefix_length)); + inet_ntop (AF_INET6, &in6, buf, buflen); + sprintf(&buf[strlen(buf)], "/%d", prefix->prefix_length); + return (buf); + } + } while (current <= end); + } + return (buf); +} static int ospf6_intra_prefix_lsa_show (struct vty *vty, struct ospf6_lsa *lsa) @@ -1103,6 +1274,20 @@ ospf6_intra_prefix_lsa_add (struct ospf6_lsa *lsa) if (end < current + OSPF6_PREFIX_SIZE (op)) break; + /* Appendix A.4.1.1 */ + if (CHECK_FLAG(op->prefix_options, OSPF6_PREFIX_OPTION_NU) || + CHECK_FLAG(op->prefix_options, OSPF6_PREFIX_OPTION_LA)) + { + if (IS_OSPF6_DEBUG_EXAMIN (INTRA_PREFIX)) + { + ospf6_linkstate_prefix2str ((struct prefix *)OSPF6_PREFIX_BODY(op), + buf, sizeof (buf)); + zlog_debug ("%s: Skipping Prefix %s has NU/LA option set", + __func__, buf); + } + continue; + } + route = ospf6_route_create (); memset (&route->prefix, 0, sizeof (struct prefix)); @@ -1454,28 +1639,36 @@ struct ospf6_lsa_handler router_handler = { OSPF6_LSTYPE_ROUTER, "Router", - ospf6_router_lsa_show + "Rtr", + ospf6_router_lsa_show, + ospf6_router_lsa_get_nbr_id }; struct ospf6_lsa_handler network_handler = { OSPF6_LSTYPE_NETWORK, "Network", - ospf6_network_lsa_show + "Net", + ospf6_network_lsa_show, + ospf6_network_lsa_get_ar_id }; struct ospf6_lsa_handler link_handler = { OSPF6_LSTYPE_LINK, "Link", - ospf6_link_lsa_show + "Lnk", + ospf6_link_lsa_show, + ospf6_link_lsa_get_prefix_str }; struct ospf6_lsa_handler intra_prefix_handler = { OSPF6_LSTYPE_INTRA_PREFIX, "Intra-Prefix", - ospf6_intra_prefix_lsa_show + "INP", + ospf6_intra_prefix_lsa_show, + ospf6_intra_prefix_lsa_get_prefix_str }; void diff --git a/ospf6d/ospf6_lsa.c b/ospf6d/ospf6_lsa.c index 592aad9b..4aa2b12f 100644 --- a/ospf6d/ospf6_lsa.c +++ b/ospf6d/ospf6_lsa.c @@ -74,8 +74,10 @@ ospf6_unknown_lsa_show (struct vty *vty, struct ospf6_lsa *lsa) struct ospf6_lsa_handler unknown_handler = { OSPF6_LSTYPE_UNKNOWN, - "unknown", + "Unknown", + "Unk", ospf6_unknown_lsa_show, + NULL, OSPF6_LSA_DEBUG, }; @@ -118,6 +120,20 @@ ospf6_lstype_name (u_int16_t type) return buf; } +const char * +ospf6_lstype_short_name (u_int16_t type) +{ + static char buf[8]; + struct ospf6_lsa_handler *handler; + + handler = ospf6_get_lsa_handler (type); + if (handler && handler != &unknown_handler) + return handler->short_name; + + snprintf (buf, sizeof (buf), "0x%04hx", ntohs (type)); + return buf; +} + u_char ospf6_lstype_debug (u_int16_t type) { @@ -371,17 +387,19 @@ ospf6_lsa_header_print (struct ospf6_lsa *lsa) void ospf6_lsa_show_summary_header (struct vty *vty) { - vty_out (vty, "%-12s %-15s %-15s %4s %8s %4s %4s %-8s%s", + vty_out (vty, "%-4s %-15s%-15s%4s %8s %30s%s", "Type", "LSId", "AdvRouter", "Age", "SeqNum", - "Cksm", "Len", "Duration", VNL); + "Payload", VNL); } void ospf6_lsa_show_summary (struct vty *vty, struct ospf6_lsa *lsa) { char adv_router[16], id[16]; - struct timeval now, res; - char duration[16]; + int type; + struct ospf6_lsa_handler *handler; + char buf[64], tmpbuf[80]; + int cnt = 0; assert (lsa); assert (lsa->header); @@ -390,16 +408,38 @@ ospf6_lsa_show_summary (struct vty *vty, struct ospf6_lsa *lsa) inet_ntop (AF_INET, &lsa->header->adv_router, adv_router, sizeof (adv_router)); - quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); - timersub (&now, &lsa->installed, &res); - timerstring (&res, duration, sizeof (duration)); - - vty_out (vty, "%-12s %-15s %-15s %4hu %8lx %04hx %4hu %8s%s", - ospf6_lstype_name (lsa->header->type), - id, adv_router, ospf6_lsa_age_current (lsa), - (u_long) ntohl (lsa->header->seqnum), - ntohs (lsa->header->checksum), ntohs (lsa->header->length), - duration, VNL); + type = ntohs(lsa->header->type); + handler = ospf6_get_lsa_handler (lsa->header->type); + if ((type == OSPF6_LSTYPE_INTER_PREFIX) || + (type == OSPF6_LSTYPE_INTER_ROUTER) || + (type == OSPF6_LSTYPE_AS_EXTERNAL)) + { + vty_out (vty, "%-4s %-15s%-15s%4hu %8lx %30s%s", + ospf6_lstype_short_name (lsa->header->type), + id, adv_router, ospf6_lsa_age_current (lsa), + (u_long) ntohl (lsa->header->seqnum), + handler->get_prefix_str(lsa, buf, sizeof(buf), 0), VNL); + } + else if (type != OSPF6_LSTYPE_UNKNOWN) + { + sprintf (tmpbuf, "%-4s %-15s%-15s%4hu %8lx", + ospf6_lstype_short_name (lsa->header->type), + id, adv_router, ospf6_lsa_age_current (lsa), + (u_long) ntohl (lsa->header->seqnum)); + + while (handler->get_prefix_str(lsa, buf, sizeof(buf), cnt) != NULL) + { + vty_out (vty, "%s %30s%s", tmpbuf, buf, VNL); + cnt++; + } + } + else + { + vty_out (vty, "%-4s %-15s%-15s%4hu %8lx%s", + ospf6_lstype_short_name (lsa->header->type), + id, adv_router, ospf6_lsa_age_current (lsa), + (u_long) ntohl (lsa->header->seqnum), VNL); + } } void @@ -464,6 +504,8 @@ ospf6_lsa_show (struct vty *vty, struct ospf6_lsa *lsa) { char adv_router[64], id[64]; struct ospf6_lsa_handler *handler; + struct timeval now, res; + char duration[16]; assert (lsa && lsa->header); @@ -471,6 +513,10 @@ ospf6_lsa_show (struct vty *vty, struct ospf6_lsa *lsa) inet_ntop (AF_INET, &lsa->header->adv_router, adv_router, sizeof (adv_router)); + quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); + timersub (&now, &lsa->installed, &res); + timerstring (&res, duration, sizeof (duration)); + vty_out (vty, "Age: %4hu Type: %s%s", ospf6_lsa_age_current (lsa), ospf6_lstype_name (lsa->header->type), VNL); vty_out (vty, "Link State ID: %s%s", id, VNL); @@ -480,6 +526,7 @@ ospf6_lsa_show (struct vty *vty, struct ospf6_lsa *lsa) vty_out (vty, "CheckSum: %#06hx Length: %hu%s", ntohs (lsa->header->checksum), ntohs (lsa->header->length), VNL); + vty_out (vty, "Duration: %s%s", duration, VNL); handler = ospf6_get_lsa_handler (lsa->header->type); if (handler->show == NULL) diff --git a/ospf6d/ospf6_lsa.h b/ospf6d/ospf6_lsa.h index ffd6ae0a..aed89df2 100644 --- a/ospf6d/ospf6_lsa.h +++ b/ospf6d/ospf6_lsa.h @@ -147,7 +147,9 @@ struct ospf6_lsa_handler { u_int16_t type; /* host byte order */ const char *name; + const char *short_name; int (*show) (struct vty *, struct ospf6_lsa *); + char *(*get_prefix_str) (struct ospf6_lsa *, char *buf, int buflen, int pos); u_char debug; }; @@ -210,6 +212,7 @@ extern struct ospf6_lsa_handler unknown_handler; /* Function Prototypes */ extern const char *ospf6_lstype_name (u_int16_t type); +extern const char *ospf6_lstype_short_name (u_int16_t type); extern u_char ospf6_lstype_debug (u_int16_t type); extern int ospf6_lsa_is_differ (struct ospf6_lsa *lsa1, struct ospf6_lsa *lsa2); extern int ospf6_lsa_is_changed (struct ospf6_lsa *lsa1, struct ospf6_lsa *lsa2); From 01879114f73adaf1cd4c9f5e7ae1550b72ff9ca9 Mon Sep 17 00:00:00 2001 From: Dinesh Dutt Date: Sun, 25 Aug 2013 03:03:31 +0000 Subject: [PATCH 152/482] ospf6d: handle Prefix and Router Options bits correctly Ensure that prefixes with the NU/LA bit set do not get added to the routing table. Ensure that routers with the V6/R bit set do not get added as transit routes. Signed-off-by: Dinesh Dutt [DL: adjust to rebase] Signed-off-by: David Lamparter --- ospf6d/ospf6_abr.c | 33 +++++++++++++++++++++++++++++---- ospf6d/ospf6_asbr.c | 7 +++++++ ospf6d/ospf6_intra.c | 4 ++++ 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c index 379a62ed..54404ab8 100644 --- a/ospf6d/ospf6_abr.c +++ b/ospf6d/ospf6_abr.c @@ -537,13 +537,13 @@ ospf6_abr_examin_summary (struct ospf6_lsa *lsa, struct ospf6_area *oa) int i; char buf[64]; int is_debug = 0; + struct ospf6_inter_prefix_lsa *prefix_lsa = NULL; + struct ospf6_inter_router_lsa *router_lsa = NULL; memset (&prefix, 0, sizeof (prefix)); if (lsa->header->type == htons (OSPF6_LSTYPE_INTER_PREFIX)) { - struct ospf6_inter_prefix_lsa *prefix_lsa; - if (IS_OSPF6_DEBUG_EXAMIN (INTER_PREFIX)) { is_debug++; @@ -564,8 +564,6 @@ ospf6_abr_examin_summary (struct ospf6_lsa *lsa, struct ospf6_area *oa) } else if (lsa->header->type == htons (OSPF6_LSTYPE_INTER_ROUTER)) { - struct ospf6_inter_router_lsa *router_lsa; - if (IS_OSPF6_DEBUG_EXAMIN (INTER_ROUTER)) { is_debug++; @@ -632,6 +630,7 @@ ospf6_abr_examin_summary (struct ospf6_lsa *lsa, struct ospf6_area *oa) } /* (3) if the prefix is equal to an active configured address range */ + /* or if the NU bit is set in the prefix */ if (lsa->header->type == htons (OSPF6_LSTYPE_INTER_PREFIX)) { range = ospf6_route_lookup (&prefix, oa->range_table); @@ -643,6 +642,32 @@ ospf6_abr_examin_summary (struct ospf6_lsa *lsa, struct ospf6_area *oa) ospf6_route_remove (old, table); return; } + + if (CHECK_FLAG (prefix_lsa->prefix.prefix_options, + OSPF6_PREFIX_OPTION_NU) || + CHECK_FLAG (prefix_lsa->prefix.prefix_options, + OSPF6_PREFIX_OPTION_LA)) + { + if (is_debug) + zlog_debug ("Prefix has NU/LA bit set, ignore"); + if (old) + ospf6_route_remove (old, table); + return; + } + } + + if (lsa->header->type == htons (OSPF6_LSTYPE_INTER_ROUTER)) + { + /* To pass test suites */ + if (! OSPF6_OPT_ISSET (router_lsa->options, OSPF6_OPT_R) || + ! OSPF6_OPT_ISSET (router_lsa->options, OSPF6_OPT_V6)) + { + if (is_debug) + zlog_debug ("Prefix has NU/LA bit set, ignore"); + if (old) + ospf6_route_remove (old, table); + return; + } } /* (4) if the routing table entry for the ABR does not exist */ diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index 27726c67..60df6e6c 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -181,6 +181,13 @@ ospf6_asbr_lsa_add (struct ospf6_lsa *lsa) return; } + if (CHECK_FLAG(external->prefix.prefix_options, OSPF6_PREFIX_OPTION_NU)) + { + if (IS_OSPF6_DEBUG_EXAMIN (AS_EXTERNAL)) + zlog_debug ("Ignore LSA with NU bit set Metric"); + return; + } + ospf6_linkstate_prefix (lsa->header->adv_router, htonl (0), &asbr_id); asbr_entry = ospf6_route_lookup (&asbr_id, ospf6->brouter_table); if (asbr_entry == NULL || diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c index 9c84e657..e86e46bf 100644 --- a/ospf6d/ospf6_intra.c +++ b/ospf6d/ospf6_intra.c @@ -1562,6 +1562,10 @@ ospf6_intra_brouter_calculation (struct ospf6_area *oa) ! CHECK_FLAG (brouter->path.router_bits, OSPF6_ROUTER_BIT_B)) continue; + if (! OSPF6_OPT_ISSET (brouter->path.options, OSPF6_OPT_V6) || + ! OSPF6_OPT_ISSET (brouter->path.options, OSPF6_OPT_R)) + continue; + copy = ospf6_route_copy (brouter); copy->type = OSPF6_DEST_TYPE_ROUTER; copy->path.area_id = oa->area_id; From 7cf997226e86d98839f1e7872ca98b023ffea98e Mon Sep 17 00:00:00 2001 From: Dinesh Dutt Date: Sun, 25 Aug 2013 03:03:39 +0000 Subject: [PATCH 153/482] ospf6d: don't send LSAck on an interface if we've flooded the LSU out that i/f If we flood an LSA back out the same interface we received it from, don't send an LSAck out that interface for that LSA. This is as per RFC 2328, section 13.5 Signed-off-by: Dinesh G Dutt Reviewed-by: Pradosh Mohapatra Reviewed-by: Scott Feldman Signed-off-by: David Lamparter --- ospf6d/ospf6_flood.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/ospf6d/ospf6_flood.c b/ospf6d/ospf6_flood.c index 7f6b2850..dc9ecbfb 100644 --- a/ospf6d/ospf6_flood.c +++ b/ospf6d/ospf6_flood.c @@ -387,11 +387,15 @@ ospf6_flood_interface (struct ospf6_neighbor *from, /* (4) If the new LSA was received on this interface, and the interface state is BDR, examin next interface */ - if (from && from->ospf6_if == oi && oi->state == OSPF6_INTERFACE_BDR) + if (from && from->ospf6_if == oi) { - if (is_debug) - zlog_debug ("Received is from the I/F, itself BDR, next interface"); - return; + if (oi->state == OSPF6_INTERFACE_BDR) + { + if (is_debug) + zlog_debug ("Received is from the I/F, itself BDR, next interface"); + return; + } + SET_FLAG(lsa->flag, OSPF6_LSA_FLOODBACK); } /* (5) flood the LSA out the interface. */ @@ -560,15 +564,6 @@ ospf6_acknowledge_lsa_bdrouter (struct ospf6_lsa *lsa, int ismore_recent, assert (from && from->ospf6_if); oi = from->ospf6_if; - /* LSA has been flood back out receiving interface. - No acknowledgement sent. */ - if (CHECK_FLAG (lsa->flag, OSPF6_LSA_FLOODBACK)) - { - if (is_debug) - zlog_debug ("No acknowledgement (BDR & FloodBack)"); - return; - } - /* LSA is more recent than database copy, but was not flooded back out receiving interface. Delayed acknowledgement sent if advertisement received from Designated Router, From 3d35ca482babab4267570143b8327fc894df0ff8 Mon Sep 17 00:00:00 2001 From: Dinesh Dutt Date: Mon, 26 Aug 2013 03:40:16 +0000 Subject: [PATCH 154/482] ospf6d: add 'log-adjacency-changes [detail]' Similar to OSPFv2, add support for 'log-adjacency-changes [detail]' to log changes in adjacency state of ospfv3 neighbors. Signed-off-by: Pradosh Mohapatra Reviewed-by: Dinesh G Dutt Reviewed-by: Scott Feldman Reviewed-by: Shrijeet Mukherjee Signed-off-by: David Lamparter --- ospf6d/ospf6_neighbor.c | 57 +++++++++++++++++++++++--------- ospf6d/ospf6_neighbor.h | 38 +++++++++++++++++++++ ospf6d/ospf6_top.c | 73 +++++++++++++++++++++++++++++++++++++++++ ospf6d/ospf6_top.h | 5 +++ 4 files changed, 157 insertions(+), 16 deletions(-) diff --git a/ospf6d/ospf6_neighbor.c b/ospf6d/ospf6_neighbor.c index b7d2e40d..fb209fdf 100644 --- a/ospf6d/ospf6_neighbor.c +++ b/ospf6d/ospf6_neighbor.c @@ -142,7 +142,7 @@ ospf6_neighbor_delete (struct ospf6_neighbor *on) } static void -ospf6_neighbor_state_change (u_char next_state, struct ospf6_neighbor *on) +ospf6_neighbor_state_change (u_char next_state, struct ospf6_neighbor *on, int event) { u_char prev_state; @@ -158,11 +158,23 @@ ospf6_neighbor_state_change (u_char next_state, struct ospf6_neighbor *on) /* log */ if (IS_OSPF6_DEBUG_NEIGHBOR (STATE)) { - zlog_debug ("Neighbor state change %s: [%s]->[%s]", on->name, + zlog_debug ("Neighbor state change %s: [%s]->[%s] (%s)", on->name, ospf6_neighbor_state_str[prev_state], - ospf6_neighbor_state_str[next_state]); + ospf6_neighbor_state_str[next_state], + ospf6_neighbor_event_string(event)); } + /* Optionally notify about adjacency changes */ + if (CHECK_FLAG(on->ospf6_if->area->ospf6->config_flags, + OSPF6_LOG_ADJACENCY_CHANGES) && + (CHECK_FLAG(on->ospf6_if->area->ospf6->config_flags, + OSPF6_LOG_ADJACENCY_DETAIL) || + (next_state == OSPF6_NEIGHBOR_FULL) || (next_state < prev_state))) + zlog_notice("AdjChg: Nbr %s: %s -> %s (%s)", on->name, + ospf6_neighbor_state_str[prev_state], + ospf6_neighbor_state_str[next_state], + ospf6_neighbor_event_string(event)); + if (prev_state == OSPF6_NEIGHBOR_FULL || next_state == OSPF6_NEIGHBOR_FULL) { OSPF6_ROUTER_LSA_SCHEDULE (on->ospf6_if->area); @@ -223,7 +235,8 @@ hello_received (struct thread *thread) on->ospf6_if->dead_interval); if (on->state <= OSPF6_NEIGHBOR_DOWN) - ospf6_neighbor_state_change (OSPF6_NEIGHBOR_INIT, on); + ospf6_neighbor_state_change (OSPF6_NEIGHBOR_INIT, on, + OSPF6_NEIGHBOR_EVENT_HELLO_RCVD); return 0; } @@ -246,11 +259,13 @@ twoway_received (struct thread *thread) if (! need_adjacency (on)) { - ospf6_neighbor_state_change (OSPF6_NEIGHBOR_TWOWAY, on); + ospf6_neighbor_state_change (OSPF6_NEIGHBOR_TWOWAY, on, + OSPF6_NEIGHBOR_EVENT_TWOWAY_RCVD); return 0; } - ospf6_neighbor_state_change (OSPF6_NEIGHBOR_EXSTART, on); + ospf6_neighbor_state_change (OSPF6_NEIGHBOR_EXSTART, on, + OSPF6_NEIGHBOR_EVENT_TWOWAY_RCVD); SET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MSBIT); SET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MBIT); SET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_IBIT); @@ -327,7 +342,8 @@ negotiation_done (struct thread *thread) } UNSET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_IBIT); - ospf6_neighbor_state_change (OSPF6_NEIGHBOR_EXCHANGE, on); + ospf6_neighbor_state_change (OSPF6_NEIGHBOR_EXCHANGE, on, + OSPF6_NEIGHBOR_EVENT_NEGOTIATION_DONE); return 0; } @@ -355,10 +371,12 @@ exchange_done (struct thread *thread) */ if (on->request_list->count == 0) - ospf6_neighbor_state_change (OSPF6_NEIGHBOR_FULL, on); + ospf6_neighbor_state_change (OSPF6_NEIGHBOR_FULL, on, + OSPF6_NEIGHBOR_EVENT_EXCHANGE_DONE); else { - ospf6_neighbor_state_change (OSPF6_NEIGHBOR_LOADING, on); + ospf6_neighbor_state_change (OSPF6_NEIGHBOR_LOADING, on, + OSPF6_NEIGHBOR_EVENT_EXCHANGE_DONE); if (on->thread_send_lsreq == NULL) on->thread_send_lsreq = @@ -408,7 +426,8 @@ loading_done (struct thread *thread) assert (on->request_list->count == 0); - ospf6_neighbor_state_change (OSPF6_NEIGHBOR_FULL, on); + ospf6_neighbor_state_change (OSPF6_NEIGHBOR_FULL, on, + OSPF6_NEIGHBOR_EVENT_LOADING_DONE); return 0; } @@ -427,7 +446,8 @@ adj_ok (struct thread *thread) if (on->state == OSPF6_NEIGHBOR_TWOWAY && need_adjacency (on)) { - ospf6_neighbor_state_change (OSPF6_NEIGHBOR_EXSTART, on); + ospf6_neighbor_state_change (OSPF6_NEIGHBOR_EXSTART, on, + OSPF6_NEIGHBOR_EVENT_ADJ_OK); SET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MSBIT); SET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MBIT); SET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_IBIT); @@ -440,7 +460,8 @@ adj_ok (struct thread *thread) else if (on->state >= OSPF6_NEIGHBOR_EXSTART && ! need_adjacency (on)) { - ospf6_neighbor_state_change (OSPF6_NEIGHBOR_TWOWAY, on); + ospf6_neighbor_state_change (OSPF6_NEIGHBOR_TWOWAY, on, + OSPF6_NEIGHBOR_EVENT_ADJ_OK); ospf6_lsdb_remove_all (on->summary_list); ospf6_lsdb_remove_all (on->request_list); for (lsa = ospf6_lsdb_head (on->retrans_list); lsa; @@ -469,7 +490,8 @@ seqnumber_mismatch (struct thread *thread) if (IS_OSPF6_DEBUG_NEIGHBOR (EVENT)) zlog_debug ("Neighbor Event %s: *SeqNumberMismatch*", on->name); - ospf6_neighbor_state_change (OSPF6_NEIGHBOR_EXSTART, on); + ospf6_neighbor_state_change (OSPF6_NEIGHBOR_EXSTART, on, + OSPF6_NEIGHBOR_EVENT_SEQNUMBER_MISMATCH); SET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MSBIT); SET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MBIT); SET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_IBIT); @@ -507,7 +529,8 @@ bad_lsreq (struct thread *thread) if (IS_OSPF6_DEBUG_NEIGHBOR (EVENT)) zlog_debug ("Neighbor Event %s: *BadLSReq*", on->name); - ospf6_neighbor_state_change (OSPF6_NEIGHBOR_EXSTART, on); + ospf6_neighbor_state_change (OSPF6_NEIGHBOR_EXSTART, on, + OSPF6_NEIGHBOR_EVENT_BAD_LSREQ); SET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MSBIT); SET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MBIT); SET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_IBIT); @@ -545,7 +568,8 @@ oneway_received (struct thread *thread) if (IS_OSPF6_DEBUG_NEIGHBOR (EVENT)) zlog_debug ("Neighbor Event %s: *1Way-Received*", on->name); - ospf6_neighbor_state_change (OSPF6_NEIGHBOR_INIT, on); + ospf6_neighbor_state_change (OSPF6_NEIGHBOR_INIT, on, + OSPF6_NEIGHBOR_EVENT_ONEWAY_RCVD); thread_add_event (master, neighbor_change, on->ospf6_if, 0); ospf6_lsdb_remove_all (on->summary_list); @@ -580,7 +604,8 @@ inactivity_timer (struct thread *thread) on->drouter = on->prev_drouter = 0; on->bdrouter = on->prev_bdrouter = 0; - ospf6_neighbor_state_change (OSPF6_NEIGHBOR_DOWN, on); + ospf6_neighbor_state_change (OSPF6_NEIGHBOR_DOWN, on, + OSPF6_NEIGHBOR_EVENT_INACTIVITY_TIMER); thread_add_event (master, neighbor_change, on->ospf6_if, 0); listnode_delete (on->ospf6_if->neighbor_list, on); diff --git a/ospf6d/ospf6_neighbor.h b/ospf6d/ospf6_neighbor.h index 750e1b24..88821898 100644 --- a/ospf6d/ospf6_neighbor.h +++ b/ospf6d/ospf6_neighbor.h @@ -108,6 +108,44 @@ struct ospf6_neighbor #define OSPF6_NEIGHBOR_LOADING 7 #define OSPF6_NEIGHBOR_FULL 8 +/* Neighbor Events */ +#define OSPF6_NEIGHBOR_EVENT_NO_EVENT 0 +#define OSPF6_NEIGHBOR_EVENT_HELLO_RCVD 1 +#define OSPF6_NEIGHBOR_EVENT_TWOWAY_RCVD 2 +#define OSPF6_NEIGHBOR_EVENT_NEGOTIATION_DONE 3 +#define OSPF6_NEIGHBOR_EVENT_EXCHANGE_DONE 4 +#define OSPF6_NEIGHBOR_EVENT_LOADING_DONE 5 +#define OSPF6_NEIGHBOR_EVENT_ADJ_OK 6 +#define OSPF6_NEIGHBOR_EVENT_SEQNUMBER_MISMATCH 7 +#define OSPF6_NEIGHBOR_EVENT_BAD_LSREQ 8 +#define OSPF6_NEIGHBOR_EVENT_ONEWAY_RCVD 9 +#define OSPF6_NEIGHBOR_EVENT_INACTIVITY_TIMER 10 +#define OSPF6_NEIGHBOR_EVENT_MAX_EVENT 11 + +static const char *ospf6_neighbor_event_str[] = + { + "NoEvent", + "HelloReceived", + "2-WayReceived", + "NegotiationDone", + "ExchangeDone", + "LoadingDone", + "AdjOK?", + "SeqNumberMismatch", + "BadLSReq", + "1-WayReceived", + "InactivityTimer", + }; + +static const char *ospf6_neighbor_event_string (int event) +{ + #define OSPF6_NEIGHBOR_UNKNOWN_EVENT_STRING "UnknownEvent" + + if (event < OSPF6_NEIGHBOR_EVENT_MAX_EVENT) + return ospf6_neighbor_event_str[event]; + return OSPF6_NEIGHBOR_UNKNOWN_EVENT_STRING; +} + extern const char *ospf6_neighbor_state_str[]; diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c index dec7096c..1f7cdc85 100644 --- a/ospf6d/ospf6_top.c +++ b/ospf6d/ospf6_top.c @@ -338,6 +338,56 @@ DEFUN (ospf6_router_id, return CMD_SUCCESS; } +DEFUN (ospf6_log_adjacency_changes, + ospf6_log_adjacency_changes_cmd, + "log-adjacency-changes", + "Log changes in adjacency state\n") +{ + struct ospf6 *ospf6 = vty->index; + + SET_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_CHANGES); + return CMD_SUCCESS; +} + +DEFUN (ospf6_log_adjacency_changes_detail, + ospf6_log_adjacency_changes_detail_cmd, + "log-adjacency-changes detail", + "Log changes in adjacency state\n" + "Log all state changes\n") +{ + struct ospf6 *ospf6 = vty->index; + + SET_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_CHANGES); + SET_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_DETAIL); + return CMD_SUCCESS; +} + +DEFUN (no_ospf6_log_adjacency_changes, + no_ospf6_log_adjacency_changes_cmd, + "no log-adjacency-changes", + NO_STR + "Log changes in adjacency state\n") +{ + struct ospf6 *ospf6 = vty->index; + + UNSET_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_DETAIL); + UNSET_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_CHANGES); + return CMD_SUCCESS; +} + +DEFUN (no_ospf6_log_adjacency_changes_detail, + no_ospf6_log_adjacency_changes_detail_cmd, + "no log-adjacency-changes detail", + NO_STR + "Log changes in adjacency state\n" + "Log all state changes\n") +{ + struct ospf6 *ospf6 = vty->index; + + UNSET_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_DETAIL); + return CMD_SUCCESS; +} + DEFUN (ospf6_interface_area, ospf6_interface_area_cmd, "interface IFNAME area A.B.C.D", @@ -592,6 +642,16 @@ ospf6_show (struct vty *vty, struct ospf6 *o) vty_out (vty, " Number of areas in this router is %u%s", listcount (o->area_list), VNL); + if (CHECK_FLAG(o->config_flags, OSPF6_LOG_ADJACENCY_CHANGES)) + { + if (CHECK_FLAG(o->config_flags, OSPF6_LOG_ADJACENCY_DETAIL)) + vty_out(vty, " All adjacency changes are logged%s",VTY_NEWLINE); + else + vty_out(vty, " Adjacency changes are logged%s",VTY_NEWLINE); + } + + vty_out (vty, "%s",VTY_NEWLINE); + for (ALL_LIST_ELEMENTS_RO (o->area_list, n, oa)) ospf6_area_show (vty, oa); } @@ -782,6 +842,15 @@ config_write_ospf6 (struct vty *vty) if (ospf6->router_id_static != 0) vty_out (vty, " router-id %s%s", router_id, VNL); + /* log-adjacency-changes flag print. */ + if (CHECK_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_CHANGES)) + { + vty_out(vty, " log-adjacency-changes"); + if (CHECK_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_DETAIL)) + vty_out(vty, " detail"); + vty_out(vty, "%s", VTY_NEWLINE); + } + ospf6_stub_router_config_write (vty); ospf6_redistribute_config_write (vty); ospf6_area_config_write (vty); @@ -836,6 +905,10 @@ ospf6_top_init (void) install_default (OSPF6_NODE); install_element (OSPF6_NODE, &ospf6_router_id_cmd); + install_element (OSPF6_NODE, &ospf6_log_adjacency_changes_cmd); + install_element (OSPF6_NODE, &ospf6_log_adjacency_changes_detail_cmd); + install_element (OSPF6_NODE, &no_ospf6_log_adjacency_changes_cmd); + install_element (OSPF6_NODE, &no_ospf6_log_adjacency_changes_detail_cmd); install_element (OSPF6_NODE, &ospf6_interface_area_cmd); install_element (OSPF6_NODE, &no_ospf6_interface_area_cmd); install_element (OSPF6_NODE, &ospf6_stub_router_admin_cmd); diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h index 02eb9154..9d7cfd91 100644 --- a/ospf6d/ospf6_top.h +++ b/ospf6d/ospf6_top.h @@ -60,6 +60,11 @@ struct ospf6 u_char flag; + /* Configured flags */ + u_char config_flags; +#define OSPF6_LOG_ADJACENCY_CHANGES (1 << 0) +#define OSPF6_LOG_ADJACENCY_DETAIL (1 << 1) + /* SPF parameters */ unsigned int spf_delay; /* SPF delay time. */ unsigned int spf_holdtime; /* SPF hold time. */ From a0edf6740e8203abec1ee3efa344a417c16fec7b Mon Sep 17 00:00:00 2001 From: Dinesh Dutt Date: Mon, 26 Aug 2013 03:40:23 +0000 Subject: [PATCH 155/482] ospf6d: add SPF logs, statistics, and display of SPF parameters Signed-off-by: Pradosh Mohapatra Reviewed-by: Scott Feldman [DL: adapted to rebase / readded randomly lost line] [DL: killed timeval_subtract] Signed-off-by: David Lamparter --- ospf6d/ospf6_area.c | 6 ++-- ospf6d/ospf6_interface.c | 20 +++++++++--- ospf6d/ospf6_intra.c | 2 +- ospf6d/ospf6_spf.c | 52 ++++++++++++++++++++++++++++-- ospf6d/ospf6_spf.h | 69 +++++++++++++++++++++++++++++++++++++++- ospf6d/ospf6_top.c | 29 ++++++++++++++++- ospf6d/ospf6_top.h | 2 ++ ospf6d/ospf6d.h | 11 +++++++ 8 files changed, 180 insertions(+), 11 deletions(-) diff --git a/ospf6d/ospf6_area.c b/ospf6d/ospf6_area.c index 37e5c004..b09d9613 100644 --- a/ospf6d/ospf6_area.c +++ b/ospf6d/ospf6_area.c @@ -67,7 +67,8 @@ ospf6_area_lsdb_hook_add (struct ospf6_lsa *lsa) zlog_debug ("Schedule SPF Calculation for %s", OSPF6_AREA (lsa->lsdb->data)->name); } - ospf6_spf_schedule (OSPF6_PROCESS(OSPF6_AREA (lsa->lsdb->data)->ospf6)); + ospf6_spf_schedule (OSPF6_PROCESS(OSPF6_AREA (lsa->lsdb->data)->ospf6), + ospf6_lsadd_to_spf_reason(lsa)); break; case OSPF6_LSTYPE_INTRA_PREFIX: @@ -97,7 +98,8 @@ ospf6_area_lsdb_hook_remove (struct ospf6_lsa *lsa) zlog_debug ("Schedule SPF Calculation for %s", OSPF6_AREA (lsa->lsdb->data)->name); } - ospf6_spf_schedule (OSPF6_PROCESS(OSPF6_AREA (lsa->lsdb->data)->ospf6)); + ospf6_spf_schedule (OSPF6_PROCESS(OSPF6_AREA (lsa->lsdb->data)->ospf6), + ospf6_lsremove_to_spf_reason(lsa)); break; case OSPF6_LSTYPE_INTRA_PREFIX: diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index d1f5cba1..fee1632a 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -73,7 +73,7 @@ ospf6_interface_lookup_by_ifindex (int ifindex) /* schedule routing table recalculation */ static void -ospf6_interface_lsdb_hook (struct ospf6_lsa *lsa) +ospf6_interface_lsdb_hook (struct ospf6_lsa *lsa, unsigned int reason) { struct ospf6_interface *oi; @@ -86,7 +86,7 @@ ospf6_interface_lsdb_hook (struct ospf6_lsa *lsa) case OSPF6_LSTYPE_LINK: if (oi->state == OSPF6_INTERFACE_DR) OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT (oi); - ospf6_spf_schedule (oi->area->ospf6); + ospf6_spf_schedule (oi->area->ospf6, reason); break; default: @@ -94,6 +94,18 @@ ospf6_interface_lsdb_hook (struct ospf6_lsa *lsa) } } +static void +ospf6_interface_lsdb_hook_add (struct ospf6_lsa *lsa) +{ + ospf6_interface_lsdb_hook(lsa, ospf6_lsadd_to_spf_reason(lsa)); +} + +static void +ospf6_interface_lsdb_hook_remove (struct ospf6_lsa *lsa) +{ + ospf6_interface_lsdb_hook(lsa, ospf6_lsremove_to_spf_reason(lsa)); +} + static u_char ospf6_default_iftype(struct interface *ifp) { @@ -152,8 +164,8 @@ ospf6_interface_create (struct interface *ifp) oi->lsupdate_list = ospf6_lsdb_create (oi); oi->lsack_list = ospf6_lsdb_create (oi); oi->lsdb = ospf6_lsdb_create (oi); - oi->lsdb->hook_add = ospf6_interface_lsdb_hook; - oi->lsdb->hook_remove = ospf6_interface_lsdb_hook; + oi->lsdb->hook_add = ospf6_interface_lsdb_hook_add; + oi->lsdb->hook_remove = ospf6_interface_lsdb_hook_remove; oi->lsdb_self = ospf6_lsdb_create (oi); oi->route_connected = OSPF6_ROUTE_TABLE_CREATE (INTERFACE, CONNECTED_ROUTES); diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c index e86e46bf..4ad7521e 100644 --- a/ospf6d/ospf6_intra.c +++ b/ospf6d/ospf6_intra.c @@ -46,7 +46,7 @@ #include "ospf6_abr.h" #include "ospf6_flood.h" #include "ospf6d.h" - +#include "ospf6_spf.h" unsigned char conf_debug_ospf6_brouter = 0; u_int32_t conf_debug_ospf6_brouter_specific_router_id; diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c index 845e206c..3ef5485f 100644 --- a/ospf6d/ospf6_spf.c +++ b/ospf6d/ospf6_spf.c @@ -377,6 +377,36 @@ ospf6_spf_table_finish (struct ospf6_route_table *result_table) } } +static const char *ospf6_spf_reason_str[] = + { + "R+", + "R-", + "N+", + "N-", + "L+", + "L-", + "R*", + "N*", + }; + +void ospf6_spf_reason_string (unsigned int reason, char *buf, int size) +{ + int bit; + int len = 0; + + if (!buf) + return; + + for (bit = 0; bit <= (sizeof(ospf6_spf_reason_str) / sizeof(char *)); bit++) + { + if ((reason & (1 << bit)) && (len < size)) + { + len += snprintf((buf + len), (size - len), "%s%s", + (len > 0) ? ", " : "", ospf6_spf_reason_str[bit]); + } + } +} + /* RFC2328 16.1. Calculating the shortest-path tree for an area */ /* RFC2740 3.8.1. Calculating the shortest path tree for an area */ void @@ -515,6 +545,8 @@ ospf6_spf_calculation_thread (struct thread *t) struct timeval start, end, runtime; struct listnode *node; struct ospf6_route *route; + int areas_processed = 0; + char rbuf[32]; ospf6 = (struct ospf6 *)THREAD_ARG (t); ospf6->t_spf_calc = NULL; @@ -536,6 +568,8 @@ ospf6_spf_calculation_thread (struct thread *t) ospf6_spf_calculation (ospf6->router_id, oa->spf_table, oa); ospf6_intra_route_calculation (oa); ospf6_intra_brouter_calculation (oa); + + areas_processed++; } if (ospf6->backbone) @@ -550,6 +584,7 @@ ospf6_spf_calculation_thread (struct thread *t) ospf6->backbone); ospf6_intra_route_calculation(ospf6->backbone); ospf6_intra_brouter_calculation(ospf6->backbone); + areas_processed++; } /* Redo summaries if required */ @@ -562,23 +597,36 @@ ospf6_spf_calculation_thread (struct thread *t) ospf6->ts_spf_duration = runtime; + ospf6_spf_reason_string(ospf6->spf_reason, rbuf, sizeof(rbuf)); + if (IS_OSPF6_DEBUG_SPF (PROCESS) || IS_OSPF6_DEBUG_SPF (TIME)) zlog_debug ("SPF runtime: %ld sec %ld usec", runtime.tv_sec, runtime.tv_usec); + zlog_info("SPF processing: # Areas: %d, SPF runtime: %ld sec %ld usec, " + "Reason: %s\n", areas_processed, runtime.tv_sec, runtime.tv_usec, + rbuf); + ospf6->last_spf_reason = ospf6->spf_reason; + ospf6_reset_spf_reason(ospf6); return 0; } /* Add schedule for SPF calculation. To avoid frequenst SPF calc, we set timer for SPF calc. */ void -ospf6_spf_schedule (struct ospf6 *ospf6) +ospf6_spf_schedule (struct ospf6 *ospf6, unsigned int reason) { unsigned long delay, elapsed, ht; struct timeval now, result; + ospf6_set_spf_reason(ospf6, reason); + if (IS_OSPF6_DEBUG_SPF(PROCESS) || IS_OSPF6_DEBUG_SPF (TIME)) - zlog_debug ("SPF: calculation timer scheduled"); + { + char rbuf[32]; + ospf6_spf_reason_string(reason, rbuf, sizeof(rbuf)); + zlog_debug ("SPF: calculation timer scheduled (reason %s)", rbuf); + } /* OSPF instance does not exist. */ if (ospf6 == NULL) diff --git a/ospf6d/ospf6_spf.h b/ospf6d/ospf6_spf.h index 6c40424f..b3481dc3 100644 --- a/ospf6d/ospf6_spf.h +++ b/ospf6d/ospf6_spf.h @@ -79,11 +79,77 @@ struct ospf6_vertex #define VERTEX_IS_TYPE(t, v) \ ((v)->type == OSPF6_VERTEX_TYPE_ ## t ? 1 : 0) +/* What triggered the SPF? */ +#define OSPF6_SPF_FLAGS_ROUTER_LSA_ADDED (1 << 0) +#define OSPF6_SPF_FLAGS_ROUTER_LSA_REMOVED (1 << 1) +#define OSPF6_SPF_FLAGS_NETWORK_LSA_ADDED (1 << 2) +#define OSPF6_SPF_FLAGS_NETWORK_LSA_REMOVED (1 << 3) +#define OSPF6_SPF_FLAGS_LINK_LSA_ADDED (1 << 4) +#define OSPF6_SPF_FLAGS_LINK_LSA_REMOVED (1 << 5) +#define OSPF6_SPF_FLAGS_ROUTER_LSA_ORIGINATED (1 << 6) +#define OSPF6_SPF_FLAGS_NETWORK_LSA_ORIGINATED (1 << 7) + +static inline void +ospf6_set_spf_reason (struct ospf6* ospf, unsigned int reason) +{ + ospf->spf_reason |= reason; +} + +static inline void +ospf6_reset_spf_reason (struct ospf6 *ospf) +{ + ospf->spf_reason = 0; +} + +static inline unsigned int +ospf6_lsadd_to_spf_reason (struct ospf6_lsa *lsa) +{ + unsigned int reason = 0; + + switch (ntohs (lsa->header->type)) + { + case OSPF6_LSTYPE_ROUTER: + reason = OSPF6_SPF_FLAGS_ROUTER_LSA_ADDED; + break; + case OSPF6_LSTYPE_NETWORK: + reason = OSPF6_SPF_FLAGS_NETWORK_LSA_ADDED; + break; + case OSPF6_LSTYPE_LINK: + reason = OSPF6_SPF_FLAGS_LINK_LSA_ADDED; + break; + default: + break; + } + return (reason); +} + +static inline unsigned int +ospf6_lsremove_to_spf_reason (struct ospf6_lsa *lsa) +{ + unsigned int reason = 0; + + switch (ntohs (lsa->header->type)) + { + case OSPF6_LSTYPE_ROUTER: + reason = OSPF6_SPF_FLAGS_ROUTER_LSA_REMOVED; + break; + case OSPF6_LSTYPE_NETWORK: + reason = OSPF6_SPF_FLAGS_NETWORK_LSA_REMOVED; + break; + case OSPF6_LSTYPE_LINK: + reason = OSPF6_SPF_FLAGS_LINK_LSA_REMOVED; + break; + default: + break; + } + return (reason); +} + extern void ospf6_spf_table_finish (struct ospf6_route_table *result_table); extern void ospf6_spf_calculation (u_int32_t router_id, struct ospf6_route_table *result_table, struct ospf6_area *oa); -extern void ospf6_spf_schedule (struct ospf6 *ospf); +extern void ospf6_spf_schedule (struct ospf6 *ospf, unsigned int reason); extern void ospf6_spf_display_subtree (struct vty *vty, const char *prefix, int rest, struct ospf6_vertex *v); @@ -92,6 +158,7 @@ extern void ospf6_spf_config_write (struct vty *vty); extern int config_write_ospf6_debug_spf (struct vty *vty); extern void install_element_ospf6_debug_spf (void); extern void ospf6_spf_init (void); +extern void ospf6_spf_reason_string (unsigned int reason, char *buf, int size); #endif /* OSPF6_SPF_H */ diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c index 1f7cdc85..f83e6ab5 100644 --- a/ospf6d/ospf6_top.c +++ b/ospf6d/ospf6_top.c @@ -615,7 +615,8 @@ ospf6_show (struct vty *vty, struct ospf6 *o) struct listnode *n; struct ospf6_area *oa; char router_id[16], duration[32]; - struct timeval now, running; + struct timeval now, running, result; + char buf[32], rbuf[32]; /* process id, router id */ inet_ntop (AF_INET, &o->router_id, router_id, sizeof (router_id)); @@ -631,6 +632,32 @@ ospf6_show (struct vty *vty, struct ospf6 *o) /* Redistribute configuration */ /* XXX */ + /* Show SPF parameters */ + vty_out(vty, " Initial SPF scheduling delay %d millisec(s)%s" + " Minimum hold time between consecutive SPFs %d millsecond(s)%s" + " Maximum hold time between consecutive SPFs %d millsecond(s)%s" + " Hold time multiplier is currently %d%s", + o->spf_delay, VNL, + o->spf_holdtime, VNL, + o->spf_max_holdtime, VNL, + o->spf_hold_multiplier, VNL); + + vty_out(vty, " SPF algorithm "); + if (o->ts_spf.tv_sec || o->ts_spf.tv_usec) + { + timersub(&now, &o->ts_spf, &result); + timerstring(&result, buf, sizeof(buf)); + ospf6_spf_reason_string(o->last_spf_reason, rbuf, sizeof(rbuf)); + vty_out(vty, "last executed %s ago, reason %s%s", buf, rbuf, VNL); + vty_out (vty, " Last SPF duration %ld sec %ld usec%s", + o->ts_spf_duration.tv_sec, o->ts_spf_duration.tv_usec, VNL); + } + else + vty_out(vty, "has not been run$%s", VNL); + threadtimer_string(now, o->t_spf_calc, buf, sizeof(buf)); + vty_out (vty, " SPF timer %s%s%s", + (o->t_spf_calc ? "due in " : "is "), buf, VNL); + if (CHECK_FLAG (o->flag, OSPF6_STUB_ROUTER)) vty_out (vty, " Router Is Stub Router%s", VNL); diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h index 9d7cfd91..866f92f9 100644 --- a/ospf6d/ospf6_top.h +++ b/ospf6d/ospf6_top.h @@ -70,9 +70,11 @@ struct ospf6 unsigned int spf_holdtime; /* SPF hold time. */ unsigned int spf_max_holdtime; /* SPF maximum-holdtime */ unsigned int spf_hold_multiplier; /* Adaptive multiplier for hold time */ + unsigned int spf_reason; /* reason bits while scheduling SPF */ struct timeval ts_spf; /* SPF calculation time stamp. */ struct timeval ts_spf_duration; /* Execution time of last SPF */ + unsigned int last_spf_reason; /* Last SPF reason */ /* Threads */ struct thread *t_spf_calc; /* SPF calculation timer. */ diff --git a/ospf6d/ospf6d.h b/ospf6d/ospf6d.h index 13699d61..0c86386a 100644 --- a/ospf6d/ospf6d.h +++ b/ospf6d/ospf6d.h @@ -101,6 +101,17 @@ extern struct thread_master *master; zlog_warn ("strftime error"); \ } while (0) +#define threadtimer_string(now, t, buf, size) \ + do { \ + struct timeval result; \ + if (!t) \ + snprintf(buf, size, "inactive"); \ + else { \ + timersub(&t->u.sands, &now, &result); \ + timerstring(&result, buf, size); \ + } \ +} while (0) + /* for commands */ #define OSPF6_AREA_STR "Area information\n" #define OSPF6_AREA_ID_STR "Area ID (as an IPv4 notation)\n" From ba960d5ae43e49991ed70fbe9ffa2e4567437a31 Mon Sep 17 00:00:00 2001 From: Dinesh Dutt Date: Mon, 26 Aug 2013 03:40:37 +0000 Subject: [PATCH 156/482] ospf6d: fix integrated config With integrated config, the line defining an interface to be p2p is defined before assigning the interface to an area. When during the interface transition, there is an attempt to generate a router LSA, the process crashes. This fix addresses that. Signed-off-by: Dinesh G Dutt Reviewed-by: Pradosh Mohapatra Reviewed-by: Scott Feldman Signed-off-by: David Lamparter --- ospf6d/ospf6_interface.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index fee1632a..111b929a 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -654,6 +654,14 @@ interface_up (struct thread *thread) return 0; } + /* If no area assigned, return */ + if (oi->area == NULL) + { + zlog_debug ("%s: Not scheduleing Hello for %s as there is no area assigned yet", __func__, + oi->interface->name); + return 0; + } + /* Join AllSPFRouters */ ospf6_sso (oi->interface->ifindex, &allspfrouters6, IPV6_JOIN_GROUP); From 7d4aa1d57d54a57aae78e6b12cf4524e2c43a514 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Thu, 13 Dec 2012 16:10:05 +0100 Subject: [PATCH 157/482] ospf6d: don't run DR election early on "ipv6 ospf6 priority" On changing the router priority, DR election should only be run when it was completed at least once before. Signed-off-by: Christian Franke --- ospf6d/ospf6_interface.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index 111b929a..c692f2c7 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -1333,7 +1333,10 @@ DEFUN (ipv6_ospf6_priority, oi->priority = strtol (argv[0], NULL, 10); - if (oi->area) + if (oi->area && + (oi->state == OSPF6_INTERFACE_DROTHER || + oi->state == OSPF6_INTERFACE_BDR || + oi->state == OSPF6_INTERFACE_DR)) ospf6_interface_state_change (dr_election (oi), oi); return CMD_SUCCESS; From 1579a67f130ca34df9acefac14ebcdfdd8f6600a Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Fri, 8 Mar 2013 02:35:38 +0100 Subject: [PATCH 158/482] ospf6d: set cmsg size correctly On both Linux and FreeBSD, msg_controllen should be set to CMSG_LEN, not CMSG_SPACE. This avoids sending 4 bytes of trailing garbage to the kernel. Signed-off-by: Christian Franke --- ospf6d/ospf6_network.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ospf6d/ospf6_network.c b/ospf6d/ospf6_network.c index eed7f9d6..74cfbec7 100644 --- a/ospf6d/ospf6_network.c +++ b/ospf6d/ospf6_network.c @@ -206,7 +206,7 @@ ospf6_sendmsg (struct in6_addr *src, struct in6_addr *dst, smsghdr.msg_name = (caddr_t) &dst_sin6; smsghdr.msg_namelen = sizeof (struct sockaddr_in6); smsghdr.msg_control = (caddr_t) cmsgbuf; - smsghdr.msg_controllen = sizeof (cmsgbuf); + smsghdr.msg_controllen = scmsgp->cmsg_len; retval = sendmsg (ospf6_sock, &smsghdr, 0); if (retval != iov_totallen (message)) From 37531a7ec380554b18c004bcae9f5a070385d132 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Thu, 13 Dec 2012 13:50:28 +0100 Subject: [PATCH 159/482] ospf6d: clear DR info on interface_down This fixes an issue where ospf6d would send incorrect hellos and perform wrong DR election when an interface went down and up again. Signed-off-by: Christian Franke --- ospf6d/ospf6_interface.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index c692f2c7..86c0bf68 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -769,6 +769,10 @@ interface_down (struct thread *thread) list_delete_all_node (oi->neighbor_list); + /* When interface state is reset, also reset information about + * DR election, as it is no longer valid. */ + oi->drouter = oi->prev_drouter = htonl(0); + oi->bdrouter = oi->prev_bdrouter = htonl(0); return 0; } From 6ee06fa9ed91412cb745668d462031cdbe2642e0 Mon Sep 17 00:00:00 2001 From: Pradosh Mohapatra Date: Sun, 12 Jan 2014 18:30:13 +0000 Subject: [PATCH 160/482] bgpd: bgpd-set-v4-nexthop-for-v6-peering.patch BGP: While advertising v4 prefixes over a v6 session, set the correct v4 nexthop. ISSUE: For an IPv6 peer, BGPd sets the local router-id as the next-hop's v4 address. This is incorrect as the router-id may not be a valid next-hop to be included in UPDATEs that contain v4 prefixes. PATCH: Set the v4 address in the next-hop field based on the interface that the peering is on (directly connected interface or loopback). Signed-off-by: Pradosh Mohapatra Reviewed-by: Scott Feldman Acked-by: Feng Lu --- bgpd/bgp_zebra.c | 24 ++++++++++++++++++++++-- lib/prefix.h | 10 ++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 60443830..26b97c2c 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -534,6 +534,25 @@ if_get_ipv6_local (struct interface *ifp, struct in6_addr *addr) } #endif /* HAVE_IPV6 */ +static int +if_get_ipv4_address (struct interface *ifp, struct in_addr *addr) +{ + struct listnode *cnode; + struct connected *connected; + struct prefix *cp; + + for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, connected)) + { + cp = connected->address; + if ((cp->family == AF_INET) && !ipv4_martian(&(cp->u.prefix4))) + { + *addr = cp->u.prefix4; + return 1; + } + } + return 0; +} + int bgp_nexthop_set (union sockunion *local, union sockunion *remote, struct bgp_nexthop *nexthop, struct peer *peer) @@ -592,8 +611,9 @@ bgp_nexthop_set (union sockunion *local, union sockunion *remote, { struct interface *direct = NULL; - /* IPv4 nexthop. I don't care about it. */ - if (peer->local_id.s_addr) + /* IPv4 nexthop. */ + ret = if_get_ipv4_address(ifp, &nexthop->v4); + if (!ret && peer->local_id.s_addr) nexthop->v4 = peer->local_id; /* Global address*/ diff --git a/lib/prefix.h b/lib/prefix.h index 7f0d3607..8c8992e8 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -196,4 +196,14 @@ extern const char *inet6_ntoa (struct in6_addr); extern int all_digit (const char *); +static inline int ipv4_martian (struct in_addr *addr) +{ + in_addr_t ip = addr->s_addr; + + if (IPV4_NET0(ip) || IPV4_NET127(ip) || IPV4_CLASS_DE(ip)) { + return 1; + } + return 0; +} + #endif /* _ZEBRA_PREFIX_H */ From a25a1264a5615a90e9ca9f60ccc1f397ca55bc56 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Wed, 27 Nov 2013 14:36:05 +0000 Subject: [PATCH 161/482] ospfd: fixup log message in ospf_zebra_delete Signed-off-by: Christian Franke Acked-by: Feng Lu --- ospfd/ospf_zebra.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index 34a3b2a7..b5268a3b 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -482,7 +482,7 @@ ospf_zebra_delete (struct prefix_ipv4 *p, struct ospf_route *or) if (IS_DEBUG_OSPF (zebra, ZEBRA_REDISTRIBUTE)) { char buf[2][INET_ADDRSTRLEN]; - zlog_debug("Zebra: Route add %s/%d nexthop %s", + zlog_debug("Zebra: Route delete %s/%d nexthop %s", inet_ntop(AF_INET, &p->prefix, buf[0], sizeof(buf[0])), p->prefixlen, From 23f5f7c3dd805b7d6a46d86d23aaa5c71273a84a Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Wed, 27 Nov 2013 17:06:14 +0000 Subject: [PATCH 162/482] zebra: match gateway when deleting NEXTHOP_IPV4_IFINDEX routes Signed-off-by: Christian Franke Acked-by: Feng Lu --- zebra/zserv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/zebra/zserv.c b/zebra/zserv.c index 5df521b0..55ac6e4f 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -879,6 +879,7 @@ zread_ipv4_delete (struct zserv *client, u_short length) break; case ZEBRA_NEXTHOP_IPV4_IFINDEX: nexthop.s_addr = stream_get_ipv4 (s); + nexthop_p = &nexthop; ifindex = stream_getl (s); break; case ZEBRA_NEXTHOP_IPV6: From b52aef18a9f3acc8b24ab5c2631dc574b8e2ec70 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Wed, 27 Nov 2013 17:06:15 +0000 Subject: [PATCH 163/482] zebra: log routes w/o gateway in rib_delete_ipv4 Signed-off-by: Christian Franke Acked-by: Feng Lu Signed-off-by: David Lamparter --- zebra/zebra_rib.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 77c0d8ca..6616f9a1 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -2160,12 +2160,20 @@ rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p, /* Apply mask. */ apply_mask_ipv4 (p); - if (IS_ZEBRA_DEBUG_KERNEL && gate) - zlog_debug ("rib_delete_ipv4(): route delete %s/%d via %s ifindex %d", - inet_ntop (AF_INET, &p->prefix, buf1, INET_ADDRSTRLEN), - p->prefixlen, - inet_ntoa (*gate), - ifindex); + if (IS_ZEBRA_DEBUG_KERNEL) + { + if (gate) + zlog_debug ("rib_delete_ipv4(): route delete %s/%d via %s ifindex %d", + inet_ntop (AF_INET, &p->prefix, buf1, INET_ADDRSTRLEN), + p->prefixlen, + inet_ntoa (*gate), + ifindex); + else + zlog_debug ("rib_delete_ipv4(): route delete %s/%d ifindex %d", + inet_ntop (AF_INET, &p->prefix, buf1, INET_ADDRSTRLEN), + p->prefixlen, + ifindex); + } /* Lookup route node. */ rn = route_node_lookup (table, (struct prefix *) p); From a11e012e8661629d665e992e765741a5eaa7d017 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Sun, 8 Sep 2013 13:48:34 +0000 Subject: [PATCH 164/482] security: Fix some typos and potential NULL-deref This patch against the git tree fixes minor typos, some of them possibily leading to NULL-pointer dereference in rare conditions. Signed-off-by: Remi Gacogne Signed-off-by: Joachim Nilsson Acked-by: Feng Lu --- lib/vty.c | 2 ++ ospf6d/ospf6_snmp.c | 2 +- ospfd/ospf_asbr.c | 3 ++- ospfd/ospf_te.c | 3 ++- zebra/irdp_packet.c | 2 +- 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/vty.c b/lib/vty.c index 0d6345c8..96cb1e4b 100644 --- a/lib/vty.c +++ b/lib/vty.c @@ -1856,9 +1856,11 @@ vty_serv_sock_family (const char* addr, unsigned short port, int family) { case AF_INET: naddr=&su.sin.sin_addr; + break; #ifdef HAVE_IPV6 case AF_INET6: naddr=&su.sin6.sin6_addr; + break; #endif } diff --git a/ospf6d/ospf6_snmp.c b/ospf6d/ospf6_snmp.c index f8a3b920..46603927 100644 --- a/ospf6d/ospf6_snmp.c +++ b/ospf6d/ospf6_snmp.c @@ -528,7 +528,7 @@ ospfv3AreaEntry (struct variable *v, oid *name, size_t *length, return NULL; len = *length - v->namelen; - len = (len >= 1 ? sizeof 1 : 0); + len = (len >= 1 ? 1 : 0); if (exact && len != 1) return NULL; if (len) diff --git a/ospfd/ospf_asbr.c b/ospfd/ospf_asbr.c index a23b4f2b..7e7c84fd 100644 --- a/ospfd/ospf_asbr.c +++ b/ospfd/ospf_asbr.c @@ -164,7 +164,8 @@ ospf_external_info_add (u_char type, struct prefix_ipv4 p, new->nexthop = nexthop; new->tag = 0; - rn->info = new; + if (rn) + rn->info = new; if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) zlog_debug ("Redistribute[%s]: %s/%d external info created.", diff --git a/ospfd/ospf_te.c b/ospfd/ospf_te.c index 587564a1..c605ce68 100644 --- a/ospfd/ospf_te.c +++ b/ospfd/ospf_te.c @@ -1036,7 +1036,8 @@ ospf_mpls_te_lsa_refresh (struct ospf_lsa *lsa) /* If the lsa's age reached to MaxAge, start flushing procedure. */ if (IS_LSA_MAXAGE (lsa)) { - lp->flags &= ~LPFLG_LSA_ENGAGED; + if (lp) + lp->flags &= ~LPFLG_LSA_ENGAGED; ospf_opaque_lsa_flush_schedule (lsa); goto out; } diff --git a/zebra/irdp_packet.c b/zebra/irdp_packet.c index 28dc171e..50525043 100644 --- a/zebra/irdp_packet.c +++ b/zebra/irdp_packet.c @@ -287,7 +287,7 @@ send_packet(struct interface *ifp, if (!(ifp->flags & IFF_UP)) return; - if (!p) + if (p) src = ntohl(p->u.prefix4.s_addr); else src = 0; /* Is filled in */ From d9628728e0924ae13ef6e8f8a67a2c9802745184 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Fri, 8 Mar 2013 21:47:35 +0100 Subject: [PATCH 165/482] ospf6d: improve ordered shutdown Improve the _disable/_enable infrastructure so it gets into a more usable shape and make 'no router ospf6' actually work. Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- ospf6d/ospf6_area.c | 29 +++++++++++++++++++++++------ ospf6d/ospf6_asbr.c | 18 ++++++++++++++++-- ospf6d/ospf6_asbr.h | 1 + ospf6d/ospf6_interface.c | 22 ++++++++++++---------- ospf6d/ospf6_intra.h | 15 ++++++++++----- ospf6d/ospf6_main.c | 10 ++++++++-- ospf6d/ospf6_message.c | 2 +- ospf6d/ospf6_top.c | 28 ++++++++++++++++++++-------- ospf6d/ospf6_zebra.c | 4 +++- 9 files changed, 94 insertions(+), 35 deletions(-) diff --git a/ospf6d/ospf6_area.c b/ospf6d/ospf6_area.c index b09d9613..9a4e30e1 100644 --- a/ospf6d/ospf6_area.c +++ b/ospf6d/ospf6_area.c @@ -193,18 +193,21 @@ ospf6_area_create (u_int32_t area_id, struct ospf6 *o) void ospf6_area_delete (struct ospf6_area *oa) { - struct listnode *n, *nnode; + struct listnode *n; struct ospf6_interface *oi; ospf6_route_table_delete (oa->range_table); ospf6_route_table_delete (oa->summary_prefix); ospf6_route_table_delete (oa->summary_router); - /* ospf6 interface list */ - for (ALL_LIST_ELEMENTS (oa->if_list, n, nnode, oi)) - { - ospf6_interface_delete (oi); - } + /* The ospf6_interface structs store configuration + * information which should not be lost/reset when + * deleting an area. + * So just detach the interface from the area and + * keep it around. */ + for (ALL_LIST_ELEMENTS_RO (oa->if_list, n, oi)) + oi->area = NULL; + list_delete (oa->if_list); ospf6_lsdb_delete (oa->lsdb); @@ -257,6 +260,7 @@ ospf6_area_enable (struct ospf6_area *oa) for (ALL_LIST_ELEMENTS (oa->if_list, node, nnode, oi)) ospf6_interface_enable (oi); + ospf6_abr_enable_area (oa); } void @@ -269,6 +273,19 @@ ospf6_area_disable (struct ospf6_area *oa) for (ALL_LIST_ELEMENTS (oa->if_list, node, nnode, oi)) ospf6_interface_disable (oi); + + ospf6_abr_disable_area (oa); + ospf6_lsdb_remove_all (oa->lsdb); + ospf6_lsdb_remove_all (oa->lsdb_self); + + ospf6_spf_table_finish(oa->spf_table); + ospf6_route_remove_all(oa->route_table); + + THREAD_OFF (oa->thread_spf_calculation); + THREAD_OFF (oa->thread_route_calculation); + + THREAD_OFF (oa->thread_router_lsa); + THREAD_OFF (oa->thread_intra_prefix_lsa); } diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index 60df6e6c..3605e3f8 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -409,6 +409,8 @@ ospf6_asbr_redistribute_unset (int type) ospf6_asbr_redistribute_remove (info->type, route->nexthop[0].ifindex, &route->prefix); } + + ospf6_asbr_routemap_unset (type); } void @@ -636,7 +638,6 @@ DEFUN (ospf6_redistribute, return CMD_WARNING; ospf6_asbr_redistribute_unset (type); - ospf6_asbr_routemap_unset (type); ospf6_asbr_redistribute_set (type); return CMD_SUCCESS; } @@ -677,7 +678,6 @@ DEFUN (no_ospf6_redistribute, return CMD_WARNING; ospf6_asbr_redistribute_unset (type); - ospf6_asbr_routemap_unset (type); return CMD_SUCCESS; } @@ -1312,6 +1312,20 @@ ospf6_asbr_init (void) install_element (OSPF6_NODE, &no_ospf6_redistribute_cmd); } +void +ospf6_asbr_redistribute_reset (void) +{ + int type; + + for (type = 0; type < ZEBRA_ROUTE_MAX; type++) + { + if (type == ZEBRA_ROUTE_OSPF6) + continue; + if (ospf6_zebra_is_redistribute (type)) + ospf6_asbr_redistribute_unset(type); + } +} + void ospf6_asbr_terminate (void) { diff --git a/ospf6d/ospf6_asbr.h b/ospf6d/ospf6_asbr.h index 72e49143..73770cc0 100644 --- a/ospf6d/ospf6_asbr.h +++ b/ospf6d/ospf6_asbr.h @@ -89,6 +89,7 @@ extern void ospf6_asbr_redistribute_remove (int type, int ifindex, extern int ospf6_redistribute_config_write (struct vty *vty); extern void ospf6_asbr_init (void); +extern void ospf6_asbr_redistribute_reset (void); extern void ospf6_asbr_terminate (void); extern int config_write_ospf6_debug_asbr (struct vty *vty); diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index 86c0bf68..d9d2d03b 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -219,31 +219,28 @@ void ospf6_interface_enable (struct ospf6_interface *oi) { UNSET_FLAG (oi->flag, OSPF6_INTERFACE_DISABLE); - - oi->thread_send_hello = - thread_add_event (master, ospf6_hello_send, oi, 0); + ospf6_interface_state_update (oi->interface); } void ospf6_interface_disable (struct ospf6_interface *oi) { - struct listnode *node, *nnode; - struct ospf6_neighbor *on; - SET_FLAG (oi->flag, OSPF6_INTERFACE_DISABLE); - for (ALL_LIST_ELEMENTS (oi->neighbor_list, node, nnode, on)) - ospf6_neighbor_delete (on); - - list_delete_all_node (oi->neighbor_list); + thread_execute (master, interface_down, oi, 0); ospf6_lsdb_remove_all (oi->lsdb); + ospf6_lsdb_remove_all (oi->lsdb_self); ospf6_lsdb_remove_all (oi->lsupdate_list); ospf6_lsdb_remove_all (oi->lsack_list); THREAD_OFF (oi->thread_send_hello); THREAD_OFF (oi->thread_send_lsupdate); THREAD_OFF (oi->thread_send_lsack); + + THREAD_OFF (oi->thread_network_lsa); + THREAD_OFF (oi->thread_link_lsa); + THREAD_OFF (oi->thread_intra_prefix_lsa); } static struct in6_addr * @@ -327,6 +324,8 @@ ospf6_interface_state_update (struct interface *ifp) return; if (oi->area == NULL) return; + if (CHECK_FLAG (oi->flag, OSPF6_INTERFACE_DISABLE)) + return; if (if_is_operative (ifp)) thread_add_event (master, interface_up, oi, 0); @@ -355,6 +354,9 @@ ospf6_interface_connected_route_update (struct interface *ifp) if (oi->area == NULL) return; + if (CHECK_FLAG (oi->flag, OSPF6_INTERFACE_DISABLE)) + return; + /* update "route to advertise" interface route table */ ospf6_route_remove_all (oi->route_connected); diff --git a/ospf6d/ospf6_intra.h b/ospf6d/ospf6_intra.h index a25efa12..e909da23 100644 --- a/ospf6d/ospf6_intra.h +++ b/ospf6d/ospf6_intra.h @@ -156,32 +156,37 @@ struct ospf6_intra_prefix_lsa #define OSPF6_ROUTER_LSA_SCHEDULE(oa) \ do { \ - if (! (oa)->thread_router_lsa) \ + if (! (oa)->thread_router_lsa \ + && CHECK_FLAG((oa)->flag, OSPF6_AREA_ENABLE)) \ (oa)->thread_router_lsa = \ thread_add_event (master, ospf6_router_lsa_originate, oa, 0); \ } while (0) #define OSPF6_NETWORK_LSA_SCHEDULE(oi) \ do { \ - if (! (oi)->thread_network_lsa) \ + if (! (oi)->thread_network_lsa \ + && ! CHECK_FLAG((oi)->flag, OSPF6_INTERFACE_DISABLE)) \ (oi)->thread_network_lsa = \ thread_add_event (master, ospf6_network_lsa_originate, oi, 0); \ } while (0) #define OSPF6_LINK_LSA_SCHEDULE(oi) \ do { \ - if (! (oi)->thread_link_lsa) \ + if (! (oi)->thread_link_lsa \ + && ! CHECK_FLAG((oi)->flag, OSPF6_INTERFACE_DISABLE)) \ (oi)->thread_link_lsa = \ thread_add_event (master, ospf6_link_lsa_originate, oi, 0); \ } while (0) #define OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(oa) \ do { \ - if (! (oa)->thread_intra_prefix_lsa) \ + if (! (oa)->thread_intra_prefix_lsa \ + && CHECK_FLAG((oa)->flag, OSPF6_AREA_ENABLE)) \ (oa)->thread_intra_prefix_lsa = \ thread_add_event (master, ospf6_intra_prefix_lsa_originate_stub, \ oa, 0); \ } while (0) #define OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT(oi) \ do { \ - if (! (oi)->thread_intra_prefix_lsa) \ + if (! (oi)->thread_intra_prefix_lsa \ + && ! CHECK_FLAG((oi)->flag, OSPF6_INTERFACE_DISABLE)) \ (oi)->thread_intra_prefix_lsa = \ thread_add_event (master, ospf6_intra_prefix_lsa_originate_transit, \ oi, 0); \ diff --git a/ospf6d/ospf6_main.c b/ospf6d/ospf6_main.c index 17d7654e..e9919713 100644 --- a/ospf6d/ospf6_main.c +++ b/ospf6d/ospf6_main.c @@ -41,6 +41,8 @@ #include "ospf6_message.h" #include "ospf6_asbr.h" #include "ospf6_lsa.h" +#include "ospf6_interface.h" +#include "ospf6_zebra.h" /* Default configuration file name for ospf6d. */ #define OSPF6_DEFAULT_CONFIG "ospf6d.conf" @@ -134,12 +136,16 @@ Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS); static void __attribute__ ((noreturn)) ospf6_exit (int status) { - extern struct ospf6 *ospf6; - extern struct zclient *zclient; + struct listnode *node; + struct interface *ifp; if (ospf6) ospf6_delete (ospf6); + for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) + if (ifp->info != NULL) + ospf6_interface_delete(ifp->info); + ospf6_message_terminate (); ospf6_asbr_terminate (); ospf6_lsa_terminate (); diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c index caebf5d6..5fb5a216 100644 --- a/ospf6d/ospf6_message.c +++ b/ospf6d/ospf6_message.c @@ -1543,7 +1543,7 @@ ospf6_receive (struct thread *thread) } oi = ospf6_interface_lookup_by_ifindex (ifindex); - if (oi == NULL || oi->area == NULL) + if (oi == NULL || oi->area == NULL || CHECK_FLAG(oi->flag, OSPF6_INTERFACE_DISABLE)) { zlog_debug ("Message received on disabled interface"); return 0; diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c index f83e6ab5..7c0922a6 100644 --- a/ospf6d/ospf6_top.c +++ b/ospf6d/ospf6_top.c @@ -161,6 +161,8 @@ ospf6_delete (struct ospf6 *o) for (ALL_LIST_ELEMENTS (o->area_list, node, nnode, oa)) ospf6_area_delete (oa); + + list_delete (o->area_list); ospf6_lsdb_delete (o->lsdb); @@ -202,9 +204,16 @@ ospf6_disable (struct ospf6 *o) for (ALL_LIST_ELEMENTS (o->area_list, node, nnode, oa)) ospf6_area_disable (oa); + /* XXX: This also changes persistent settings */ + ospf6_asbr_redistribute_reset(); + ospf6_lsdb_remove_all (o->lsdb); ospf6_route_remove_all (o->route_table); ospf6_route_remove_all (o->brouter_table); + + THREAD_OFF(o->maxage_remover); + THREAD_OFF(o->t_spf_calc); + THREAD_OFF(o->t_ase_calc); } } @@ -282,8 +291,6 @@ DEFUN (router_ospf6, { if (ospf6 == NULL) ospf6 = ospf6_create (); - if (CHECK_FLAG (ospf6->flag, OSPF6_DISABLED)) - ospf6_enable (ospf6); /* set current ospf point. */ vty->node = OSPF6_NODE; @@ -299,10 +306,13 @@ DEFUN (no_router_ospf6, NO_STR OSPF6_ROUTER_STR) { - if (ospf6 == NULL || CHECK_FLAG (ospf6->flag, OSPF6_DISABLED)) - vty_out (vty, "OSPFv3 is not running%s", VNL); + if (ospf6 == NULL) + vty_out (vty, "OSPFv3 is not configured%s", VNL); else - ospf6_disable (ospf6); + { + ospf6_delete (ospf6); + ospf6 = NULL; + } /* return to config node . */ vty->node = CONFIG_NODE; @@ -435,8 +445,12 @@ DEFUN (ospf6_interface_area, SET_FLAG (oa->flag, OSPF6_AREA_ENABLE); + /* ospf6 process is currently disabled, not much more to do */ + if (CHECK_FLAG (o->flag, OSPF6_DISABLED)) + return CMD_SUCCESS; + /* start up */ - thread_add_event (master, interface_up, oi, 0); + ospf6_interface_enable (oi); /* If the router is ABR, originate summary routes */ if (ospf6_is_router_abr (o)) @@ -861,8 +875,6 @@ config_write_ospf6 (struct vty *vty) /* OSPFv6 configuration. */ if (ospf6 == NULL) return CMD_SUCCESS; - if (CHECK_FLAG (ospf6->flag, OSPF6_DISABLED)) - return CMD_SUCCESS; inet_ntop (AF_INET, &ospf6->router_id_static, router_id, sizeof (router_id)); vty_out (vty, "router ospf6%s", VNL); diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c index f09e9d22..50ecc170 100644 --- a/ospf6d/ospf6_zebra.c +++ b/ospf6d/ospf6_zebra.c @@ -117,7 +117,9 @@ ospf6_zebra_if_del (int command, struct zclient *zclient, zebra_size_t length) ifp->name, ifp->ifindex, ifp->mtu6); #if 0 - /* Why is this commented out? */ + /* XXX: ospf6_interface_if_del is not the right way to handle this, + * because among other thinkable issues, it will also clear all + * settings as they are contained in the struct ospf6_interface. */ ospf6_interface_if_del (ifp); #endif /*0*/ From b13c1d9299d6426f48f074545f3e403e5a9b8a61 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Thu, 13 Dec 2012 16:11:16 +0100 Subject: [PATCH 166/482] ospf6d: handle missing link local address more gracefully ospf6 can't run on an interface without a link local address. Don't start the state machine when an interface comes up without such an ip and bring it up later, when a usable link local address is added. Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- ospf6d/ospf6_interface.c | 17 ++++++++++++++--- ospf6d/ospf6_zebra.c | 11 ++++++++--- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index d9d2d03b..8d427645 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -287,8 +287,7 @@ ospf6_interface_if_add (struct interface *ifp) } /* interface start */ - if (oi->area) - thread_add_event (master, interface_up, oi, 0); + ospf6_interface_state_update(oi->interface); } void @@ -327,7 +326,9 @@ ospf6_interface_state_update (struct interface *ifp) if (CHECK_FLAG (oi->flag, OSPF6_INTERFACE_DISABLE)) return; - if (if_is_operative (ifp)) + if (if_is_operative (ifp) + && (ospf6_interface_get_linklocal_address(oi->interface) + || if_is_loopback(oi->interface))) thread_add_event (master, interface_up, oi, 0); else thread_add_event (master, interface_down, oi, 0); @@ -647,6 +648,16 @@ interface_up (struct thread *thread) return 0; } + /* check interface has a link-local address */ + if (! (ospf6_interface_get_linklocal_address(oi->interface) + || if_is_loopback(oi->interface))) + { + if (IS_OSPF6_DEBUG_INTERFACE) + zlog_debug ("Interface %s has no link local address, can't execute [InterfaceUp]", + oi->interface->name); + return 0; + } + /* if already enabled, do nothing */ if (oi->state > OSPF6_INTERFACE_DOWN) { diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c index 50ecc170..cffd7675 100644 --- a/ospf6d/ospf6_zebra.c +++ b/ospf6d/ospf6_zebra.c @@ -165,8 +165,10 @@ ospf6_zebra_if_address_update_add (int command, struct zclient *zclient, buf, sizeof (buf)), c->address->prefixlen); if (c->address->family == AF_INET6) - ospf6_interface_connected_route_update (c->ifp); - + { + ospf6_interface_state_update (c->ifp); + ospf6_interface_connected_route_update (c->ifp); + } return 0; } @@ -188,7 +190,10 @@ ospf6_zebra_if_address_update_delete (int command, struct zclient *zclient, buf, sizeof (buf)), c->address->prefixlen); if (c->address->family == AF_INET6) - ospf6_interface_connected_route_update (c->ifp); + { + ospf6_interface_connected_route_update (c->ifp); + ospf6_interface_state_update (c->ifp); + } return 0; } From 11b4f01355703d34099d4da145c7d92e32d98636 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Mon, 9 Dec 2013 16:48:55 +0100 Subject: [PATCH 167/482] ospf6d: clear lsa->refresh before clearing LSAs This fixes a SEGV when we receive a higher-SeqNum copy of a LSA that we originated ourselves, before a reboot of ospf6d. We create a new copy of the LSA to resync the SeqNum, but then half an hour later the old refresh thread ends up trying to refresh the free()'d old LSA. The SEGV is triggered by this chain: ospf6_lsdb_maxage_remover -> thread_execute(ospf6_lsa_refresh) -> old->refresh = NULL Which assumes that old->refresh is no longer scheduled to run, as it is being run right there. But the thread_execute() doesn't know about old->refresh and therefore didn't remove it. (Found by ANVL OSPFV3-16.17) Signed-off-by: David Lamparter --- ospf6d/ospf6_flood.c | 1 + ospf6d/ospf6_lsdb.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/ospf6d/ospf6_flood.c b/ospf6d/ospf6_flood.c index dc9ecbfb..49ed6e26 100644 --- a/ospf6d/ospf6_flood.c +++ b/ospf6d/ospf6_flood.c @@ -220,6 +220,7 @@ ospf6_install_lsa (struct ospf6_lsa *lsa) if (old) { THREAD_OFF (old->expire); + THREAD_OFF (old->refresh); ospf6_flood_clear (old); } diff --git a/ospf6d/ospf6_lsdb.c b/ospf6d/ospf6_lsdb.c index 5138d1c1..707afc67 100644 --- a/ospf6d/ospf6_lsdb.c +++ b/ospf6d/ospf6_lsdb.c @@ -473,6 +473,8 @@ ospf6_lsdb_maxage_remover (struct ospf6_lsdb *lsdb) */ lsa->header->seqnum = htonl(OSPF_MAX_SEQUENCE_NUMBER + 1); ospf6_lsa_checksum (lsa->header); + + THREAD_OFF(lsa->refresh); thread_execute (master, ospf6_lsa_refresh, lsa, 0); } else { ospf6_lsdb_remove (lsa, lsdb); From 424cc3bd48da0f417c9056c5c2ade697a3386cd4 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sat, 23 Nov 2013 16:55:36 +0100 Subject: [PATCH 168/482] ospf6d: fix interface_down() stopping hellos interface_down() - which also handles some nonobvious cases like the last linklocal address disappearing - was previously not cancelling the hello timer. This had the effect of multiple such threads ending up scheduled after a quick down-up cycle. Signed-off-by: David Lamparter --- ospf6d/ospf6_interface.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index 8d427645..b0f11194 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -683,7 +683,7 @@ interface_up (struct thread *thread) /* Schedule Hello */ if (! CHECK_FLAG (oi->flag, OSPF6_INTERFACE_PASSIVE)) - thread_add_event (master, ospf6_hello_send, oi, 0); + oi->thread_send_hello = thread_add_event (master, ospf6_hello_send, oi, 0); /* decide next interface state */ if ((if_is_pointopoint (oi->interface)) || @@ -771,6 +771,9 @@ interface_down (struct thread *thread) zlog_debug ("Interface Event %s: [InterfaceDown]", oi->interface->name); + /* Stop Hellos */ + THREAD_OFF (oi->thread_send_hello); + /* Leave AllSPFRouters */ if (oi->state > OSPF6_INTERFACE_DOWN) ospf6_sso (oi->interface->ifindex, &allspfrouters6, IPV6_LEAVE_GROUP); From 1db65fadf627637621c342b789b9a3604ca5fab5 Mon Sep 17 00:00:00 2001 From: Ingo Flaschberger Date: Sun, 17 Apr 2011 18:28:20 +0000 Subject: [PATCH 169/482] ospf6d: solve segfaults with ospf6d on FreeBSD Do not send ospf6d hellos on fresh created interfaces without configuration (ie. no vlan configured). Ospf6d use ip6_mtu, if it's not initalised, Ospf6d tries to alloc indefinite size of memory. Signed-off-by: David Lamparter --- ospf6d/ospf6_message.c | 7 +++++++ zebra/kernel_socket.c | 1 + 2 files changed, 8 insertions(+) diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c index 5fb5a216..0756ef34 100644 --- a/ospf6d/ospf6_message.c +++ b/ospf6d/ospf6_message.c @@ -1721,6 +1721,13 @@ ospf6_hello_send (struct thread *thread) return 0; } + if (iobuflen == 0) + { + zlog_debug ("Unable to send Hello on interface %s iobuflen is 0", + oi->interface->name); + return 0; + } + /* set next thread */ oi->thread_send_hello = thread_add_timer (master, ospf6_hello_send, oi, oi->hello_interval); diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index 73fabd4c..37b2ae23 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -299,6 +299,7 @@ ifan_read (struct if_announcemsghdr *ifan) sizeof(ifan->ifan_name))); ifp->ifindex = ifan->ifan_index; + if_get_metric (ifp); if_add_update (ifp); } else if (ifp != NULL && ifan->ifan_what == IFAN_DEPARTURE) From c19543b223d3b8463c048f346b8044589e0cce39 Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Wed, 24 Oct 2012 14:45:53 +0000 Subject: [PATCH 170/482] ospf6d: compute interface cost from its bandwidth Previously, the interface cost was a fixed default value that a user was allowed to change with "ipv6 ospf6 cost XX". As what is done with OSPFv2, we change this behaviour to compute the default interface cost from the interface bandwidth. The user can still force a cost with "ipv6 ospf6 cost XX". He can get the default value with "no ipv6 ospf6 cost". Moreover, the default cost value was 1. The cost is now computed from the bandwidth and a default reference bandwidth of 100 MBps (as for OSPFv2). Since the default bandwidth for an interface is 10 MBps, the "default" cost becomes 10 instead of 1. [DL: resolved conflict in ospf6d/ospf6_interface.c] Signed-off-by: David Lamparter --- doc/ospf6d.texi | 2 +- ospf6d/README | 2 +- ospf6d/ospf6_interface.c | 98 ++++++++++++++++++++++++++++++++++------ ospf6d/ospf6_interface.h | 4 ++ ospf6d/ospf6_zebra.c | 4 +- 5 files changed, 92 insertions(+), 18 deletions(-) diff --git a/doc/ospf6d.texi b/doc/ospf6d.texi index d981820c..d9bf06dd 100644 --- a/doc/ospf6d.texi +++ b/doc/ospf6d.texi @@ -75,7 +75,7 @@ Area support for OSPFv3 is not yet implemented. @section OSPF6 interface @deffn {Interface Command} {ipv6 ospf6 cost COST} {} -Sets interface's output cost. Default value is 1. +Sets interface's output cost. Default value depends on the interface bandwidth. @end deffn @deffn {Interface Command} {ipv6 ospf6 hello-interval HELLOINTERVAL} {} diff --git a/ospf6d/README b/ospf6d/README index 883486fa..6db347f9 100644 --- a/ospf6d/README +++ b/ospf6d/README @@ -60,7 +60,7 @@ CONFIG NODE: INTERFACE NODE: ipv6 ospf6 cost COST - Sets the interface's output cost. default 1 + Sets the interface's output cost. Depends on interface bandwidth by default. ipv6 ospf6 hello-interval HELLOINTERVAL Sets the interface's Hello Interval. default 10 diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index b0f11194..e6988930 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -117,6 +117,52 @@ ospf6_default_iftype(struct interface *ifp) return OSPF_IFTYPE_BROADCAST; } +static u_int32_t +ospf6_interface_get_cost (struct ospf6_interface *oi) +{ + /* If all else fails, use default OSPF cost */ + u_int32_t cost; + u_int32_t bw, refbw; + + bw = oi->interface->bandwidth ? oi->interface->bandwidth : OSPF6_INTERFACE_BANDWIDTH; + refbw = OSPF6_REFERENCE_BANDWIDTH; + + /* A specifed ip ospf cost overrides a calculated one. */ + if (CHECK_FLAG (oi->flag, OSPF6_INTERFACE_NOAUTOCOST)) + cost = oi->cost; + else + { + cost = (u_int32_t) ((double)refbw / (double)bw + (double)0.5); + if (cost < 1) cost = 1; + else if (cost > UINT32_MAX) cost = UINT32_MAX; + } + + return cost; +} + +static void +ospf6_interface_recalculate_cost (struct ospf6_interface *oi) +{ + u_int32_t newcost; + + newcost = ospf6_interface_get_cost (oi); + if (newcost == oi->cost) return; + oi->cost = newcost; + + /* update cost held in route_connected list in ospf6_interface */ + ospf6_interface_connected_route_update (oi->interface); + + /* execute LSA hooks */ + if (oi->area) + { + OSPF6_LINK_LSA_SCHEDULE (oi); + OSPF6_ROUTER_LSA_SCHEDULE (oi->area); + OSPF6_NETWORK_LSA_SCHEDULE (oi); + OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT (oi); + OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB (oi->area); + } +} + /* Create new ospf6 interface structure */ struct ospf6_interface * ospf6_interface_create (struct interface *ifp) @@ -144,7 +190,6 @@ ospf6_interface_create (struct interface *ifp) oi->hello_interval = OSPF_HELLO_INTERVAL_DEFAULT; oi->dead_interval = OSPF_ROUTER_DEAD_INTERVAL_DEFAULT; oi->rxmt_interval = OSPF_RETRANSMIT_INTERVAL_DEFAULT; - oi->cost = OSPF6_INTERFACE_COST; oi->type = ospf6_default_iftype (ifp); oi->state = OSPF6_INTERFACE_DOWN; oi->flag = 0; @@ -175,6 +220,9 @@ ospf6_interface_create (struct interface *ifp) oi->interface = ifp; ifp->info = oi; + /* Compute cost. */ + oi->cost = ospf6_interface_get_cost(oi); + return oi; } @@ -658,6 +706,9 @@ interface_up (struct thread *thread) return 0; } + /* Recompute cost */ + ospf6_interface_recalculate_cost (oi); + /* if already enabled, do nothing */ if (oi->state > OSPF6_INTERFACE_DOWN) { @@ -1214,19 +1265,37 @@ DEFUN (ipv6_ospf6_cost, return CMD_SUCCESS; oi->cost = lcost; - - /* update cost held in route_connected list in ospf6_interface */ - ospf6_interface_connected_route_update (oi->interface); + SET_FLAG (oi->flag, OSPF6_INTERFACE_NOAUTOCOST); - /* execute LSA hooks */ - if (oi->area) - { - OSPF6_LINK_LSA_SCHEDULE (oi); - OSPF6_ROUTER_LSA_SCHEDULE (oi->area); - OSPF6_NETWORK_LSA_SCHEDULE (oi); - OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT (oi); - OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB (oi->area); - } + ospf6_interface_recalculate_cost(oi); + + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_ospf6_cost, + no_ipv6_ospf6_cost_cmd, + "no ipv6 ospf6 cost", + NO_STR + IP6_STR + OSPF6_STR + "Calculate interface cost from bandwidth\n" + ) +{ + struct ospf6_interface *oi; + struct interface *ifp; + unsigned long int lcost; + + ifp = (struct interface *) vty->index; + assert (ifp); + + oi = (struct ospf6_interface *) ifp->info; + if (oi == NULL) + oi = ospf6_interface_create (ifp); + assert (oi); + + UNSET_FLAG (oi->flag, OSPF6_INTERFACE_NOAUTOCOST); + + ospf6_interface_recalculate_cost(oi); return CMD_SUCCESS; } @@ -1681,7 +1750,7 @@ config_write_ospf6_interface (struct vty *vty) if (ifp->mtu6 != oi->ifmtu) vty_out (vty, " ipv6 ospf6 ifmtu %d%s", oi->ifmtu, VNL); - if (oi->cost != OSPF6_INTERFACE_COST) + if (CHECK_FLAG (oi->flag, OSPF6_INTERFACE_NOAUTOCOST)) vty_out (vty, " ipv6 ospf6 cost %d%s", oi->cost, VNL); @@ -1764,6 +1833,7 @@ ospf6_interface_init (void) install_element (INTERFACE_NODE, &interface_desc_cmd); install_element (INTERFACE_NODE, &no_interface_desc_cmd); install_element (INTERFACE_NODE, &ipv6_ospf6_cost_cmd); + install_element (INTERFACE_NODE, &no_ipv6_ospf6_cost_cmd); install_element (INTERFACE_NODE, &ipv6_ospf6_ifmtu_cmd); install_element (INTERFACE_NODE, &no_ipv6_ospf6_ifmtu_cmd); install_element (INTERFACE_NODE, &ipv6_ospf6_deadinterval_cmd); diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h index 808f0968..34f75233 100644 --- a/ospf6d/ospf6_interface.h +++ b/ospf6d/ospf6_interface.h @@ -128,6 +128,7 @@ extern const char *ospf6_interface_state_str[]; /* flags */ #define OSPF6_INTERFACE_DISABLE 0x01 #define OSPF6_INTERFACE_PASSIVE 0x02 +#define OSPF6_INTERFACE_NOAUTOCOST 0x04 /* default values */ #define OSPF6_INTERFACE_HELLO_INTERVAL 10 @@ -137,6 +138,9 @@ extern const char *ospf6_interface_state_str[]; #define OSPF6_INTERFACE_PRIORITY 1 #define OSPF6_INTERFACE_TRANSDELAY 1 #define OSPF6_INTERFACE_INSTANCE_ID 0 +#define OSPF6_INTERFACE_BANDWIDTH 10000 /* Kbps */ +#define OSPF6_REFERENCE_BANDWIDTH 100000 /* Kbps */ + /* Function Prototypes */ diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c index cffd7675..8ee63fe6 100644 --- a/ospf6d/ospf6_zebra.c +++ b/ospf6d/ospf6_zebra.c @@ -139,9 +139,9 @@ ospf6_zebra_if_state_update (int command, struct zclient *zclient, if (IS_OSPF6_DEBUG_ZEBRA (RECV)) zlog_debug ("Zebra Interface state change: " - "%s index %d flags %llx metric %d mtu %d", + "%s index %d flags %llx metric %d mtu %d bandwidth %d", ifp->name, ifp->ifindex, (unsigned long long)ifp->flags, - ifp->metric, ifp->mtu6); + ifp->metric, ifp->mtu6, ifp->bandwidth); ospf6_interface_state_update (ifp); return 0; From fd5006896fce2816244c1ef4cabc736279548538 Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Wed, 24 Oct 2012 14:45:54 +0000 Subject: [PATCH 171/482] ospf6d: add "auto-cost reference-bandwidth" command This command allows the user to change to default reference bandwidth for cost calculations. The default value is 100 Mbps. With a default bandwidth of 10 MBps, the default cost becomes 10. Those values are consistent with OSPFv2. [DL: resolved conflicts in vty command additions & docs] Signed-off-by: David Lamparter --- doc/ospf6d.texi | 15 +++++++++- ospf6d/README | 9 ++++++ ospf6d/ospf6_interface.c | 61 +++++++++++++++++++++++++++++++++++++++- ospf6d/ospf6_snmp.c | 4 ++- ospf6d/ospf6_top.c | 6 ++++ ospf6d/ospf6_top.h | 2 ++ 6 files changed, 94 insertions(+), 3 deletions(-) diff --git a/doc/ospf6d.texi b/doc/ospf6d.texi index d9bf06dd..31f4db0c 100644 --- a/doc/ospf6d.texi +++ b/doc/ospf6d.texi @@ -66,6 +66,18 @@ within the hold-time of the previous SPF calculation. @end deffn +@deffn {OSPF6 Command} {auto-cost reference-bandwidth @var{cost}} {} +@deffnx {OSPF6 Command} {no auto-cost reference-bandwidth} {} +This sets the reference bandwidth for cost calculations, where this +bandwidth is considered equivalent to an OSPF cost of 1, specified in +Mbits/s. The default is 100Mbit/s (i.e. a link of bandwidth 100Mbit/s +or higher will have a cost of 1. Cost of lower bandwidth links will be +scaled with reference to this cost). + +This configuration setting MUST be consistent across all routers +within the OSPF domain. +@end deffn + @node OSPF6 area @section OSPF6 area @@ -75,7 +87,8 @@ Area support for OSPFv3 is not yet implemented. @section OSPF6 interface @deffn {Interface Command} {ipv6 ospf6 cost COST} {} -Sets interface's output cost. Default value depends on the interface bandwidth. +Sets interface's output cost. Default value depends on the interface +bandwidth and on the auto-cost reference bandwidth. @end deffn @deffn {Interface Command} {ipv6 ospf6 hello-interval HELLOINTERVAL} {} diff --git a/ospf6d/README b/ospf6d/README index 6db347f9..f5a00464 100644 --- a/ospf6d/README +++ b/ospf6d/README @@ -85,6 +85,15 @@ OSPF6 NODE: Binds interface to specified Area, and start sending OSPFv3 packets. + auto-cost reference-bandwidth COST + Sets the reference bandwidth for cost calculations, where this + bandwidth is considered equivalent to an OSPF cost of 1, specified + in Mbits/s. The default is 100Mbit/s (i.e. a link of bandwidth + 100Mbit/s or higher will have a cost of 1. Cost of lower bandwidth + links will be scaled with reference to this cost). This + configuration setting MUST be consistent across all routers within + the OSPF domain. + Sample configuration is in ospf6d.conf.sample. -- diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index e6988930..4bc61551 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -125,7 +125,7 @@ ospf6_interface_get_cost (struct ospf6_interface *oi) u_int32_t bw, refbw; bw = oi->interface->bandwidth ? oi->interface->bandwidth : OSPF6_INTERFACE_BANDWIDTH; - refbw = OSPF6_REFERENCE_BANDWIDTH; + refbw = ospf6 ? ospf6->ref_bandwidth : OSPF6_REFERENCE_BANDWIDTH; /* A specifed ip ospf cost overrides a calculated one. */ if (CHECK_FLAG (oi->flag, OSPF6_INTERFACE_NOAUTOCOST)) @@ -1300,6 +1300,61 @@ DEFUN (no_ipv6_ospf6_cost, return CMD_SUCCESS; } +DEFUN (auto_cost_reference_bandwidth, + auto_cost_reference_bandwidth_cmd, + "auto-cost reference-bandwidth <1-4294967>", + "Calculate OSPF interface cost according to bandwidth\n" + "Use reference bandwidth method to assign OSPF cost\n" + "The reference bandwidth in terms of Mbits per second\n") +{ + struct ospf6 *o = vty->index; + struct ospf6_area *oa; + struct ospf6_interface *oi; + struct listnode *i, *j; + u_int32_t refbw; + + refbw = strtol (argv[0], NULL, 10); + if (refbw < 1 || refbw > 4294967) + { + vty_out (vty, "reference-bandwidth value is invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* If reference bandwidth is changed. */ + if ((refbw * 1000) == o->ref_bandwidth) + return CMD_SUCCESS; + + o->ref_bandwidth = refbw * 1000; + for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa)) + for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi)) + ospf6_interface_recalculate_cost (oi); + + return CMD_SUCCESS; +} + +DEFUN (no_auto_cost_reference_bandwidth, + no_auto_cost_reference_bandwidth_cmd, + "no auto-cost reference-bandwidth", + NO_STR + "Calculate OSPF interface cost according to bandwidth\n" + "Use reference bandwidth method to assign OSPF cost\n") +{ + struct ospf6 *o = vty->index; + struct ospf6_area *oa; + struct ospf6_interface *oi; + struct listnode *i, *j; + + if (o->ref_bandwidth == OSPF6_REFERENCE_BANDWIDTH) + return CMD_SUCCESS; + + o->ref_bandwidth = OSPF6_REFERENCE_BANDWIDTH; + for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa)) + for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi)) + ospf6_interface_recalculate_cost (oi); + + return CMD_SUCCESS; +} + DEFUN (ipv6_ospf6_hellointerval, ipv6_ospf6_hellointerval_cmd, "ipv6 ospf6 hello-interval <1-65535>", @@ -1854,6 +1909,10 @@ ospf6_interface_init (void) install_element (INTERFACE_NODE, &ipv6_ospf6_network_cmd); install_element (INTERFACE_NODE, &no_ipv6_ospf6_network_cmd); + + /* reference bandwidth commands */ + install_element (OSPF6_NODE, &auto_cost_reference_bandwidth_cmd); + install_element (OSPF6_NODE, &no_auto_cost_reference_bandwidth_cmd); } DEFUN (debug_ospf6_interface, diff --git a/ospf6d/ospf6_snmp.c b/ospf6d/ospf6_snmp.c index 46603927..4be8be04 100644 --- a/ospf6d/ospf6_snmp.c +++ b/ospf6d/ospf6_snmp.c @@ -488,7 +488,9 @@ ospfv3GeneralGroup (struct variable *v, oid *name, size_t *length, case OSPFv3DEMANDEXTENSIONS: return SNMP_INTEGER (0); /* Not supported */ case OSPFv3REFERENCEBANDWIDTH: - return SNMP_INTEGER (100000); + if (ospf6) + return SNMP_INTEGER (ospf6->ref_bandwidth); + /* Otherwise, like for "not implemented". */ case OSPFv3RESTARTSUPPORT: case OSPFv3RESTARTINTERVAL: case OSPFv3RESTARTSTRICTLSACHECKING: diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c index 7c0922a6..71912701 100644 --- a/ospf6d/ospf6_top.c +++ b/ospf6d/ospf6_top.c @@ -148,6 +148,8 @@ ospf6_create (void) o->external_id_table = route_table_init (); + o->ref_bandwidth = OSPF6_REFERENCE_BANDWIDTH; + return o; } @@ -890,6 +892,10 @@ config_write_ospf6 (struct vty *vty) vty_out(vty, "%s", VTY_NEWLINE); } + if (ospf6->ref_bandwidth != OSPF6_REFERENCE_BANDWIDTH) + vty_out (vty, " auto-cost reference-bandwidth %d%s", ospf6->ref_bandwidth / 1000, + VNL); + ospf6_stub_router_config_write (vty); ospf6_redistribute_config_write (vty); ospf6_area_config_write (vty); diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h index 866f92f9..d6f4bf0f 100644 --- a/ospf6d/ospf6_top.h +++ b/ospf6d/ospf6_top.h @@ -80,6 +80,8 @@ struct ospf6 struct thread *t_spf_calc; /* SPF calculation timer. */ struct thread *t_ase_calc; /* ASE calculation timer. */ struct thread *maxage_remover; + + u_int32_t ref_bandwidth; }; #define OSPF6_DISABLED 0x01 From c78a46c27f6dfdd42fe0800cebabc1e49cb0a4bf Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Wed, 20 Mar 2013 10:50:09 +0000 Subject: [PATCH 172/482] ospf6d: fix refcounting in ospf6_asbr_lsa_remove When iterating over a list, also the last node should be unlocked again. Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- ospf6d/ospf6_asbr.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index 3605e3f8..c414970b 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -297,6 +297,8 @@ ospf6_asbr_lsa_remove (struct ospf6_lsa *lsa) } ospf6_route_remove (route, ospf6->route_table); } + if (route != NULL) + ospf6_route_unlock (route); } void From 4d474fa3297c0d5d632e2c0bff6ccb0edbedaa5d Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 19 Nov 2013 15:00:06 +0100 Subject: [PATCH 173/482] lib: fix backtraces broken by 837d16c... 837d16c ("*: use array_size() helper macro") accidentally changed one of the expressions in the backtrace code, which afterwards read: zlog_backtrace_sigsafe(): if (((size = backtrace(array,array_size(array)) <= 0) || which boils down to: (size = backtrace(...) <= 0). The braces were intended to go: (size = backtrace(...)) <= 0. All in all, this makes a nice textbook example of the original author being too clever (trying to save a single line by pulling the assignment into the condition) and the next person touching the code tripping over it... This code occurs another time in zlog_backtrace() where it is actually correct. Pulling out the assignment nonetheless. Also, new test program. Cc: Andrew J. Schorr Cc: Balaji.G Cc: Scott Feldman Signed-off-by: David Lamparter --- lib/log.c | 8 +++---- tests/.gitignore | 1 + tests/Makefile.am | 4 +++- tests/test-segv.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 tests/test-segv.c diff --git a/lib/log.c b/lib/log.c index e4ec7c22..55a3b052 100644 --- a/lib/log.c +++ b/lib/log.c @@ -443,8 +443,8 @@ zlog_backtrace_sigsafe(int priority, void *program_counter) #define LOC s,buf+sizeof(buf)-s #ifdef HAVE_GLIBC_BACKTRACE - if (((size = backtrace(array,array_size(array)) <= 0) || - ((size_t)size > array_size(array)))) + size = backtrace(array, array_size(array)); + if (size <= 0 || (size_t)size > array_size(array)) return; #define DUMP(FD) { \ @@ -526,8 +526,8 @@ zlog_backtrace(int priority) int size, i; char **strings; - if (((size = backtrace(array,array_size(array))) <= 0) || - ((size_t)size > array_size(array))) + size = backtrace(array, array_size(array)); + if (size <= 0 || (size_t)size > array_size(array)) { zlog_err("Cannot get backtrace, returned invalid # of frames %d " "(valid range is between 1 and %lu)", diff --git a/tests/.gitignore b/tests/.gitignore index 31fb70a4..8d00a3df 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -29,6 +29,7 @@ testbuffer testchecksum testmemory testprivs +testsegv testsig teststream testnexthopiter diff --git a/tests/Makefile.am b/tests/Makefile.am index 9260a900..5e631d6a 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -24,13 +24,14 @@ else TESTS_BGPD = endif -check_PROGRAMS = testsig testbuffer testmemory heavy heavywq heavythread \ +check_PROGRAMS = testsig testsegv testbuffer testmemory heavy heavywq heavythread \ testprivs teststream testchecksum tabletest testnexthopiter \ $(TESTS_BGPD) noinst_HEADERS = prng.h testsig_SOURCES = test-sig.c +testsegv_SOURCES = test-segv.c testbuffer_SOURCES = test-buffer.c testmemory_SOURCES = test-memory.c testprivs_SOURCES = test-privs.c @@ -48,6 +49,7 @@ tabletest_SOURCES = table_test.c testnexthopiter_SOURCES = test-nexthop-iter.c prng.c testsig_LDADD = ../lib/libzebra.la @LIBCAP@ +testsegv_LDADD = ../lib/libzebra.la @LIBCAP@ testbuffer_LDADD = ../lib/libzebra.la @LIBCAP@ testmemory_LDADD = ../lib/libzebra.la @LIBCAP@ testprivs_LDADD = ../lib/libzebra.la @LIBCAP@ diff --git a/tests/test-segv.c b/tests/test-segv.c new file mode 100644 index 00000000..55bd25a5 --- /dev/null +++ b/tests/test-segv.c @@ -0,0 +1,61 @@ +/* + * SEGV / backtrace handling test. + * + * copied from test-sig.c + * + * Copyright (C) 2013 by David Lamparter, Open Source Routing. + * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC") + * + * This file is part of Quagga + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include +#include +#include "lib/log.h" +#include "lib/memory.h" + +struct quagga_signal_t sigs[] = +{ +}; + +struct thread_master *master; + +int +threadfunc (struct thread *thread) +{ + int *null = NULL; + *null += 1; + return 0; +} + +int +main (void) +{ + master = thread_master_create (); + signal_init (master, array_size(sigs), sigs); + + zlog_default = openzlog("testsegv", ZLOG_NONE, + LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON); + zlog_set_level (NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED); + zlog_set_level (NULL, ZLOG_DEST_STDOUT, LOG_DEBUG); + zlog_set_level (NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED); + + thread_execute (master, threadfunc, 0, 0); + + exit (0); +} From 6d729eeac91578dca29961e0e46f246f33c37f0c Mon Sep 17 00:00:00 2001 From: "Jorge Boncompte [DTI2]" Date: Wed, 31 Jul 2013 15:01:18 +0000 Subject: [PATCH 174/482] lib: fix for dynamically grown hashes Fixes commit 97c84db00c (hash: dynamically grow hash table). The no_expand field it's not initialized and could make the hashes to never grow the table index. Signed-off-by: Jorge Boncompte [DTI2] Acked-by: Feng Lu Signed-off-by: David Lamparter --- lib/hash.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/hash.c b/lib/hash.c index 987012a5..56e41fa8 100644 --- a/lib/hash.c +++ b/lib/hash.c @@ -36,6 +36,7 @@ hash_create_size (unsigned int size, unsigned int (*hash_key) (void *), hash->index = XCALLOC (MTYPE_HASH_INDEX, sizeof (struct hash_backet *) * size); hash->size = size; + hash->no_expand = 0; hash->hash_key = hash_key; hash->hash_cmp = hash_cmp; hash->count = 0; From af514777f4327932a3e84f83d79e941967503e15 Mon Sep 17 00:00:00 2001 From: "Jorge Boncompte [DTI2]" Date: Wed, 31 Jul 2013 16:16:05 +0000 Subject: [PATCH 175/482] lib: fix possible off-by-one in stream_put_prefix() The STREAM_WRITEABLE() call only checks if there is space for the prefix in the stream but does not account for the prefixlen. The stream_putc() call reduces available space by 1 and we can end copying one byte too much and with "endp" off by one if we are near the buffer end. Instead of moving the stream_putc() call before STREAM_WRITEABLE(), we check before hand for the required space, and open-code it. This avoids a function call and verifying again the stream buffer. Signed-off-by: Jorge Boncompte [DTI2] Signed-off-by: David Lamparter --- lib/stream.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/stream.c b/lib/stream.c index ee2920e6..ccd4623f 100644 --- a/lib/stream.c +++ b/lib/stream.c @@ -700,13 +700,13 @@ stream_put_prefix (struct stream *s, struct prefix *p) psize = PSIZE (p->prefixlen); - if (STREAM_WRITEABLE (s) < psize) + if (STREAM_WRITEABLE (s) < (psize + sizeof (u_char))) { STREAM_BOUND_WARN (s, "put"); return 0; } - stream_putc (s, p->prefixlen); + s->data[s->endp++] = p->prefixlen; memcpy (s->data + s->endp, &p->u.prefix, psize); s->endp += psize; From 8c99b4c11e69e4cf0ac03c551764cccc0a3fe35a Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Thu, 1 Aug 2013 07:43:36 +0000 Subject: [PATCH 176/482] build: improve backtrace support/detection libexecinfo is used to provide backtrace() on *BSD. The API is compatible with glibc's, so this is a "free" improvement. To improve configure behaviour, the following configure options are modified/introduced: * --enable-gcc-rdynamic now defaults to "on" if the compiler is gcc. (I sadly wasn't able to find any documentation on the availability of this option for llvm, even though at least the version I have installed does support it) * --enable-backtrace has been added. This behaves as off/auto/on switch, i.e. giving either {dis,en}able will result in the requested behaviour (or an error if support wasn't found) Signed-off-by: David Lamparter --- INSTALL.quagga.txt | 3 +++ configure.ac | 29 +++++++++++++++++++++-------- doc/install.texi | 10 ++++++++++ 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/INSTALL.quagga.txt b/INSTALL.quagga.txt index 0d465879..11c85b1a 100644 --- a/INSTALL.quagga.txt +++ b/INSTALL.quagga.txt @@ -31,6 +31,9 @@ not a guarantee of support, merely "we agree that it is broken".) OpenBSD ? [info needed on what should work] Solaris (modern/supported versions, including OpenSolaris forks) +On BSD systems, installing libexecinfo is strongly recommended in order +to get backtrace support. + For further Quagga specific information on 'configure' and build-time configuration of the software, please read the Quagga info documentation, (doc/quagga.info). To read the info page included with diff --git a/configure.ac b/configure.ac index ff7a4d54..8964006a 100755 --- a/configure.ac +++ b/configure.ac @@ -273,7 +273,9 @@ AC_ARG_ENABLE(gcc_ultra_verbose, AC_ARG_ENABLE(linux24_tcp_md5, [ --enable-linux24-tcp-md5 enable support for old, Linux-2.4 RFC2385 patch]) AC_ARG_ENABLE(gcc-rdynamic, -[ --enable-gcc-rdynamic enable gcc linking with -rdynamic for better backtraces]) +[ --enable-gcc-rdynamic enable linking with -rdynamic for better backtraces (default if gcc)]) +AC_ARG_ENABLE(backtrace, +[ --disable-backtrace, disable crash backtraces (default autodetect)]) AC_ARG_ENABLE(time-check, [ --disable-time-check disable slow thread warning messages]) AC_ARG_ENABLE(pcreposix, @@ -288,8 +290,10 @@ if test x"${enable_gcc_ultra_verbose}" = x"yes" ; then CFLAGS="${CFLAGS} -Wpacked -Wpadded" fi -if test x"${enable_gcc_rdynamic}" = x"yes" ; then - LDFLAGS="${LDFLAGS} -rdynamic" +if test x"${enable_gcc_rdynamic}" != x"no" ; then + if test x"${enable_gcc_rdynamic}" = x"yes" -o x"$COMPILER" = x"GCC"; then + LDFLAGS="${LDFLAGS} -rdynamic" + fi fi if test x"${enable_time_check}" != x"no" ; then @@ -1566,12 +1570,21 @@ AX_SYS_WEAK_ALIAS dnl --------------------------- dnl check for glibc 'backtrace' dnl --------------------------- -AC_CHECK_HEADER([execinfo.h], - [AC_CHECK_FUNC([backtrace], - [AC_DEFINE(HAVE_GLIBC_BACKTRACE,,[Glibc backtrace]) - AC_DEFINE(HAVE_STACK_TRACE,,[Stack symbol decoding]) +if test x"${enable_backtrace}" != x"no" ; then + backtrace_ok=no + AC_CHECK_HEADER([execinfo.h], [ + AC_SEARCH_LIBS([backtrace], [execinfo], [ + AC_DEFINE(HAVE_GLIBC_BACKTRACE,,[Glibc backtrace]) + AC_DEFINE(HAVE_STACK_TRACE,,[Stack symbol decoding]) + backtrace_ok=yes + ],, [-lm]) ]) -]) + + if test x"${enable_backtrace}" = x"yes" -a x"${backtrace_ok}" = x"no"; then + dnl user explicitly requested backtrace but we failed to find support + AC_MSG_FAILURE([failed to find backtrace support]) + fi +fi dnl ----------------------------------------- dnl check for malloc mallinfo struct and call diff --git a/doc/install.texi b/doc/install.texi index 0f8f65fa..1e8d965b 100644 --- a/doc/install.texi +++ b/doc/install.texi @@ -96,6 +96,16 @@ installed. They can be excluded from build with this option, which will minimally decrease compile time and overhead. They can always be built and executed at a later time by running @command{make check} in the @file{tests/} subdirectory, even if they're excluded from build. +@item --enable-gcc-rdynamic +Pass the @command{-rdynamic} option to the linker driver. This is in most +cases neccessary for getting usable backtraces. This option defaults to on +if the compiler is detected as gcc, but giving an explicit enable/disable is +suggested. +@item --enable-backtrace +Controls backtrace support for the crash handlers. This is autodetected by +default. Using the switch will enforce the requested behaviour, failing with +an error if support is requested but not available. On BSD systems, this +needs libexecinfo, while on glibc support for this is part of libc itself. @end table You may specify any combination of the above options to the configure From 98a59492d9152df8c93612d2d12f170b5c034189 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Thu, 1 Aug 2013 08:12:25 +0000 Subject: [PATCH 177/482] build: remove now-useless --{en, dis}able-tests commit d771020 "don't build tests unless make check is run" has made the --{en,dis}able-tests switch completely useless. The differentiation is now made by running "make check" or not doing so. The only effect of the switch is an "empty" excursion of make into the tests/ directory. (well, and it turns "make check" useless from the main directory if --disable-tests is given, which I don't think makes sense either) Acked-by: Greg Troxel Signed-off-by: David Lamparter --- Makefile.am | 2 +- configure.ac | 10 ---------- doc/install.texi | 6 ------ 3 files changed, 1 insertion(+), 17 deletions(-) diff --git a/Makefile.am b/Makefile.am index 37fea43b..6916470b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,7 +2,7 @@ SUBDIRS = lib @ZEBRA@ @BGPD@ @RIPD@ @RIPNGD@ @OSPFD@ @OSPF6D@ @BABELD@ \ @ISISD@ @WATCHQUAGGA@ @VTYSH@ @OSPFCLIENT@ @DOC@ m4 @pkgsrcdir@ \ - redhat @SOLARIS@ @BUILD_TESTS@ + redhat @SOLARIS@ tests DIST_SUBDIRS = lib zebra bgpd ripd ripngd ospfd ospf6d babeld \ isisd watchquagga vtysh ospfclient doc m4 pkgsrc redhat tests \ diff --git a/configure.ac b/configure.ac index 8964006a..f9b17635 100755 --- a/configure.ac +++ b/configure.ac @@ -202,8 +202,6 @@ AC_ARG_ENABLE(ipv6, [ --disable-ipv6 turn off IPv6 related features and daemons]) AC_ARG_ENABLE(doc, [ --disable-doc do not build docs]) -AC_ARG_ENABLE(tests, -[ --disable-tests do not build tests]) AC_ARG_ENABLE(zebra, [ --disable-zebra do not build zebra daemon]) AC_ARG_ENABLE(bgpd, @@ -1272,13 +1270,6 @@ else DOC="doc" fi -dnl can't use TESTS as name, that's special with automake -if test "${enable_tests}" = "no";then - BUILD_TESTS="" -else - BUILD_TESTS="tests" -fi - dnl -------------------- dnl Daemon disable check dnl -------------------- @@ -1372,7 +1363,6 @@ else fi AC_SUBST(DOC) -AC_SUBST(BUILD_TESTS) AC_SUBST(ZEBRA) AC_SUBST(BGPD) AC_SUBST(RIPD) diff --git a/doc/install.texi b/doc/install.texi index 1e8d965b..e958d845 100644 --- a/doc/install.texi +++ b/doc/install.texi @@ -90,12 +90,6 @@ Enable support for Equal Cost Multipath. @var{ARG} is the maximum number of ECMP paths to allow, set to 0 to allow unlimited number of paths. @item --disable-rtadv Disable support IPV6 router advertisement in zebra. -@item --disable-tests -Do not build tests. Test programs are built by default, but not ran or -installed. They can be excluded from build with this option, which will -minimally decrease compile time and overhead. They can always be built and -executed at a later time by running @command{make check} in the @file{tests/} -subdirectory, even if they're excluded from build. @item --enable-gcc-rdynamic Pass the @command{-rdynamic} option to the linker driver. This is in most cases neccessary for getting usable backtraces. This option defaults to on From db19c85679b08668c3dce73a655c21753042cf06 Mon Sep 17 00:00:00 2001 From: Brett Ciphery Date: Thu, 3 Oct 2013 13:48:54 +0000 Subject: [PATCH 178/482] zebra: set metric for directly connected routes via netlink to 0 a value of 1 is hard coded for the metric field, much like the ifconfig utility it may have roots in. in order to be in line with the metric used in the linux kernel itself, we switch this to 0. Signed-off-by: Brett Ciphery Signed-off-by: David Lamparter --- zebra/rt_netlink.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 7a820bfd..ba0b0d7d 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -504,7 +504,7 @@ netlink_interface (struct sockaddr_nl *snl, struct nlmsghdr *h) set_ifindex(ifp, ifi->ifi_index); ifp->flags = ifi->ifi_flags & 0x0000fffff; ifp->mtu6 = ifp->mtu = *(uint32_t *) RTA_DATA (tb[IFLA_MTU]); - ifp->metric = 1; + ifp->metric = 0; /* Hardware type and address. */ ifp->hw_type = ifi->ifi_type; @@ -1084,7 +1084,7 @@ netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h) set_ifindex(ifp, ifi->ifi_index); ifp->flags = ifi->ifi_flags & 0x0000fffff; ifp->mtu6 = ifp->mtu = *(int *) RTA_DATA (tb[IFLA_MTU]); - ifp->metric = 1; + ifp->metric = 0; netlink_interface_update_hw_addr (tb, ifp); @@ -1096,7 +1096,7 @@ netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h) /* Interface status change. */ set_ifindex(ifp, ifi->ifi_index); ifp->mtu6 = ifp->mtu = *(int *) RTA_DATA (tb[IFLA_MTU]); - ifp->metric = 1; + ifp->metric = 0; netlink_interface_update_hw_addr (tb, ifp); From f7bf41534e885c7bc077529c591a1bce24a5f1e9 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 22 Oct 2013 17:10:21 +0000 Subject: [PATCH 179/482] zebra: apply syntactic sugar to rib_dump() strip the explicit __func__ present on all calls and make the prefix argument a transparent union. Signed-off-by: David Lamparter --- lib/prefix.h | 19 +++++++++++++++++++ zebra/rib.h | 4 +++- zebra/rt_socket.c | 2 +- zebra/zebra_rib.c | 16 +++++++++------- 4 files changed, 32 insertions(+), 9 deletions(-) diff --git a/lib/prefix.h b/lib/prefix.h index 8c8992e8..9ef70ff5 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -89,6 +89,25 @@ struct prefix_rd u_char val[8] __attribute__ ((aligned (8))); }; +/* helper to get type safety/avoid casts on calls + * (w/o this, functions accepting all prefix types need casts on the caller + * side, which strips type safety since the cast will accept any pointer + * type.) + */ +union prefix46ptr +{ + struct prefix *p; + struct prefix_ipv4 *p4; + struct prefix_ipv6 *p6; +} __attribute__ ((transparent_union)); + +union prefix46constptr +{ + const struct prefix *p; + const struct prefix_ipv4 *p4; + const struct prefix_ipv6 *p6; +} __attribute__ ((transparent_union)); + #ifndef INET_ADDRSTRLEN #define INET_ADDRSTRLEN 16 #endif /* INET_ADDRSTRLEN */ diff --git a/zebra/rib.h b/zebra/rib.h index 97a20af3..d3a83c68 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -386,7 +386,9 @@ extern struct nexthop *nexthop_ipv4_ifindex_add (struct rib *, extern int nexthop_has_fib_child(struct nexthop *); extern void rib_lookup_and_dump (struct prefix_ipv4 *); extern void rib_lookup_and_pushup (struct prefix_ipv4 *); -extern void rib_dump (const char *, const struct prefix *, const struct rib *); +#define rib_dump(prefix ,rib) _rib_dump(__func__, prefix, rib) +extern void _rib_dump (const char *, + union prefix46constptr, const struct rib *); extern int rib_lookup_ipv4_route (struct prefix_ipv4 *, union sockunion *); #define ZEBRA_RIB_LOOKUP_ERROR -1 #define ZEBRA_RIB_FOUND_EXACT 0 diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index 5d175d84..90ed73d0 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -160,7 +160,7 @@ kernel_rtm_ipv4 (int cmd, struct prefix *p, struct rib *rib, int family) { zlog_debug ("%s: %s/%d: attention! gate not found for rib %p", __func__, prefix_buf, p->prefixlen, rib); - rib_dump (__func__, (struct prefix_ipv4 *)p, rib); + rib_dump (p, rib); } else inet_ntop (AF_INET, &sin_gate.sin_addr, gate_buf, INET_ADDRSTRLEN); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 6616f9a1..8835ef37 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -1912,8 +1912,10 @@ rib_add_ipv4 (int type, int flags, struct prefix_ipv4 *p, * question are passed as 1st and 2nd arguments. */ -void rib_dump (const char * func, const struct prefix * p, const struct rib * rib) +void _rib_dump (const char * func, + union prefix46constptr pp, const struct rib * rib) { + const struct prefix *p = pp.p; char straddr[INET6_ADDRSTRLEN]; struct nexthop *nexthop, *tnexthop; int recursing; @@ -2009,7 +2011,7 @@ void rib_lookup_and_dump (struct prefix_ipv4 * p) (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED) ? "removed" : "NOT removed"), (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED) ? "selected" : "NOT selected") ); - rib_dump (__func__, (struct prefix *) p, rib); + rib_dump (p, rib); } } @@ -2056,7 +2058,7 @@ void rib_lookup_and_pushup (struct prefix_ipv4 * p) char buf[INET_ADDRSTRLEN]; inet_ntop (rn->p.family, &p->prefix, buf, INET_ADDRSTRLEN); zlog_debug ("%s: freeing way for connected prefix %s/%d", __func__, buf, p->prefixlen); - rib_dump (__func__, &rn->p, rib); + rib_dump (&rn->p, rib); } rib_uninstall (rn, rib); } @@ -2118,7 +2120,7 @@ rib_add_ipv4_multipath (struct prefix_ipv4 *p, struct rib *rib, safi_t safi) { zlog_debug ("%s: called rib_addnode (%p, %p) on new RIB entry", __func__, rn, rib); - rib_dump (__func__, (struct prefix *) p, rib); + rib_dump (p, rib); } /* Free implicit route.*/ @@ -2128,7 +2130,7 @@ rib_add_ipv4_multipath (struct prefix_ipv4 *p, struct rib *rib, safi_t safi) { zlog_debug ("%s: calling rib_delnode (%p, %p) on existing RIB entry", __func__, rn, same); - rib_dump (__func__, (struct prefix *) p, same); + rib_dump (p, same); } rib_delnode (rn, same); } @@ -2706,7 +2708,7 @@ rib_add_ipv6 (int type, int flags, struct prefix_ipv6 *p, { zlog_debug ("%s: called rib_addnode (%p, %p) on new RIB entry", __func__, rn, rib); - rib_dump (__func__, (struct prefix *) p, rib); + rib_dump (p, rib); } /* Free implicit route.*/ @@ -2716,7 +2718,7 @@ rib_add_ipv6 (int type, int flags, struct prefix_ipv6 *p, { zlog_debug ("%s: calling rib_delnode (%p, %p) on existing RIB entry", __func__, rn, same); - rib_dump (__func__, (struct prefix *) p, same); + rib_dump (p, same); } rib_delnode (rn, same); } From 2b00515a9b639fd1e057f3ebf10ded2dde920764 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Mon, 30 Sep 2013 12:27:49 +0000 Subject: [PATCH 180/482] bgpd, ospfd, zebra: fix some DEFUN definitions Fixup some DEFUNS with incorrect command strings or mixed up helpstrings. Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- bgpd/bgp_route.c | 58 +++++++++++++++++++++++------------------------ ospfd/ospf_vty.c | 2 ++ zebra/debug.c | 6 ++--- zebra/rtadv.c | 2 +- zebra/zebra_vty.c | 8 +++---- 5 files changed, 39 insertions(+), 37 deletions(-) diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index fb35fab8..335543e0 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -4375,7 +4375,7 @@ ALIAS_DEPRECATED (bgp_network_mask_natural, "AS-Pathlimit TTL, in number of AS-Path hops\n") ALIAS_DEPRECATED (bgp_network_mask_natural_backdoor, bgp_network_mask_natural_backdoor_ttl_cmd, - "network A.B.C.D backdoor pathlimit (1-255>", + "network A.B.C.D backdoor pathlimit <1-255>", "Specify a network to announce via BGP\n" "Network number\n" "Specify a BGP backdoor route\n" @@ -6796,7 +6796,7 @@ DEFUN (show_ip_bgp_view, IP_STR BGP_STR "BGP view\n" - "BGP view name\n") + "View name\n") { struct bgp *bgp; @@ -6818,7 +6818,7 @@ DEFUN (show_ip_bgp_view_route, IP_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Network in the BGP routing table to display\n") { return bgp_show_route (vty, argv[0], argv[1], AFI_IP, SAFI_UNICAST, NULL, 0); @@ -6831,7 +6831,7 @@ DEFUN (show_ip_bgp_view_prefix, IP_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "IP prefix /, e.g., 35.0.0.0/8\n") { return bgp_show_route (vty, argv[0], argv[1], AFI_IP, SAFI_UNICAST, NULL, 1); @@ -7904,14 +7904,14 @@ DEFUN (show_bgp_view_afi_safi_community_all, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Address family\n" #ifdef HAVE_IPV6 "Address family\n" #endif "Address Family modifier\n" "Address Family modifier\n" - "Display routes containing communities\n") + "Display routes matching the communities\n") { int afi; int safi; @@ -7945,7 +7945,7 @@ DEFUN (show_bgp_view_afi_safi_community, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Address family\n" #ifdef HAVE_IPV6 "Address family\n" @@ -7982,7 +7982,7 @@ ALIAS (show_bgp_view_afi_safi_community, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Address family\n" #ifdef HAVE_IPV6 "Address family\n" @@ -8009,7 +8009,7 @@ ALIAS (show_bgp_view_afi_safi_community, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Address family\n" #ifdef HAVE_IPV6 "Address family\n" @@ -8040,7 +8040,7 @@ ALIAS (show_bgp_view_afi_safi_community, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Address family\n" #ifdef HAVE_IPV6 "Address family\n" @@ -10198,7 +10198,7 @@ DEFUN (show_bgp_view_afi_safi_neighbor_adv_recd_routes, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Address family\n" #ifdef HAVE_IPV6 "Address family\n" @@ -10613,7 +10613,7 @@ DEFUN (show_ip_bgp_view_rsclient, IP_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Information about Route Server Client\n" NEIGHBOR_ADDR_STR) { @@ -10663,7 +10663,7 @@ DEFUN (show_bgp_view_ipv4_safi_rsclient, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Address family\n" "Address Family modifier\n" "Address Family modifier\n" @@ -10723,7 +10723,7 @@ DEFUN (show_ip_bgp_view_rsclient_route, IP_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Information about Route Server Client\n" NEIGHBOR_ADDR_STR "Network in the BGP routing table to display\n") @@ -10795,7 +10795,7 @@ DEFUN (show_bgp_view_ipv4_safi_rsclient_route, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Address family\n" "Address Family modifier\n" "Address Family modifier\n" @@ -10877,7 +10877,7 @@ DEFUN (show_ip_bgp_view_rsclient_prefix, IP_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Information about Route Server Client\n" NEIGHBOR_ADDR_STR "IP prefix /, e.g., 35.0.0.0/8\n") @@ -10949,7 +10949,7 @@ DEFUN (show_bgp_view_ipv4_safi_rsclient_prefix, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Address family\n" "Address Family modifier\n" "Address Family modifier\n" @@ -11031,7 +11031,7 @@ DEFUN (show_bgp_view_neighbor_routes, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" "Neighbor to display information about\n" @@ -11057,7 +11057,7 @@ ALIAS (show_bgp_view_neighbor_routes, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Address family\n" "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" @@ -11070,7 +11070,7 @@ DEFUN (show_bgp_view_neighbor_damp, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" "Neighbor to display information about\n" @@ -11096,7 +11096,7 @@ ALIAS (show_bgp_view_neighbor_damp, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Address family\n" "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" @@ -11109,7 +11109,7 @@ DEFUN (show_bgp_view_neighbor_flap, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" "Neighbor to display information about\n" @@ -11135,7 +11135,7 @@ ALIAS (show_bgp_view_neighbor_flap, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Address family\n" "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" @@ -11246,7 +11246,7 @@ DEFUN (show_bgp_view_rsclient, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Information about Route Server Client\n" NEIGHBOR_ADDR_STR) { @@ -11295,7 +11295,7 @@ DEFUN (show_bgp_view_ipv6_safi_rsclient, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Address family\n" "Address Family modifier\n" "Address Family modifier\n" @@ -11354,7 +11354,7 @@ DEFUN (show_bgp_view_rsclient_route, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Information about Route Server Client\n" NEIGHBOR_ADDR_STR "Network in the BGP routing table to display\n") @@ -11425,7 +11425,7 @@ DEFUN (show_bgp_view_ipv6_safi_rsclient_route, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Address family\n" "Address Family modifier\n" "Address Family modifier\n" @@ -11506,7 +11506,7 @@ DEFUN (show_bgp_view_rsclient_prefix, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Information about Route Server Client\n" NEIGHBOR_ADDR_STR "IPv6 prefix /, e.g., 3ffe::/16\n") @@ -11577,7 +11577,7 @@ DEFUN (show_bgp_view_ipv6_safi_rsclient_prefix, SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" "Address family\n" "Address Family modifier\n" "Address Family modifier\n" diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 2ba8188c..5e5a0b0d 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -1976,6 +1976,8 @@ DEFUN (ospf_area_authentication_message_digest, ospf_area_authentication_message_digest_cmd, "area (A.B.C.D|<0-4294967295>) authentication message-digest", "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" "Enable authentication\n" "Use message-digest authentication\n") { diff --git a/zebra/debug.c b/zebra/debug.c index 7bfdb77d..c3b00e0f 100644 --- a/zebra/debug.c +++ b/zebra/debug.c @@ -35,8 +35,8 @@ DEFUN (show_debugging_zebra, show_debugging_zebra_cmd, "show debugging zebra", SHOW_STR - "Zebra configuration\n" - "Debugging information\n") + "Debugging information\n" + "Zebra configuration\n") { vty_out (vty, "Zebra debugging status:%s", VTY_NEWLINE); @@ -128,7 +128,7 @@ DEFUN (debug_zebra_packet_detail, "Debug option set for zebra packet\n" "Debug option set for receive packet\n" "Debug option set for send packet\n" - "Debug option set detaied information\n") + "Debug option set detailed information\n") { zebra_debug_packet = ZEBRA_DEBUG_PACKET; if (strncmp ("send", argv[0], strlen (argv[0])) == 0) diff --git a/zebra/rtadv.c b/zebra/rtadv.c index ae5c5a1c..91d7b324 100644 --- a/zebra/rtadv.c +++ b/zebra/rtadv.c @@ -1483,7 +1483,7 @@ DEFUN (no_ipv6_nd_router_preference, ALIAS (no_ipv6_nd_router_preference, no_ipv6_nd_router_preference_val_cmd, - "no ipv6 nd router-preference (high|medium|low", + "no ipv6 nd router-preference (high|medium|low)", NO_STR "Interface IPv6 config commands\n" "Neighbor discovery\n" diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 45928e93..f4946f46 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -280,9 +280,9 @@ DEFUN (ip_route_mask_flags_distance, "IP destination prefix mask\n" "IP gateway address\n" "IP gateway interface name\n" - "Distance value for this route\n" "Emit an ICMP unreachable when matched\n" - "Silently discard pkts when matched\n") + "Silently discard pkts when matched\n" + "Distance value for this route\n") { return zebra_static_ipv4 (vty, 1, argv[0], argv[1], argv[2], argv[3], argv[4]); } @@ -294,9 +294,9 @@ DEFUN (ip_route_mask_flags_distance2, "Establish static routes\n" "IP destination prefix\n" "IP destination prefix mask\n" - "Distance value for this route\n" "Emit an ICMP unreachable when matched\n" - "Silently discard pkts when matched\n") + "Silently discard pkts when matched\n" + "Distance value for this route\n") { return zebra_static_ipv4 (vty, 1, argv[0], argv[1], NULL, argv[2], argv[3]); } From 8f399b0e4fff2344d75ebf709e1ce55f15269db2 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Mon, 30 Sep 2013 12:27:50 +0000 Subject: [PATCH 181/482] tests: add a test program for lib/command.c Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- tests/.gitignore | 2 + tests/Makefile.am | 18 +- tests/libzebra.tests/Makefile.am | 1 + tests/libzebra.tests/testcommands.exp | 23 + tests/prng.c | 48 ++ tests/prng.h | 4 + tests/test-commands.c | 417 ++++++++++++ tests/testcommands.in | 201 ++++++ tests/testcommands.out | 900 ++++++++++++++++++++++++++ 9 files changed, 1613 insertions(+), 1 deletion(-) create mode 100644 tests/libzebra.tests/testcommands.exp create mode 100644 tests/test-commands.c create mode 100644 tests/testcommands.in create mode 100644 tests/testcommands.out diff --git a/tests/.gitignore b/tests/.gitignore index 8d00a3df..0ace3656 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -33,4 +33,6 @@ testsegv testsig teststream testnexthopiter +testcommands +test-commands-defun.c site.exp diff --git a/tests/Makefile.am b/tests/Makefile.am index 5e631d6a..ceca606a 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -9,7 +9,9 @@ EXTRA_DIST = \ config/unix.exp \ lib/bgpd.exp \ lib/libzebra.exp \ - global-conf.exp + global-conf.exp \ + testcommands.in \ + testcommands.out INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib DEFS = @DEFS@ $(LOCAL_OPTS) -DSYSCONFDIR=\"$(sysconfdir)/\" @@ -26,8 +28,20 @@ endif check_PROGRAMS = testsig testsegv testbuffer testmemory heavy heavywq heavythread \ testprivs teststream testchecksum tabletest testnexthopiter \ + testcommands \ $(TESTS_BGPD) +../vtysh/vtysh_cmd.c: + $(MAKE) -C ../vtysh vtysh_cmd.c + +test-commands-defun.c: ../vtysh/vtysh_cmd.c + sed \ + -e '/"vtysh.h"/d' \ + -e 's/vtysh_init_cmd/test_init_cmd/' \ + -e 's/VTYSH_[A-Z][A-Z_0-9]*/0/g' \ + < ../vtysh/vtysh_cmd.c \ + > test-commands-defun.c + noinst_HEADERS = prng.h testsig_SOURCES = test-sig.c @@ -47,6 +61,7 @@ testchecksum_SOURCES = test-checksum.c testbgpmpath_SOURCES = bgp_mpath_test.c tabletest_SOURCES = table_test.c testnexthopiter_SOURCES = test-nexthop-iter.c prng.c +testcommands_SOURCES = test-commands-defun.c test-commands.c prng.c testsig_LDADD = ../lib/libzebra.la @LIBCAP@ testsegv_LDADD = ../lib/libzebra.la @LIBCAP@ @@ -65,3 +80,4 @@ testchecksum_LDADD = ../lib/libzebra.la @LIBCAP@ testbgpmpath_LDADD = ../bgpd/libbgp.a ../lib/libzebra.la @LIBCAP@ -lm tabletest_LDADD = ../lib/libzebra.la @LIBCAP@ -lm testnexthopiter_LDADD = ../lib/libzebra.la @LIBCAP@ +testcommands_LDADD = ../lib/libzebra.la @LIBCAP@ diff --git a/tests/libzebra.tests/Makefile.am b/tests/libzebra.tests/Makefile.am index 14138a08..a1ffea3d 100644 --- a/tests/libzebra.tests/Makefile.am +++ b/tests/libzebra.tests/Makefile.am @@ -1,3 +1,4 @@ EXTRA_DIST = \ tabletest.exp \ + testcommands.exp \ testnexthopiter.exp diff --git a/tests/libzebra.tests/testcommands.exp b/tests/libzebra.tests/testcommands.exp new file mode 100644 index 00000000..f760c6d7 --- /dev/null +++ b/tests/libzebra.tests/testcommands.exp @@ -0,0 +1,23 @@ +set timeout 30 +set test_name "testcommands" + +spawn sh -c "./testcommands -e 0 < testcommands.in | diff -au - testcommands.out" + +expect { + eof { + } + timeout { + exp_close + fail "$test_name: timeout" + } +} + +catch wait result +set os_error [lindex $result 2] +set exit_status [lindex $result 3] + +if { $os_error == 0 && $exit_status == 0 } { + pass "$test_name" +} else { + fail "$test_name" +} diff --git a/tests/prng.c b/tests/prng.c index 7b1b4282..8d78ea52 100644 --- a/tests/prng.c +++ b/tests/prng.c @@ -25,6 +25,7 @@ #include #include +#include #include "prng.h" @@ -75,6 +76,53 @@ prng_rand(struct prng *prng) return rv; } +const char * +prng_fuzz(struct prng *prng, + const char *string, + const char *charset, + unsigned int operations) +{ + static char buf[256]; + unsigned int charset_len; + unsigned int i; + unsigned int offset; + unsigned int op; + unsigned int character; + + assert(strlen(string) < sizeof(buf)); + + strncpy(buf, string, sizeof(buf)); + charset_len = strlen(charset); + + for (i = 0; i < operations; i++) + { + offset = prng_rand(prng) % strlen(buf); + op = prng_rand(prng) % 3; + + switch (op) + { + case 0: + /* replace */ + character = prng_rand(prng) % charset_len; + buf[offset] = charset[character]; + break; + case 1: + /* remove */ + memmove(buf + offset, buf + offset + 1, strlen(buf) - offset); + break; + case 2: + /* insert */ + assert(strlen(buf) + 1 < sizeof(buf)); + + memmove(buf + offset + 1, buf + offset, strlen(buf) + 1 - offset); + character = prng_rand(prng) % charset_len; + buf[offset] = charset[character]; + break; + } + } + return buf; +} + void prng_free(struct prng *prng) { diff --git a/tests/prng.h b/tests/prng.h index ed364986..cf0bacc5 100644 --- a/tests/prng.h +++ b/tests/prng.h @@ -29,6 +29,10 @@ struct prng; struct prng* prng_new(unsigned long long seed); unsigned int prng_rand(struct prng*); +const char * prng_fuzz(struct prng*, + const char *string, + const char *charset, + unsigned int operations); void prng_free(struct prng *); #endif diff --git a/tests/test-commands.c b/tests/test-commands.c new file mode 100644 index 00000000..e2f40c6a --- /dev/null +++ b/tests/test-commands.c @@ -0,0 +1,417 @@ +/* + * Test code for lib/command.c + * + * Copyright (C) 2013 by Open Source Routing. + * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC") + * + * This program reads in a list of commandlines from stdin + * and calls all the public functions of lib/command.c for + * both the given command lines and fuzzed versions thereof. + * + * The output is currently not validated but only logged. It can + * be diffed to find regressions between versions. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#define REALLY_NEED_PLAIN_GETOPT 1 + +#include + +#include +#include +#include + +#include "command.h" +#include "memory.h" +#include "vector.h" +#include "prng.h" + +extern vector cmdvec; +extern struct cmd_node vty_node; +extern void test_init_cmd(void); /* provided in test-commands-defun.c */ + +struct thread_master *master; /* dummy for libzebra*/ + +static vector test_cmds; +static char test_buf[32768]; + +static struct cmd_node bgp_node = +{ + BGP_NODE, + "%s(config-router)# ", +}; + +static struct cmd_node rip_node = +{ + RIP_NODE, + "%s(config-router)# ", +}; + +static struct cmd_node isis_node = +{ + ISIS_NODE, + "%s(config-router)# ", +}; + +static struct cmd_node interface_node = +{ + INTERFACE_NODE, + "%s(config-if)# ", +}; + +static struct cmd_node rmap_node = +{ + RMAP_NODE, + "%s(config-route-map)# " +}; + +static struct cmd_node zebra_node = +{ + ZEBRA_NODE, + "%s(config-router)# " +}; + +static struct cmd_node bgp_vpnv4_node = +{ + BGP_VPNV4_NODE, + "%s(config-router-af)# " +}; + +static struct cmd_node bgp_ipv4_node = +{ + BGP_IPV4_NODE, + "%s(config-router-af)# " +}; + +static struct cmd_node bgp_ipv4m_node = +{ + BGP_IPV4M_NODE, + "%s(config-router-af)# " +}; + +static struct cmd_node bgp_ipv6_node = +{ + BGP_IPV6_NODE, + "%s(config-router-af)# " +}; + +static struct cmd_node bgp_ipv6m_node = +{ + BGP_IPV6M_NODE, + "%s(config-router-af)# " +}; + +static struct cmd_node ospf_node = +{ + OSPF_NODE, + "%s(config-router)# " +}; + +static struct cmd_node ripng_node = +{ + RIPNG_NODE, + "%s(config-router)# " +}; + +static struct cmd_node ospf6_node = +{ + OSPF6_NODE, + "%s(config-ospf6)# " +}; + +static struct cmd_node babel_node = +{ + BABEL_NODE, + "%s(config-babel)# " +}; + +static struct cmd_node keychain_node = +{ + KEYCHAIN_NODE, + "%s(config-keychain)# " +}; + +static struct cmd_node keychain_key_node = +{ + KEYCHAIN_KEY_NODE, + "%s(config-keychain-key)# " +}; + +static int +test_callback(struct cmd_element *cmd, struct vty *vty, int argc, const char *argv[]) +{ + int offset; + int rv; + int i; + + offset = 0; + rv = snprintf(test_buf, sizeof(test_buf), "'%s'", cmd->string); + if (rv < 0) + abort(); + + offset += rv; + + for (i = 0; i < argc; i++) + { + rv = snprintf(test_buf + offset, sizeof(test_buf) - offset, "%s'%s'", + (i == 0) ? ": " : ", ", argv[i]); + if (rv < 0) + abort(); + offset += rv; + } + + return CMD_SUCCESS; +} + +static void +test_load(void) +{ + char line[4096]; + + test_cmds = vector_init(VECTOR_MIN_SIZE); + + while (fgets(line, sizeof(line), stdin) != NULL) + { + if (strlen(line)) + line[strlen(line) - 1] = '\0'; + if (line[0] == '#') + continue; + vector_set(test_cmds, XSTRDUP(MTYPE_STRVEC, line)); + } +} + +static void +test_init(void) +{ + unsigned int node; + unsigned int i; + struct cmd_node *cnode; + struct cmd_element *cmd; + + cmd_init(1); + + install_node (&bgp_node, NULL); + install_node (&rip_node, NULL); + install_node (&interface_node, NULL); + install_node (&rmap_node, NULL); + install_node (&zebra_node, NULL); + install_node (&bgp_vpnv4_node, NULL); + install_node (&bgp_ipv4_node, NULL); + install_node (&bgp_ipv4m_node, NULL); + install_node (&bgp_ipv6_node, NULL); + install_node (&bgp_ipv6m_node, NULL); + install_node (&ospf_node, NULL); + install_node (&ripng_node, NULL); + install_node (&ospf6_node, NULL); + install_node (&babel_node, NULL); + install_node (&keychain_node, NULL); + install_node (&keychain_key_node, NULL); + install_node (&isis_node, NULL); + install_node (&vty_node, NULL); + + test_init_cmd(); + + for (node = 0; node < vector_active(cmdvec); node++) + if ((cnode = vector_slot(cmdvec, node)) != NULL) + for (i = 0; i < vector_active(cnode->cmd_vector); i++) + if ((cmd = vector_slot(cnode->cmd_vector, i)) != NULL) + { + cmd->daemon = 0; + cmd->func = test_callback; + } + sort_node(); + + test_load(); + vty_init_vtysh(); +} + +static void +test_terminate(void) +{ + unsigned int i; + + vty_terminate(); + for (i = 0; i < vector_active(test_cmds); i++) + XFREE(MTYPE_STRVEC, vector_slot(test_cmds, i)); + vector_free(test_cmds); + cmd_terminate(); +} + +static void +test_run(struct prng *prng, struct vty *vty, const char *cmd, unsigned int edit_dist, unsigned int node_index, int verbose) +{ + const char *test_str; + vector vline; + int ret; + unsigned int i; + char **completions; + unsigned int j; + struct cmd_node *cnode; + vector descriptions; + int appended_null; + int no_match; + + test_str = prng_fuzz(prng, cmd, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_:. /", edit_dist); + vline = cmd_make_strvec(test_str); + + if (vline == NULL) + return; + + appended_null = 0; + for (i = 0; i < vector_active(cmdvec); i++) + if ((cnode = vector_slot(cmdvec, i)) != NULL) + { + if (node_index != (unsigned int)-1 && i != node_index) + continue; + + if (appended_null) + { + vector_unset(vline, vector_active(vline) - 1); + appended_null = 0; + } + vty->node = cnode->node; + test_buf[0] = '\0'; + ret = cmd_execute_command(vline, vty, NULL, 0); + no_match = (ret == CMD_ERR_NO_MATCH); + if (verbose || !no_match) + printf("execute relaxed '%s'@%d: rv==%d%s%s\n", + test_str, + cnode->node, + ret, + (test_buf[0] != '\0') ? ", " : "", + test_buf); + + vty->node = cnode->node; + test_buf[0] = '\0'; + ret = cmd_execute_command_strict(vline, vty, NULL); + if (verbose || !no_match) + printf("execute strict '%s'@%d: rv==%d%s%s\n", + test_str, + cnode->node, + ret, + (test_buf[0] != '\0') ? ", " : "", + test_buf); + + if (isspace((int) test_str[strlen(test_str) - 1])) + { + vector_set (vline, NULL); + appended_null = 1; + } + + vty->node = cnode->node; + completions = cmd_complete_command(vline, vty, &ret); + if (verbose || !no_match) + printf("complete '%s'@%d: rv==%d\n", + test_str, + cnode->node, + ret); + if (completions != NULL) + { + for (j = 0; completions[j] != NULL; j++) + { + printf(" '%s'\n", completions[j]); + XFREE(MTYPE_TMP, completions[j]); + } + XFREE(MTYPE_VECTOR_INDEX, completions); + } + + vty->node = cnode->node; + descriptions = cmd_describe_command(vline, vty, &ret); + if (verbose || !no_match) + printf("describe '%s'@%d: rv==%d\n", + test_str, + cnode->node, + ret); + if (descriptions != NULL) + { + for (j = 0; j < vector_active(descriptions); j++) + { + struct desc *cmd = vector_slot(descriptions, j); + printf(" '%s' '%s'\n", cmd->cmd, cmd->str); + } + vector_free(descriptions); + } + } + cmd_free_strvec(vline); +} + +int +main(int argc, char **argv) +{ + int opt; + struct prng *prng; + struct vty *vty; + unsigned int edit_distance; + unsigned int max_edit_distance; + unsigned int node_index; + int verbose; + unsigned int test_cmd; + unsigned int iteration; + unsigned int num_iterations; + + max_edit_distance = 3; + node_index = -1; + verbose = 0; + + while ((opt = getopt(argc, argv, "e:n:v")) != -1) + { + switch (opt) + { + case 'e': + max_edit_distance = atoi(optarg); + break; + case 'n': + node_index = atoi(optarg); + break; + case 'v': + verbose++; + break; + default: + fprintf(stderr, "Usage: %s [-e ] [-n ] [-v]\n", argv[0]); + exit(1); + break; + } + } + + test_init(); + prng = prng_new(0); + + vty = vty_new(); + vty->type = VTY_TERM; + + fprintf(stderr, "Progress:\n0/%u", vector_active(test_cmds)); + for (test_cmd = 0; test_cmd < vector_active(test_cmds); test_cmd++) + { + for (edit_distance = 0; + edit_distance <= max_edit_distance; + edit_distance++) + { + num_iterations = 1 << edit_distance; + num_iterations *= num_iterations * num_iterations; + + for (iteration = 0; iteration < num_iterations; iteration++) + test_run(prng, vty, vector_slot(test_cmds, test_cmd), edit_distance, node_index, verbose); + } + fprintf(stderr, "\r%u/%u", test_cmd + 1, vector_active(test_cmds)); + } + fprintf(stderr, "\nDone.\n"); + + vty_close(vty); + prng_free(prng); + test_terminate(); + return 0; +} diff --git a/tests/testcommands.in b/tests/testcommands.in new file mode 100644 index 00000000..70c57052 --- /dev/null +++ b/tests/testcommands.in @@ -0,0 +1,201 @@ +# +# +# Some randomly chosen valid commands +# +# +area 0 virtual-link 1.2.3.4 authentication null message-digest-key 1 md5 VARIABLE +area 0 virtual-link 1.2.3.4 dead-interval 1 hello-interval 1 retransmit-interval 1 transmit-delay 1 +area 0 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 dead-interval 1 retransmit-interval 1 +area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 hello-interval 1 dead-interval 1 +area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 transmit-delay 1 dead-interval 1 +clear bgp 1 out +clear bgp ipv6 2001:db8::1 out +clear bgp view VARIABLE * soft +clear ip bgp 1.2.3.4 ipv4 multicast out +ipv6 nd prefix 2001:db8::/32 infinite infinite no-autoconfig +ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1 +network 1.0.0.0/8 area 0 +no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval hello-interval transmit-delay +no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval retransmit-interval transmit-delay +no area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval dead-interval retransmit-interval hello-interval +no area 1.2.3.4 virtual-link 1.2.3.4 transmit-delay retransmit-interval retransmit-interval hello-interval +no bgp graceful-restart +no ipv6 nd mtu 1 +no neighbor 1.2.3.4 distribute-list 1 in +no neighbor 2001:db8::1 send-community both +no neighbor VARIABLE maximum-prefix +redistribute isis route-map VARIABLE metric 0 metric-type 2 +redistribute rip metric 0 route-map VARIABLE metric-type 1 +show bgp community VARIABLE local-AS no-export VARIABLE exact-match +show bgp ipv6 community no-advertise no-export no-export no-export +show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match +show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match +show bgp view VARIABLE +show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE +show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS +show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise +show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE +show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS +show ip bgp community no-advertise local-AS no-advertise VARIABLE +show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match +show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match +show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match +show ipv6 bgp community no-export no-export VARIABLE VARIABLE +show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match +show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match +show ipv6 mbgp community local-AS local-AS no-export no-export exact-match +show ipv6 mbgp community no-export no-export local-AS no-export exact-match +show ipv6 ospf6 database as-external dump +show ipv6 ospf6 database inter-prefix 1.2.3.4 detail +show ipv6 ospf6 database intra-prefix 1.2.3.4 internal +# +# +# Slightly Fuzzed commands +# +# +a8ra 0 range 1.0.0.0/8 adverOise +accept-lifetime VARIABE 1 VA6IABLE 19I3 VARIABLE 1 VARIABLE 1993 +arAea 1.2.M.4 virtual-link 1.2.3.4 dead-interval 1 dead-interval 1 dead-inter6val 1 transmit-delay 1 +area 0 virtu0al-link 1.2.3.i hello-interval 1 ello-interval 1 transmit-delay 1 retransmit-interval 1 +area 0 virtual-lin 1.2.3.4 retransmit-interval 1 tranwmit-delay 1 retransmit-interval 1 retransmit-interval 1 +area 0 virtual-link 1.2.3.4 retransmit-interal 1 trasmit-dely 1 +area 1.2.3.4 virtual-link 1.2.3.4 deadCinterval 1 dead-intervalK 1 retransmit-interval 1 dead-interval 1 +area 1.2.3.4 virtual-link 1.2.3.4 dead-intervalo I1 dead-interval 1 retransmit-interval1 dead-interval 1 +area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 dead-interva 1 retransmit-interval 1 transmit-delay 1 +area 1.2.3.4 virtuyl-link 1.2.3.4 dead-interval 1 dead-inervalI 1 retransmit-interval 1 dead-interval 1 +area 1.2.3.4 virual-link 1.2.34 retransmit-interval 1 dead-interval 1 dead-interva 1 +area1.2.83.4 virtual-link 1.2.3.4 retra0smit-interval 1 dead-interval 1 dead-interval 1 +clear bgAp 2001g:dbK::1 +clear ip bgp 1.2.3.4 pv4 mlticat out +cleau bg i2001:db8::1 rsclient +de:ug ospf6 messag2 lsreq :recv +how ip bgp communiQy no-advertise no-adve:tise no-advertise +ip route 1.0Q0.0/8 1.2.3.s4 reGject +ipv6 nd prefix 2O01:db8::/32 0 infinEite off-link +ipv6 nwd prefix 2001:db8::/32 0 infinite oUUff-link +ipv6 route 2001:db8::/32q2001:db8:k: blackhole 1 +kshow ip rIute bgp +matcch peer .2.30.4 +mcogin +mhow ipv6 mbgp community o-advertise yocal-AS no-advertise +neighbor1.2..4 attribute-unchnged next-hop +neihbcr 2001:d b8::1 distribute-list 1 in +nko key-tqring +no area 0 viertual-link 1.2.3k.4 retransmit-iterval retransmit-interval retransmit-interval hello-interval +no area 0 virtual-link 1.2.3.4 dead-intaerval dead-intervIl hello-interval retransmit-interval +no area 0 virtual-link 1.2.3.4 retransmit-interval retransmit-intervIl dead-interval tranImit-deqlay +no area 0 virtual-link S1.2.3.4 d-ead-interval hello-interval transmit-deay transmit-delay +no area 1.2.3.4 virtua -link 1.2.3.4 transmit-delay hello-interval hello-interval retransmt-interval +no area 1.2.3.4 virtual-link 1.2.3.4 dea-iterval retransmit-interva- dead-interval hello-interval +no area 1.2.3.4 virtual-link 1.2.3.4 hello-interSval dead-interval retransmit-interval transmitdelay +no a:rea 1.2.3.4 virtual-link 1.2.3.4 retransmit-interSvalW dead-interval retransmit-interval hello-interval +noarea 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval trynsmit-delay hello-interval +no area 1.2.3.4 virtual-link 1.2.3.4 transmt:delay retransmit-interval retransmit-interval dead-Mnterval +no ares 1.2.3.4 virtual-link 1.2.3.4 dead-interval retransmit-interval dead-inesval retransmit-interval +no ayrea 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval transmi-delay hello-interval +no bg2 grace2fuy-restart +no debug ospk6 nter2face +noimatch ipv6 addrMss VARIABLE +nomStch iA next-hop prefix-list +no neighbCr 200 :db8::1oroute-map VARIABLE export +no neighbor VARIABLE attributeaw8changed next-hop +no orea 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval ead-interval retransmit-inteSval hello-interval +no ospcdead-inkerval +no redistribute kernelrote-map VARIABLE metric 0 +no redistribute s4taik metric 0 +nos Ceighbor 1.2.3.4 route-mapEVARIABLE in +o :neighbor VAIABLE attribute-unchanged next-hop +ooa router ip +redistribute isis meGtric-type2 Q route-map VARIABLE +redistribute static metric-type 1 metri 0 rowute-map VARIABLE +set-Koveroadbit +sh2w ipv6 mbgp comAunity VARIABLE +shgw bgp ipv6 community no-export VARIABLE no-xport no-expmrt +shiow Wgp neighbors +shoAw ip bgpipv4 unicast com6munity no-export no-export no-advertise no-export exact-match +sho bgp view gARIABLE nyeighbors 2001:db8::1 received-routes +shoow bgp ommunity local-AS no--export +show6 bgp community no-advertise local4-AS no-advertise VARIABLE exact-math +show8 bgp view VARIABLE ipv4 multicast community ARIABLE VARIABLE local-S +show bgp cCommunity VARIABLE VOARIABL no-advertise +show bgp cimAunity loal-AS local-AS no-export local-AS +show bgp cmmunity n-advertise no-export local-S no-advertise +show bgp communi0y no-export no-Cexport no-0xport no-export +show bgp communityOlocal-A no-advertise local-WAS +show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match +show bgp communiy no-export no-adsvertise VARIABLOE local-AS +show bgp communiYty no-export VARIABLE VARIABLE locali-AS exact-math +show bgp commuUityW no-advertis local-AS no-advertise no-advertise +show bgp commuWnity VAIABLE local-AS no-advertise n-export +show bgp com:unity no-exportqno-export VARIABLE no-expoIrt exact-match +show bgp ipv6 community local-AS no-expor no-xport VARIABCLE +show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE +show bgp ipv6 community no-advertise no6-export lcal-AS local-AS +show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math +show bgp ipv6 comm-unity no-advertise no-export local-AS local-kS exact-match +show bgp ipv6 community no-export local-AS no-adertise no-adve-tie +show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE +show bgp naighbors 201:db8::1 rUeceived-routes +show bgp viewVAIABLE ipv4 multicast community VARIABLE4no-export no-advertise local-AS +show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS +show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export +show bgp view VARIABLE ipv4 multicast omsunity local-AS VARIABLE no-advertise nUo-export +show bgp view VARIABLE ipv4 mutiast community no-export no-export VARIBLE no-export +show bgp view VARIABLE ipv4 unicast 0community VARIABqLE local-AS no-export VARIABwE +show bgp view VARIABLE ipv4 unicast communeity no-export AcRIABLE no-advertise local-AS +show bgp view VARIABLE ipv4 unicasU comunity no-export VARIABL no-advertise +show bgp view VARIABLE ipv6 unicast cocmmunity VARIABLE no-advet6ise VARIABLE +show bgp view VARIABLE ipvk4 unicast communty no-advertie local-AS local-AS no-export +show bgp view VARIALE ipv4 multicast cyommunity no-xport local-AS local-AS +show i6 bge community no-export VARIABLE no-advegtise VARIABLE exact-match +show iI bgp community no-advertise no-ad2vertsse VARIABLE exact-match +show ip6osp6 database dump +show ipA6 bgp community local-AS local-AS no-advertse lo:cal-AS +show ip bg comunity VARIABLE lcal-AS no-advertise +show ip bgp communityno-export2no-export no-advertise locaE-AS +Show ip bgp community no-export loqcal-AS no-adverise no-export +show ip bgp community no-expor VARIABLEono-export VARIAuBLE +show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match +show ip bgp cWmmunity no-expoWrt VARIABLE no-advertise VARIABLEexact-match +show ip bgp ip4 nicast community no-advertise no-expoIt local-AS local-AS exact-match +show ip bgp ipAv4 multicast community no-export no-export no-export no-advertiqe exact-mach +show ip bgp ipv4 Aulticast community no-advertise VARIABLE no-advertisKe no-exort +show ip bgp ipv4 meuqlticast community VARIABLE VARIABLE no-export n-export +show ip bgp ipv4 mlticast coQmmunity localg-AS local-AS no-advertise local-AS +show ip bgp ipv4 multicast communiy VARIABLE no-export VARIABLE no-advertise yxact-atch +show ip bgp ipv4 unicast commu0nity local-AS no-export no-exrt VARIABLE exact-match +show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match +showip bgp ipv4 unicast community no-export VARIABLE no-exp-ort VAR6IABLE exact-match +show ip bgp ipv4 unicat community no-exportlocal-AS VARIABLE no-export exa0t-match +show ip bgp ipv4 unicst community no-advertiseG local-AS no-advertise +show ip bgp i:v4 multicast community VARIABLE VARIABLE VARIABLE no-export eMxact-match +show ip bgp Mv4 unicast community no-export VARIABLE VARIABLE VAoRIABLE +show ipgexecommunity-list 1 +show ipkv6 bgp community no-export no-export VARIABL VARIBLE +show ipv6 bgp commu2nity local-AS local-AS noEadvertise local-AS +show ipv6 bgp communitK VARIABLE lcocal-AS no-advertie no-advertise exact-match +show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match +show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE +show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match +show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE +show ipv6 bgp comu-ity VARIABLE local-AS no-advertise no-export exact-match +show ipv6 bgp comunity no- export local-AS no-advertisge VARIABLE +show ipv6 bgp ommunity sno-advcrtise VARIABLE no-export no-advertise exact-match +show ipv6 igp community no-advertise no-advertise no-ecxpo0rt no-export +show ipv6 mb communyty VARIABLE +show ipv6 osp8f6 database nQtwork adv-ruter 1.2.3.4 detail +show ipv6 ospf6 dataase type-7 adv-router 1.2.3.4 inernal +show ipv6 ospf6 Edatabase intuer8-prefix 1.2.3.4 detail +show ipvq6 ospf6 database as-externa detil +show ip Wbgp ipv4 unicast community no-advertise no-exprt no-export VARIABLEK exact-match +show ip Ybgp attribute-in ufo +showMbgp ipv6 community ARIABLE local-AS local-AS no8advertise exact-match +show p bgp community no-dvertise no-export no-advertiseIno-export exact-match +show uipv6 mbgp coqmmunKty VARIABLE +shQw ipv6 mbgp community no-advetise local-AS no-export no-export ex8ct-match +shuw ipv6 mbgp community VARIABLyUE no-export no-export no-advertise +shw bgp view VARIABLE ipv4 un0icast Gcommunity no-export VARIABLE no-advertise +sow ip bgp ipv4 mulicast community no-export no-adertise no-export no-advertise +sow ipv6 ospf6 databIase as-external adv-router 1.2.3.4 +Whow bgp view VARIAeBLE ipv4 unicast community local-AS no-advrtise no-advertise local-AS +Wneighbor 1.2.3.4 dot-capabiliy-negotiate diff --git a/tests/testcommands.out b/tests/testcommands.out new file mode 100644 index 00000000..1422aefc --- /dev/null +++ b/tests/testcommands.out @@ -0,0 +1,900 @@ +execute relaxed 'area 0 virtual-link 1.2.3.4 authentication null message-digest-key 1 md5 VARIABLE'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (authentication|) (message-digest|null) (message-digest-key|) <1-255> md5 KEY': '0', '1.2.3.4', 'authentication', 'null', 'message-digest-key', '1', 'VARIABLE' +execute strict 'area 0 virtual-link 1.2.3.4 authentication null message-digest-key 1 md5 VARIABLE'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (authentication|) (message-digest|null) (message-digest-key|) <1-255> md5 KEY': '0', '1.2.3.4', 'authentication', 'null', 'message-digest-key', '1', 'VARIABLE' +complete 'area 0 virtual-link 1.2.3.4 authentication null message-digest-key 1 md5 VARIABLE'@23: rv==2 +describe 'area 0 virtual-link 1.2.3.4 authentication null message-digest-key 1 md5 VARIABLE'@23: rv==0 + 'KEY' 'The OSPF password (key)' +execute relaxed 'area 0 virtual-link 1.2.3.4 dead-interval 1 hello-interval 1 retransmit-interval 1 transmit-delay 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '0', '1.2.3.4', 'dead-interval', '1', 'hello-interval', '1', 'retransmit-interval', '1', 'transmit-delay', '1' +execute strict 'area 0 virtual-link 1.2.3.4 dead-interval 1 hello-interval 1 retransmit-interval 1 transmit-delay 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '0', '1.2.3.4', 'dead-interval', '1', 'hello-interval', '1', 'retransmit-interval', '1', 'transmit-delay', '1' +complete 'area 0 virtual-link 1.2.3.4 dead-interval 1 hello-interval 1 retransmit-interval 1 transmit-delay 1'@23: rv==2 +describe 'area 0 virtual-link 1.2.3.4 dead-interval 1 hello-interval 1 retransmit-interval 1 transmit-delay 1'@23: rv==0 + '<1-65535>' 'Seconds' +execute relaxed 'area 0 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 dead-interval 1 retransmit-interval 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '0', '1.2.3.4', 'retransmit-interval', '1', 'retransmit-interval', '1', 'dead-interval', '1', 'retransmit-interval', '1' +execute strict 'area 0 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 dead-interval 1 retransmit-interval 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '0', '1.2.3.4', 'retransmit-interval', '1', 'retransmit-interval', '1', 'dead-interval', '1', 'retransmit-interval', '1' +complete 'area 0 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 dead-interval 1 retransmit-interval 1'@23: rv==2 +describe 'area 0 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 dead-interval 1 retransmit-interval 1'@23: rv==0 + '<1-65535>' 'Seconds' +execute relaxed 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 hello-interval 1 dead-interval 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '1.2.3.4', '1.2.3.4', 'hello-interval', '1', 'hello-interval', '1', 'dead-interval', '1' +execute strict 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 hello-interval 1 dead-interval 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '1.2.3.4', '1.2.3.4', 'hello-interval', '1', 'hello-interval', '1', 'dead-interval', '1' +complete 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 hello-interval 1 dead-interval 1'@23: rv==2 +describe 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 hello-interval 1 dead-interval 1'@23: rv==0 + '<1-65535>' 'Seconds' +execute relaxed 'area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 transmit-delay 1 dead-interval 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '1.2.3.4', '1.2.3.4', 'retransmit-interval', '1', 'retransmit-interval', '1', 'transmit-delay', '1', 'dead-interval', '1' +execute strict 'area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 transmit-delay 1 dead-interval 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '1.2.3.4', '1.2.3.4', 'retransmit-interval', '1', 'retransmit-interval', '1', 'transmit-delay', '1', 'dead-interval', '1' +complete 'area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 transmit-delay 1 dead-interval 1'@23: rv==2 +describe 'area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 transmit-delay 1 dead-interval 1'@23: rv==0 + '<1-65535>' 'Seconds' +execute relaxed 'clear bgp 1 out'@4: rv==0, 'clear bgp <1-4294967295> out': '1' +execute strict 'clear bgp 1 out'@4: rv==0, 'clear bgp <1-4294967295> out': '1' +complete 'clear bgp 1 out'@4: rv==7 + 'out' +describe 'clear bgp 1 out'@4: rv==0 + 'out' 'Soft reconfig outbound update' +execute relaxed 'clear bgp ipv6 2001:db8::1 out'@4: rv==0, 'clear bgp ipv6 (A.B.C.D|X:X::X:X) out': '2001:db8::1' +execute strict 'clear bgp ipv6 2001:db8::1 out'@4: rv==0, 'clear bgp ipv6 (A.B.C.D|X:X::X:X) out': '2001:db8::1' +complete 'clear bgp ipv6 2001:db8::1 out'@4: rv==7 + 'out' +describe 'clear bgp ipv6 2001:db8::1 out'@4: rv==0 + 'out' 'Soft reconfig outbound update' +execute relaxed 'clear bgp view VARIABLE * soft'@4: rv==0, 'clear bgp view WORD * soft': 'VARIABLE' +execute strict 'clear bgp view VARIABLE * soft'@4: rv==0, 'clear bgp view WORD * soft': 'VARIABLE' +complete 'clear bgp view VARIABLE * soft'@4: rv==7 + 'soft' +describe 'clear bgp view VARIABLE * soft'@4: rv==0 + 'soft' 'Soft reconfig' +execute relaxed 'clear ip bgp 1.2.3.4 ipv4 multicast out'@4: rv==0, 'clear ip bgp A.B.C.D ipv4 (unicast|multicast) out': '1.2.3.4', 'multicast' +execute strict 'clear ip bgp 1.2.3.4 ipv4 multicast out'@4: rv==0, 'clear ip bgp A.B.C.D ipv4 (unicast|multicast) out': '1.2.3.4', 'multicast' +complete 'clear ip bgp 1.2.3.4 ipv4 multicast out'@4: rv==7 + 'out' +describe 'clear ip bgp 1.2.3.4 ipv4 multicast out'@4: rv==0 + 'out' 'Soft reconfig outbound update' +execute relaxed 'ipv6 nd prefix 2001:db8::/32 infinite infinite no-autoconfig'@11: rv==0, 'ipv6 nd prefix X:X::X:X/M (<0-4294967295>|infinite) (<0-4294967295>|infinite) (no-autoconfig|)': '2001:db8::/32', 'infinite', 'infinite', 'no-autoconfig' +execute strict 'ipv6 nd prefix 2001:db8::/32 infinite infinite no-autoconfig'@11: rv==0, 'ipv6 nd prefix X:X::X:X/M (<0-4294967295>|infinite) (<0-4294967295>|infinite) (no-autoconfig|)': '2001:db8::/32', 'infinite', 'infinite', 'no-autoconfig' +complete 'ipv6 nd prefix 2001:db8::/32 infinite infinite no-autoconfig'@11: rv==7 + 'no-autoconfig' +describe 'ipv6 nd prefix 2001:db8::/32 infinite infinite no-autoconfig'@11: rv==0 + 'no-autoconfig' 'Do not use prefix for autoconfiguration' +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@5: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@5: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@5: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@5: rv==0 + '<1-255>' 'Distance value for this prefix' +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@9: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@9: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@9: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@9: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@10: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@10: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@10: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@10: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@11: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@11: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@11: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@11: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@12: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@12: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@12: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@12: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@14: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@14: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@14: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@14: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@15: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@15: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@15: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@15: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@16: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@16: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@16: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@16: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@17: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@17: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@17: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@17: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@18: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@18: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@18: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@18: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@19: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@19: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@19: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@19: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@20: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@20: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@20: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@20: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@21: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@21: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@21: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@21: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@22: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@22: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@22: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@22: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@23: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@23: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@23: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@23: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@24: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@24: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@24: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@24: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@25: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@25: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@25: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@25: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@35: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@35: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@35: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@35: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@40: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@40: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@40: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@40: rv==2 +execute relaxed 'network 1.0.0.0/8 area 0'@23: rv==0, 'network A.B.C.D/M area (A.B.C.D|<0-4294967295>)': '1.0.0.0/8', '0' +execute strict 'network 1.0.0.0/8 area 0'@23: rv==0, 'network A.B.C.D/M area (A.B.C.D|<0-4294967295>)': '1.0.0.0/8', '0' +complete 'network 1.0.0.0/8 area 0'@23: rv==2 +describe 'network 1.0.0.0/8 area 0'@23: rv==0 + '<0-4294967295>' 'OSPF area ID as a decimal value' + 'A.B.C.D' 'OSPF area ID in IP address format' +execute relaxed 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval hello-interval transmit-delay'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'hello-interval', 'dead-interval', 'hello-interval', 'transmit-delay' +execute strict 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval hello-interval transmit-delay'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'hello-interval', 'dead-interval', 'hello-interval', 'transmit-delay' +complete 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval hello-interval transmit-delay'@23: rv==7 + 'transmit-delay' +describe 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval hello-interval transmit-delay'@23: rv==0 + 'transmit-delay' 'Seconds' +execute relaxed 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval retransmit-interval transmit-delay'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'hello-interval', 'dead-interval', 'retransmit-interval', 'transmit-delay' +execute strict 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval retransmit-interval transmit-delay'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'hello-interval', 'dead-interval', 'retransmit-interval', 'transmit-delay' +complete 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval retransmit-interval transmit-delay'@23: rv==7 + 'transmit-delay' +describe 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval retransmit-interval transmit-delay'@23: rv==0 + 'transmit-delay' 'Seconds' +execute relaxed 'no area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval dead-interval retransmit-interval hello-interval'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'retransmit-interval', 'dead-interval', 'retransmit-interval', 'hello-interval' +execute strict 'no area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval dead-interval retransmit-interval hello-interval'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'retransmit-interval', 'dead-interval', 'retransmit-interval', 'hello-interval' +complete 'no area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval dead-interval retransmit-interval hello-interval'@23: rv==7 + 'hello-interval' +describe 'no area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval dead-interval retransmit-interval hello-interval'@23: rv==0 + 'hello-interval' 'Link state transmit delay' +execute relaxed 'no area 1.2.3.4 virtual-link 1.2.3.4 transmit-delay retransmit-interval retransmit-interval hello-interval'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'transmit-delay', 'retransmit-interval', 'retransmit-interval', 'hello-interval' +execute strict 'no area 1.2.3.4 virtual-link 1.2.3.4 transmit-delay retransmit-interval retransmit-interval hello-interval'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'transmit-delay', 'retransmit-interval', 'retransmit-interval', 'hello-interval' +complete 'no area 1.2.3.4 virtual-link 1.2.3.4 transmit-delay retransmit-interval retransmit-interval hello-interval'@23: rv==7 + 'hello-interval' +describe 'no area 1.2.3.4 virtual-link 1.2.3.4 transmit-delay retransmit-interval retransmit-interval hello-interval'@23: rv==0 + 'hello-interval' 'Link state transmit delay' +execute relaxed 'no bgp graceful-restart'@17: rv==0, 'no bgp graceful-restart' +execute strict 'no bgp graceful-restart'@17: rv==0, 'no bgp graceful-restart' +complete 'no bgp graceful-restart'@17: rv==7 + 'graceful-restart' +describe 'no bgp graceful-restart'@17: rv==0 + 'graceful-restart' 'Graceful restart capability parameters' +execute relaxed 'no bgp graceful-restart'@18: rv==0, 'no bgp graceful-restart' +execute strict 'no bgp graceful-restart'@18: rv==2 +complete 'no bgp graceful-restart'@18: rv==2 +describe 'no bgp graceful-restart'@18: rv==2 +execute relaxed 'no bgp graceful-restart'@19: rv==0, 'no bgp graceful-restart' +execute strict 'no bgp graceful-restart'@19: rv==2 +complete 'no bgp graceful-restart'@19: rv==2 +describe 'no bgp graceful-restart'@19: rv==2 +execute relaxed 'no bgp graceful-restart'@20: rv==0, 'no bgp graceful-restart' +execute strict 'no bgp graceful-restart'@20: rv==2 +complete 'no bgp graceful-restart'@20: rv==2 +describe 'no bgp graceful-restart'@20: rv==2 +execute relaxed 'no bgp graceful-restart'@21: rv==0, 'no bgp graceful-restart' +execute strict 'no bgp graceful-restart'@21: rv==2 +complete 'no bgp graceful-restart'@21: rv==2 +describe 'no bgp graceful-restart'@21: rv==2 +execute relaxed 'no bgp graceful-restart'@22: rv==0, 'no bgp graceful-restart' +execute strict 'no bgp graceful-restart'@22: rv==2 +complete 'no bgp graceful-restart'@22: rv==2 +describe 'no bgp graceful-restart'@22: rv==2 +execute relaxed 'no ipv6 nd mtu 1'@11: rv==0, 'no ipv6 nd mtu <1-65535>': '1' +execute strict 'no ipv6 nd mtu 1'@11: rv==0, 'no ipv6 nd mtu <1-65535>': '1' +complete 'no ipv6 nd mtu 1'@11: rv==2 +describe 'no ipv6 nd mtu 1'@11: rv==0 + '<1-65535>' 'MTU in bytes' +execute relaxed 'no neighbor 1.2.3.4 distribute-list 1 in'@17: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list (<1-199>|<1300-2699>|WORD) (in|out)': '1.2.3.4', '1', 'in' +execute strict 'no neighbor 1.2.3.4 distribute-list 1 in'@17: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list (<1-199>|<1300-2699>|WORD) (in|out)': '1.2.3.4', '1', 'in' +complete 'no neighbor 1.2.3.4 distribute-list 1 in'@17: rv==7 + 'in' +describe 'no neighbor 1.2.3.4 distribute-list 1 in'@17: rv==0 + 'in' 'Filter incoming updates' +execute relaxed 'no neighbor 1.2.3.4 distribute-list 1 in'@18: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list (<1-199>|<1300-2699>|WORD) (in|out)': '1.2.3.4', '1', 'in' +execute strict 'no neighbor 1.2.3.4 distribute-list 1 in'@18: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list (<1-199>|<1300-2699>|WORD) (in|out)': '1.2.3.4', '1', 'in' +complete 'no neighbor 1.2.3.4 distribute-list 1 in'@18: rv==7 + 'in' +describe 'no neighbor 1.2.3.4 distribute-list 1 in'@18: rv==0 + 'in' 'Filter incoming updates' +execute relaxed 'no neighbor 1.2.3.4 distribute-list 1 in'@19: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list (<1-199>|<1300-2699>|WORD) (in|out)': '1.2.3.4', '1', 'in' +execute strict 'no neighbor 1.2.3.4 distribute-list 1 in'@19: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list (<1-199>|<1300-2699>|WORD) (in|out)': '1.2.3.4', '1', 'in' +complete 'no neighbor 1.2.3.4 distribute-list 1 in'@19: rv==7 + 'in' +describe 'no neighbor 1.2.3.4 distribute-list 1 in'@19: rv==0 + 'in' 'Filter incoming updates' +execute relaxed 'no neighbor 1.2.3.4 distribute-list 1 in'@20: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list (<1-199>|<1300-2699>|WORD) (in|out)': '1.2.3.4', '1', 'in' +execute strict 'no neighbor 1.2.3.4 distribute-list 1 in'@20: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list (<1-199>|<1300-2699>|WORD) (in|out)': '1.2.3.4', '1', 'in' +complete 'no neighbor 1.2.3.4 distribute-list 1 in'@20: rv==7 + 'in' +describe 'no neighbor 1.2.3.4 distribute-list 1 in'@20: rv==0 + 'in' 'Filter incoming updates' +execute relaxed 'no neighbor 1.2.3.4 distribute-list 1 in'@21: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list (<1-199>|<1300-2699>|WORD) (in|out)': '1.2.3.4', '1', 'in' +execute strict 'no neighbor 1.2.3.4 distribute-list 1 in'@21: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list (<1-199>|<1300-2699>|WORD) (in|out)': '1.2.3.4', '1', 'in' +complete 'no neighbor 1.2.3.4 distribute-list 1 in'@21: rv==7 + 'in' +describe 'no neighbor 1.2.3.4 distribute-list 1 in'@21: rv==0 + 'in' 'Filter incoming updates' +execute relaxed 'no neighbor 1.2.3.4 distribute-list 1 in'@22: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list (<1-199>|<1300-2699>|WORD) (in|out)': '1.2.3.4', '1', 'in' +execute strict 'no neighbor 1.2.3.4 distribute-list 1 in'@22: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list (<1-199>|<1300-2699>|WORD) (in|out)': '1.2.3.4', '1', 'in' +complete 'no neighbor 1.2.3.4 distribute-list 1 in'@22: rv==7 + 'in' +describe 'no neighbor 1.2.3.4 distribute-list 1 in'@22: rv==0 + 'in' 'Filter incoming updates' +execute relaxed 'no neighbor 2001:db8::1 send-community both'@17: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both' +execute strict 'no neighbor 2001:db8::1 send-community both'@17: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both' +complete 'no neighbor 2001:db8::1 send-community both'@17: rv==7 + 'both' +describe 'no neighbor 2001:db8::1 send-community both'@17: rv==0 + 'both' 'Send Standard and Extended Community attributes' +execute relaxed 'no neighbor 2001:db8::1 send-community both'@18: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both' +execute strict 'no neighbor 2001:db8::1 send-community both'@18: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both' +complete 'no neighbor 2001:db8::1 send-community both'@18: rv==7 + 'both' +describe 'no neighbor 2001:db8::1 send-community both'@18: rv==0 + 'both' 'Send Standard and Extended Community attributes' +execute relaxed 'no neighbor 2001:db8::1 send-community both'@19: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both' +execute strict 'no neighbor 2001:db8::1 send-community both'@19: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both' +complete 'no neighbor 2001:db8::1 send-community both'@19: rv==7 + 'both' +describe 'no neighbor 2001:db8::1 send-community both'@19: rv==0 + 'both' 'Send Standard and Extended Community attributes' +execute relaxed 'no neighbor 2001:db8::1 send-community both'@20: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both' +execute strict 'no neighbor 2001:db8::1 send-community both'@20: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both' +complete 'no neighbor 2001:db8::1 send-community both'@20: rv==7 + 'both' +describe 'no neighbor 2001:db8::1 send-community both'@20: rv==0 + 'both' 'Send Standard and Extended Community attributes' +execute relaxed 'no neighbor 2001:db8::1 send-community both'@21: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both' +execute strict 'no neighbor 2001:db8::1 send-community both'@21: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both' +complete 'no neighbor 2001:db8::1 send-community both'@21: rv==7 + 'both' +describe 'no neighbor 2001:db8::1 send-community both'@21: rv==0 + 'both' 'Send Standard and Extended Community attributes' +execute relaxed 'no neighbor 2001:db8::1 send-community both'@22: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both' +execute strict 'no neighbor 2001:db8::1 send-community both'@22: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both' +complete 'no neighbor 2001:db8::1 send-community both'@22: rv==7 + 'both' +describe 'no neighbor 2001:db8::1 send-community both'@22: rv==0 + 'both' 'Send Standard and Extended Community attributes' +execute relaxed 'no neighbor VARIABLE maximum-prefix'@17: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE' +execute strict 'no neighbor VARIABLE maximum-prefix'@17: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE' +complete 'no neighbor VARIABLE maximum-prefix'@17: rv==7 + 'maximum-prefix' +describe 'no neighbor VARIABLE maximum-prefix'@17: rv==0 + 'maximum-prefix' 'Maximum number of prefix accept from this peer' +execute relaxed 'no neighbor VARIABLE maximum-prefix'@18: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE' +execute strict 'no neighbor VARIABLE maximum-prefix'@18: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE' +complete 'no neighbor VARIABLE maximum-prefix'@18: rv==7 + 'maximum-prefix' +describe 'no neighbor VARIABLE maximum-prefix'@18: rv==0 + 'maximum-prefix' 'Maximum number of prefix accept from this peer' +execute relaxed 'no neighbor VARIABLE maximum-prefix'@19: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE' +execute strict 'no neighbor VARIABLE maximum-prefix'@19: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE' +complete 'no neighbor VARIABLE maximum-prefix'@19: rv==7 + 'maximum-prefix' +describe 'no neighbor VARIABLE maximum-prefix'@19: rv==0 + 'maximum-prefix' 'Maximum number of prefix accept from this peer' +execute relaxed 'no neighbor VARIABLE maximum-prefix'@20: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE' +execute strict 'no neighbor VARIABLE maximum-prefix'@20: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE' +complete 'no neighbor VARIABLE maximum-prefix'@20: rv==7 + 'maximum-prefix' +describe 'no neighbor VARIABLE maximum-prefix'@20: rv==0 + 'maximum-prefix' 'Maximum number of prefix accept from this peer' +execute relaxed 'no neighbor VARIABLE maximum-prefix'@21: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE' +execute strict 'no neighbor VARIABLE maximum-prefix'@21: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE' +complete 'no neighbor VARIABLE maximum-prefix'@21: rv==7 + 'maximum-prefix' +describe 'no neighbor VARIABLE maximum-prefix'@21: rv==0 + 'maximum-prefix' 'Maximum number of prefix accept from this peer' +execute relaxed 'no neighbor VARIABLE maximum-prefix'@22: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE' +execute strict 'no neighbor VARIABLE maximum-prefix'@22: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE' +complete 'no neighbor VARIABLE maximum-prefix'@22: rv==7 + 'maximum-prefix' +describe 'no neighbor VARIABLE maximum-prefix'@22: rv==0 + 'maximum-prefix' 'Maximum number of prefix accept from this peer' +execute relaxed 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@1: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' +execute strict 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@1: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' +complete 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@1: rv==7 + 'exact-match' +describe 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@1: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@2: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' +execute strict 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@2: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' +complete 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@2: rv==7 + 'exact-match' +describe 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@4: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' +execute strict 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@4: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' +complete 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@4: rv==7 + 'exact-match' +describe 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show bgp ipv6 community no-advertise no-export no-export no-export'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-export', 'no-export', 'no-export' +execute strict 'show bgp ipv6 community no-advertise no-export no-export no-export'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-export', 'no-export', 'no-export' +complete 'show bgp ipv6 community no-advertise no-export no-export no-export'@1: rv==7 + 'no-export' +describe 'show bgp ipv6 community no-advertise no-export no-export no-export'@1: rv==0 + 'AA:NN' 'community number' + 'no-export' 'Do not export to next AS (well-known community)' +execute relaxed 'show bgp ipv6 community no-advertise no-export no-export no-export'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-export', 'no-export', 'no-export' +execute strict 'show bgp ipv6 community no-advertise no-export no-export no-export'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-export', 'no-export', 'no-export' +complete 'show bgp ipv6 community no-advertise no-export no-export no-export'@2: rv==7 + 'no-export' +describe 'show bgp ipv6 community no-advertise no-export no-export no-export'@2: rv==0 + 'AA:NN' 'community number' + 'no-export' 'Do not export to next AS (well-known community)' +execute relaxed 'show bgp ipv6 community no-advertise no-export no-export no-export'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-export', 'no-export', 'no-export' +execute strict 'show bgp ipv6 community no-advertise no-export no-export no-export'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-export', 'no-export', 'no-export' +complete 'show bgp ipv6 community no-advertise no-export no-export no-export'@4: rv==7 + 'no-export' +describe 'show bgp ipv6 community no-advertise no-export no-export no-export'@4: rv==0 + 'AA:NN' 'community number' + 'no-export' 'Do not export to next AS (well-known community)' +execute relaxed 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'local-AS', 'no-advertise' +execute strict 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'local-AS', 'no-advertise' +complete 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@1: rv==7 + 'exact-match' +describe 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@1: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'local-AS', 'no-advertise' +execute strict 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'local-AS', 'no-advertise' +complete 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@2: rv==7 + 'exact-match' +describe 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'local-AS', 'no-advertise' +execute strict 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'local-AS', 'no-advertise' +complete 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@4: rv==7 + 'exact-match' +describe 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'VARIABLE', 'local-AS' +execute strict 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'VARIABLE', 'local-AS' +complete 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@1: rv==7 + 'exact-match' +describe 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@1: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'VARIABLE', 'local-AS' +execute strict 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'VARIABLE', 'local-AS' +complete 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@2: rv==7 + 'exact-match' +describe 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'VARIABLE', 'local-AS' +execute strict 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'VARIABLE', 'local-AS' +complete 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@4: rv==7 + 'exact-match' +describe 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show bgp view VARIABLE'@1: rv==4 +execute strict 'show bgp view VARIABLE'@1: rv==4 +complete 'show bgp view VARIABLE'@1: rv==2 +describe 'show bgp view VARIABLE'@1: rv==0 + 'WORD' 'View name' +execute relaxed 'show bgp view VARIABLE'@2: rv==0, 'show bgp view WORD': 'VARIABLE' +execute strict 'show bgp view VARIABLE'@2: rv==0, 'show bgp view WORD': 'VARIABLE' +complete 'show bgp view VARIABLE'@2: rv==2 +describe 'show bgp view VARIABLE'@2: rv==0 + 'WORD' 'View name' +execute relaxed 'show bgp view VARIABLE'@4: rv==0, 'show bgp view WORD': 'VARIABLE' +execute strict 'show bgp view VARIABLE'@4: rv==0, 'show bgp view WORD': 'VARIABLE' +complete 'show bgp view VARIABLE'@4: rv==2 +describe 'show bgp view VARIABLE'@4: rv==0 + 'WORD' 'View name' +execute relaxed 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'no-export', 'no-export', 'local-AS', 'VARIABLE' +execute strict 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'no-export', 'no-export', 'local-AS', 'VARIABLE' +complete 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@1: rv==2 +describe 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@1: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'no-export', 'no-export', 'local-AS', 'VARIABLE' +execute strict 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'no-export', 'no-export', 'local-AS', 'VARIABLE' +complete 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@2: rv==2 +describe 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@2: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'no-export', 'no-export', 'local-AS', 'VARIABLE' +execute strict 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'no-export', 'no-export', 'local-AS', 'VARIABLE' +complete 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@4: rv==2 +describe 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@4: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertise', 'no-advertise', 'local-AS' +execute strict 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertise', 'no-advertise', 'local-AS' +complete 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@1: rv==7 + 'local-AS' +describe 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@1: rv==0 + 'AA:NN' 'community number' + 'local-AS' 'Do not send outside local AS (well-known community)' +execute relaxed 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertise', 'no-advertise', 'local-AS' +execute strict 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertise', 'no-advertise', 'local-AS' +complete 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@2: rv==7 + 'local-AS' +describe 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@2: rv==0 + 'AA:NN' 'community number' + 'local-AS' 'Do not send outside local AS (well-known community)' +execute relaxed 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertise', 'no-advertise', 'local-AS' +execute strict 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertise', 'no-advertise', 'local-AS' +complete 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@4: rv==7 + 'local-AS' +describe 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@4: rv==0 + 'AA:NN' 'community number' + 'local-AS' 'Do not send outside local AS (well-known community)' +execute relaxed 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'no-export', 'VARIABLE', 'no-advertise' +execute strict 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'no-export', 'VARIABLE', 'no-advertise' +complete 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@1: rv==7 + 'no-advertise' +describe 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@1: rv==0 + 'AA:NN' 'community number' + 'no-advertise' 'Do not advertise to any peer (well-known community)' +execute relaxed 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'no-export', 'VARIABLE', 'no-advertise' +execute strict 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'no-export', 'VARIABLE', 'no-advertise' +complete 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@2: rv==7 + 'no-advertise' +describe 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@2: rv==0 + 'AA:NN' 'community number' + 'no-advertise' 'Do not advertise to any peer (well-known community)' +execute relaxed 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'no-export', 'VARIABLE', 'no-advertise' +execute strict 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'no-export', 'VARIABLE', 'no-advertise' +complete 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@4: rv==7 + 'no-advertise' +describe 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@4: rv==0 + 'AA:NN' 'community number' + 'no-advertise' 'Do not advertise to any peer (well-known community)' +execute relaxed 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' +execute strict 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' +complete 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@1: rv==2 +describe 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@1: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' +execute strict 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' +complete 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@2: rv==2 +describe 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@2: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' +execute strict 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' +complete 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@4: rv==2 +describe 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@4: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv6', 'multicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS' +execute strict 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv6', 'multicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS' +complete 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@1: rv==7 + 'local-AS' +describe 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@1: rv==0 + 'AA:NN' 'community number' + 'local-AS' 'Do not send outside local AS (well-known community)' +execute relaxed 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv6', 'multicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS' +execute strict 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv6', 'multicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS' +complete 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@2: rv==7 + 'local-AS' +describe 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@2: rv==0 + 'AA:NN' 'community number' + 'local-AS' 'Do not send outside local AS (well-known community)' +execute relaxed 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv6', 'multicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS' +execute strict 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv6', 'multicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS' +complete 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@4: rv==7 + 'local-AS' +describe 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@4: rv==0 + 'AA:NN' 'community number' + 'local-AS' 'Do not send outside local AS (well-known community)' +execute relaxed 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@1: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'local-AS', 'no-advertise', 'VARIABLE' +execute strict 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@1: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'local-AS', 'no-advertise', 'VARIABLE' +complete 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@1: rv==2 +describe 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@1: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@2: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'local-AS', 'no-advertise', 'VARIABLE' +execute strict 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@2: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'local-AS', 'no-advertise', 'VARIABLE' +complete 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@2: rv==2 +describe 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@2: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@4: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'local-AS', 'no-advertise', 'VARIABLE' +execute strict 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@4: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'local-AS', 'no-advertise', 'VARIABLE' +complete 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@4: rv==2 +describe 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@4: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS' +execute strict 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS' +complete 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@1: rv==7 + 'exact-match' +describe 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@1: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS' +execute strict 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS' +complete 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@2: rv==7 + 'exact-match' +describe 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS' +execute strict 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS' +complete 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@4: rv==7 + 'exact-match' +describe 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'local-AS', 'VARIABLE' +execute strict 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'local-AS', 'VARIABLE' +complete 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@1: rv==7 + 'exact-match' +describe 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@1: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'local-AS', 'VARIABLE' +execute strict 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'local-AS', 'VARIABLE' +complete 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@2: rv==7 + 'exact-match' +describe 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'local-AS', 'VARIABLE' +execute strict 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'local-AS', 'VARIABLE' +complete 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@4: rv==7 + 'exact-match' +describe 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'no-export', 'local-AS' +execute strict 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'no-export', 'local-AS' +complete 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@1: rv==7 + 'exact-match' +describe 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@1: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'no-export', 'local-AS' +execute strict 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'no-export', 'local-AS' +complete 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@2: rv==7 + 'exact-match' +describe 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'no-export', 'local-AS' +execute strict 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'no-export', 'local-AS' +complete 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@4: rv==7 + 'exact-match' +describe 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'no-export', 'VARIABLE', 'VARIABLE' +execute strict 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'no-export', 'VARIABLE', 'VARIABLE' +complete 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@2: rv==2 +describe 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@2: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'no-export', 'VARIABLE', 'VARIABLE' +execute strict 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'no-export', 'VARIABLE', 'VARIABLE' +complete 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@4: rv==2 +describe 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@4: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-advertise', 'no-advertise' +execute strict 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-advertise', 'no-advertise' +complete 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@2: rv==7 + 'exact-match' +describe 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-advertise', 'no-advertise' +execute strict 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-advertise', 'no-advertise' +complete 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@4: rv==7 + 'exact-match' +describe 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@2: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'local-AS', 'VARIABLE' +execute strict 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@2: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'local-AS', 'VARIABLE' +complete 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@2: rv==7 + 'exact-match' +describe 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@4: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'local-AS', 'VARIABLE' +execute strict 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@4: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'local-AS', 'VARIABLE' +complete 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@4: rv==7 + 'exact-match' +describe 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@2: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'no-export', 'no-export' +execute strict 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@2: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'no-export', 'no-export' +complete 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@2: rv==7 + 'exact-match' +describe 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@4: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'no-export', 'no-export' +execute strict 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@4: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'no-export', 'no-export' +complete 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@4: rv==7 + 'exact-match' +describe 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@2: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'no-export', 'no-export', 'local-AS', 'no-export' +execute strict 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@2: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'no-export', 'no-export', 'local-AS', 'no-export' +complete 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@2: rv==7 + 'exact-match' +describe 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@4: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'no-export', 'no-export', 'local-AS', 'no-export' +execute strict 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@4: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'no-export', 'no-export', 'local-AS', 'no-export' +complete 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@4: rv==7 + 'exact-match' +describe 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 ospf6 database as-external dump'@2: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) (detail|dump|internal)': 'as-external', 'dump' +execute strict 'show ipv6 ospf6 database as-external dump'@2: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) (detail|dump|internal)': 'as-external', 'dump' +complete 'show ipv6 ospf6 database as-external dump'@2: rv==7 + 'dump' +describe 'show ipv6 ospf6 database as-external dump'@2: rv==0 + 'dump' 'Dump LSAs' +execute relaxed 'show ipv6 ospf6 database as-external dump'@4: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) (detail|dump|internal)': 'as-external', 'dump' +execute strict 'show ipv6 ospf6 database as-external dump'@4: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) (detail|dump|internal)': 'as-external', 'dump' +complete 'show ipv6 ospf6 database as-external dump'@4: rv==7 + 'dump' +describe 'show ipv6 ospf6 database as-external dump'@4: rv==0 + 'dump' 'Dump LSAs' +execute relaxed 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@2: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'inter-prefix', '1.2.3.4', 'detail' +execute strict 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@2: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'inter-prefix', '1.2.3.4', 'detail' +complete 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@2: rv==7 + 'detail' +describe 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@2: rv==0 + 'detail' 'Display details of LSAs' +execute relaxed 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@4: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'inter-prefix', '1.2.3.4', 'detail' +execute strict 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@4: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'inter-prefix', '1.2.3.4', 'detail' +complete 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@4: rv==7 + 'detail' +describe 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@4: rv==0 + 'detail' 'Display details of LSAs' +execute relaxed 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@2: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'intra-prefix', '1.2.3.4', 'internal' +execute strict 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@2: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'intra-prefix', '1.2.3.4', 'internal' +complete 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@2: rv==7 + 'internal' +describe 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@2: rv==0 + 'internal' 'Display LSA's internal information' +execute relaxed 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@4: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'intra-prefix', '1.2.3.4', 'internal' +execute strict 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@4: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'intra-prefix', '1.2.3.4', 'internal' +complete 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@4: rv==7 + 'internal' +describe 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@4: rv==0 + 'internal' 'Display LSA's internal information' +execute relaxed 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 dead-interva 1 retransmit-interval 1 transmit-delay 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '1.2.3.4', '1.2.3.4', 'hello-interval', '1', 'dead-interva', '1', 'retransmit-interval', '1', 'transmit-delay', '1' +execute strict 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 dead-interva 1 retransmit-interval 1 transmit-delay 1'@23: rv==2 +complete 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 dead-interva 1 retransmit-interval 1 transmit-delay 1'@23: rv==2 +describe 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 dead-interva 1 retransmit-interval 1 transmit-delay 1'@23: rv==0 + '<1-65535>' 'Seconds' +execute relaxed 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@1: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABL', 'no-agdvertise', 'locl-AS', 'no-advertise' +execute strict 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@1: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABL', 'no-agdvertise', 'locl-AS', 'no-advertise' +complete 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@1: rv==7 + 'exact-match' +describe 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@1: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@2: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABL', 'no-agdvertise', 'locl-AS', 'no-advertise' +execute strict 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@2: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABL', 'no-agdvertise', 'locl-AS', 'no-advertise' +complete 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@2: rv==7 + 'exact-match' +describe 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@4: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABL', 'no-agdvertise', 'locl-AS', 'no-advertise' +execute strict 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@4: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABL', 'no-agdvertise', 'locl-AS', 'no-advertise' +complete 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@4: rv==7 + 'exact-match' +describe 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'local-AS', 'no-expor', 'no-xport', 'VARIABCLE' +execute strict 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'local-AS', 'no-expor', 'no-xport', 'VARIABCLE' +complete 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@1: rv==2 +describe 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@1: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'local-AS', 'no-expor', 'no-xport', 'VARIABCLE' +execute strict 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'local-AS', 'no-expor', 'no-xport', 'VARIABCLE' +complete 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@2: rv==2 +describe 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@2: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'local-AS', 'no-expor', 'no-xport', 'VARIABCLE' +execute strict 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'local-AS', 'no-expor', 'no-xport', 'VARIABCLE' +complete 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@4: rv==2 +describe 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@4: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-adsertise', 'local-AS', 'no8-advertise', 'VARIABLE' +execute strict 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-adsertise', 'local-AS', 'no8-advertise', 'VARIABLE' +complete 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@1: rv==2 +describe 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@1: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-adsertise', 'local-AS', 'no8-advertise', 'VARIABLE' +execute strict 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-adsertise', 'local-AS', 'no8-advertise', 'VARIABLE' +complete 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@2: rv==2 +describe 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@2: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-adsertise', 'local-AS', 'no8-advertise', 'VARIABLE' +execute strict 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-adsertise', 'local-AS', 'no8-advertise', 'VARIABLE' +complete 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@4: rv==2 +describe 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@4: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no6-export', 'lcal-AS', 'local-AS' +execute strict 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no6-export', 'lcal-AS', 'local-AS' +complete 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@1: rv==7 + 'local-AS' +describe 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@1: rv==0 + 'AA:NN' 'community number' + 'local-AS' 'Do not send outside local AS (well-known community)' +execute relaxed 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no6-export', 'lcal-AS', 'local-AS' +execute strict 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no6-export', 'lcal-AS', 'local-AS' +complete 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@2: rv==7 + 'local-AS' +describe 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@2: rv==0 + 'AA:NN' 'community number' + 'local-AS' 'Do not send outside local AS (well-known community)' +execute relaxed 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no6-export', 'lcal-AS', 'local-AS' +execute strict 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no6-export', 'lcal-AS', 'local-AS' +complete 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@4: rv==7 + 'local-AS' +describe 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@4: rv==0 + 'AA:NN' 'community number' + 'local-AS' 'Do not send outside local AS (well-known community)' +execute relaxed 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-advertise', 'no-advertiseno-xport', 'exact-math' +execute strict 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-advertise', 'no-advertiseno-xport', 'exact-math' +complete 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@1: rv==2 +describe 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@1: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-advertise', 'no-advertiseno-xport', 'exact-math' +execute strict 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-advertise', 'no-advertiseno-xport', 'exact-math' +complete 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@2: rv==2 +describe 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@2: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-advertise', 'no-advertiseno-xport', 'exact-math' +execute strict 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-advertise', 'no-advertiseno-xport', 'exact-math' +complete 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@4: rv==2 +describe 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@4: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'local-AS', 'no-adertise', 'no-adve-tie' +execute strict 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'local-AS', 'no-adertise', 'no-adve-tie' +complete 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@1: rv==2 +describe 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@1: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'local-AS', 'no-adertise', 'no-adve-tie' +execute strict 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'local-AS', 'no-adertise', 'no-adve-tie' +complete 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@2: rv==2 +describe 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@2: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'local-AS', 'no-adertise', 'no-adve-tie' +execute strict 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'local-AS', 'no-adertise', 'no-adve-tie' +complete 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@4: rv==2 +describe 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@4: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'yno-advertis', 'VAyRIABLE', 'no-advertise', 'VARIABLE' +execute strict 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'yno-advertis', 'VAyRIABLE', 'no-advertise', 'VARIABLE' +complete 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@1: rv==2 +describe 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@1: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'yno-advertis', 'VAyRIABLE', 'no-advertise', 'VARIABLE' +execute strict 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'yno-advertis', 'VAyRIABLE', 'no-advertise', 'VARIABLE' +complete 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@2: rv==2 +describe 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@2: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'yno-advertis', 'VAyRIABLE', 'no-advertise', 'VARIABLE' +execute strict 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'yno-advertis', 'VAyRIABLE', 'no-advertise', 'VARIABLE' +complete 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@4: rv==2 +describe 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@4: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VAR0IABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertie', 'no-advertise', 'local-AS' +execute strict 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VAR0IABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertie', 'no-advertise', 'local-AS' +complete 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@1: rv==7 + 'local-AS' +describe 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@1: rv==0 + 'AA:NN' 'community number' + 'local-AS' 'Do not send outside local AS (well-known community)' +execute relaxed 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VAR0IABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertie', 'no-advertise', 'local-AS' +execute strict 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VAR0IABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertie', 'no-advertise', 'local-AS' +complete 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@2: rv==7 + 'local-AS' +describe 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@2: rv==0 + 'AA:NN' 'community number' + 'local-AS' 'Do not send outside local AS (well-known community)' +execute relaxed 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VAR0IABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertie', 'no-advertise', 'local-AS' +execute strict 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VAR0IABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertie', 'no-advertise', 'local-AS' +complete 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@4: rv==7 + 'local-AS' +describe 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@4: rv==0 + 'AA:NN' 'community number' + 'local-AS' 'Do not send outside local AS (well-known community)' +execute relaxed 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'local-AS', 'VARIABLE', 'loqal-AS', 'no-export' +execute strict 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'local-AS', 'VARIABLE', 'loqal-AS', 'no-export' +complete 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@1: rv==7 + 'no-export' +describe 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@1: rv==0 + 'AA:NN' 'community number' + 'no-export' 'Do not export to next AS (well-known community)' +execute relaxed 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'local-AS', 'VARIABLE', 'loqal-AS', 'no-export' +execute strict 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'local-AS', 'VARIABLE', 'loqal-AS', 'no-export' +complete 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@2: rv==7 + 'no-export' +describe 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@2: rv==0 + 'AA:NN' 'community number' + 'no-export' 'Do not export to next AS (well-known community)' +execute relaxed 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'local-AS', 'VARIABLE', 'loqal-AS', 'no-export' +execute strict 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'local-AS', 'VARIABLE', 'loqal-AS', 'no-export' +complete 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@4: rv==7 + 'no-export' +describe 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@4: rv==0 + 'AA:NN' 'community number' + 'no-export' 'Do not export to next AS (well-known community)' +execute relaxed 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@1: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-expor', 'VARIABLEono-export', 'VARIAuBLE' +execute strict 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@1: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-expor', 'VARIABLEono-export', 'VARIAuBLE' +complete 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@1: rv==2 +describe 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@1: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@2: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-expor', 'VARIABLEono-export', 'VARIAuBLE' +execute strict 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@2: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-expor', 'VARIABLEono-export', 'VARIAuBLE' +complete 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@2: rv==2 +describe 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@2: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@4: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-expor', 'VARIABLEono-export', 'VARIAuBLE' +execute strict 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@4: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-expor', 'VARIABLEono-export', 'VARIAuBLE' +complete 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@4: rv==2 +describe 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@4: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@1: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLElocal-AS', 'no-advertise', 'local-AS', 'xack-match' +execute strict 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@1: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLElocal-AS', 'no-advertise', 'local-AS', 'xack-match' +complete 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@1: rv==2 +describe 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@1: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@2: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLElocal-AS', 'no-advertise', 'local-AS', 'xack-match' +execute strict 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@2: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLElocal-AS', 'no-advertise', 'local-AS', 'xack-match' +complete 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@2: rv==2 +describe 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@2: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@4: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLElocal-AS', 'no-advertise', 'local-AS', 'xack-match' +execute strict 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@4: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLElocal-AS', 'no-advertise', 'local-AS', 'xack-match' +complete 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@4: rv==2 +describe 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@4: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'no-export', 'no-advrtWise', 'mno-export' +execute strict 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'no-export', 'no-advrtWise', 'mno-export' +complete 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@1: rv==7 + 'exact-match' +describe 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@1: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'no-export', 'no-advrtWise', 'mno-export' +execute strict 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'no-export', 'no-advrtWise', 'mno-export' +complete 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@2: rv==7 + 'exact-match' +describe 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'no-export', 'no-advrtWise', 'mno-export' +execute strict 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'no-export', 'no-advrtWise', 'mno-export' +complete 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@4: rv==7 + 'exact-match' +describe 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'noeexport', 'VARIABLE', 'VARIABLE', 'no-aMdverise' +execute strict 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'noeexport', 'VARIABLE', 'VARIABLE', 'no-aMdverise' +complete 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@2: rv==7 + 'exact-match' +describe 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'noeexport', 'VARIABLE', 'VARIABLE', 'no-aMdverise' +execute strict 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'noeexport', 'VARIABLE', 'VARIABLE', 'no-aMdverise' +complete 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@4: rv==7 + 'exact-match' +describe 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'loal-AS', 'noK-advertise', 'VARIABLE' +execute strict 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'loal-AS', 'noK-advertise', 'VARIABLE' +complete 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@2: rv==2 +describe 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@2: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'loal-AS', 'noK-advertise', 'VARIABLE' +execute strict 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'loal-AS', 'noK-advertise', 'VARIABLE' +complete 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@4: rv==2 +describe 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@4: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABaE', 'no-export', 'no-adertise', 'lo0cal-AS' +execute strict 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABaE', 'no-export', 'no-adertise', 'lo0cal-AS' +complete 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@2: rv==7 + 'exact-match' +describe 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABaE', 'no-export', 'no-adertise', 'lo0cal-AS' +execute strict 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABaE', 'no-export', 'no-adertise', 'lo0cal-AS' +complete 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@4: rv==7 + 'exact-match' +describe 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'wARIBLE', 'VARIABLE', '8ARIABLE' +execute strict 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'wARIBLE', 'VARIABLE', '8ARIABLE' +complete 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@2: rv==2 +describe 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@2: rv==0 + 'AA:NN' 'community number' +execute relaxed 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'wARIBLE', 'VARIABLE', '8ARIABLE' +execute strict 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'wARIBLE', 'VARIABLE', '8ARIABLE' +complete 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@4: rv==2 +describe 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@4: rv==0 + 'AA:NN' 'community number' From e712d0e3667ffad8109ef8bce3ce01927ee95bb7 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 1 Apr 2014 19:34:55 +0200 Subject: [PATCH 182/482] tests: fix build & disable testcommands The perils of having tests, the test wasn't tested thoroughly enough... Fixup various automake problems, and then disable it since it depends on configure parameters in its current version. For 0.99.24 we can ship a static copy of vtysh_cmd.c and have it reenabled. Signed-off-by: David Lamparter --- tests/Makefile.am | 3 ++- tests/libzebra.tests/testcommands.exp | 10 +++++++++- tests/{testcommands.out => testcommands.refout} | 0 3 files changed, 11 insertions(+), 2 deletions(-) rename tests/{testcommands.out => testcommands.refout} (100%) diff --git a/tests/Makefile.am b/tests/Makefile.am index ceca606a..8707fe7d 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -11,7 +11,7 @@ EXTRA_DIST = \ lib/libzebra.exp \ global-conf.exp \ testcommands.in \ - testcommands.out + testcommands.refout INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib DEFS = @DEFS@ $(LOCAL_OPTS) -DSYSCONFDIR=\"$(sysconfdir)/\" @@ -42,6 +42,7 @@ test-commands-defun.c: ../vtysh/vtysh_cmd.c < ../vtysh/vtysh_cmd.c \ > test-commands-defun.c +BUILT_SOURCES = test-commands-defun.c noinst_HEADERS = prng.h testsig_SOURCES = test-sig.c diff --git a/tests/libzebra.tests/testcommands.exp b/tests/libzebra.tests/testcommands.exp index f760c6d7..d4bfc823 100644 --- a/tests/libzebra.tests/testcommands.exp +++ b/tests/libzebra.tests/testcommands.exp @@ -1,7 +1,15 @@ set timeout 30 set test_name "testcommands" -spawn sh -c "./testcommands -e 0 < testcommands.in | diff -au - testcommands.out" +if {![info exists env(QUAGGA_TEST_COMMANDS)]} { + # sadly, the test randomly fails when configure parameters differ from + # what was used to create testcommands.refout. this can be fixed by + # shipping a matching vtysh_cmd.c, which we'll add after 0.99.23 + unresolved "$test_name" + exit 0 +} + +spawn sh -c "./testcommands -e 0 < $env(srcdir)/testcommands.in | diff -au - $env(srcdir)/testcommands.refout" expect { eof { diff --git a/tests/testcommands.out b/tests/testcommands.refout similarity index 100% rename from tests/testcommands.out rename to tests/testcommands.refout From cd40b329a2e4da882bcad0431c048c876bbeafbd Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Mon, 30 Sep 2013 12:27:51 +0000 Subject: [PATCH 183/482] lib/command.c: rewrite command matching/parsing Add support for keyword commands. Includes new documentation for DEFUN() in lib/command.h, for preexisting features as well as new keyword specification. Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- babeld/babel_main.c | 3 - bgpd/bgp_main.c | 3 - isisd/isis_main.c | 2 - lib/command.c | 1977 ++++++++++++++++++++++++++--------------- lib/command.h | 193 +++- lib/memtypes.c | 2 +- lib/vty.c | 54 +- ospf6d/ospf6_main.c | 3 - ospfd/ospf_main.c | 2 - ripd/rip_main.c | 3 - ripngd/ripng_main.c | 3 - tests/main.c | 2 - tests/test-commands.c | 6 +- vtysh/vtysh.c | 22 +- vtysh/vtysh_main.c | 2 - zebra/main.c | 3 - zebra/test_main.c | 3 - 17 files changed, 1495 insertions(+), 788 deletions(-) diff --git a/babeld/babel_main.c b/babeld/babel_main.c index 7b1d8799..529c3f29 100644 --- a/babeld/babel_main.c +++ b/babeld/babel_main.c @@ -277,9 +277,6 @@ babel_init(int argc, char **argv) /* this replace kernel_setup && kernel_setup_socket */ babelz_zebra_init (); - /* Sort all installed commands. */ - sort_node (); - /* Get zebra configuration file. */ zlog_set_level (NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED); vty_read_config (babel_config_file, babel_config_default); diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index 1ff1ac95..1be65043 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -431,9 +431,6 @@ main (int argc, char **argv) /* BGP related initialization. */ bgp_init (); - /* Sort CLI commands. */ - sort_node (); - /* Parse config file. */ vty_read_config (config_file, config_default); diff --git a/isisd/isis_main.c b/isisd/isis_main.c index 96816ebb..283b7eaa 100644 --- a/isisd/isis_main.c +++ b/isisd/isis_main.c @@ -339,8 +339,6 @@ main (int argc, char **argv, char **envp) isis_zebra_init (); - sort_node (); - /* parse config file */ /* this is needed three times! because we have interfaces before the areas */ vty_read_config (config_file, config_default); diff --git a/lib/command.c b/lib/command.c index 3b3fadac..a2373644 100644 --- a/lib/command.c +++ b/lib/command.c @@ -1,6 +1,8 @@ /* Command interpreter routine for virtual terminal [aka TeletYpe] Copyright (C) 1997, 98, 99 Kunihiro Ishiguro + Copyright (C) 2013 by Open Source Routing. + Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC") This file is part of GNU Zebra. @@ -35,9 +37,32 @@ Boston, MA 02111-1307, USA. */ each daemon maintains each own cmdvec. */ vector cmdvec = NULL; -struct desc desc_cr; +struct cmd_token token_cr; char *command_cr = NULL; +enum filter_type +{ + FILTER_RELAXED, + FILTER_STRICT +}; + +enum matcher_rv +{ + MATCHER_OK, + MATCHER_COMPLETE, + MATCHER_INCOMPLETE, + MATCHER_NO_MATCH, + MATCHER_AMBIGUOUS, + MATCHER_EXCEED_ARGC_MAX +}; + +#define MATCHER_ERROR(matcher_rv) \ + ( (matcher_rv) == MATCHER_INCOMPLETE \ + || (matcher_rv) == MATCHER_NO_MATCH \ + || (matcher_rv) == MATCHER_AMBIGUOUS \ + || (matcher_rv) == MATCHER_EXCEED_ARGC_MAX \ + ) + /* Host information structure. */ struct host host; @@ -196,53 +221,6 @@ install_node (struct cmd_node *node, node->cmd_vector = vector_init (VECTOR_MIN_SIZE); } -/* Compare two command's string. Used in sort_node (). */ -static int -cmp_node (const void *p, const void *q) -{ - const struct cmd_element *a = *(struct cmd_element * const *)p; - const struct cmd_element *b = *(struct cmd_element * const *)q; - - return strcmp (a->string, b->string); -} - -static int -cmp_desc (const void *p, const void *q) -{ - const struct desc *a = *(struct desc * const *)p; - const struct desc *b = *(struct desc * const *)q; - - return strcmp (a->cmd, b->cmd); -} - -/* Sort each node's command element according to command string. */ -void -sort_node () -{ - unsigned int i, j; - struct cmd_node *cnode; - vector descvec; - struct cmd_element *cmd_element; - - for (i = 0; i < vector_active (cmdvec); i++) - if ((cnode = vector_slot (cmdvec, i)) != NULL) - { - vector cmd_vector = cnode->cmd_vector; - qsort (cmd_vector->index, vector_active (cmd_vector), - sizeof (void *), cmp_node); - - for (j = 0; j < vector_active (cmd_vector); j++) - if ((cmd_element = vector_slot (cmd_vector, j)) != NULL - && vector_active (cmd_element->strvec)) - { - descvec = vector_slot (cmd_element->strvec, - vector_active (cmd_element->strvec) - 1); - qsort (descvec->index, vector_active (descvec), - sizeof (void *), cmp_desc); - } - } -} - /* Breaking up string into each command piece. I assume given character is separated by a space character. Return value is a vector which includes char ** data element. */ @@ -312,15 +290,46 @@ cmd_free_strvec (vector v) vector_free (v); } -/* Fetch next description. Used in cmd_make_descvec(). */ +struct format_parser_state +{ + vector topvect; /* Top level vector */ + vector intvect; /* Intermediate level vector, used when there's + * a multiple in a keyword. */ + vector curvect; /* current vector where read tokens should be + appended. */ + + const char *string; /* pointer to command string, not modified */ + const char *cp; /* pointer in command string, moved along while + parsing */ + const char *dp; /* pointer in description string, moved along while + parsing */ + + int in_keyword; /* flag to remember if we are in a keyword group */ + int in_multiple; /* flag to remember if we are in a multiple group */ + int just_read_word; /* flag to remember if the last thing we red was a + * real word and not some abstract token */ +}; + +static void +format_parser_error(struct format_parser_state *state, const char *message) +{ + int offset = state->cp - state->string + 1; + + fprintf(stderr, "\nError parsing command: \"%s\"\n", state->string); + fprintf(stderr, " %*c\n", offset, '^'); + fprintf(stderr, "%s at offset %d.\n", message, offset); + fprintf(stderr, "This is a programming error. Check your DEFUNs etc.\n"); + exit(1); +} + static char * -cmd_desc_str (const char **string) +format_parser_desc_str(struct format_parser_state *state) { const char *cp, *start; char *token; int strlen; - - cp = *string; + + cp = state->dp; if (cp == NULL) return NULL; @@ -339,132 +348,226 @@ cmd_desc_str (const char **string) cp++; strlen = cp - start; - token = XMALLOC (MTYPE_STRVEC, strlen + 1); + token = XMALLOC (MTYPE_CMD_TOKENS, strlen + 1); memcpy (token, start, strlen); *(token + strlen) = '\0'; - *string = cp; + state->dp = cp; return token; } -/* New string vector. */ -static vector -cmd_make_descvec (const char *string, const char *descstr) +static void +format_parser_begin_keyword(struct format_parser_state *state) { - int multiple = 0; - const char *sp; - char *token; - int len; - const char *cp; - const char *dp; - vector allvec; - vector strvec = NULL; - struct desc *desc; + struct cmd_token *token; + vector keyword_vect; - cp = string; - dp = descstr; + if (state->in_keyword + || state->in_multiple) + format_parser_error(state, "Unexpected '{'"); - if (cp == NULL) - return NULL; + state->cp++; + state->in_keyword = 1; - allvec = vector_init (VECTOR_MIN_SIZE); + token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token)); + token->type = TOKEN_KEYWORD; + token->keyword = vector_init(VECTOR_MIN_SIZE); - while (1) - { - while (isspace ((int) *cp) && *cp != '\0') - cp++; + keyword_vect = vector_init(VECTOR_MIN_SIZE); + vector_set(token->keyword, keyword_vect); - if (*cp == '(') - { - multiple = 1; - cp++; - } - if (*cp == ')') - { - multiple = 0; - cp++; - } - if (*cp == '|') - { - if (! multiple) - { - fprintf (stderr, "Command parse error!: %s\n", string); - exit (1); - } - cp++; - } - - while (isspace ((int) *cp) && *cp != '\0') - cp++; + vector_set(state->curvect, token); + state->curvect = keyword_vect; +} - if (*cp == '(') - { - multiple = 1; - cp++; - } +static void +format_parser_begin_multiple(struct format_parser_state *state) +{ + struct cmd_token *token; - if (*cp == '\0') - return allvec; + if (state->in_keyword == 1) + format_parser_error(state, "Keyword starting with '('"); - sp = cp; + if (state->in_multiple) + format_parser_error(state, "Nested group"); - while (! (isspace ((int) *cp) || *cp == '\r' || *cp == '\n' || *cp == ')' || *cp == '|') && *cp != '\0') - cp++; + state->cp++; + state->in_multiple = 1; + state->just_read_word = 0; + + token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token)); + token->type = TOKEN_MULTIPLE; + token->multiple = vector_init(VECTOR_MIN_SIZE); - len = cp - sp; + vector_set(state->curvect, token); + if (state->curvect != state->topvect) + state->intvect = state->curvect; + state->curvect = token->multiple; +} - token = XMALLOC (MTYPE_STRVEC, len + 1); - memcpy (token, sp, len); - *(token + len) = '\0'; +static void +format_parser_end_keyword(struct format_parser_state *state) +{ + if (state->in_multiple + || !state->in_keyword) + format_parser_error(state, "Unexpected '}'"); - desc = XCALLOC (MTYPE_DESC, sizeof (struct desc)); - desc->cmd = token; - desc->str = cmd_desc_str (&dp); + if (state->in_keyword == 1) + format_parser_error(state, "Empty keyword group"); - if (multiple) - { - if (multiple == 1) - { - strvec = vector_init (VECTOR_MIN_SIZE); - vector_set (allvec, strvec); - } - multiple++; - } - else - { - strvec = vector_init (VECTOR_MIN_SIZE); - vector_set (allvec, strvec); - } - vector_set (strvec, desc); + state->cp++; + state->in_keyword = 0; + state->curvect = state->topvect; +} + +static void +format_parser_end_multiple(struct format_parser_state *state) +{ + char *dummy; + + if (!state->in_multiple) + format_parser_error(state, "Unepexted ')'"); + + if (vector_active(state->curvect) == 0) + format_parser_error(state, "Empty multiple section"); + + if (!state->just_read_word) + { + /* There are constructions like + * 'show ip ospf database ... (self-originate|)' + * in use. + * The old parser reads a description string for the + * word '' between |) which will never match. + * Simulate this behvaior by dropping the next desc + * string in such a case. */ + + dummy = format_parser_desc_str(state); + XFREE(MTYPE_CMD_TOKENS, dummy); } + + state->cp++; + state->in_multiple = 0; + + if (state->intvect) + state->curvect = state->intvect; + else + state->curvect = state->topvect; } -/* Count mandantory string vector size. This is to determine inputed - command has enough command length. */ -static int -cmd_cmdsize (vector strvec) +static void +format_parser_handle_pipe(struct format_parser_state *state) { - unsigned int i; - int size = 0; - vector descvec; - struct desc *desc; + struct cmd_token *keyword_token; + vector keyword_vect; - for (i = 0; i < vector_active (strvec); i++) - if ((descvec = vector_slot (strvec, i)) != NULL) + if (state->in_multiple) { - if ((vector_active (descvec)) == 1 - && (desc = vector_slot (descvec, 0)) != NULL) - { - if (desc->cmd == NULL || CMD_OPTION (desc->cmd)) - return size; - else - size++; - } - else - size++; + state->just_read_word = 0; + state->cp++; + } + else if (state->in_keyword) + { + state->in_keyword = 1; + state->cp++; + + keyword_token = vector_slot(state->topvect, + vector_active(state->topvect) - 1); + keyword_vect = vector_init(VECTOR_MIN_SIZE); + vector_set(keyword_token->keyword, keyword_vect); + state->curvect = keyword_vect; + } + else + { + format_parser_error(state, "Unexpected '|'"); + } +} + +static void +format_parser_read_word(struct format_parser_state *state) +{ + const char *start; + int len; + char *cmd; + struct cmd_token *token; + + start = state->cp; + + while (state->cp[0] != '\0' + && !strchr("\r\n(){}|", state->cp[0]) + && !isspace((int)state->cp[0])) + state->cp++; + + len = state->cp - start; + cmd = XMALLOC(MTYPE_CMD_TOKENS, len + 1); + memcpy(cmd, start, len); + cmd[len] = '\0'; + + token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token)); + token->type = TOKEN_TERMINAL; + token->cmd = cmd; + token->desc = format_parser_desc_str(state); + vector_set(state->curvect, token); + + if (state->in_keyword == 1) + state->in_keyword = 2; + + state->just_read_word = 1; +} + +/** + * Parse a given command format string and build a tree of tokens from + * it that is suitable to be used by the command subsystem. + * + * @param string Command format string. + * @param descstr Description string. + * @return A vector of struct cmd_token representing the given command, + * or NULL on error. + */ +static vector +cmd_parse_format(const char *string, const char *descstr) +{ + struct format_parser_state state; + + if (string == NULL) + return NULL; + + memset(&state, 0, sizeof(state)); + state.topvect = state.curvect = vector_init(VECTOR_MIN_SIZE); + state.cp = state.string = string; + state.dp = descstr; + + while (1) + { + while (isspace((int)state.cp[0]) && state.cp[0] != '\0') + state.cp++; + + switch (state.cp[0]) + { + case '\0': + if (state.in_keyword + || state.in_multiple) + format_parser_error(&state, "Unclosed group/keyword"); + return state.topvect; + case '{': + format_parser_begin_keyword(&state); + break; + case '(': + format_parser_begin_multiple(&state); + break; + case '}': + format_parser_end_keyword(&state); + break; + case ')': + format_parser_end_multiple(&state); + break; + case '|': + format_parser_handle_pipe(&state); + break; + default: + format_parser_read_word(&state); + } } - return size; } /* Return prompt character of specified node. */ @@ -497,11 +600,8 @@ install_element (enum node_type ntype, struct cmd_element *cmd) } vector_set (cnode->cmd_vector, cmd); - - if (cmd->strvec == NULL) - cmd->strvec = cmd_make_descvec (cmd->string, cmd->doc); - - cmd->cmdsize = cmd_cmdsize (cmd->strvec); + if (cmd->tokens == NULL) + cmd->tokens = cmd_parse_format(cmd->string, cmd->doc); } static const unsigned char itoa64[] = @@ -847,9 +947,6 @@ cmd_ipv4_prefix_match (const char *str) static enum match_type cmd_ipv6_match (const char *str) { - int state = STATE_START; - int colons = 0, nums = 0, double_colon = 0; - const char *sp = NULL; struct sockaddr_in6 sin6_dummy; int ret; @@ -1056,254 +1153,700 @@ cmd_range_match (const char *range, const char *str) return 1; } -/* Make completion match and return match type flag. */ static enum match_type -cmd_filter_by_completion (char *command, vector v, unsigned int index) +cmd_word_match(struct cmd_token *token, + enum filter_type filter, + const char *word) { - unsigned int i; const char *str; - struct cmd_element *cmd_element; enum match_type match_type; - vector descvec; - struct desc *desc; - match_type = no_match; + str = token->cmd; - /* If command and cmd_element string does not match set NULL to vector */ - for (i = 0; i < vector_active (v); i++) - if ((cmd_element = vector_slot (v, i)) != NULL) - { - if (index >= vector_active (cmd_element->strvec)) - vector_slot (v, i) = NULL; - else - { - unsigned int j; - int matched = 0; + if (filter == FILTER_RELAXED) + if (!word || !strlen(word)) + return partly_match; - descvec = vector_slot (cmd_element->strvec, index); - - for (j = 0; j < vector_active (descvec); j++) - if ((desc = vector_slot (descvec, j))) - { - str = desc->cmd; - - if (CMD_VARARG (str)) - { - if (match_type < vararg_match) - match_type = vararg_match; - matched++; - } - else if (CMD_RANGE (str)) - { - if (cmd_range_match (str, command)) - { - if (match_type < range_match) - match_type = range_match; + if (!word) + return no_match; - matched++; - } - } + if (CMD_VARARG(str)) + { + return vararg_match; + } + else if (CMD_RANGE(str)) + { + if (cmd_range_match(str, word)) + return range_match; + } #ifdef HAVE_IPV6 - else if (CMD_IPV6 (str)) - { - if (cmd_ipv6_match (command)) - { - if (match_type < ipv6_match) - match_type = ipv6_match; + else if (CMD_IPV6(str)) + { + match_type = cmd_ipv6_match(word); + if ((filter == FILTER_RELAXED && match_type != no_match) + || (filter == FILTER_STRICT && match_type == exact_match)) + return ipv6_match; + } + else if (CMD_IPV6_PREFIX(str)) + { + match_type = cmd_ipv6_prefix_match(word); + if ((filter == FILTER_RELAXED && match_type != no_match) + || (filter == FILTER_STRICT && match_type == exact_match)) + return ipv6_prefix_match; + } +#endif /* HAVE_IPV6 */ + else if (CMD_IPV4(str)) + { + match_type = cmd_ipv4_match(word); + if ((filter == FILTER_RELAXED && match_type != no_match) + || (filter == FILTER_STRICT && match_type == exact_match)) + return ipv4_match; + } + else if (CMD_IPV4_PREFIX(str)) + { + match_type = cmd_ipv4_prefix_match(word); + if ((filter == FILTER_RELAXED && match_type != no_match) + || (filter == FILTER_STRICT && match_type == exact_match)) + return ipv4_prefix_match; + } + else if (CMD_OPTION(str) || CMD_VARIABLE(str)) + { + return extend_match; + } + else + { + if (filter == FILTER_RELAXED && !strncmp(str, word, strlen(word))) + { + if (!strcmp(str, word)) + return exact_match; + return partly_match; + } + if (filter == FILTER_STRICT && !strcmp(str, word)) + return exact_match; + } - matched++; - } - } - else if (CMD_IPV6_PREFIX (str)) - { - if (cmd_ipv6_prefix_match (command)) - { - if (match_type < ipv6_prefix_match) - match_type = ipv6_prefix_match; + return no_match; +} - matched++; - } - } -#endif /* HAVE_IPV6 */ - else if (CMD_IPV4 (str)) - { - if (cmd_ipv4_match (command)) - { - if (match_type < ipv4_match) - match_type = ipv4_match; +struct cmd_matcher +{ + struct cmd_element *cmd; /* The command element the matcher is using */ + enum filter_type filter; /* Whether to use strict or relaxed matching */ + vector vline; /* The tokenized commandline which is to be matched */ + unsigned int index; /* The index up to which matching should be done */ - matched++; - } - } - else if (CMD_IPV4_PREFIX (str)) - { - if (cmd_ipv4_prefix_match (command)) - { - if (match_type < ipv4_prefix_match) - match_type = ipv4_prefix_match; - matched++; - } - } - else - /* Check is this point's argument optional ? */ - if (CMD_OPTION (str) || CMD_VARIABLE (str)) - { - if (match_type < extend_match) - match_type = extend_match; - matched++; - } - else if (strncmp (command, str, strlen (command)) == 0) - { - if (strcmp (command, str) == 0) - match_type = exact_match; - else - { - if (match_type < partly_match) - match_type = partly_match; - } - matched++; - } - } - if (!matched) - vector_slot (v, i) = NULL; - } + /* If set, construct a list of matches at the position given by index */ + enum match_type *match_type; + vector *match; + + unsigned int word_index; /* iterating over vline */ +}; + +static int +push_argument(int *argc, const char **argv, const char *arg) +{ + if (!arg || !strlen(arg)) + arg = NULL; + + if (!argc || !argv) + return 0; + + if (*argc >= CMD_ARGC_MAX) + return -1; + + argv[(*argc)++] = arg; + return 0; +} + +static void +cmd_matcher_record_match(struct cmd_matcher *matcher, + enum match_type match_type, + struct cmd_token *token) +{ + if (matcher->word_index != matcher->index) + return; + + if (matcher->match) + { + if (!*matcher->match) + *matcher->match = vector_init(VECTOR_MIN_SIZE); + vector_set(*matcher->match, token); + } + + if (matcher->match_type) + { + if (match_type > *matcher->match_type) + *matcher->match_type = match_type; + } +} + +static int +cmd_matcher_words_left(struct cmd_matcher *matcher) +{ + return matcher->word_index < vector_active(matcher->vline); +} + +static const char* +cmd_matcher_get_word(struct cmd_matcher *matcher) +{ + assert(cmd_matcher_words_left(matcher)); + + return vector_slot(matcher->vline, matcher->word_index); +} + +static enum matcher_rv +cmd_matcher_match_terminal(struct cmd_matcher *matcher, + struct cmd_token *token, + int *argc, const char **argv) +{ + const char *word; + enum match_type word_match; + + assert(token->type == TOKEN_TERMINAL); + + if (!cmd_matcher_words_left(matcher)) + { + if (CMD_OPTION(token->cmd)) + return MATCHER_OK; /* missing optional args are NOT pushed as NULL */ + else + return MATCHER_INCOMPLETE; + } + + word = cmd_matcher_get_word(matcher); + word_match = cmd_word_match(token, matcher->filter, word); + if (word_match == no_match) + return MATCHER_NO_MATCH; + + /* We have to record the input word as argument if it matched + * against a variable. */ + if (CMD_VARARG(token->cmd) + || CMD_VARIABLE(token->cmd) + || CMD_OPTION(token->cmd)) + { + if (push_argument(argc, argv, word)) + return MATCHER_EXCEED_ARGC_MAX; + } + + cmd_matcher_record_match(matcher, word_match, token); + + matcher->word_index++; + + /* A vararg token should consume all left over words as arguments */ + if (CMD_VARARG(token->cmd)) + while (cmd_matcher_words_left(matcher)) + { + word = cmd_matcher_get_word(matcher); + if (word && strlen(word)) + push_argument(argc, argv, word); + matcher->word_index++; } - return match_type; + + return MATCHER_OK; } -/* Filter vector by command character with index. */ -static enum match_type -cmd_filter_by_string (char *command, vector v, unsigned int index) +static enum matcher_rv +cmd_matcher_match_multiple(struct cmd_matcher *matcher, + struct cmd_token *token, + int *argc, const char **argv) +{ + enum match_type multiple_match; + unsigned int multiple_index; + const char *word; + const char *arg; + struct cmd_token *word_token; + enum match_type word_match; + + assert(token->type == TOKEN_MULTIPLE); + + multiple_match = no_match; + + if (!cmd_matcher_words_left(matcher)) + return MATCHER_INCOMPLETE; + + word = cmd_matcher_get_word(matcher); + for (multiple_index = 0; + multiple_index < vector_active(token->multiple); + multiple_index++) + { + word_token = vector_slot(token->multiple, multiple_index); + + word_match = cmd_word_match(word_token, matcher->filter, word); + if (word_match == no_match) + continue; + + cmd_matcher_record_match(matcher, word_match, word_token); + + if (word_match > multiple_match) + { + multiple_match = word_match; + arg = word; + } + /* To mimic the behavior of the old command implementation, we + * tolerate any ambiguities here :/ */ + } + + matcher->word_index++; + + if (multiple_match == no_match) + return MATCHER_NO_MATCH; + + if (push_argument(argc, argv, arg)) + return MATCHER_EXCEED_ARGC_MAX; + + return MATCHER_OK; +} + +static enum matcher_rv +cmd_matcher_read_keywords(struct cmd_matcher *matcher, + struct cmd_token *token, + vector args_vector) { unsigned int i; - const char *str; - struct cmd_element *cmd_element; - enum match_type match_type; - vector descvec; - struct desc *desc; + unsigned long keyword_mask; + unsigned int keyword_found; + enum match_type keyword_match; + enum match_type word_match; + vector keyword_vector; + struct cmd_token *word_token; + const char *word; + int keyword_argc; + const char **keyword_argv; + enum matcher_rv rv; + + keyword_mask = 0; + while (1) + { + if (!cmd_matcher_words_left(matcher)) + return MATCHER_OK; - match_type = no_match; + word = cmd_matcher_get_word(matcher); - /* If command and cmd_element string does not match set NULL to vector */ - for (i = 0; i < vector_active (v); i++) - if ((cmd_element = vector_slot (v, i)) != NULL) - { - /* If given index is bigger than max string vector of command, - set NULL */ - if (index >= vector_active (cmd_element->strvec)) - vector_slot (v, i) = NULL; - else - { - unsigned int j; - int matched = 0; + keyword_found = -1; + keyword_match = no_match; + for (i = 0; i < vector_active(token->keyword); i++) + { + if (keyword_mask & (1 << i)) + continue; + + keyword_vector = vector_slot(token->keyword, i); + word_token = vector_slot(keyword_vector, 0); + + word_match = cmd_word_match(word_token, matcher->filter, word); + if (word_match == no_match) + continue; + + cmd_matcher_record_match(matcher, word_match, word_token); + + if (word_match > keyword_match) + { + keyword_match = word_match; + keyword_found = i; + } + else if (word_match == keyword_match) + { + if (matcher->word_index != matcher->index || args_vector) + return MATCHER_AMBIGUOUS; + } + } - descvec = vector_slot (cmd_element->strvec, index); + if (keyword_found == (unsigned int)-1) + return MATCHER_NO_MATCH; - for (j = 0; j < vector_active (descvec); j++) - if ((desc = vector_slot (descvec, j))) - { - str = desc->cmd; + matcher->word_index++; - if (CMD_VARARG (str)) - { - if (match_type < vararg_match) - match_type = vararg_match; - matched++; - } - else if (CMD_RANGE (str)) - { - if (cmd_range_match (str, command)) - { - if (match_type < range_match) - match_type = range_match; - matched++; - } - } -#ifdef HAVE_IPV6 - else if (CMD_IPV6 (str)) - { - if (cmd_ipv6_match (command) == exact_match) - { - if (match_type < ipv6_match) - match_type = ipv6_match; - matched++; - } - } - else if (CMD_IPV6_PREFIX (str)) - { - if (cmd_ipv6_prefix_match (command) == exact_match) - { - if (match_type < ipv6_prefix_match) - match_type = ipv6_prefix_match; - matched++; - } - } -#endif /* HAVE_IPV6 */ - else if (CMD_IPV4 (str)) - { - if (cmd_ipv4_match (command) == exact_match) - { - if (match_type < ipv4_match) - match_type = ipv4_match; - matched++; - } - } - else if (CMD_IPV4_PREFIX (str)) - { - if (cmd_ipv4_prefix_match (command) == exact_match) - { - if (match_type < ipv4_prefix_match) - match_type = ipv4_prefix_match; - matched++; - } - } - else if (CMD_OPTION (str) || CMD_VARIABLE (str)) - { - if (match_type < extend_match) - match_type = extend_match; - matched++; - } - else - { - if (strcmp (command, str) == 0) - { - match_type = exact_match; - matched++; - } - } - } - if (!matched) - vector_slot (v, i) = NULL; - } + if (matcher->word_index > matcher->index) + return MATCHER_OK; + + keyword_mask |= (1 << keyword_found); + + if (args_vector) + { + keyword_argc = 0; + keyword_argv = XMALLOC(MTYPE_TMP, (CMD_ARGC_MAX + 1) * sizeof(char*)); + /* We use -1 as a marker for unused fields as NULL might be a valid value */ + for (i = 0; i < CMD_ARGC_MAX + 1; i++) + keyword_argv[i] = (void*)-1; + vector_set_index(args_vector, keyword_found, keyword_argv); + } + else + { + keyword_argv = NULL; + } + + keyword_vector = vector_slot(token->keyword, keyword_found); + /* the keyword itself is at 0. We are only interested in the arguments, + * so start counting at 1. */ + for (i = 1; i < vector_active(keyword_vector); i++) + { + word_token = vector_slot(keyword_vector, i); + + switch (word_token->type) + { + case TOKEN_TERMINAL: + rv = cmd_matcher_match_terminal(matcher, word_token, + &keyword_argc, keyword_argv); + break; + case TOKEN_MULTIPLE: + rv = cmd_matcher_match_multiple(matcher, word_token, + &keyword_argc, keyword_argv); + break; + case TOKEN_KEYWORD: + assert(!"Keywords should never be nested."); + break; + } + + if (MATCHER_ERROR(rv)) + return rv; + + if (matcher->word_index > matcher->index) + return MATCHER_OK; + } + } + /* not reached */ +} + +static enum matcher_rv +cmd_matcher_build_keyword_args(struct cmd_matcher *matcher, + struct cmd_token *token, + int *argc, const char **argv, + vector keyword_args_vector) +{ + unsigned int i, j; + const char **keyword_args; + vector keyword_vector; + struct cmd_token *word_token; + const char *arg; + enum matcher_rv rv; + + rv = MATCHER_OK; + + if (keyword_args_vector == NULL) + return rv; + + for (i = 0; i < vector_active(token->keyword); i++) + { + keyword_vector = vector_slot(token->keyword, i); + keyword_args = vector_lookup(keyword_args_vector, i); + + if (vector_active(keyword_vector) == 1) + { + /* this is a keyword without arguments */ + if (keyword_args) + { + word_token = vector_slot(keyword_vector, 0); + arg = word_token->cmd; + } + else + { + arg = NULL; + } + + if (push_argument(argc, argv, arg)) + rv = MATCHER_EXCEED_ARGC_MAX; + } + else + { + /* this is a keyword with arguments */ + if (keyword_args) + { + /* the keyword was present, so just fill in the arguments */ + for (j = 0; keyword_args[j] != (void*)-1; j++) + if (push_argument(argc, argv, keyword_args[j])) + rv = MATCHER_EXCEED_ARGC_MAX; + XFREE(MTYPE_TMP, keyword_args); + } + else + { + /* the keyword was not present, insert NULL for the arguments + * the keyword would have taken. */ + for (j = 1; j < vector_active(keyword_vector); j++) + { + word_token = vector_slot(keyword_vector, j); + if ((word_token->type == TOKEN_TERMINAL + && (CMD_VARARG(word_token->cmd) + || CMD_VARIABLE(word_token->cmd) + || CMD_OPTION(word_token->cmd))) + || word_token->type == TOKEN_MULTIPLE) + { + if (push_argument(argc, argv, NULL)) + rv = MATCHER_EXCEED_ARGC_MAX; + } + } + } + } + } + vector_free(keyword_args_vector); + return rv; +} + +static enum matcher_rv +cmd_matcher_match_keyword(struct cmd_matcher *matcher, + struct cmd_token *token, + int *argc, const char **argv) +{ + vector keyword_args_vector; + enum matcher_rv reader_rv; + enum matcher_rv builder_rv; + + assert(token->type == TOKEN_KEYWORD); + + if (argc && argv) + keyword_args_vector = vector_init(VECTOR_MIN_SIZE); + else + keyword_args_vector = NULL; + + reader_rv = cmd_matcher_read_keywords(matcher, token, keyword_args_vector); + builder_rv = cmd_matcher_build_keyword_args(matcher, token, argc, + argv, keyword_args_vector); + /* keyword_args_vector is consumed by cmd_matcher_build_keyword_args */ + + if (!MATCHER_ERROR(reader_rv) && MATCHER_ERROR(builder_rv)) + return builder_rv; + + return reader_rv; +} + +static void +cmd_matcher_init(struct cmd_matcher *matcher, + struct cmd_element *cmd, + enum filter_type filter, + vector vline, + unsigned int index, + enum match_type *match_type, + vector *match) +{ + memset(matcher, 0, sizeof(*matcher)); + + matcher->cmd = cmd; + matcher->filter = filter; + matcher->vline = vline; + matcher->index = index; + + matcher->match_type = match_type; + if (matcher->match_type) + *matcher->match_type = no_match; + matcher->match = match; + + matcher->word_index = 0; +} + +static enum matcher_rv +cmd_element_match(struct cmd_element *cmd_element, + enum filter_type filter, + vector vline, + unsigned int index, + enum match_type *match_type, + vector *match, + int *argc, + const char **argv) +{ + struct cmd_matcher matcher; + unsigned int token_index; + enum matcher_rv rv; + + cmd_matcher_init(&matcher, cmd_element, filter, + vline, index, match_type, match); + + if (argc != NULL) + *argc = 0; + + for (token_index = 0; + token_index < vector_active(cmd_element->tokens); + token_index++) + { + struct cmd_token *token = vector_slot(cmd_element->tokens, token_index); + + switch (token->type) + { + case TOKEN_TERMINAL: + rv = cmd_matcher_match_terminal(&matcher, token, argc, argv); + break; + case TOKEN_MULTIPLE: + rv = cmd_matcher_match_multiple(&matcher, token, argc, argv); + break; + case TOKEN_KEYWORD: + rv = cmd_matcher_match_keyword(&matcher, token, argc, argv); + } + + if (MATCHER_ERROR(rv)) + return rv; + + if (matcher.word_index > index) + return MATCHER_OK; + } + + /* return MATCHER_COMPLETE if all words were consumed */ + if (matcher.word_index >= vector_active(vline)) + return MATCHER_COMPLETE; + + /* return MATCHER_COMPLETE also if only an empty word is left. */ + if (matcher.word_index == vector_active(vline) - 1 + && (!vector_slot(vline, matcher.word_index) + || !strlen((char*)vector_slot(vline, matcher.word_index)))) + return MATCHER_COMPLETE; + + return MATCHER_NO_MATCH; /* command is too long to match */ +} + +/** + * Filter a given vector of commands against a given commandline and + * calculate possible completions. + * + * @param commands A vector of struct cmd_element*. Commands that don't + * match against the given command line will be overwritten + * with NULL in that vector. + * @param filter Either FILTER_RELAXED or FILTER_STRICT. This basically + * determines how incomplete commands are handled, compare with + * cmd_word_match for details. + * @param vline A vector of char* containing the tokenized commandline. + * @param index Only match up to the given token of the commandline. + * @param match_type Record the type of the best match here. + * @param matches Record the matches here. For each cmd_element in the commands + * vector, a match vector will be created in the matches vector. + * That vector will contain all struct command_token* of the + * cmd_element which matched against the given vline at the given + * index. + * @return A code specifying if an error occured. If all went right, it's + * CMD_SUCCESS. + */ +static int +cmd_vector_filter(vector commands, + enum filter_type filter, + vector vline, + unsigned int index, + enum match_type *match_type, + vector *matches) +{ + unsigned int i; + struct cmd_element *cmd_element; + enum match_type best_match; + enum match_type element_match; + enum matcher_rv matcher_rv; + + best_match = no_match; + *matches = vector_init(VECTOR_MIN_SIZE); + + for (i = 0; i < vector_active (commands); i++) + if ((cmd_element = vector_slot (commands, i)) != NULL) + { + vector_set_index(*matches, i, NULL); + matcher_rv = cmd_element_match(cmd_element, filter, + vline, index, + &element_match, + (vector*)&vector_slot(*matches, i), + NULL, NULL); + if (MATCHER_ERROR(matcher_rv)) + { + vector_slot(commands, i) = NULL; + if (matcher_rv == MATCHER_AMBIGUOUS) + return CMD_ERR_AMBIGUOUS; + if (matcher_rv == MATCHER_EXCEED_ARGC_MAX) + return CMD_ERR_EXEED_ARGC_MAX; + } + else if (element_match > best_match) + { + best_match = element_match; + } } - return match_type; + *match_type = best_match; + return CMD_SUCCESS; +} + +/** + * Check whether a given commandline is complete if used for a specific + * cmd_element. + * + * @param cmd_element A cmd_element against which the commandline should be + * checked. + * @param vline The tokenized commandline. + * @return 1 if the given commandline is complete, 0 otherwise. + */ +static int +cmd_is_complete(struct cmd_element *cmd_element, + vector vline) +{ + enum matcher_rv rv; + + rv = cmd_element_match(cmd_element, + FILTER_RELAXED, + vline, -1, + NULL, NULL, + NULL, NULL); + return (rv == MATCHER_COMPLETE); +} + +/** + * Parse a given commandline and construct a list of arguments for the + * given command_element. + * + * @param cmd_element The cmd_element for which we want to construct arguments. + * @param vline The tokenized commandline. + * @param argc Where to store the argument count. + * @param argv Where to store the argument list. Should be at least + * CMD_ARGC_MAX elements long. + * @return CMD_SUCCESS if everything went alright, an error otherwise. + */ +static int +cmd_parse(struct cmd_element *cmd_element, + vector vline, + int *argc, const char **argv) +{ + enum matcher_rv rv = cmd_element_match(cmd_element, + FILTER_RELAXED, + vline, -1, + NULL, NULL, + argc, argv); + switch (rv) + { + case MATCHER_COMPLETE: + return CMD_SUCCESS; + + case MATCHER_NO_MATCH: + return CMD_ERR_NO_MATCH; + + case MATCHER_AMBIGUOUS: + return CMD_ERR_AMBIGUOUS; + + case MATCHER_EXCEED_ARGC_MAX: + return CMD_ERR_EXEED_ARGC_MAX; + + default: + return CMD_ERR_INCOMPLETE; + } } /* Check ambiguous match */ static int -is_cmd_ambiguous (char *command, vector v, int index, enum match_type type) +is_cmd_ambiguous (vector cmd_vector, + const char *command, + vector matches, + enum match_type type) { unsigned int i; unsigned int j; const char *str = NULL; - struct cmd_element *cmd_element; const char *matched = NULL; - vector descvec; - struct desc *desc; + vector match_vector; + struct cmd_token *cmd_token; - for (i = 0; i < vector_active (v); i++) - if ((cmd_element = vector_slot (v, i)) != NULL) + if (command == NULL) + command = ""; + + for (i = 0; i < vector_active (matches); i++) + if ((match_vector = vector_slot (matches, i)) != NULL) { int match = 0; - descvec = vector_slot (cmd_element->strvec, index); - - for (j = 0; j < vector_active (descvec); j++) - if ((desc = vector_slot (descvec, j))) + for (j = 0; j < vector_active (match_vector); j++) + if ((cmd_token = vector_slot (match_vector, j)) != NULL) { enum match_type ret; - - str = desc->cmd; + + assert(cmd_token->type == TOKEN_TERMINAL); + if (cmd_token->type != TOKEN_TERMINAL) + continue; + + str = cmd_token->cmd; switch (type) { @@ -1371,7 +1914,7 @@ is_cmd_ambiguous (char *command, vector v, int index, enum match_type type) } } if (!match) - vector_slot (v, i) = NULL; + vector_slot (cmd_vector, i) = NULL; } return 0; } @@ -1461,8 +2004,12 @@ cmd_entry_function_desc (const char *src, const char *dst) return NULL; } -/* Check same string element existence. If it isn't there return - 1. */ +/** + * Check whether a string is already present in a vector of strings. + * @param v A vector of char*. + * @param str A char*. + * @return 0 if str is already present in the vector, 1 otherwise. + */ static int cmd_unique_string (vector v, const char *str) { @@ -1476,19 +2023,25 @@ cmd_unique_string (vector v, const char *str) return 1; } -/* Compare string to description vector. If there is same string - return 1 else return 0. */ +/** + * Check whether a struct cmd_token matching a given string is already + * present in a vector of struct cmd_token. + * @param v A vector of struct cmd_token*. + * @param str A char* which should be searched for. + * @return 0 if there is a struct cmd_token* with its cmd matching str, + * 1 otherwise. + */ static int desc_unique_string (vector v, const char *str) { unsigned int i; - struct desc *desc; + struct cmd_token *token; for (i = 0; i < vector_active (v); i++) - if ((desc = vector_slot (v, i)) != NULL) - if (strcmp (desc->cmd, str) == 0) - return 1; - return 0; + if ((token = vector_slot (v, i)) != NULL) + if (strcmp (token->cmd, str) == 0) + return 0; + return 1; } static int @@ -1504,6 +2057,35 @@ cmd_try_do_shortcut (enum node_type node, char* first_word) { return 0; } +static void +cmd_matches_free(vector *matches) +{ + unsigned int i; + vector cmd_matches; + + for (i = 0; i < vector_active(*matches); i++) + if ((cmd_matches = vector_slot(*matches, i)) != NULL) + vector_free(cmd_matches); + vector_free(*matches); + *matches = NULL; +} + +static int +cmd_describe_cmp(const void *a, const void *b) +{ + const struct cmd_token *first = *(struct cmd_token * const *)a; + const struct cmd_token *second = *(struct cmd_token * const *)b; + + return strcmp(first->cmd, second->cmd); +} + +static void +cmd_describe_sort(vector matchvec) +{ + qsort(matchvec->index, vector_active(matchvec), + sizeof(void*), cmd_describe_cmp); +} + /* '?' describe command support. */ static vector cmd_describe_command_real (vector vline, struct vty *vty, int *status) @@ -1517,6 +2099,8 @@ cmd_describe_command_real (vector vline, struct vty *vty, int *status) int ret; enum match_type match; char *command; + vector matches = NULL; + vector match_vector; /* Set index. */ if (vector_active (vline) == 0) @@ -1524,111 +2108,121 @@ cmd_describe_command_real (vector vline, struct vty *vty, int *status) *status = CMD_ERR_NO_MATCH; return NULL; } - else - index = vector_active (vline) - 1; - + + index = vector_active (vline) - 1; + /* Make copy vector of current node's command vector. */ cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); /* Prepare match vector */ matchvec = vector_init (INIT_MATCHVEC_SIZE); - /* Filter commands. */ - /* Only words precedes current word will be checked in this loop. */ - for (i = 0; i < index; i++) - if ((command = vector_slot (vline, i))) - { - match = cmd_filter_by_completion (command, cmd_vector, i); - - if (match == vararg_match) - { - struct cmd_element *cmd_element; - vector descvec; - unsigned int j, k; + /* Filter commands and build a list how they could possibly continue. */ + for (i = 0; i <= index; i++) + { + command = vector_slot (vline, i); - for (j = 0; j < vector_active (cmd_vector); j++) - if ((cmd_element = vector_slot (cmd_vector, j)) != NULL - && (vector_active (cmd_element->strvec))) - { - descvec = vector_slot (cmd_element->strvec, - vector_active (cmd_element->strvec) - 1); - for (k = 0; k < vector_active (descvec); k++) - { - struct desc *desc = vector_slot (descvec, k); - vector_set (matchvec, desc); - } - } - - vector_set (matchvec, &desc_cr); - vector_free (cmd_vector); + if (matches) + cmd_matches_free(&matches); - return matchvec; - } + ret = cmd_vector_filter(cmd_vector, + FILTER_RELAXED, + vline, i, + &match, + &matches); - if ((ret = is_cmd_ambiguous (command, cmd_vector, i, match)) == 1) - { - vector_free (cmd_vector); - vector_free (matchvec); - *status = CMD_ERR_AMBIGUOUS; - return NULL; - } - else if (ret == 2) - { - vector_free (cmd_vector); - vector_free (matchvec); - *status = CMD_ERR_NO_MATCH; - return NULL; - } - } + if (ret != CMD_SUCCESS) + { + vector_free (cmd_vector); + vector_free (matchvec); + cmd_matches_free(&matches); + *status = ret; + return NULL; + } - /* Prepare match vector */ - /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */ + /* The last match may well be ambigious, so break here */ + if (i == index) + break; + + if (match == vararg_match) + { + /* We found a vararg match - so we can throw out the current matches here + * and don't need to continue checking the command input */ + unsigned int j, k; + + for (j = 0; j < vector_active (matches); j++) + if ((match_vector = vector_slot (matches, j)) != NULL) + for (k = 0; k < vector_active (match_vector); k++) + { + struct cmd_token *token = vector_slot (match_vector, k); + vector_set (matchvec, token); + } + + *status = CMD_SUCCESS; + vector_set(matchvec, &token_cr); + vector_free (cmd_vector); + cmd_matches_free(&matches); + cmd_describe_sort(matchvec); + return matchvec; + } - /* Make sure that cmd_vector is filtered based on current word */ - command = vector_slot (vline, index); - if (command) - match = cmd_filter_by_completion (command, cmd_vector, index); + ret = is_cmd_ambiguous(cmd_vector, command, matches, match); + if (ret == 1) + { + vector_free (cmd_vector); + vector_free (matchvec); + cmd_matches_free(&matches); + *status = CMD_ERR_AMBIGUOUS; + return NULL; + } + else if (ret == 2) + { + vector_free (cmd_vector); + vector_free (matchvec); + cmd_matches_free(&matches); + *status = CMD_ERR_NO_MATCH; + return NULL; + } + } /* Make description vector. */ - for (i = 0; i < vector_active (cmd_vector); i++) + for (i = 0; i < vector_active (matches); i++) if ((cmd_element = vector_slot (cmd_vector, i)) != NULL) { - vector strvec = cmd_element->strvec; + unsigned int j; + const char *last_word; + vector vline_trimmed; - /* if command is NULL, index may be equal to vector_active */ - if (command && index >= vector_active (strvec)) - vector_slot (cmd_vector, i) = NULL; - else - { - /* Check if command is completed. */ - if (command == NULL && index == vector_active (strvec)) - { - if (!desc_unique_string (matchvec, command_cr)) - vector_set (matchvec, &desc_cr); - } - else - { - unsigned int j; - vector descvec = vector_slot (strvec, index); - struct desc *desc; - - for (j = 0; j < vector_active (descvec); j++) - if ((desc = vector_slot (descvec, j))) - { - const char *string; - - string = cmd_entry_function_desc (command, desc->cmd); - if (string) - { - /* Uniqueness check */ - if (!desc_unique_string (matchvec, string)) - vector_set (matchvec, desc); - } - } - } - } + last_word = vector_slot(vline, vector_active(vline) - 1); + if (last_word == NULL || !strlen(last_word)) + { + vline_trimmed = vector_copy(vline); + vector_unset(vline_trimmed, vector_active(vline_trimmed) - 1); + + if (cmd_is_complete(cmd_element, vline_trimmed) + && desc_unique_string(matchvec, command_cr)) + { + if (match != vararg_match) + vector_set(matchvec, &token_cr); + } + + vector_free(vline_trimmed); + } + + match_vector = vector_slot (matches, i); + if (match_vector) + for (j = 0; j < vector_active(match_vector); j++) + { + struct cmd_token *token = vector_slot(match_vector, j); + const char *string; + + string = cmd_entry_function_desc(command, token->cmd); + if (string && desc_unique_string(matchvec, string)) + vector_set(matchvec, token); + } } vector_free (cmd_vector); + cmd_matches_free(&matches); if (vector_slot (matchvec, 0) == NULL) { @@ -1638,6 +2232,7 @@ cmd_describe_command_real (vector vline, struct vty *vty, int *status) } *status = CMD_SUCCESS; + cmd_describe_sort(matchvec); return matchvec; } @@ -1708,6 +2303,31 @@ cmd_lcd (char **matched) return lcd; } +static int +cmd_complete_cmp(const void *a, const void *b) +{ + const char *first = *(char * const *)a; + const char *second = *(char * const *)b; + + if (!first) + { + if (!second) + return 0; + return 1; + } + if (!second) + return -1; + + return strcmp(first, second); +} + +static void +cmd_complete_sort(vector matchvec) +{ + qsort(matchvec->index, vector_active(matchvec), + sizeof(void*), cmd_complete_cmp); +} + /* Command line completion support. */ static char ** cmd_complete_command_real (vector vline, struct vty *vty, int *status) @@ -1716,13 +2336,13 @@ cmd_complete_command_real (vector vline, struct vty *vty, int *status) vector cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); #define INIT_MATCHVEC_SIZE 10 vector matchvec; - struct cmd_element *cmd_element; unsigned int index; char **match_str; - struct desc *desc; - vector descvec; + struct cmd_token *token; char *command; int lcd; + vector matches = NULL; + vector match_vector; if (vector_active (vline) == 0) { @@ -1733,66 +2353,80 @@ cmd_complete_command_real (vector vline, struct vty *vty, int *status) else index = vector_active (vline) - 1; - /* First, filter by preceeding command string */ - for (i = 0; i < index; i++) - if ((command = vector_slot (vline, i))) - { - enum match_type match; - int ret; + /* First, filter by command string */ + for (i = 0; i <= index; i++) + { + command = vector_slot (vline, i); + enum match_type match; + int ret; - /* First try completion match, if there is exactly match return 1 */ - match = cmd_filter_by_completion (command, cmd_vector, i); + if (matches) + cmd_matches_free(&matches); - /* If there is exact match then filter ambiguous match else check - ambiguousness. */ - if ((ret = is_cmd_ambiguous (command, cmd_vector, i, match)) == 1) - { - vector_free (cmd_vector); - *status = CMD_ERR_AMBIGUOUS; - return NULL; - } - /* + /* First try completion match, if there is exactly match return 1 */ + ret = cmd_vector_filter(cmd_vector, + FILTER_RELAXED, + vline, i, + &match, + &matches); + + if (ret != CMD_SUCCESS) + { + vector_free(cmd_vector); + cmd_matches_free(&matches); + *status = ret; + return NULL; + } + + /* Break here - the completion mustn't be checked to be non-ambiguous */ + if (i == index) + break; + + /* If there is exact match then filter ambiguous match else check + ambiguousness. */ + ret = is_cmd_ambiguous (cmd_vector, command, matches, match); + if (ret == 1) + { + vector_free (cmd_vector); + cmd_matches_free(&matches); + *status = CMD_ERR_AMBIGUOUS; + return NULL; + } + /* else if (ret == 2) { vector_free (cmd_vector); + cmd_matches_free(&matches); *status = CMD_ERR_NO_MATCH; return NULL; } */ - } + } /* Prepare match vector. */ matchvec = vector_init (INIT_MATCHVEC_SIZE); - /* Now we got into completion */ - for (i = 0; i < vector_active (cmd_vector); i++) - if ((cmd_element = vector_slot (cmd_vector, i))) + /* Build the possible list of continuations into a list of completions */ + for (i = 0; i < vector_active (matches); i++) + if ((match_vector = vector_slot (matches, i))) { const char *string; - vector strvec = cmd_element->strvec; - - /* Check field length */ - if (index >= vector_active (strvec)) - vector_slot (cmd_vector, i) = NULL; - else - { - unsigned int j; + unsigned int j; - descvec = vector_slot (strvec, index); - for (j = 0; j < vector_active (descvec); j++) - if ((desc = vector_slot (descvec, j))) + for (j = 0; j < vector_active (match_vector); j++) + if ((token = vector_slot (match_vector, j))) { if ((string = cmd_entry_function (vector_slot (vline, index), - desc->cmd))) + token->cmd))) if (cmd_unique_string (matchvec, string)) vector_set (matchvec, XSTRDUP (MTYPE_TMP, string)); } - } } /* We don't need cmd_vector any more. */ vector_free (cmd_vector); + cmd_matches_free(&matches); /* No matched command */ if (vector_slot (matchvec, 0) == NULL) @@ -1832,7 +2466,7 @@ cmd_complete_command_real (vector vline, struct vty *vty, int *status) { char *lcdstr; - lcdstr = XMALLOC (MTYPE_STRVEC, lcd + 1); + lcdstr = XMALLOC (MTYPE_TMP, lcd + 1); memcpy (lcdstr, matchvec->index[0], lcd); lcdstr[lcd] = '\0'; @@ -1842,7 +2476,7 @@ cmd_complete_command_real (vector vline, struct vty *vty, int *status) for (i = 0; i < vector_active (matchvec); i++) { if (vector_slot (matchvec, i)) - XFREE (MTYPE_STRVEC, vector_slot (matchvec, i)); + XFREE (MTYPE_TMP, vector_slot (matchvec, i)); } vector_free (matchvec); @@ -1859,6 +2493,7 @@ cmd_complete_command_real (vector vline, struct vty *vty, int *status) } match_str = (char **) matchvec->index; + cmd_complete_sort(matchvec); vector_only_wrapper_free (matchvec); *status = CMD_COMPLETE_LIST_MATCH; return match_str; @@ -1927,7 +2562,9 @@ node_parent ( enum node_type node ) /* Execute command by argument vline vector. */ static int -cmd_execute_command_real (vector vline, struct vty *vty, +cmd_execute_command_real (vector vline, + enum filter_type filter, + struct vty *vty, struct cmd_element **cmd) { unsigned int i; @@ -1939,35 +2576,48 @@ cmd_execute_command_real (vector vline, struct vty *vty, int argc; const char *argv[CMD_ARGC_MAX]; enum match_type match = 0; - int varflag; char *command; + int ret; + vector matches; /* Make copy of command elements. */ cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); for (index = 0; index < vector_active (vline); index++) - if ((command = vector_slot (vline, index))) - { - int ret; - - match = cmd_filter_by_completion (command, cmd_vector, index); + { + command = vector_slot (vline, index); + ret = cmd_vector_filter(cmd_vector, + filter, + vline, index, + &match, + &matches); + + if (ret != CMD_SUCCESS) + { + cmd_matches_free(&matches); + return ret; + } - if (match == vararg_match) + if (match == vararg_match) + { + cmd_matches_free(&matches); break; - - ret = is_cmd_ambiguous (command, cmd_vector, index, match); + } - if (ret == 1) - { - vector_free (cmd_vector); - return CMD_ERR_AMBIGUOUS; - } - else if (ret == 2) - { - vector_free (cmd_vector); - return CMD_ERR_NO_MATCH; - } - } + ret = is_cmd_ambiguous (cmd_vector, command, matches, match); + cmd_matches_free(&matches); + + if (ret == 1) + { + vector_free(cmd_vector); + return CMD_ERR_AMBIGUOUS; + } + else if (ret == 2) + { + vector_free(cmd_vector); + return CMD_ERR_NO_MATCH; + } + } /* Check matched count. */ matched_element = NULL; @@ -1977,12 +2627,9 @@ cmd_execute_command_real (vector vline, struct vty *vty, for (i = 0; i < vector_active (cmd_vector); i++) if ((cmd_element = vector_slot (cmd_vector, i))) { - if (match == vararg_match || index >= cmd_element->cmdsize) + if (cmd_is_complete(cmd_element, vline)) { matched_element = cmd_element; -#if 0 - printf ("DEBUG: %s\n", cmd_element->string); -#endif matched_count++; } else @@ -2006,35 +2653,9 @@ cmd_execute_command_real (vector vline, struct vty *vty, if (matched_count > 1) return CMD_ERR_AMBIGUOUS; - /* Argument treatment */ - varflag = 0; - argc = 0; - - for (i = 0; i < vector_active (vline); i++) - { - if (varflag) - argv[argc++] = vector_slot (vline, i); - else - { - vector descvec = vector_slot (matched_element->strvec, i); - - if (vector_active (descvec) == 1) - { - struct desc *desc = vector_slot (descvec, 0); - - if (CMD_VARARG (desc->cmd)) - varflag = 1; - - if (varflag || CMD_VARIABLE (desc->cmd) || CMD_OPTION (desc->cmd)) - argv[argc++] = vector_slot (vline, i); - } - else - argv[argc++] = vector_slot (vline, i); - } - - if (argc >= CMD_ARGC_MAX) - return CMD_ERR_EXEED_ARGC_MAX; - } + ret = cmd_parse(matched_element, vline, &argc, argv); + if (ret != CMD_SUCCESS) + return ret; /* For vtysh execution. */ if (cmd) @@ -2047,6 +2668,21 @@ cmd_execute_command_real (vector vline, struct vty *vty, return (*matched_element->func) (matched_element, vty, argc, argv); } +/** + * Execute a given command, handling things like "do ..." and checking + * whether the given command might apply at a parent node if doesn't + * apply for the current node. + * + * @param vline Command line input, vector of char* where each element is + * one input token. + * @param vty The vty context in which the command should be executed. + * @param cmd Pointer where the struct cmd_element of the matched command + * will be stored, if any. May be set to NULL if this info is + * not needed. + * @param vtysh If set != 0, don't lookup the command at parent nodes. + * @return The status of the command that has been executed or an error code + * as to why no command could be executed. + */ int cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd, int vtysh) { @@ -2070,7 +2706,7 @@ cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd, vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); } - ret = cmd_execute_command_real (shifted_vline, vty, cmd); + ret = cmd_execute_command_real (shifted_vline, FILTER_RELAXED, vty, cmd); vector_free(shifted_vline); vty->node = onode; @@ -2078,7 +2714,7 @@ cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd, } - saved_ret = ret = cmd_execute_command_real (vline, vty, cmd); + saved_ret = ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd); if (vtysh) return saved_ret; @@ -2089,7 +2725,7 @@ cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd, { try_node = node_parent(try_node); vty->node = try_node; - ret = cmd_execute_command_real (vline, vty, cmd); + ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd); tried = 1; if (ret == CMD_SUCCESS || ret == CMD_WARNING) { @@ -2104,123 +2740,24 @@ cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd, return saved_ret; } -/* Execute command by argument readline. */ +/** + * Execute a given command, matching it strictly against the current node. + * This mode is used when reading config files. + * + * @param vline Command line input, vector of char* where each element is + * one input token. + * @param vty The vty context in which the command should be executed. + * @param cmd Pointer where the struct cmd_element* of the matched command + * will be stored, if any. May be set to NULL if this info is + * not needed. + * @return The status of the command that has been executed or an error code + * as to why no command could be executed. + */ int cmd_execute_command_strict (vector vline, struct vty *vty, struct cmd_element **cmd) { - unsigned int i; - unsigned int index; - vector cmd_vector; - struct cmd_element *cmd_element; - struct cmd_element *matched_element; - unsigned int matched_count, incomplete_count; - int argc; - const char *argv[CMD_ARGC_MAX]; - int varflag; - enum match_type match = 0; - char *command; - - /* Make copy of command element */ - cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); - - for (index = 0; index < vector_active (vline); index++) - if ((command = vector_slot (vline, index))) - { - int ret; - - match = cmd_filter_by_string (vector_slot (vline, index), - cmd_vector, index); - - /* If command meets '.VARARG' then finish matching. */ - if (match == vararg_match) - break; - - ret = is_cmd_ambiguous (command, cmd_vector, index, match); - if (ret == 1) - { - vector_free (cmd_vector); - return CMD_ERR_AMBIGUOUS; - } - if (ret == 2) - { - vector_free (cmd_vector); - return CMD_ERR_NO_MATCH; - } - } - - /* Check matched count. */ - matched_element = NULL; - matched_count = 0; - incomplete_count = 0; - for (i = 0; i < vector_active (cmd_vector); i++) - if (vector_slot (cmd_vector, i) != NULL) - { - cmd_element = vector_slot (cmd_vector, i); - - if (match == vararg_match || index >= cmd_element->cmdsize) - { - matched_element = cmd_element; - matched_count++; - } - else - incomplete_count++; - } - - /* Finish of using cmd_vector. */ - vector_free (cmd_vector); - - /* To execute command, matched_count must be 1. */ - if (matched_count == 0) - { - if (incomplete_count) - return CMD_ERR_INCOMPLETE; - else - return CMD_ERR_NO_MATCH; - } - - if (matched_count > 1) - return CMD_ERR_AMBIGUOUS; - - /* Argument treatment */ - varflag = 0; - argc = 0; - - for (i = 0; i < vector_active (vline); i++) - { - if (varflag) - argv[argc++] = vector_slot (vline, i); - else - { - vector descvec = vector_slot (matched_element->strvec, i); - - if (vector_active (descvec) == 1) - { - struct desc *desc = vector_slot (descvec, 0); - - if (CMD_VARARG (desc->cmd)) - varflag = 1; - - if (varflag || CMD_VARIABLE (desc->cmd) || CMD_OPTION (desc->cmd)) - argv[argc++] = vector_slot (vline, i); - } - else - argv[argc++] = vector_slot (vline, i); - } - - if (argc >= CMD_ARGC_MAX) - return CMD_ERR_EXEED_ARGC_MAX; - } - - /* For vtysh execution. */ - if (cmd) - *cmd = matched_element; - - if (matched_element->daemon) - return CMD_SUCCESS_DAEMON; - - /* Now execute matched command */ - return (*matched_element->func) (matched_element, vty, argc, argv); + return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd); } /* Configration make from file. */ @@ -3461,9 +3998,10 @@ install_default (enum node_type node) void cmd_init (int terminal) { - command_cr = XSTRDUP(MTYPE_STRVEC, ""); - desc_cr.cmd = command_cr; - desc_cr.str = XSTRDUP(MTYPE_STRVEC, ""); + command_cr = XSTRDUP(MTYPE_CMD_TOKENS, ""); + token_cr.type = TOKEN_TERMINAL; + token_cr.cmd = command_cr; + token_cr.desc = XSTRDUP(MTYPE_CMD_TOKENS, ""); /* Allocate initial top vector of commands. */ cmdvec = vector_init (VECTOR_MIN_SIZE); @@ -3584,14 +4122,61 @@ cmd_init (int terminal) srand(time(NULL)); } +static void +cmd_terminate_token(struct cmd_token *token) +{ + unsigned int i, j; + vector keyword_vect; + + if (token->multiple) + { + for (i = 0; i < vector_active(token->multiple); i++) + cmd_terminate_token(vector_slot(token->multiple, i)); + vector_free(token->multiple); + token->multiple = NULL; + } + + if (token->keyword) + { + for (i = 0; i < vector_active(token->keyword); i++) + { + keyword_vect = vector_slot(token->keyword, i); + for (j = 0; j < vector_active(keyword_vect); j++) + cmd_terminate_token(vector_slot(keyword_vect, j)); + vector_free(keyword_vect); + } + vector_free(token->keyword); + token->keyword = NULL; + } + + XFREE(MTYPE_CMD_TOKENS, token->cmd); + XFREE(MTYPE_CMD_TOKENS, token->desc); + + XFREE(MTYPE_CMD_TOKENS, token); +} + +static void +cmd_terminate_element(struct cmd_element *cmd) +{ + unsigned int i; + + if (cmd->tokens == NULL) + return; + + for (i = 0; i < vector_active(cmd->tokens); i++) + cmd_terminate_token(vector_slot(cmd->tokens, i)); + + vector_free(cmd->tokens); + cmd->tokens = NULL; +} + void cmd_terminate () { - unsigned int i, j, k, l; + unsigned int i, j; struct cmd_node *cmd_node; struct cmd_element *cmd_element; - struct desc *desc; - vector cmd_node_v, cmd_element_v, desc_v; + vector cmd_node_v; if (cmdvec) { @@ -3601,30 +4186,8 @@ cmd_terminate () cmd_node_v = cmd_node->cmd_vector; for (j = 0; j < vector_active (cmd_node_v); j++) - if ((cmd_element = vector_slot (cmd_node_v, j)) != NULL && - cmd_element->strvec != NULL) - { - cmd_element_v = cmd_element->strvec; - - for (k = 0; k < vector_active (cmd_element_v); k++) - if ((desc_v = vector_slot (cmd_element_v, k)) != NULL) - { - for (l = 0; l < vector_active (desc_v); l++) - if ((desc = vector_slot (desc_v, l)) != NULL) - { - if (desc->cmd) - XFREE (MTYPE_STRVEC, desc->cmd); - if (desc->str) - XFREE (MTYPE_STRVEC, desc->str); - - XFREE (MTYPE_DESC, desc); - } - vector_free (desc_v); - } - - cmd_element->strvec = NULL; - vector_free (cmd_element_v); - } + if ((cmd_element = vector_slot (cmd_node_v, j)) != NULL) + cmd_terminate_element(cmd_element); vector_free (cmd_node_v); } @@ -3634,9 +4197,9 @@ cmd_terminate () } if (command_cr) - XFREE(MTYPE_STRVEC, command_cr); - if (desc_cr.str) - XFREE(MTYPE_STRVEC, desc_cr.str); + XFREE(MTYPE_CMD_TOKENS, command_cr); + if (token_cr.desc) + XFREE(MTYPE_CMD_TOKENS, token_cr.desc); if (host.name) XFREE (MTYPE_HOST, host.name); if (host.password) diff --git a/lib/command.h b/lib/command.h index 2d708d8e..e47c4255 100644 --- a/lib/command.h +++ b/lib/command.h @@ -138,18 +138,32 @@ struct cmd_element int (*func) (struct cmd_element *, struct vty *, int, const char *[]); const char *doc; /* Documentation of this command. */ int daemon; /* Daemon to which this command belong. */ - vector strvec; /* Pointing out each description vector. */ - unsigned int cmdsize; /* Command index count. */ - char *config; /* Configuration string */ - vector subconfig; /* Sub configuration string */ + vector tokens; /* Vector of cmd_tokens */ u_char attr; /* Command attributes */ }; + +enum cmd_token_type +{ + TOKEN_TERMINAL = 0, + TOKEN_MULTIPLE, + TOKEN_KEYWORD, +}; + /* Command description structure. */ -struct desc +struct cmd_token { + enum cmd_token_type type; + + /* Used for type == MULTIPLE */ + vector multiple; /* vector of cmd_token, type == FINAL */ + + /* Used for type == KEYWORD */ + vector keyword; /* vector of vector of cmd_tokens */ + + /* Used for type == TERMINAL */ char *cmd; /* Command string. */ - char *str; /* Command's description. */ + char *desc; /* Command's description. */ }; /* Return value of the commands. */ @@ -192,7 +206,170 @@ struct desc int argc __attribute__ ((unused)), \ const char *argv[] __attribute__ ((unused)) ) -/* DEFUN for vty command interafce. Little bit hacky ;-). */ +/* DEFUN for vty command interafce. Little bit hacky ;-). + * + * DEFUN(funcname, cmdname, cmdstr, helpstr) + * + * funcname + * ======== + * + * Name of the function that will be defined. + * + * cmdname + * ======= + * + * Name of the struct that will be defined for the command. + * + * cmdstr + * ====== + * + * The cmdstr defines the command syntax. It is used by the vty subsystem + * and vtysh to perform matching and completion in the cli. So you have to take + * care to construct it adhering to the following grammar. The names used + * for the production rules losely represent the names used in lib/command.c + * + * cmdstr = cmd_token , { " " , cmd_token } ; + * + * cmd_token = cmd_terminal + * | cmd_multiple + * | cmd_keyword ; + * + * cmd_terminal_fixed = fixed_string + * | variable + * | range + * | ipv4 + * | ipv4_prefix + * | ipv6 + * | ipv6_prefix ; + * + * cmd_terminal = cmd_terminal_fixed + * | option + * | vararg ; + * + * multiple_part = cmd_terminal_fixed ; + * cmd_multiple = "(" , multiple_part , ( "|" | { "|" , multiple_part } ) , ")" ; + * + * keyword_part = fixed_string , { " " , ( cmd_terminal_fixed | cmd_multiple ) } ; + * cmd_keyword = "{" , keyword_part , { "|" , keyword_part } , "}" ; + * + * lowercase = "a" | ... | "z" ; + * uppercase = "A" | ... | "Z" ; + * digit = "0" | ... | "9" ; + * number = digit , { digit } ; + * + * fixed_string = (lowercase | digit) , { lowercase | digit | uppercase | "-" | "_" } ; + * variable = uppercase , { uppercase | "_" } ; + * range = "<" , number , "-" , number , ">" ; + * ipv4 = "A.B.C.D" ; + * ipv4_prefix = "A.B.C.D/M" ; + * ipv6 = "X:X::X:X" ; + * ipv6_prefix = "X:X::X:X/M" ; + * option = "[" , variable , "]" ; + * vararg = "." , variable ; + * + * To put that all in a textual description: A cmdstr is a sequence of tokens, + * separated by spaces. + * + * Terminal Tokens: + * + * A very simple cmdstring would be something like: "show ip bgp". It consists + * of three Terminal Tokens, each containing a fixed string. When this command + * is called, no arguments will be passed down to the function implementing it, + * as it only consists of fixed strings. + * + * Apart from fixed strings, Terminal Tokens can also contain variables: + * An example would be "show ip bgp A.B.C.D". This command expects an IPv4 + * as argument. As this is a variable, the IP address entered by the user will + * be passed down as an argument. Apart from two exceptions, the other options + * for Terminal Tokens behave exactly as we just discussed and only make a + * difference for the CLI. The two exceptions will be discussed in the next + * paragraphs. + * + * A Terminal Token can contain a so called option match. This is a simple + * string variable that the user may omit. An example would be: + * "show interface [IFNAME]". If the user calls this without an interface as + * argument, no arguments will be passed down to the function implementing + * this command. Otherwise, the interface name will be provided to the function + * as a regular argument. + + * Also, a Terminal Token can contain a so called vararg. This is used e.g. in + * "show ip bgp regexp .LINE". The last token is a vararg match and will + * consume all the arguments the user inputs on the command line and append + * those to the list of arguments passed down to the function implementing this + * command. (Therefore, it doesn't make much sense to have any tokens after a + * vararg because the vararg will already consume all the words the user entered + * in the CLI) + * + * Multiple Tokens: + * + * The Multiple Token type can be used if there are multiple possibilities what + * arguments may be used for a command, but it should map to the same function + * nonetheless. An example would be "ip route A.B.C.D/M (reject|blackhole)" + * In that case both "reject" and "blackhole" would be acceptable as last + * arguments. The words matched by Multiple Tokens are always added to the + * argument list, even if they are matched by fixed strings. Such a Multiple + * Token can contain almost any type of token that would also be acceptable + * for a Terminal Token, the exception are optional variables and varag. + * + * There is one special case that is used in some places of Quagga that should be + * pointed out here shortly. An example would be "password (8|) WORD". This + * construct is used to have fixed strings communicated as arguments. (The "8" + * will be passed down as an argument in this case) It does not mean that + * the "8" is optional. Another historic and possibly surprising property of + * this construct is that it consumes two parts of helpstr. (Help + * strings will be explained later) + * + * Keyword Tokens: + * + * There are commands that take a lot of different and possibly optional arguments. + * An example from ospf would be the "default-information originate" command. This + * command takes a lot of optional arguments that may be provided in any order. + * To accomodate such commands, the Keyword Token has been implemented. + * Using the keyword token, the "default-information originate" command and all + * its possible options can be represented using this single cmdstr: + * "default-information originate \ + * {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}" + * + * Keywords always start with a fixed string and may be followed by arguments. + * Except optional variables and vararg, everything is permitted here. + * + * For the special case of a keyword without arguments, either NULL or the + * keyword itself will be pushed as an argument, depending on whether the + * keyword is present. + * For the other keywords, arguments will be only pushed for + * variables/Multiple Tokens. If the keyword is not present, the arguments that + * would have been pushed will be substituted by NULL. + * + * A few examples: + * "default information originate metric-type 1 metric 1000" + * would yield the following arguments: + * { NULL, "1000", "1", NULL } + * + * "default information originate always route-map RMAP-DEFAULT" + * would yield the following arguments: + * { "always", NULL, NULL, "RMAP-DEFAULT" } + * + * helpstr + * ======= + * + * The helpstr is used to show a short explantion for the commands that + * are available when the user presses '?' on the CLI. It is the concatenation + * of the helpstrings for all the tokens that make up the command. + * + * There should be one helpstring for each token in the cmdstr except those + * containing other tokens, like Multiple or Keyword Tokens. For those, there + * will only be the helpstrings of the contained tokens. + * + * The individual helpstrings are expected to be in the same order as their + * respective Tokens appear in the cmdstr. They should each be terminated with + * a linefeed. The last helpstring should be terminated with a linefeed as well. + * + * Care should also be taken to avoid having similar tokens with different + * helpstrings. Imagine e.g. the commands "show ip ospf" and "show ip bgp". + * they both contain a helpstring for "show", but only one will be displayed + * when the user enters "sh?". If those two helpstrings differ, it is not + * defined which one will be shown and the behavior is therefore unpredictable. + */ #define DEFUN(funcname, cmdname, cmdstr, helpstr) \ DEFUN_CMD_FUNC_DECL(funcname) \ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \ @@ -330,7 +507,6 @@ struct desc extern void install_node (struct cmd_node *, int (*) (struct vty *)); extern void install_default (enum node_type); extern void install_element (enum node_type, struct cmd_element *); -extern void sort_node (void); /* Concatenates argv[shift] through argv[argc-1] into a single NUL-terminated string with a space between each element (allocated using @@ -346,7 +522,6 @@ extern int config_from_file (struct vty *, FILE *); extern enum node_type node_parent (enum node_type); extern int cmd_execute_command (vector, struct vty *, struct cmd_element **, int); extern int cmd_execute_command_strict (vector, struct vty *, struct cmd_element **); -extern void config_replace_string (struct cmd_element *, char *, ...); extern void cmd_init (int); extern void cmd_terminate (void); diff --git a/lib/memtypes.c b/lib/memtypes.c index 50b6fa42..47a34387 100644 --- a/lib/memtypes.c +++ b/lib/memtypes.c @@ -54,7 +54,7 @@ struct memory_list memory_list_lib[] = { MTYPE_ROUTE_MAP_RULE, "Route map rule" }, { MTYPE_ROUTE_MAP_RULE_STR, "Route map rule str" }, { MTYPE_ROUTE_MAP_COMPILED, "Route map compiled" }, - { MTYPE_DESC, "Command desc" }, + { MTYPE_CMD_TOKENS, "Command desc" }, { MTYPE_KEY, "Key" }, { MTYPE_KEYCHAIN, "Key chain" }, { MTYPE_IF_RMAP, "Interface route map" }, diff --git a/lib/vty.c b/lib/vty.c index 96cb1e4b..9908b023 100644 --- a/lib/vty.c +++ b/lib/vty.c @@ -931,23 +931,23 @@ vty_complete_command (struct vty *vty) static void vty_describe_fold (struct vty *vty, int cmd_width, - unsigned int desc_width, struct desc *desc) + unsigned int desc_width, struct cmd_token *token) { char *buf; const char *cmd, *p; int pos; - cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd; + cmd = token->cmd[0] == '.' ? token->cmd + 1 : token->cmd; if (desc_width <= 0) { - vty_out (vty, " %-*s %s%s", cmd_width, cmd, desc->str, VTY_NEWLINE); + vty_out (vty, " %-*s %s%s", cmd_width, cmd, token->desc, VTY_NEWLINE); return; } - buf = XCALLOC (MTYPE_TMP, strlen (desc->str) + 1); + buf = XCALLOC (MTYPE_TMP, strlen (token->desc) + 1); - for (p = desc->str; strlen (p) > desc_width; p += pos + 1) + for (p = token->desc; strlen (p) > desc_width; p += pos + 1) { for (pos = desc_width; pos > 0; pos--) if (*(p + pos) == ' ') @@ -976,7 +976,7 @@ vty_describe_command (struct vty *vty) vector vline; vector describe; unsigned int i, width, desc_width; - struct desc *desc, *desc_cr = NULL; + struct cmd_token *token, *token_cr = NULL; vline = cmd_make_strvec (vty->buf); @@ -1010,15 +1010,15 @@ vty_describe_command (struct vty *vty) /* Get width of command string. */ width = 0; for (i = 0; i < vector_active (describe); i++) - if ((desc = vector_slot (describe, i)) != NULL) + if ((token = vector_slot (describe, i)) != NULL) { unsigned int len; - if (desc->cmd[0] == '\0') + if (token->cmd[0] == '\0') continue; - len = strlen (desc->cmd); - if (desc->cmd[0] == '.') + len = strlen (token->cmd); + if (token->cmd[0] == '.') len--; if (width < len) @@ -1030,27 +1030,27 @@ vty_describe_command (struct vty *vty) /* Print out description. */ for (i = 0; i < vector_active (describe); i++) - if ((desc = vector_slot (describe, i)) != NULL) + if ((token = vector_slot (describe, i)) != NULL) { - if (desc->cmd[0] == '\0') + if (token->cmd[0] == '\0') continue; - if (strcmp (desc->cmd, command_cr) == 0) + if (strcmp (token->cmd, command_cr) == 0) { - desc_cr = desc; + token_cr = token; continue; } - if (!desc->str) + if (!token->desc) vty_out (vty, " %-s%s", - desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, + token->cmd[0] == '.' ? token->cmd + 1 : token->cmd, VTY_NEWLINE); - else if (desc_width >= strlen (desc->str)) + else if (desc_width >= strlen (token->desc)) vty_out (vty, " %-*s %s%s", width, - desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, - desc->str, VTY_NEWLINE); + token->cmd[0] == '.' ? token->cmd + 1 : token->cmd, + token->desc, VTY_NEWLINE); else - vty_describe_fold (vty, width, desc_width, desc); + vty_describe_fold (vty, width, desc_width, token); #if 0 vty_out (vty, " %-*s %s%s", width @@ -1059,18 +1059,18 @@ vty_describe_command (struct vty *vty) #endif /* 0 */ } - if ((desc = desc_cr)) + if ((token = token_cr)) { - if (!desc->str) + if (!token->desc) vty_out (vty, " %-s%s", - desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, + token->cmd[0] == '.' ? token->cmd + 1 : token->cmd, VTY_NEWLINE); - else if (desc_width >= strlen (desc->str)) + else if (desc_width >= strlen (token->desc)) vty_out (vty, " %-*s %s%s", width, - desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, - desc->str, VTY_NEWLINE); + token->cmd[0] == '.' ? token->cmd + 1 : token->cmd, + token->desc, VTY_NEWLINE); else - vty_describe_fold (vty, width, desc_width, desc); + vty_describe_fold (vty, width, desc_width, token); } out: diff --git a/ospf6d/ospf6_main.c b/ospf6d/ospf6_main.c index e9919713..4f6d9e51 100644 --- a/ospf6d/ospf6_main.c +++ b/ospf6d/ospf6_main.c @@ -325,9 +325,6 @@ main (int argc, char *argv[], char *envp[]) /* initialize ospf6 */ ospf6_init (); - /* sort command vector */ - sort_node (); - /* parse config file */ vty_read_config (config_file, config_default); diff --git a/ospfd/ospf_main.c b/ospfd/ospf_main.c index 6d58b4ea..c68aa4dd 100644 --- a/ospfd/ospf_main.c +++ b/ospfd/ospf_main.c @@ -310,8 +310,6 @@ main (int argc, char **argv) ospf_opaque_init (); #endif /* HAVE_OPAQUE_LSA */ - sort_node (); - /* Get configuration file. */ vty_read_config (config_file, config_default); diff --git a/ripd/rip_main.c b/ripd/rip_main.c index 6a9fa71d..a512fbc2 100644 --- a/ripd/rip_main.c +++ b/ripd/rip_main.c @@ -287,9 +287,6 @@ main (int argc, char **argv) rip_zclient_init (); rip_peer_init (); - /* Sort all installed commands. */ - sort_node (); - /* Get configuration file. */ vty_read_config (config_file, config_default); diff --git a/ripngd/ripng_main.c b/ripngd/ripng_main.c index 20225b7d..7525a267 100644 --- a/ripngd/ripng_main.c +++ b/ripngd/ripng_main.c @@ -282,9 +282,6 @@ main (int argc, char **argv) zebra_init (); ripng_peer_init (); - /* Sort all installed commands. */ - sort_node (); - /* Get configuration file. */ vty_read_config (config_file, config_default); diff --git a/tests/main.c b/tests/main.c index e0fbb4d5..2d8cb0c5 100644 --- a/tests/main.c +++ b/tests/main.c @@ -171,8 +171,6 @@ main (int argc, char **argv) /* OSPF vty inits. */ test_vty_init (); - sort_node (); - /* Change to the daemon program. */ if (daemon_mode && daemon (0, 0) < 0) { diff --git a/tests/test-commands.c b/tests/test-commands.c index e2f40c6a..18b3b50d 100644 --- a/tests/test-commands.c +++ b/tests/test-commands.c @@ -233,8 +233,6 @@ test_init(void) cmd->daemon = 0; cmd->func = test_callback; } - sort_node(); - test_load(); vty_init_vtysh(); } @@ -340,8 +338,8 @@ test_run(struct prng *prng, struct vty *vty, const char *cmd, unsigned int edit_ { for (j = 0; j < vector_active(descriptions); j++) { - struct desc *cmd = vector_slot(descriptions, j); - printf(" '%s' '%s'\n", cmd->cmd, cmd->str); + struct cmd_token *cmd = vector_slot(descriptions, j); + printf(" '%s' '%s'\n", cmd->cmd, cmd->desc); } vector_free(descriptions); } diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index c575902c..34c3bd62 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -554,7 +554,7 @@ vtysh_rl_describe (void) vector vline; vector describe; int width; - struct desc *desc; + struct cmd_token *token; vline = cmd_make_strvec (rl_line_buffer); @@ -592,15 +592,15 @@ vtysh_rl_describe (void) /* Get width of command string. */ width = 0; for (i = 0; i < vector_active (describe); i++) - if ((desc = vector_slot (describe, i)) != NULL) + if ((token = vector_slot (describe, i)) != NULL) { int len; - if (desc->cmd[0] == '\0') + if (token->cmd[0] == '\0') continue; - len = strlen (desc->cmd); - if (desc->cmd[0] == '.') + len = strlen (token->cmd); + if (token->cmd[0] == '.') len--; if (width < len) @@ -608,19 +608,19 @@ vtysh_rl_describe (void) } for (i = 0; i < vector_active (describe); i++) - if ((desc = vector_slot (describe, i)) != NULL) + if ((token = vector_slot (describe, i)) != NULL) { - if (desc->cmd[0] == '\0') + if (token->cmd[0] == '\0') continue; - if (! desc->str) + if (! token->desc) fprintf (stdout," %-s\n", - desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd); + token->cmd[0] == '.' ? token->cmd + 1 : token->cmd); else fprintf (stdout," %-*s %s\n", width, - desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, - desc->str); + token->cmd[0] == '.' ? token->cmd + 1 : token->cmd, + token->desc); } cmd_free_strvec (vline); diff --git a/vtysh/vtysh_main.c b/vtysh/vtysh_main.c index 4a315a5c..48958f0f 100644 --- a/vtysh/vtysh_main.c +++ b/vtysh/vtysh_main.c @@ -299,8 +299,6 @@ main (int argc, char **argv, char **env) vty_init_vtysh (); - sort_node (); - /* Read vtysh configuration file before connecting to daemons. */ vtysh_read_config (config_default); diff --git a/zebra/main.c b/zebra/main.c index 742e0292..523b1911 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -343,9 +343,6 @@ main (int argc, char **argv) interface_list (); route_read (); - /* Sort VTY commands. */ - sort_node (); - #ifdef HAVE_SNMP zebra_snmp_init (); #endif /* HAVE_SNMP */ diff --git a/zebra/test_main.c b/zebra/test_main.c index a9518637..c6951729 100644 --- a/zebra/test_main.c +++ b/zebra/test_main.c @@ -298,9 +298,6 @@ main (int argc, char **argv) route_read (); zebra_vty_init(); - /* Sort VTY commands. */ - sort_node (); - /* Configuration file read*/ vty_read_config (config_file, config_default); From 6f2a67031cfb21362fc7ecd3251761799c8ffe27 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Mon, 30 Sep 2013 12:27:52 +0000 Subject: [PATCH 184/482] ospfd/ospf_vty.c: use keyword cmd style Use the new keyword command style for: - default-information originate - distance ospf - redistribute Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- ospfd/ospf_vty.c | 990 ++++---------------------------------- tests/testcommands.in | 15 + tests/testcommands.refout | 107 ++++ 3 files changed, 211 insertions(+), 901 deletions(-) diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 5e5a0b0d..1489b20a 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -5787,11 +5787,10 @@ ALIAS (no_ip_ospf_transmit_delay, "OSPF interface commands\n" "Link state transmit delay\n") - -DEFUN (ospf_redistribute_source_metric_type, - ospf_redistribute_source_metric_type_routemap_cmd, - "redistribute " QUAGGA_REDIST_STR_OSPFD - " metric <0-16777214> metric-type (1|2) route-map WORD", +DEFUN (ospf_redistribute_source, + ospf_redistribute_source_cmd, + "redistribute " QUAGGA_REDIST_STR_OSPFD + " {metric <0-16777214>|metric-type (1|2)|route-map WORD}", REDIST_STR QUAGGA_REDIST_HELP_STR_OSPFD "Metric for redistributed routes\n" @@ -5807,84 +5806,25 @@ DEFUN (ospf_redistribute_source_metric_type, int type = -1; int metric = -1; + if (argc < 4) + return CMD_WARNING; /* should not happen */ + /* Get distribute source. */ source = proto_redistnum(AFI_IP, argv[0]); if (source < 0 || source == ZEBRA_ROUTE_OSPF) return CMD_WARNING; /* Get metric value. */ - if (argc >= 2) + if (argv[1] != NULL) if (!str2metric (argv[1], &metric)) return CMD_WARNING; /* Get metric type. */ - if (argc >= 3) + if (argv[2] != NULL) if (!str2metric_type (argv[2], &type)) return CMD_WARNING; - if (argc == 4) - ospf_routemap_set (ospf, source, argv[3]); - else - ospf_routemap_unset (ospf, source); - - return ospf_redistribute_set (ospf, source, type, metric); -} - -ALIAS (ospf_redistribute_source_metric_type, - ospf_redistribute_source_metric_type_cmd, - "redistribute " QUAGGA_REDIST_STR_OSPFD - " metric <0-16777214> metric-type (1|2)", - REDIST_STR - QUAGGA_REDIST_HELP_STR_OSPFD - "Metric for redistributed routes\n" - "OSPF default metric\n" - "OSPF exterior metric type for redistributed routes\n" - "Set OSPF External Type 1 metrics\n" - "Set OSPF External Type 2 metrics\n") - -ALIAS (ospf_redistribute_source_metric_type, - ospf_redistribute_source_metric_cmd, - "redistribute " QUAGGA_REDIST_STR_OSPFD " metric <0-16777214>", - REDIST_STR - QUAGGA_REDIST_HELP_STR_OSPFD - "Metric for redistributed routes\n" - "OSPF default metric\n") - -DEFUN (ospf_redistribute_source_type_metric, - ospf_redistribute_source_type_metric_routemap_cmd, - "redistribute " QUAGGA_REDIST_STR_OSPFD - " metric-type (1|2) metric <0-16777214> route-map WORD", - REDIST_STR - QUAGGA_REDIST_HELP_STR_OSPFD - "OSPF exterior metric type for redistributed routes\n" - "Set OSPF External Type 1 metrics\n" - "Set OSPF External Type 2 metrics\n" - "Metric for redistributed routes\n" - "OSPF default metric\n" - "Route map reference\n" - "Pointer to route-map entries\n") -{ - struct ospf *ospf = vty->index; - int source; - int type = -1; - int metric = -1; - - /* Get distribute source. */ - source = proto_redistnum(AFI_IP, argv[0]); - if (source < 0 || source == ZEBRA_ROUTE_OSPF) - return CMD_WARNING; - - /* Get metric value. */ - if (argc >= 2) - if (!str2metric_type (argv[1], &type)) - return CMD_WARNING; - - /* Get metric type. */ - if (argc >= 3) - if (!str2metric (argv[2], &metric)) - return CMD_WARNING; - - if (argc == 4) + if (argv[3] != NULL) ospf_routemap_set (ospf, source, argv[3]); else ospf_routemap_unset (ospf, source); @@ -5892,124 +5832,6 @@ DEFUN (ospf_redistribute_source_type_metric, return ospf_redistribute_set (ospf, source, type, metric); } -ALIAS (ospf_redistribute_source_type_metric, - ospf_redistribute_source_type_metric_cmd, - "redistribute " QUAGGA_REDIST_STR_OSPFD - " metric-type (1|2) metric <0-16777214>", - REDIST_STR - QUAGGA_REDIST_HELP_STR_OSPFD - "OSPF exterior metric type for redistributed routes\n" - "Set OSPF External Type 1 metrics\n" - "Set OSPF External Type 2 metrics\n" - "Metric for redistributed routes\n" - "OSPF default metric\n") - -ALIAS (ospf_redistribute_source_type_metric, - ospf_redistribute_source_type_cmd, - "redistribute " QUAGGA_REDIST_STR_OSPFD " metric-type (1|2)", - REDIST_STR - QUAGGA_REDIST_HELP_STR_OSPFD - "OSPF exterior metric type for redistributed routes\n" - "Set OSPF External Type 1 metrics\n" - "Set OSPF External Type 2 metrics\n") - -ALIAS (ospf_redistribute_source_type_metric, - ospf_redistribute_source_cmd, - "redistribute " QUAGGA_REDIST_STR_OSPFD, - REDIST_STR - QUAGGA_REDIST_HELP_STR_OSPFD) - -DEFUN (ospf_redistribute_source_metric_routemap, - ospf_redistribute_source_metric_routemap_cmd, - "redistribute " QUAGGA_REDIST_STR_OSPFD - " metric <0-16777214> route-map WORD", - REDIST_STR - QUAGGA_REDIST_HELP_STR_OSPFD - "Metric for redistributed routes\n" - "OSPF default metric\n" - "Route map reference\n" - "Pointer to route-map entries\n") -{ - struct ospf *ospf = vty->index; - int source; - int metric = -1; - - /* Get distribute source. */ - source = proto_redistnum(AFI_IP, argv[0]); - if (source < 0 || source == ZEBRA_ROUTE_OSPF) - return CMD_WARNING; - - /* Get metric value. */ - if (argc >= 2) - if (!str2metric (argv[1], &metric)) - return CMD_WARNING; - - if (argc == 3) - ospf_routemap_set (ospf, source, argv[2]); - else - ospf_routemap_unset (ospf, source); - - return ospf_redistribute_set (ospf, source, -1, metric); -} - -DEFUN (ospf_redistribute_source_type_routemap, - ospf_redistribute_source_type_routemap_cmd, - "redistribute " QUAGGA_REDIST_STR_OSPFD - " metric-type (1|2) route-map WORD", - REDIST_STR - QUAGGA_REDIST_HELP_STR_OSPFD - "OSPF exterior metric type for redistributed routes\n" - "Set OSPF External Type 1 metrics\n" - "Set OSPF External Type 2 metrics\n" - "Route map reference\n" - "Pointer to route-map entries\n") -{ - struct ospf *ospf = vty->index; - int source; - int type = -1; - - /* Get distribute source. */ - source = proto_redistnum(AFI_IP, argv[0]); - if (source < 0 || source == ZEBRA_ROUTE_OSPF) - return CMD_WARNING; - - /* Get metric value. */ - if (argc >= 2) - if (!str2metric_type (argv[1], &type)) - return CMD_WARNING; - - if (argc == 3) - ospf_routemap_set (ospf, source, argv[2]); - else - ospf_routemap_unset (ospf, source); - - return ospf_redistribute_set (ospf, source, type, -1); -} - -DEFUN (ospf_redistribute_source_routemap, - ospf_redistribute_source_routemap_cmd, - "redistribute " QUAGGA_REDIST_STR_OSPFD " route-map WORD", - REDIST_STR - QUAGGA_REDIST_HELP_STR_OSPFD - "Route map reference\n" - "Pointer to route-map entries\n") -{ - struct ospf *ospf = vty->index; - int source; - - /* Get distribute source. */ - source = proto_redistnum(AFI_IP, argv[0]); - if (source < 0 || source == ZEBRA_ROUTE_OSPF) - return CMD_WARNING; - - if (argc == 2) - ospf_routemap_set (ospf, source, argv[1]); - else - ospf_routemap_unset (ospf, source); - - return ospf_redistribute_set (ospf, source, -1, -1); -} - DEFUN (no_ospf_redistribute_source, no_ospf_redistribute_source_cmd, "no redistribute " QUAGGA_REDIST_STR_OSPFD, @@ -6032,432 +5854,85 @@ DEFUN (ospf_distribute_list_out, ospf_distribute_list_out_cmd, "distribute-list WORD out " QUAGGA_REDIST_STR_OSPFD, "Filter networks in routing updates\n" - "Access-list name\n" - OUT_STR - QUAGGA_REDIST_HELP_STR_OSPFD) -{ - struct ospf *ospf = vty->index; - int source; - - /* Get distribute source. */ - source = proto_redistnum(AFI_IP, argv[1]); - if (source < 0 || source == ZEBRA_ROUTE_OSPF) - return CMD_WARNING; - - return ospf_distribute_list_out_set (ospf, source, argv[0]); -} - -DEFUN (no_ospf_distribute_list_out, - no_ospf_distribute_list_out_cmd, - "no distribute-list WORD out " QUAGGA_REDIST_STR_OSPFD, - NO_STR - "Filter networks in routing updates\n" - "Access-list name\n" - OUT_STR - QUAGGA_REDIST_HELP_STR_OSPFD) -{ - struct ospf *ospf = vty->index; - int source; - - source = proto_redistnum(AFI_IP, argv[1]); - if (source < 0 || source == ZEBRA_ROUTE_OSPF) - return CMD_WARNING; - - return ospf_distribute_list_out_unset (ospf, source, argv[0]); -} - -/* Default information originate. */ -DEFUN (ospf_default_information_originate_metric_type_routemap, - ospf_default_information_originate_metric_type_routemap_cmd, - "default-information originate metric <0-16777214> metric-type (1|2) route-map WORD", - "Control distribution of default information\n" - "Distribute a default route\n" - "OSPF default metric\n" - "OSPF metric\n" - "OSPF metric type for default routes\n" - "Set OSPF External Type 1 metrics\n" - "Set OSPF External Type 2 metrics\n" - "Route map reference\n" - "Pointer to route-map entries\n") -{ - struct ospf *ospf = vty->index; - int type = -1; - int metric = -1; - - /* Get metric value. */ - if (argc >= 1) - if (!str2metric (argv[0], &metric)) - return CMD_WARNING; - - /* Get metric type. */ - if (argc >= 2) - if (!str2metric_type (argv[1], &type)) - return CMD_WARNING; - - if (argc == 3) - ospf_routemap_set (ospf, DEFAULT_ROUTE, argv[2]); - else - ospf_routemap_unset (ospf, DEFAULT_ROUTE); - - return ospf_redistribute_default_set (ospf, DEFAULT_ORIGINATE_ZEBRA, - type, metric); -} - -ALIAS (ospf_default_information_originate_metric_type_routemap, - ospf_default_information_originate_metric_type_cmd, - "default-information originate metric <0-16777214> metric-type (1|2)", - "Control distribution of default information\n" - "Distribute a default route\n" - "OSPF default metric\n" - "OSPF metric\n" - "OSPF metric type for default routes\n" - "Set OSPF External Type 1 metrics\n" - "Set OSPF External Type 2 metrics\n") - -ALIAS (ospf_default_information_originate_metric_type_routemap, - ospf_default_information_originate_metric_cmd, - "default-information originate metric <0-16777214>", - "Control distribution of default information\n" - "Distribute a default route\n" - "OSPF default metric\n" - "OSPF metric\n") - -ALIAS (ospf_default_information_originate_metric_type_routemap, - ospf_default_information_originate_cmd, - "default-information originate", - "Control distribution of default information\n" - "Distribute a default route\n") - -/* Default information originate. */ -DEFUN (ospf_default_information_originate_metric_routemap, - ospf_default_information_originate_metric_routemap_cmd, - "default-information originate metric <0-16777214> route-map WORD", - "Control distribution of default information\n" - "Distribute a default route\n" - "OSPF default metric\n" - "OSPF metric\n" - "Route map reference\n" - "Pointer to route-map entries\n") -{ - struct ospf *ospf = vty->index; - int metric = -1; - - /* Get metric value. */ - if (argc >= 1) - if (!str2metric (argv[0], &metric)) - return CMD_WARNING; - - if (argc == 2) - ospf_routemap_set (ospf, DEFAULT_ROUTE, argv[1]); - else - ospf_routemap_unset (ospf, DEFAULT_ROUTE); - - return ospf_redistribute_default_set (ospf, DEFAULT_ORIGINATE_ZEBRA, - -1, metric); -} - -/* Default information originate. */ -DEFUN (ospf_default_information_originate_routemap, - ospf_default_information_originate_routemap_cmd, - "default-information originate route-map WORD", - "Control distribution of default information\n" - "Distribute a default route\n" - "Route map reference\n" - "Pointer to route-map entries\n") -{ - struct ospf *ospf = vty->index; - - if (argc == 1) - ospf_routemap_set (ospf, DEFAULT_ROUTE, argv[0]); - else - ospf_routemap_unset (ospf, DEFAULT_ROUTE); - - return ospf_redistribute_default_set (ospf, DEFAULT_ORIGINATE_ZEBRA, -1, -1); -} - -DEFUN (ospf_default_information_originate_type_metric_routemap, - ospf_default_information_originate_type_metric_routemap_cmd, - "default-information originate metric-type (1|2) metric <0-16777214> route-map WORD", - "Control distribution of default information\n" - "Distribute a default route\n" - "OSPF metric type for default routes\n" - "Set OSPF External Type 1 metrics\n" - "Set OSPF External Type 2 metrics\n" - "OSPF default metric\n" - "OSPF metric\n" - "Route map reference\n" - "Pointer to route-map entries\n") -{ - struct ospf *ospf = vty->index; - int type = -1; - int metric = -1; - - /* Get metric type. */ - if (argc >= 1) - if (!str2metric_type (argv[0], &type)) - return CMD_WARNING; - - /* Get metric value. */ - if (argc >= 2) - if (!str2metric (argv[1], &metric)) - return CMD_WARNING; - - if (argc == 3) - ospf_routemap_set (ospf, DEFAULT_ROUTE, argv[2]); - else - ospf_routemap_unset (ospf, DEFAULT_ROUTE); - - return ospf_redistribute_default_set (ospf, DEFAULT_ORIGINATE_ZEBRA, - type, metric); -} - -ALIAS (ospf_default_information_originate_type_metric_routemap, - ospf_default_information_originate_type_metric_cmd, - "default-information originate metric-type (1|2) metric <0-16777214>", - "Control distribution of default information\n" - "Distribute a default route\n" - "OSPF metric type for default routes\n" - "Set OSPF External Type 1 metrics\n" - "Set OSPF External Type 2 metrics\n" - "OSPF default metric\n" - "OSPF metric\n") - -ALIAS (ospf_default_information_originate_type_metric_routemap, - ospf_default_information_originate_type_cmd, - "default-information originate metric-type (1|2)", - "Control distribution of default information\n" - "Distribute a default route\n" - "OSPF metric type for default routes\n" - "Set OSPF External Type 1 metrics\n" - "Set OSPF External Type 2 metrics\n") - -DEFUN (ospf_default_information_originate_type_routemap, - ospf_default_information_originate_type_routemap_cmd, - "default-information originate metric-type (1|2) route-map WORD", - "Control distribution of default information\n" - "Distribute a default route\n" - "OSPF metric type for default routes\n" - "Set OSPF External Type 1 metrics\n" - "Set OSPF External Type 2 metrics\n" - "Route map reference\n" - "Pointer to route-map entries\n") -{ - struct ospf *ospf = vty->index; - int type = -1; - - /* Get metric type. */ - if (argc >= 1) - if (!str2metric_type (argv[0], &type)) - return CMD_WARNING; - - if (argc == 2) - ospf_routemap_set (ospf, DEFAULT_ROUTE, argv[1]); - else - ospf_routemap_unset (ospf, DEFAULT_ROUTE); - - return ospf_redistribute_default_set (ospf, DEFAULT_ORIGINATE_ZEBRA, - type, -1); -} - -DEFUN (ospf_default_information_originate_always_metric_type_routemap, - ospf_default_information_originate_always_metric_type_routemap_cmd, - "default-information originate always metric <0-16777214> metric-type (1|2) route-map WORD", - "Control distribution of default information\n" - "Distribute a default route\n" - "Always advertise default route\n" - "OSPF default metric\n" - "OSPF metric\n" - "OSPF metric type for default routes\n" - "Set OSPF External Type 1 metrics\n" - "Set OSPF External Type 2 metrics\n" - "Route map reference\n" - "Pointer to route-map entries\n") -{ - struct ospf *ospf = vty->index; - int type = -1; - int metric = -1; - - /* Get metric value. */ - if (argc >= 1) - if (!str2metric (argv[0], &metric)) - return CMD_WARNING; - - /* Get metric type. */ - if (argc >= 2) - if (!str2metric_type (argv[1], &type)) - return CMD_WARNING; - - if (argc == 3) - ospf_routemap_set (ospf, DEFAULT_ROUTE, argv[2]); - else - ospf_routemap_unset (ospf, DEFAULT_ROUTE); - - return ospf_redistribute_default_set (ospf, DEFAULT_ORIGINATE_ALWAYS, - type, metric); -} - -ALIAS (ospf_default_information_originate_always_metric_type_routemap, - ospf_default_information_originate_always_metric_type_cmd, - "default-information originate always metric <0-16777214> metric-type (1|2)", - "Control distribution of default information\n" - "Distribute a default route\n" - "Always advertise default route\n" - "OSPF default metric\n" - "OSPF metric\n" - "OSPF metric type for default routes\n" - "Set OSPF External Type 1 metrics\n" - "Set OSPF External Type 2 metrics\n") - -ALIAS (ospf_default_information_originate_always_metric_type_routemap, - ospf_default_information_originate_always_metric_cmd, - "default-information originate always metric <0-16777214>", - "Control distribution of default information\n" - "Distribute a default route\n" - "Always advertise default route\n" - "OSPF default metric\n" - "OSPF metric\n" - "OSPF metric type for default routes\n") - -ALIAS (ospf_default_information_originate_always_metric_type_routemap, - ospf_default_information_originate_always_cmd, - "default-information originate always", - "Control distribution of default information\n" - "Distribute a default route\n" - "Always advertise default route\n") - -DEFUN (ospf_default_information_originate_always_metric_routemap, - ospf_default_information_originate_always_metric_routemap_cmd, - "default-information originate always metric <0-16777214> route-map WORD", - "Control distribution of default information\n" - "Distribute a default route\n" - "Always advertise default route\n" - "OSPF default metric\n" - "OSPF metric\n" - "Route map reference\n" - "Pointer to route-map entries\n") + "Access-list name\n" + OUT_STR + QUAGGA_REDIST_HELP_STR_OSPFD) { struct ospf *ospf = vty->index; - int metric = -1; - - /* Get metric value. */ - if (argc >= 1) - if (!str2metric (argv[0], &metric)) - return CMD_WARNING; + int source; - if (argc == 2) - ospf_routemap_set (ospf, DEFAULT_ROUTE, argv[1]); - else - ospf_routemap_unset (ospf, DEFAULT_ROUTE); + /* Get distribute source. */ + source = proto_redistnum(AFI_IP, argv[1]); + if (source < 0 || source == ZEBRA_ROUTE_OSPF) + return CMD_WARNING; - return ospf_redistribute_default_set (ospf, DEFAULT_ORIGINATE_ALWAYS, - -1, metric); + return ospf_distribute_list_out_set (ospf, source, argv[0]); } -DEFUN (ospf_default_information_originate_always_routemap, - ospf_default_information_originate_always_routemap_cmd, - "default-information originate always route-map WORD", - "Control distribution of default information\n" - "Distribute a default route\n" - "Always advertise default route\n" - "Route map reference\n" - "Pointer to route-map entries\n") +DEFUN (no_ospf_distribute_list_out, + no_ospf_distribute_list_out_cmd, + "no distribute-list WORD out " QUAGGA_REDIST_STR_OSPFD, + NO_STR + "Filter networks in routing updates\n" + "Access-list name\n" + OUT_STR + QUAGGA_REDIST_HELP_STR_OSPFD) { struct ospf *ospf = vty->index; + int source; - if (argc == 1) - ospf_routemap_set (ospf, DEFAULT_ROUTE, argv[0]); - else - ospf_routemap_unset (ospf, DEFAULT_ROUTE); + source = proto_redistnum(AFI_IP, argv[1]); + if (source < 0 || source == ZEBRA_ROUTE_OSPF) + return CMD_WARNING; - return ospf_redistribute_default_set (ospf, DEFAULT_ORIGINATE_ALWAYS, -1, -1); + return ospf_distribute_list_out_unset (ospf, source, argv[0]); } -DEFUN (ospf_default_information_originate_always_type_metric_routemap, - ospf_default_information_originate_always_type_metric_routemap_cmd, - "default-information originate always metric-type (1|2) metric <0-16777214> route-map WORD", +/* Default information originate. */ +DEFUN (ospf_default_information_originate, + ospf_default_information_originate_cmd, + "default-information originate " + "{always|metric <0-16777214>|metric-type (1|2)|route-map WORD}", "Control distribution of default information\n" "Distribute a default route\n" "Always advertise default route\n" + "OSPF default metric\n" + "OSPF metric\n" "OSPF metric type for default routes\n" "Set OSPF External Type 1 metrics\n" "Set OSPF External Type 2 metrics\n" - "OSPF default metric\n" - "OSPF metric\n" "Route map reference\n" "Pointer to route-map entries\n") { struct ospf *ospf = vty->index; + int default_originate = DEFAULT_ORIGINATE_ZEBRA; int type = -1; int metric = -1; - /* Get metric type. */ - if (argc >= 1) - if (!str2metric_type (argv[0], &type)) - return CMD_WARNING; + if (argc < 4) + return CMD_WARNING; /* this should not happen */ + + /* Check whether "always" was specified */ + if (argv[0] != NULL) + default_originate = DEFAULT_ORIGINATE_ALWAYS; /* Get metric value. */ - if (argc >= 2) + if (argv[1] != NULL) if (!str2metric (argv[1], &metric)) return CMD_WARNING; - if (argc == 3) - ospf_routemap_set (ospf, DEFAULT_ROUTE, argv[2]); - else - ospf_routemap_unset (ospf, DEFAULT_ROUTE); - - return ospf_redistribute_default_set (ospf, DEFAULT_ORIGINATE_ALWAYS, - type, metric); -} - -ALIAS (ospf_default_information_originate_always_type_metric_routemap, - ospf_default_information_originate_always_type_metric_cmd, - "default-information originate always metric-type (1|2) metric <0-16777214>", - "Control distribution of default information\n" - "Distribute a default route\n" - "Always advertise default route\n" - "OSPF metric type for default routes\n" - "Set OSPF External Type 1 metrics\n" - "Set OSPF External Type 2 metrics\n" - "OSPF default metric\n" - "OSPF metric\n") - -ALIAS (ospf_default_information_originate_always_type_metric_routemap, - ospf_default_information_originate_always_type_cmd, - "default-information originate always metric-type (1|2)", - "Control distribution of default information\n" - "Distribute a default route\n" - "Always advertise default route\n" - "OSPF metric type for default routes\n" - "Set OSPF External Type 1 metrics\n" - "Set OSPF External Type 2 metrics\n") - -DEFUN (ospf_default_information_originate_always_type_routemap, - ospf_default_information_originate_always_type_routemap_cmd, - "default-information originate always metric-type (1|2) route-map WORD", - "Control distribution of default information\n" - "Distribute a default route\n" - "Always advertise default route\n" - "OSPF metric type for default routes\n" - "Set OSPF External Type 1 metrics\n" - "Set OSPF External Type 2 metrics\n" - "Route map reference\n" - "Pointer to route-map entries\n") -{ - struct ospf *ospf = vty->index; - int type = -1; - /* Get metric type. */ - if (argc >= 1) - if (!str2metric_type (argv[0], &type)) + if (argv[2] != NULL) + if (!str2metric_type (argv[2], &type)) return CMD_WARNING; - if (argc == 2) - ospf_routemap_set (ospf, DEFAULT_ROUTE, argv[1]); + if (argv[3] != NULL) + ospf_routemap_set (ospf, DEFAULT_ROUTE, argv[3]); else ospf_routemap_unset (ospf, DEFAULT_ROUTE); - return ospf_redistribute_default_set (ospf, DEFAULT_ORIGINATE_ALWAYS, - type, -1); + return ospf_redistribute_default_set (ospf, default_originate, + type, metric); } DEFUN (no_ospf_default_information_originate, @@ -6552,296 +6027,73 @@ DEFUN (no_ospf_distance, DEFUN (no_ospf_distance_ospf, no_ospf_distance_ospf_cmd, - "no distance ospf", + "no distance ospf {intra-area|inter-area|external}", NO_STR "Define an administrative distance\n" "OSPF Administrative distance\n" - "OSPF Distance\n") -{ - struct ospf *ospf = vty->index; - - ospf->distance_intra = 0; - ospf->distance_inter = 0; - ospf->distance_external = 0; - - return CMD_SUCCESS; -} - -DEFUN (ospf_distance_ospf_intra, - ospf_distance_ospf_intra_cmd, - "distance ospf intra-area <1-255>", - "Define an administrative distance\n" - "OSPF Administrative distance\n" - "Intra-area routes\n" - "Distance for intra-area routes\n") -{ - struct ospf *ospf = vty->index; - - ospf->distance_intra = atoi (argv[0]); - - return CMD_SUCCESS; -} - -DEFUN (ospf_distance_ospf_intra_inter, - ospf_distance_ospf_intra_inter_cmd, - "distance ospf intra-area <1-255> inter-area <1-255>", - "Define an administrative distance\n" - "OSPF Administrative distance\n" - "Intra-area routes\n" - "Distance for intra-area routes\n" - "Inter-area routes\n" - "Distance for inter-area routes\n") -{ - struct ospf *ospf = vty->index; - - ospf->distance_intra = atoi (argv[0]); - ospf->distance_inter = atoi (argv[1]); - - return CMD_SUCCESS; -} - -DEFUN (ospf_distance_ospf_intra_external, - ospf_distance_ospf_intra_external_cmd, - "distance ospf intra-area <1-255> external <1-255>", - "Define an administrative distance\n" - "OSPF Administrative distance\n" - "Intra-area routes\n" - "Distance for intra-area routes\n" - "External routes\n" - "Distance for external routes\n") -{ - struct ospf *ospf = vty->index; - - ospf->distance_intra = atoi (argv[0]); - ospf->distance_external = atoi (argv[1]); - - return CMD_SUCCESS; -} - -DEFUN (ospf_distance_ospf_intra_inter_external, - ospf_distance_ospf_intra_inter_external_cmd, - "distance ospf intra-area <1-255> inter-area <1-255> external <1-255>", - "Define an administrative distance\n" - "OSPF Administrative distance\n" - "Intra-area routes\n" - "Distance for intra-area routes\n" - "Inter-area routes\n" - "Distance for inter-area routes\n" - "External routes\n" - "Distance for external routes\n") -{ - struct ospf *ospf = vty->index; - - ospf->distance_intra = atoi (argv[0]); - ospf->distance_inter = atoi (argv[1]); - ospf->distance_external = atoi (argv[2]); - - return CMD_SUCCESS; -} - -DEFUN (ospf_distance_ospf_intra_external_inter, - ospf_distance_ospf_intra_external_inter_cmd, - "distance ospf intra-area <1-255> external <1-255> inter-area <1-255>", - "Define an administrative distance\n" - "OSPF Administrative distance\n" + "OSPF Distance\n" "Intra-area routes\n" - "Distance for intra-area routes\n" - "External routes\n" - "Distance for external routes\n" - "Inter-area routes\n" - "Distance for inter-area routes\n") -{ - struct ospf *ospf = vty->index; - - ospf->distance_intra = atoi (argv[0]); - ospf->distance_external = atoi (argv[1]); - ospf->distance_inter = atoi (argv[2]); - - return CMD_SUCCESS; -} - -DEFUN (ospf_distance_ospf_inter, - ospf_distance_ospf_inter_cmd, - "distance ospf inter-area <1-255>", - "Define an administrative distance\n" - "OSPF Administrative distance\n" "Inter-area routes\n" - "Distance for inter-area routes\n") + "External routes\n") { struct ospf *ospf = vty->index; - ospf->distance_inter = atoi (argv[0]); - - return CMD_SUCCESS; -} + if (argc < 3) + return CMD_WARNING; -DEFUN (ospf_distance_ospf_inter_intra, - ospf_distance_ospf_inter_intra_cmd, - "distance ospf inter-area <1-255> intra-area <1-255>", - "Define an administrative distance\n" - "OSPF Administrative distance\n" - "Inter-area routes\n" - "Distance for inter-area routes\n" - "Intra-area routes\n" - "Distance for intra-area routes\n") -{ - struct ospf *ospf = vty->index; + if (argv[0] != NULL) + ospf->distance_intra = 0; - ospf->distance_inter = atoi (argv[0]); - ospf->distance_intra = atoi (argv[1]); + if (argv[1] != NULL) + ospf->distance_inter = 0; - return CMD_SUCCESS; -} + if (argv[2] != NULL) + ospf->distance_external = 0; -DEFUN (ospf_distance_ospf_inter_external, - ospf_distance_ospf_inter_external_cmd, - "distance ospf inter-area <1-255> external <1-255>", - "Define an administrative distance\n" - "OSPF Administrative distance\n" - "Inter-area routes\n" - "Distance for inter-area routes\n" - "External routes\n" - "Distance for external routes\n") -{ - struct ospf *ospf = vty->index; + if (argv[0] || argv[1] || argv[2]) + return CMD_SUCCESS; - ospf->distance_inter = atoi (argv[0]); - ospf->distance_external = atoi (argv[1]); + /* If no arguments are given, clear all distance information */ + ospf->distance_intra = 0; + ospf->distance_inter = 0; + ospf->distance_external = 0; return CMD_SUCCESS; } -DEFUN (ospf_distance_ospf_inter_intra_external, - ospf_distance_ospf_inter_intra_external_cmd, - "distance ospf inter-area <1-255> intra-area <1-255> external <1-255>", +DEFUN (ospf_distance_ospf, + ospf_distance_ospf_cmd, + "distance ospf " + "{intra-area <1-255>|inter-area <1-255>|external <1-255>}", "Define an administrative distance\n" "OSPF Administrative distance\n" - "Inter-area routes\n" - "Distance for inter-area routes\n" "Intra-area routes\n" "Distance for intra-area routes\n" - "External routes\n" - "Distance for external routes\n") -{ - struct ospf *ospf = vty->index; - - ospf->distance_inter = atoi (argv[0]); - ospf->distance_intra = atoi (argv[1]); - ospf->distance_external = atoi (argv[2]); - - return CMD_SUCCESS; -} - -DEFUN (ospf_distance_ospf_inter_external_intra, - ospf_distance_ospf_inter_external_intra_cmd, - "distance ospf inter-area <1-255> external <1-255> intra-area <1-255>", - "Define an administrative distance\n" - "OSPF Administrative distance\n" "Inter-area routes\n" "Distance for inter-area routes\n" "External routes\n" - "Distance for external routes\n" - "Intra-area routes\n" - "Distance for intra-area routes\n") -{ - struct ospf *ospf = vty->index; - - ospf->distance_inter = atoi (argv[0]); - ospf->distance_external = atoi (argv[1]); - ospf->distance_intra = atoi (argv[2]); - - return CMD_SUCCESS; -} - -DEFUN (ospf_distance_ospf_external, - ospf_distance_ospf_external_cmd, - "distance ospf external <1-255>", - "Define an administrative distance\n" - "OSPF Administrative distance\n" - "External routes\n" "Distance for external routes\n") { struct ospf *ospf = vty->index; - ospf->distance_external = atoi (argv[0]); - - return CMD_SUCCESS; -} - -DEFUN (ospf_distance_ospf_external_intra, - ospf_distance_ospf_external_intra_cmd, - "distance ospf external <1-255> intra-area <1-255>", - "Define an administrative distance\n" - "OSPF Administrative distance\n" - "External routes\n" - "Distance for external routes\n" - "Intra-area routes\n" - "Distance for intra-area routes\n") -{ - struct ospf *ospf = vty->index; - - ospf->distance_external = atoi (argv[0]); - ospf->distance_intra = atoi (argv[1]); - - return CMD_SUCCESS; -} - -DEFUN (ospf_distance_ospf_external_inter, - ospf_distance_ospf_external_inter_cmd, - "distance ospf external <1-255> inter-area <1-255>", - "Define an administrative distance\n" - "OSPF Administrative distance\n" - "External routes\n" - "Distance for external routes\n" - "Inter-area routes\n" - "Distance for inter-area routes\n") -{ - struct ospf *ospf = vty->index; - - ospf->distance_external = atoi (argv[0]); - ospf->distance_inter = atoi (argv[1]); - - return CMD_SUCCESS; -} - -DEFUN (ospf_distance_ospf_external_intra_inter, - ospf_distance_ospf_external_intra_inter_cmd, - "distance ospf external <1-255> intra-area <1-255> inter-area <1-255>", - "Define an administrative distance\n" - "OSPF Administrative distance\n" - "External routes\n" - "Distance for external routes\n" - "Intra-area routes\n" - "Distance for intra-area routes\n" - "Inter-area routes\n" - "Distance for inter-area routes\n") -{ - struct ospf *ospf = vty->index; + if (argc < 3) /* should not happen */ + return CMD_WARNING; - ospf->distance_external = atoi (argv[0]); - ospf->distance_intra = atoi (argv[1]); - ospf->distance_inter = atoi (argv[2]); + if (!argv[0] && !argv[1] && !argv[2]) + { + vty_out(vty, "%% Command incomplete. (Arguments required)%s", + VTY_NEWLINE); + return CMD_WARNING; + } - return CMD_SUCCESS; -} + if (argv[0] != NULL) + ospf->distance_intra = atoi(argv[0]); -DEFUN (ospf_distance_ospf_external_inter_intra, - ospf_distance_ospf_external_inter_intra_cmd, - "distance ospf external <1-255> inter-area <1-255> intra-area <1-255>", - "Define an administrative distance\n" - "OSPF Administrative distance\n" - "External routes\n" - "Distance for external routes\n" - "Inter-area routes\n" - "Distance for inter-area routes\n" - "Intra-area routes\n" - "Distance for intra-area routes\n") -{ - struct ospf *ospf = vty->index; + if (argv[1] != NULL) + ospf->distance_inter = atoi(argv[1]); - ospf->distance_external = atoi (argv[0]); - ospf->distance_inter = atoi (argv[1]); - ospf->distance_intra = atoi (argv[2]); + if (argv[2] != NULL) + ospf->distance_external = atoi(argv[2]); return CMD_SUCCESS; } @@ -8268,63 +7520,13 @@ ospf_vty_if_init (void) static void ospf_vty_zebra_init (void) { - install_element (OSPF_NODE, &ospf_redistribute_source_type_metric_cmd); - install_element (OSPF_NODE, &ospf_redistribute_source_metric_type_cmd); - install_element (OSPF_NODE, &ospf_redistribute_source_type_cmd); - install_element (OSPF_NODE, &ospf_redistribute_source_metric_cmd); install_element (OSPF_NODE, &ospf_redistribute_source_cmd); - install_element (OSPF_NODE, - &ospf_redistribute_source_metric_type_routemap_cmd); - install_element (OSPF_NODE, - &ospf_redistribute_source_type_metric_routemap_cmd); - install_element (OSPF_NODE, &ospf_redistribute_source_metric_routemap_cmd); - install_element (OSPF_NODE, &ospf_redistribute_source_type_routemap_cmd); - install_element (OSPF_NODE, &ospf_redistribute_source_routemap_cmd); - install_element (OSPF_NODE, &no_ospf_redistribute_source_cmd); install_element (OSPF_NODE, &ospf_distribute_list_out_cmd); install_element (OSPF_NODE, &no_ospf_distribute_list_out_cmd); - install_element (OSPF_NODE, - &ospf_default_information_originate_metric_type_cmd); - install_element (OSPF_NODE, &ospf_default_information_originate_metric_cmd); - install_element (OSPF_NODE, - &ospf_default_information_originate_type_metric_cmd); - install_element (OSPF_NODE, &ospf_default_information_originate_type_cmd); install_element (OSPF_NODE, &ospf_default_information_originate_cmd); - install_element (OSPF_NODE, - &ospf_default_information_originate_always_metric_type_cmd); - install_element (OSPF_NODE, - &ospf_default_information_originate_always_metric_cmd); - install_element (OSPF_NODE, - &ospf_default_information_originate_always_cmd); - install_element (OSPF_NODE, - &ospf_default_information_originate_always_type_metric_cmd); - install_element (OSPF_NODE, - &ospf_default_information_originate_always_type_cmd); - - install_element (OSPF_NODE, - &ospf_default_information_originate_metric_type_routemap_cmd); - install_element (OSPF_NODE, - &ospf_default_information_originate_metric_routemap_cmd); - install_element (OSPF_NODE, - &ospf_default_information_originate_routemap_cmd); - install_element (OSPF_NODE, - &ospf_default_information_originate_type_metric_routemap_cmd); - install_element (OSPF_NODE, - &ospf_default_information_originate_type_routemap_cmd); - install_element (OSPF_NODE, - &ospf_default_information_originate_always_metric_type_routemap_cmd); - install_element (OSPF_NODE, - &ospf_default_information_originate_always_metric_routemap_cmd); - install_element (OSPF_NODE, - &ospf_default_information_originate_always_routemap_cmd); - install_element (OSPF_NODE, - &ospf_default_information_originate_always_type_metric_routemap_cmd); - install_element (OSPF_NODE, - &ospf_default_information_originate_always_type_routemap_cmd); - install_element (OSPF_NODE, &no_ospf_default_information_originate_cmd); install_element (OSPF_NODE, &ospf_default_metric_cmd); @@ -8334,21 +7536,7 @@ ospf_vty_zebra_init (void) install_element (OSPF_NODE, &ospf_distance_cmd); install_element (OSPF_NODE, &no_ospf_distance_cmd); install_element (OSPF_NODE, &no_ospf_distance_ospf_cmd); - install_element (OSPF_NODE, &ospf_distance_ospf_intra_cmd); - install_element (OSPF_NODE, &ospf_distance_ospf_intra_inter_cmd); - install_element (OSPF_NODE, &ospf_distance_ospf_intra_external_cmd); - install_element (OSPF_NODE, &ospf_distance_ospf_intra_inter_external_cmd); - install_element (OSPF_NODE, &ospf_distance_ospf_intra_external_inter_cmd); - install_element (OSPF_NODE, &ospf_distance_ospf_inter_cmd); - install_element (OSPF_NODE, &ospf_distance_ospf_inter_intra_cmd); - install_element (OSPF_NODE, &ospf_distance_ospf_inter_external_cmd); - install_element (OSPF_NODE, &ospf_distance_ospf_inter_intra_external_cmd); - install_element (OSPF_NODE, &ospf_distance_ospf_inter_external_intra_cmd); - install_element (OSPF_NODE, &ospf_distance_ospf_external_cmd); - install_element (OSPF_NODE, &ospf_distance_ospf_external_intra_cmd); - install_element (OSPF_NODE, &ospf_distance_ospf_external_inter_cmd); - install_element (OSPF_NODE, &ospf_distance_ospf_external_intra_inter_cmd); - install_element (OSPF_NODE, &ospf_distance_ospf_external_inter_intra_cmd); + install_element (OSPF_NODE, &ospf_distance_ospf_cmd); #if 0 install_element (OSPF_NODE, &ospf_distance_source_cmd); install_element (OSPF_NODE, &no_ospf_distance_source_cmd); diff --git a/tests/testcommands.in b/tests/testcommands.in index 70c57052..7fe62799 100644 --- a/tests/testcommands.in +++ b/tests/testcommands.in @@ -199,3 +199,18 @@ sow ip bgp ipv4 mulicast community no-export no-adertise no-export no-advertise sow ipv6 ospf6 databIase as-external adv-router 1.2.3.4 Whow bgp view VARIAeBLE ipv4 unicast community local-AS no-advrtise no-advertise local-AS Wneighbor 1.2.3.4 dot-capabiliy-negotiate +# +# +# Some teststrings explicitly used for keyword commands +# +# +redistribute bgp +redistribute bgp m 10 +redistribute bgp metric 10 metric-type 1 +redistribute bgp metric 10 metric 10 +redistribute bgp route-map RMAP_REDIST_BGP +default-information originate metric-type 1 metric 10 +default-information originate always metric-type 1 metric 10 +default-information originate route-map RMAP_DEFAULT +default-information originate route-map RMAP_DEFAULT metric 10 +default-information originate always metric-type 2 metric 23 diff --git a/tests/testcommands.refout b/tests/testcommands.refout index 1422aefc..11483b84 100644 --- a/tests/testcommands.refout +++ b/tests/testcommands.refout @@ -299,6 +299,18 @@ complete 'no neighbor VARIABLE maximum-prefix'@22: rv==7 'maximum-prefix' describe 'no neighbor VARIABLE maximum-prefix'@22: rv==0 'maximum-prefix' 'Maximum number of prefix accept from this peer' +execute relaxed 'redistribute isis route-map VARIABLE metric 0 metric-type 2'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp|babel) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'isis', '0', '2', 'VARIABLE' +execute strict 'redistribute isis route-map VARIABLE metric 0 metric-type 2'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp|babel) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'isis', '0', '2', 'VARIABLE' +complete 'redistribute isis route-map VARIABLE metric 0 metric-type 2'@23: rv==7 + '2' +describe 'redistribute isis route-map VARIABLE metric 0 metric-type 2'@23: rv==0 + '2' 'Set OSPF External Type 2 metrics' +execute relaxed 'redistribute rip metric 0 route-map VARIABLE metric-type 1'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp|babel) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'rip', '0', '1', 'VARIABLE' +execute strict 'redistribute rip metric 0 route-map VARIABLE metric-type 1'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp|babel) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'rip', '0', '1', 'VARIABLE' +complete 'redistribute rip metric 0 route-map VARIABLE metric-type 1'@23: rv==7 + '1' +describe 'redistribute rip metric 0 route-map VARIABLE metric-type 1'@23: rv==0 + '1' 'Set OSPF External Type 1 metrics' execute relaxed 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@1: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' execute strict 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@1: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' complete 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@1: rv==7 @@ -898,3 +910,98 @@ execute strict 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@4: rv==0, 'sh complete 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@4: rv==2 describe 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@4: rv==0 'AA:NN' 'community number' +execute relaxed 'redistribute bgp'@14: rv==0, 'redistribute (kernel|connected|static|ospf|isis|bgp|babel)': 'bgp' +execute strict 'redistribute bgp'@14: rv==0, 'redistribute (kernel|connected|static|ospf|isis|bgp|babel)': 'bgp' +complete 'redistribute bgp'@14: rv==7 + 'bgp' +describe 'redistribute bgp'@14: rv==0 + 'bgp' 'Border Gateway Protocol (BGP)' +execute relaxed 'redistribute bgp'@15: rv==0, 'redistribute (kernel|connected|static|ospf6|isis|bgp|babel)': 'bgp' +execute strict 'redistribute bgp'@15: rv==0, 'redistribute (kernel|connected|static|ospf6|isis|bgp|babel)': 'bgp' +complete 'redistribute bgp'@15: rv==7 + 'bgp' +describe 'redistribute bgp'@15: rv==0 + 'bgp' 'Border Gateway Protocol (BGP)' +execute relaxed 'redistribute bgp'@16: rv==0, 'redistribute (kernel|connected|static|rip|ripng|ospf|ospf6|isis|bgp)': 'bgp' +execute strict 'redistribute bgp'@16: rv==0, 'redistribute (kernel|connected|static|rip|ripng|ospf|ospf6|isis|bgp)': 'bgp' +complete 'redistribute bgp'@16: rv==7 + 'bgp' +describe 'redistribute bgp'@16: rv==0 + 'bgp' 'Border Gateway Protocol (BGP)' +execute relaxed 'redistribute bgp'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp|babel) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'bgp', '(null)', '(null)', '(null)' +execute strict 'redistribute bgp'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp|babel) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'bgp', '(null)', '(null)', '(null)' +complete 'redistribute bgp'@23: rv==7 + 'bgp' +describe 'redistribute bgp'@23: rv==0 + 'bgp' 'Border Gateway Protocol (BGP)' +execute relaxed 'redistribute bgp'@24: rv==0, 'redistribute (kernel|connected|static|ripng|isis|bgp|babel)': 'bgp' +execute strict 'redistribute bgp'@24: rv==0, 'redistribute (kernel|connected|static|ripng|isis|bgp|babel)': 'bgp' +complete 'redistribute bgp'@24: rv==7 + 'bgp' +describe 'redistribute bgp'@24: rv==0 + 'bgp' 'Border Gateway Protocol (BGP)' +execute relaxed 'redistribute bgp m 10'@14: rv==0, 'redistribute (kernel|connected|static|ospf|isis|bgp|babel) metric <0-16>': 'bgp', '10' +execute strict 'redistribute bgp m 10'@14: rv==2 +complete 'redistribute bgp m 10'@14: rv==2 +describe 'redistribute bgp m 10'@14: rv==0 + '<0-16>' 'Metric value' +execute relaxed 'redistribute bgp m 10'@15: rv==0, 'redistribute (kernel|connected|static|ospf6|isis|bgp|babel) metric <0-16>': 'bgp', '10' +execute strict 'redistribute bgp m 10'@15: rv==2 +complete 'redistribute bgp m 10'@15: rv==2 +describe 'redistribute bgp m 10'@15: rv==0 + '<0-16>' 'Metric value' +execute relaxed 'redistribute bgp m 10'@23: rv==3 +execute strict 'redistribute bgp m 10'@23: rv==2 +complete 'redistribute bgp m 10'@23: rv==3 +describe 'redistribute bgp m 10'@23: rv==3 +execute relaxed 'redistribute bgp metric 10 metric-type 1'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp|babel) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'bgp', '10', '1', '(null)' +execute strict 'redistribute bgp metric 10 metric-type 1'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp|babel) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'bgp', '10', '1', '(null)' +complete 'redistribute bgp metric 10 metric-type 1'@23: rv==7 + '1' +describe 'redistribute bgp metric 10 metric-type 1'@23: rv==0 + '1' 'Set OSPF External Type 1 metrics' +execute relaxed 'redistribute bgp route-map RMAP_REDIST_BGP'@14: rv==0, 'redistribute (kernel|connected|static|ospf|isis|bgp|babel) route-map WORD': 'bgp', 'RMAP_REDIST_BGP' +execute strict 'redistribute bgp route-map RMAP_REDIST_BGP'@14: rv==0, 'redistribute (kernel|connected|static|ospf|isis|bgp|babel) route-map WORD': 'bgp', 'RMAP_REDIST_BGP' +complete 'redistribute bgp route-map RMAP_REDIST_BGP'@14: rv==2 +describe 'redistribute bgp route-map RMAP_REDIST_BGP'@14: rv==0 + 'WORD' 'Pointer to route-map entries' +execute relaxed 'redistribute bgp route-map RMAP_REDIST_BGP'@15: rv==0, 'redistribute (kernel|connected|static|ospf6|isis|bgp|babel) route-map WORD': 'bgp', 'RMAP_REDIST_BGP' +execute strict 'redistribute bgp route-map RMAP_REDIST_BGP'@15: rv==0, 'redistribute (kernel|connected|static|ospf6|isis|bgp|babel) route-map WORD': 'bgp', 'RMAP_REDIST_BGP' +complete 'redistribute bgp route-map RMAP_REDIST_BGP'@15: rv==2 +describe 'redistribute bgp route-map RMAP_REDIST_BGP'@15: rv==0 + 'WORD' 'Pointer to route-map entries' +execute relaxed 'redistribute bgp route-map RMAP_REDIST_BGP'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp|babel) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'bgp', '(null)', '(null)', 'RMAP_REDIST_BGP' +execute strict 'redistribute bgp route-map RMAP_REDIST_BGP'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp|babel) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'bgp', '(null)', '(null)', 'RMAP_REDIST_BGP' +complete 'redistribute bgp route-map RMAP_REDIST_BGP'@23: rv==2 +describe 'redistribute bgp route-map RMAP_REDIST_BGP'@23: rv==0 + 'WORD' 'Pointer to route-map entries' +execute relaxed 'redistribute bgp route-map RMAP_REDIST_BGP'@24: rv==0, 'redistribute (kernel|connected|static|ripng|isis|bgp|babel) route-map WORD': 'bgp', 'RMAP_REDIST_BGP' +execute strict 'redistribute bgp route-map RMAP_REDIST_BGP'@24: rv==0, 'redistribute (kernel|connected|static|ripng|isis|bgp|babel) route-map WORD': 'bgp', 'RMAP_REDIST_BGP' +complete 'redistribute bgp route-map RMAP_REDIST_BGP'@24: rv==2 +describe 'redistribute bgp route-map RMAP_REDIST_BGP'@24: rv==0 + 'WORD' 'Route map name' +execute relaxed 'default-information originate metric-type 1 metric 10'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': '(null)', '10', '1', '(null)' +execute strict 'default-information originate metric-type 1 metric 10'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': '(null)', '10', '1', '(null)' +complete 'default-information originate metric-type 1 metric 10'@23: rv==2 +describe 'default-information originate metric-type 1 metric 10'@23: rv==0 + '<0-16777214>' 'OSPF metric' +execute relaxed 'default-information originate always metric-type 1 metric 10'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'always', '10', '1', '(null)' +execute strict 'default-information originate always metric-type 1 metric 10'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'always', '10', '1', '(null)' +complete 'default-information originate always metric-type 1 metric 10'@23: rv==2 +describe 'default-information originate always metric-type 1 metric 10'@23: rv==0 + '<0-16777214>' 'OSPF metric' +execute relaxed 'default-information originate route-map RMAP_DEFAULT'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': '(null)', '(null)', '(null)', 'RMAP_DEFAULT' +execute strict 'default-information originate route-map RMAP_DEFAULT'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': '(null)', '(null)', '(null)', 'RMAP_DEFAULT' +complete 'default-information originate route-map RMAP_DEFAULT'@23: rv==2 +describe 'default-information originate route-map RMAP_DEFAULT'@23: rv==0 + 'WORD' 'Pointer to route-map entries' +execute relaxed 'default-information originate route-map RMAP_DEFAULT metric 10'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': '(null)', '10', '(null)', 'RMAP_DEFAULT' +execute strict 'default-information originate route-map RMAP_DEFAULT metric 10'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': '(null)', '10', '(null)', 'RMAP_DEFAULT' +complete 'default-information originate route-map RMAP_DEFAULT metric 10'@23: rv==2 +describe 'default-information originate route-map RMAP_DEFAULT metric 10'@23: rv==0 + '<0-16777214>' 'OSPF metric' +execute relaxed 'default-information originate always metric-type 2 metric 23'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'always', '23', '2', '(null)' +execute strict 'default-information originate always metric-type 2 metric 23'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'always', '23', '2', '(null)' +complete 'default-information originate always metric-type 2 metric 23'@23: rv==2 +describe 'default-information originate always metric-type 2 metric 23'@23: rv==0 + '<0-16777214>' 'OSPF metric' From ba32db1e854ff2b26861a2d4e4193a9f1b3816cd Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Tue, 19 Nov 2013 14:11:40 +0000 Subject: [PATCH 185/482] tests: Add tests for timers Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- tests/.gitignore | 2 + tests/Makefile.am | 6 +- tests/libzebra.tests/Makefile.am | 1 + .../libzebra.tests/test-timer-correctness.exp | 7 + tests/test-timer-correctness.c | 194 ++++++++++++++++++ tests/test-timer-performance.c | 104 ++++++++++ 6 files changed, 313 insertions(+), 1 deletion(-) create mode 100644 tests/libzebra.tests/test-timer-correctness.exp create mode 100644 tests/test-timer-correctness.c create mode 100644 tests/test-timer-performance.c diff --git a/tests/.gitignore b/tests/.gitignore index 0ace3656..1cb28c9f 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -22,6 +22,8 @@ heavy heavythread heavywq tabletest +test-timer-correctness +test-timer-performance testbgpcap testbgpmpath testbgpmpattr diff --git a/tests/Makefile.am b/tests/Makefile.am index 8707fe7d..8a086d0b 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -28,7 +28,7 @@ endif check_PROGRAMS = testsig testsegv testbuffer testmemory heavy heavywq heavythread \ testprivs teststream testchecksum tabletest testnexthopiter \ - testcommands \ + testcommands test-timer-correctness test-timer-performance \ $(TESTS_BGPD) ../vtysh/vtysh_cmd.c: @@ -63,6 +63,8 @@ testbgpmpath_SOURCES = bgp_mpath_test.c tabletest_SOURCES = table_test.c testnexthopiter_SOURCES = test-nexthop-iter.c prng.c testcommands_SOURCES = test-commands-defun.c test-commands.c prng.c +test_timer_correctness_SOURCES = test-timer-correctness.c prng.c +test_timer_performance_SOURCES = test-timer-performance.c prng.c testsig_LDADD = ../lib/libzebra.la @LIBCAP@ testsegv_LDADD = ../lib/libzebra.la @LIBCAP@ @@ -82,3 +84,5 @@ testbgpmpath_LDADD = ../bgpd/libbgp.a ../lib/libzebra.la @LIBCAP@ -lm tabletest_LDADD = ../lib/libzebra.la @LIBCAP@ -lm testnexthopiter_LDADD = ../lib/libzebra.la @LIBCAP@ testcommands_LDADD = ../lib/libzebra.la @LIBCAP@ +test_timer_correctness_LDADD = ../lib/libzebra.la @LIBCAP@ +test_timer_performance_LDADD = ../lib/libzebra.la @LIBCAP@ diff --git a/tests/libzebra.tests/Makefile.am b/tests/libzebra.tests/Makefile.am index a1ffea3d..819cce2f 100644 --- a/tests/libzebra.tests/Makefile.am +++ b/tests/libzebra.tests/Makefile.am @@ -1,4 +1,5 @@ EXTRA_DIST = \ tabletest.exp \ + test-timer-correctness.exp \ testcommands.exp \ testnexthopiter.exp diff --git a/tests/libzebra.tests/test-timer-correctness.exp b/tests/libzebra.tests/test-timer-correctness.exp new file mode 100644 index 00000000..83531c7d --- /dev/null +++ b/tests/libzebra.tests/test-timer-correctness.exp @@ -0,0 +1,7 @@ +set timeout 10 +set testprefix "test-timer-correctness" +set aborted 0 + +spawn "./test-timer-correctness" + +onesimple "" "Expected output and actual output match." diff --git a/tests/test-timer-correctness.c b/tests/test-timer-correctness.c new file mode 100644 index 00000000..94c8f1df --- /dev/null +++ b/tests/test-timer-correctness.c @@ -0,0 +1,194 @@ +/* + * Test program to verify that scheduled timers are executed in the + * correct order. + * + * Copyright (C) 2013 by Open Source Routing. + * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC") + * + * This file is part of Quagga + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include +#include + +#include + +#include "memory.h" +#include "pqueue.h" +#include "prng.h" +#include "thread.h" + +#define SCHEDULE_TIMERS 800 +#define REMOVE_TIMERS 200 + +#define TIMESTR_LEN strlen("4294967296.999999") + +struct thread_master *master; + +static size_t log_buf_len; +static size_t log_buf_pos; +static char *log_buf; + +static size_t expected_buf_len; +static size_t expected_buf_pos; +static char *expected_buf; + +static struct prng *prng; + +static struct thread **timers; + +static int timers_pending; + +static void terminate_test(void) +{ + int exit_code; + + if (strcmp(log_buf, expected_buf)) + { + fprintf(stderr, "Expected output and received output differ.\n"); + fprintf(stderr, "---Expected output: ---\n%s", expected_buf); + fprintf(stderr, "---Actual output: ---\n%s", log_buf); + exit_code = 1; + } + else + { + printf("Expected output and actual output match.\n"); + exit_code = 0; + } + + thread_master_free(master); + XFREE(MTYPE_TMP, log_buf); + XFREE(MTYPE_TMP, expected_buf); + prng_free(prng); + XFREE(MTYPE_TMP, timers); + + exit(exit_code); +} + +static int timer_func(struct thread *thread) +{ + int rv; + + rv = snprintf(log_buf + log_buf_pos, log_buf_len - log_buf_pos, + "%s\n", (char*)thread->arg); + assert(rv >= 0); + log_buf_pos += rv; + assert(log_buf_pos < log_buf_len); + XFREE(MTYPE_TMP, thread->arg); + + timers_pending--; + if (!timers_pending) + terminate_test(); + + return 0; +} + +static int cmp_timeval(const void* a, const void *b) +{ + const struct timeval *ta = *(struct timeval * const *)a; + const struct timeval *tb = *(struct timeval * const *)b; + + if (timercmp(ta, tb, <)) + return -1; + if (timercmp(ta, tb, >)) + return 1; + return 0; +} + +int main(int argc, char **argv) +{ + int i, j; + struct thread t; + struct timeval **alarms; + + master = thread_master_create(); + + log_buf_len = SCHEDULE_TIMERS * (TIMESTR_LEN + 1) + 1; + log_buf_pos = 0; + log_buf = XMALLOC(MTYPE_TMP, log_buf_len); + + expected_buf_len = SCHEDULE_TIMERS * (TIMESTR_LEN + 1) + 1; + expected_buf_pos = 0; + expected_buf = XMALLOC(MTYPE_TMP, expected_buf_len); + + prng = prng_new(0); + + timers = XMALLOC(MTYPE_TMP, SCHEDULE_TIMERS * sizeof(*timers)); + + for (i = 0; i < SCHEDULE_TIMERS; i++) + { + long interval_msec; + int ret; + char *arg; + + /* Schedule timers to expire in 0..5 seconds */ + interval_msec = prng_rand(prng) % 5000; + arg = XMALLOC(MTYPE_TMP, TIMESTR_LEN + 1); + timers[i] = thread_add_timer_msec(master, timer_func, arg, interval_msec); + ret = snprintf(arg, TIMESTR_LEN + 1, "%ld.%06ld", + timers[i]->u.sands.tv_sec, timers[i]->u.sands.tv_usec); + assert(ret > 0); + assert((size_t)ret < TIMESTR_LEN + 1); + timers_pending++; + } + + for (i = 0; i < REMOVE_TIMERS; i++) + { + int index; + + index = prng_rand(prng) % SCHEDULE_TIMERS; + if (!timers[index]) + continue; + + XFREE(MTYPE_TMP, timers[index]->arg); + thread_cancel(timers[index]); + timers[index] = NULL; + timers_pending--; + } + + /* We create an array of pointers to the alarm times and sort + * that array. That sorted array is used to generate a string + * representing the expected "output" of the timers when they + * are run. */ + j = 0; + alarms = XMALLOC(MTYPE_TMP, timers_pending * sizeof(*alarms)); + for (i = 0; i < SCHEDULE_TIMERS; i++) + { + if (!timers[i]) + continue; + alarms[j++] = &timers[i]->u.sands; + } + qsort(alarms, j, sizeof(*alarms), cmp_timeval); + for (i = 0; i < j; i++) + { + int ret; + + ret = snprintf(expected_buf + expected_buf_pos, + expected_buf_len - expected_buf_pos, + "%ld.%06ld\n", alarms[i]->tv_sec, alarms[i]->tv_usec); + assert(ret > 0); + expected_buf_pos += ret; + assert(expected_buf_pos < expected_buf_len); + } + XFREE(MTYPE_TMP, alarms); + + while (thread_fetch(master, &t)) + thread_call(&t); + + return 0; +} diff --git a/tests/test-timer-performance.c b/tests/test-timer-performance.c new file mode 100644 index 00000000..a529a5ce --- /dev/null +++ b/tests/test-timer-performance.c @@ -0,0 +1,104 @@ +/* + * Test program which measures the time it takes to schedule and + * remove timers. + * + * Copyright (C) 2013 by Open Source Routing. + * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC") + * + * This file is part of Quagga + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#include +#include + +#include + +#include "thread.h" +#include "pqueue.h" +#include "prng.h" + +#define SCHEDULE_TIMERS 1000000 +#define REMOVE_TIMERS 500000 + +struct thread_master *master; + +static int dummy_func(struct thread *thread) +{ + return 0; +} + +int main(int argc, char **argv) +{ + struct prng *prng; + int i; + struct thread **timers; + struct timeval tv_start, tv_lap, tv_stop; + unsigned long t_schedule, t_remove; + + master = thread_master_create(); + prng = prng_new(0); + timers = calloc(SCHEDULE_TIMERS, sizeof(*timers)); + + /* create thread structures so they won't be allocated during the + * time measurement */ + for (i = 0; i < SCHEDULE_TIMERS; i++) + timers[i] = thread_add_timer_msec(master, dummy_func, NULL, 0); + for (i = 0; i < SCHEDULE_TIMERS; i++) + thread_cancel(timers[i]); + + quagga_gettime(QUAGGA_CLK_MONOTONIC, &tv_start); + + for (i = 0; i < SCHEDULE_TIMERS; i++) + { + long interval_msec; + + interval_msec = prng_rand(prng) % (100 * SCHEDULE_TIMERS); + timers[i] = thread_add_timer_msec(master, dummy_func, + NULL, interval_msec); + } + + quagga_gettime(QUAGGA_CLK_MONOTONIC, &tv_lap); + + for (i = 0; i < REMOVE_TIMERS; i++) + { + int index; + + index = prng_rand(prng) % SCHEDULE_TIMERS; + if (timers[index]) + thread_cancel(timers[index]); + timers[index] = NULL; + } + + quagga_gettime(QUAGGA_CLK_MONOTONIC, &tv_stop); + + t_schedule = 1000 * (tv_lap.tv_sec - tv_start.tv_sec); + t_schedule += (tv_lap.tv_usec - tv_start.tv_usec) / 1000; + + t_remove = 1000 * (tv_stop.tv_sec - tv_lap.tv_sec); + t_remove += (tv_stop.tv_usec - tv_lap.tv_usec) / 1000; + + printf("Scheduling %d random timers took %ld.%03ld seconds.\n", + SCHEDULE_TIMERS, t_schedule/1000, t_schedule%1000); + printf("Removing %d random timers took %ld.%03ld seconds.\n", + REMOVE_TIMERS, t_remove/1000, t_remove%1000); + fflush(stdout); + + free(timers); + thread_master_free(master); + prng_free(prng); + return 0; +} From 514991c76366c2a9f4c644714ad0722df0eed532 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Tue, 19 Nov 2013 14:11:41 +0000 Subject: [PATCH 186/482] lib: remove unused thread_master_debug function Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- lib/thread.c | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/lib/thread.c b/lib/thread.c index 27c29d6c..ddb95c01 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -496,35 +496,6 @@ DEFUN(clear_thread_cpu, return CMD_SUCCESS; } -/* List allocation and head/tail print out. */ -static void -thread_list_debug (struct thread_list *list) -{ - printf ("count [%d] head [%p] tail [%p]\n", - list->count, list->head, list->tail); -} - -/* Debug print for thread_master. */ -static void __attribute__ ((unused)) -thread_master_debug (struct thread_master *m) -{ - printf ("-----------\n"); - printf ("readlist : "); - thread_list_debug (&m->read); - printf ("writelist : "); - thread_list_debug (&m->write); - printf ("timerlist : "); - thread_list_debug (&m->timer); - printf ("eventlist : "); - thread_list_debug (&m->event); - printf ("unuselist : "); - thread_list_debug (&m->unuse); - printf ("bgndlist : "); - thread_list_debug (&m->background); - printf ("total alloc: [%ld]\n", m->alloc); - printf ("-----------\n"); -} - /* Allocate new thread master. */ struct thread_master * thread_master_create () From 4becea724ccd87e88f8454622ae227308b5fa3ce Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Tue, 19 Nov 2013 14:11:42 +0000 Subject: [PATCH 187/482] lib: use heap to manage timers Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- lib/pqueue.c | 17 +++++++ lib/pqueue.h | 1 + lib/thread.c | 138 ++++++++++++++++++++++++++++++++------------------- lib/thread.h | 7 ++- 4 files changed, 110 insertions(+), 53 deletions(-) diff --git a/lib/pqueue.c b/lib/pqueue.c index 12a779f2..69ab8e65 100644 --- a/lib/pqueue.c +++ b/lib/pqueue.c @@ -168,3 +168,20 @@ pqueue_dequeue (struct pqueue *queue) trickle_down (0, queue); return data; } + +void +pqueue_remove_at (int index, struct pqueue *queue) +{ + queue->array[index] = queue->array[--queue->size]; + + if (index > 0 + && (*queue->cmp) (queue->array[index], + queue->array[PARENT_OF(index)]) < 0) + { + trickle_up (index, queue); + } + else + { + trickle_down (index, queue); + } +} diff --git a/lib/pqueue.h b/lib/pqueue.h index be37f98d..8bb6961d 100644 --- a/lib/pqueue.h +++ b/lib/pqueue.h @@ -38,6 +38,7 @@ extern void pqueue_delete (struct pqueue *queue); extern void pqueue_enqueue (void *data, struct pqueue *queue); extern void *pqueue_dequeue (struct pqueue *queue); +extern void pqueue_remove_at (int index, struct pqueue *queue); extern void trickle_down (int index, struct pqueue *queue); extern void trickle_up (int index, struct pqueue *queue); diff --git a/lib/thread.c b/lib/thread.c index ddb95c01..e2a37b14 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -27,6 +27,7 @@ #include "memory.h" #include "log.h" #include "hash.h" +#include "pqueue.h" #include "command.h" #include "sigevent.h" @@ -496,17 +497,49 @@ DEFUN(clear_thread_cpu, return CMD_SUCCESS; } +static int +thread_timer_cmp(void *a, void *b) +{ + struct thread *thread_a = a; + struct thread *thread_b = b; + + long cmp = timeval_cmp(thread_a->u.sands, thread_b->u.sands); + + if (cmp < 0) + return -1; + if (cmp > 0) + return 1; + return 0; +} + +static void +thread_timer_update(void *node, int actual_position) +{ + struct thread *thread = node; + + thread->index = actual_position; +} + /* Allocate new thread master. */ struct thread_master * thread_master_create () { + struct thread_master *rv; + if (cpu_record == NULL) cpu_record = hash_create ((unsigned int (*) (void *))cpu_record_hash_key, (int (*) (const void *, const void *))cpu_record_hash_cmp); - - return (struct thread_master *) XCALLOC (MTYPE_THREAD_MASTER, - sizeof (struct thread_master)); + + rv = XCALLOC (MTYPE_THREAD_MASTER, sizeof (struct thread_master)); + + /* Initialize the timer queues */ + rv->timer = pqueue_create(); + rv->background = pqueue_create(); + rv->timer->cmp = rv->background->cmp = thread_timer_cmp; + rv->timer->update = rv->background->update = thread_timer_update; + + return rv; } /* Add a new thread to the list. */ @@ -523,22 +556,6 @@ thread_list_add (struct thread_list *list, struct thread *thread) list->count++; } -/* Add a new thread just before the point. */ -static void -thread_list_add_before (struct thread_list *list, - struct thread *point, - struct thread *thread) -{ - thread->next = point; - thread->prev = point->prev; - if (point->prev) - point->prev->next = thread; - else - list->head = thread; - point->prev = thread; - list->count++; -} - /* Delete a thread from the list. */ static struct thread * thread_list_delete (struct thread_list *list, struct thread *thread) @@ -584,17 +601,29 @@ thread_list_free (struct thread_master *m, struct thread_list *list) } } +static void +thread_queue_free (struct thread_master *m, struct pqueue *queue) +{ + int i; + + for (i = 0; i < queue->size; i++) + XFREE(MTYPE_THREAD, queue->array[i]); + + m->alloc -= queue->size; + pqueue_delete(queue); +} + /* Stop thread scheduler. */ void thread_master_free (struct thread_master *m) { thread_list_free (m, &m->read); thread_list_free (m, &m->write); - thread_list_free (m, &m->timer); + thread_queue_free (m, m->timer); thread_list_free (m, &m->event); thread_list_free (m, &m->ready); thread_list_free (m, &m->unuse); - thread_list_free (m, &m->background); + thread_queue_free (m, m->background); XFREE (MTYPE_THREAD_MASTER, m); @@ -676,7 +705,8 @@ thread_get (struct thread_master *m, u_char type, thread->master = m; thread->func = func; thread->arg = arg; - + thread->index = -1; + strip_funcname (thread->funcname, funcname); return thread; @@ -737,16 +767,15 @@ funcname_thread_add_timer_timeval (struct thread_master *m, const char* funcname) { struct thread *thread; - struct thread_list *list; + struct pqueue *queue; struct timeval alarm_time; - struct thread *tt; assert (m != NULL); assert (type == THREAD_TIMER || type == THREAD_BACKGROUND); assert (time_relative); - list = ((type == THREAD_TIMER) ? &m->timer : &m->background); + queue = ((type == THREAD_TIMER) ? m->timer : m->background); thread = thread_get (m, type, func, arg, funcname); /* Do we need jitter here? */ @@ -755,16 +784,7 @@ funcname_thread_add_timer_timeval (struct thread_master *m, alarm_time.tv_usec = relative_time.tv_usec + time_relative->tv_usec; thread->u.sands = timeval_adjust(alarm_time); - /* Sort by timeval. */ - for (tt = list->head; tt; tt = tt->next) - if (timeval_cmp (thread->u.sands, tt->u.sands) <= 0) - break; - - if (tt) - thread_list_add_before (list, tt, thread); - else - thread_list_add (list, thread); - + pqueue_enqueue(thread, queue); return thread; } @@ -849,7 +869,8 @@ funcname_thread_add_event (struct thread_master *m, void thread_cancel (struct thread *thread) { - struct thread_list *list; + struct thread_list *list = NULL; + struct pqueue *queue = NULL; switch (thread->type) { @@ -864,7 +885,7 @@ thread_cancel (struct thread *thread) list = &thread->master->write; break; case THREAD_TIMER: - list = &thread->master->timer; + queue = thread->master->timer; break; case THREAD_EVENT: list = &thread->master->event; @@ -873,13 +894,28 @@ thread_cancel (struct thread *thread) list = &thread->master->ready; break; case THREAD_BACKGROUND: - list = &thread->master->background; + queue = thread->master->background; break; default: return; break; } - thread_list_delete (list, thread); + + if (queue) + { + assert(thread->index >= 0); + assert(thread == queue->array[thread->index]); + pqueue_remove_at(thread->index, queue); + } + else if (list) + { + thread_list_delete (list, thread); + } + else + { + assert(!"Thread should be either in queue or list!"); + } + thread->type = THREAD_UNUSED; thread_add_unuse (thread->master, thread); } @@ -929,11 +965,12 @@ thread_cancel_event (struct thread_master *m, void *arg) } static struct timeval * -thread_timer_wait (struct thread_list *tlist, struct timeval *timer_val) +thread_timer_wait (struct pqueue *queue, struct timeval *timer_val) { - if (!thread_empty (tlist)) + if (queue->size) { - *timer_val = timeval_subtract (tlist->head->u.sands, relative_time); + struct thread *next_timer = queue->array[0]; + *timer_val = timeval_subtract (next_timer->u.sands, relative_time); return timer_val; } return NULL; @@ -977,18 +1014,17 @@ thread_process_fd (struct thread_list *list, fd_set *fdset, fd_set *mfdset) /* Add all timers that have popped to the ready list. */ static unsigned int -thread_timer_process (struct thread_list *list, struct timeval *timenow) +thread_timer_process (struct pqueue *queue, struct timeval *timenow) { struct thread *thread; - struct thread *next; unsigned int ready = 0; - for (thread = list->head; thread; thread = next) + while (queue->size) { - next = thread->next; + thread = queue->array[0]; if (timeval_cmp (*timenow, thread->u.sands) < 0) return ready; - thread_list_delete (list, thread); + pqueue_dequeue(queue); thread->type = THREAD_READY; thread_list_add (&thread->master->ready, thread); ready++; @@ -1064,8 +1100,8 @@ thread_fetch (struct thread_master *m, struct thread *fetch) if (m->ready.count == 0) { quagga_get_relative (NULL); - timer_wait = thread_timer_wait (&m->timer, &timer_val); - timer_wait_bg = thread_timer_wait (&m->background, &timer_val_bg); + timer_wait = thread_timer_wait (m->timer, &timer_val); + timer_wait_bg = thread_timer_wait (m->background, &timer_val_bg); if (timer_wait_bg && (!timer_wait || (timeval_cmp (*timer_wait, *timer_wait_bg) > 0))) @@ -1121,7 +1157,7 @@ thread_fetch (struct thread_master *m, struct thread *fetch) priority than I/O threads, so let's push them onto the ready list in front of the I/O threads. */ quagga_get_relative (NULL); - thread_timer_process (&m->timer, &relative_time); + thread_timer_process (m->timer, &relative_time); /* Got IO, process it */ if (num > 0) @@ -1142,7 +1178,7 @@ thread_fetch (struct thread_master *m, struct thread *fetch) #endif /* Background timer/events, lowest priority */ - thread_timer_process (&m->background, &relative_time); + thread_timer_process (m->background, &relative_time); if ((thread = thread_trim_head (&m->ready)) != NULL) return thread_run (m, thread, fetch); diff --git a/lib/thread.h b/lib/thread.h index 67902cf6..dbf5f25b 100644 --- a/lib/thread.h +++ b/lib/thread.h @@ -44,16 +44,18 @@ struct thread_list int count; }; +struct pqueue; + /* Master of the theads. */ struct thread_master { struct thread_list read; struct thread_list write; - struct thread_list timer; + struct pqueue *timer; struct thread_list event; struct thread_list ready; struct thread_list unuse; - struct thread_list background; + struct pqueue *background; fd_set readfd; fd_set writefd; fd_set exceptfd; @@ -80,6 +82,7 @@ struct thread int fd; /* file descriptor in case of read/write. */ struct timeval sands; /* rest of time sands value. */ } u; + int index; /* used for timers to store position in queue */ struct timeval real; struct cpu_thread_history *hist; /* cache pointer to cpu_history */ char funcname[FUNCNAME_LEN]; From 24c84dbe806084552d7bb14b9f1d00514a048b9d Mon Sep 17 00:00:00 2001 From: Ken Williams Date: Tue, 15 Apr 2014 02:23:11 +0000 Subject: [PATCH 188/482] zebra: Change the mechanism for comparing route ID's. The current format uses subtraction of two ints. Unfortunately, the subtraction method does not work for all combinations of numbers. For example, the with numbers represented by 10.x.x.x and 192.x.x.x, 10.x.x.x - 192.x.x.x will yield a very large positive number indicating that 10.x.x.x is larger. Signed-off-by: Ken Williams Acked-by: Feng Lu Signed-off-by: David Lamparter --- zebra/router-id.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/zebra/router-id.c b/zebra/router-id.c index 3d6b511c..b738027e 100644 --- a/zebra/router-id.c +++ b/zebra/router-id.c @@ -230,10 +230,8 @@ router_id_cmp (void *a, void *b) { const struct connected *ifa = (const struct connected *)a; const struct connected *ifb = (const struct connected *)b; - unsigned int A = ntohl(ifa->address->u.prefix4.s_addr); - unsigned int B = ntohl(ifb->address->u.prefix4.s_addr); - return (int) (A - B); + return IPV4_ADDR_CMP(&ifa->address->u.prefix4.s_addr,&ifb->address->u.prefix4.s_addr); } void From 7a9d983e4f961c2103f2cf82a51d5d8321ad0e43 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Wed, 10 Jul 2013 11:56:18 +0000 Subject: [PATCH 189/482] ospfd: run DR election prior to LSA regeneration The results from DR election are used when constructing router-LSAs. E.g. they are used to determine whether a broadcast interface should be added with a link type of stub interface or transit interface. Therefore, we should run DR election prior before regenerating LSAs. Before commit c363d3861b5384a31465a72ddc3b0f6ff007a95a the DR election was called synchronously prior to router-LSA regeneration which was run asynchronously. This fixes bug #761 on the Quagga bugzilla. Signed-off-by: Christian Franke Acked-by: Feng Lu Signed-off-by: David Lamparter --- ospfd/ospf_nsm.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/ospfd/ospf_nsm.c b/ospfd/ospf_nsm.c index fe4ddf5b..bcabd5f7 100644 --- a/ospfd/ospf_nsm.c +++ b/ospfd/ospf_nsm.c @@ -661,6 +661,25 @@ nsm_change_state (struct ospf_neighbor *nbr, int state) if (oi->type == OSPF_IFTYPE_VIRTUALLINK) vl_area = ospf_area_lookup_by_area_id (oi->ospf, oi->vl_data->vl_area_id); + /* Generate NeighborChange ISM event. + * + * In response to NeighborChange, DR election is rerun. The information + * from the election process is required by the router-lsa construction. + * + * Therefore, trigger the event prior to refreshing the LSAs. */ + switch (oi->state) { + case ISM_DROther: + case ISM_Backup: + case ISM_DR: + if ((old_state < NSM_TwoWay && state >= NSM_TwoWay) || + (old_state >= NSM_TwoWay && state < NSM_TwoWay)) + OSPF_ISM_EVENT_EXECUTE (oi, ISM_NeighborChange); + break; + default: + /* ISM_PointToPoint -> ISM_Down, ISM_Loopback -> ISM_Down, etc. */ + break; + } + /* One of the neighboring routers changes to/from the FULL state. */ if ((old_state != NSM_Full && state == NSM_Full) || (old_state == NSM_Full && state != NSM_Full)) @@ -760,20 +779,6 @@ nsm_change_state (struct ospf_neighbor *nbr, int state) if (state == NSM_Down) nbr->crypt_seqnum = 0; - /* Generete NeighborChange ISM event. */ - switch (oi->state) { - case ISM_DROther: - case ISM_Backup: - case ISM_DR: - if ((old_state < NSM_TwoWay && state >= NSM_TwoWay) || - (old_state >= NSM_TwoWay && state < NSM_TwoWay)) - OSPF_ISM_EVENT_EXECUTE (oi, ISM_NeighborChange); - break; - default: - /* ISM_PointToPoint -> ISM_Down, ISM_Loopback -> ISM_Down, etc. */ - break; - } - /* Preserve old status? */ } From 4b4bda9bb1913579bd54667f62fafe58e2746478 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Thu, 11 Jul 2013 07:56:29 +0000 Subject: [PATCH 190/482] ospfd: don't allow to set network type on loopback interfaces OSPFd only allocates some stub information for loopback interfaces. This causes a crash when the interface state machine is started on that interface by configuring a different network type. It doesn't make much sense to configure the network type of a loopback interface, therefore, just forbid it. See also bugzilla #670. Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- ospfd/ospf_vty.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 1489b20a..ee8c9019 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -5309,7 +5309,13 @@ DEFUN (ip_ospf_network, struct interface *ifp = vty->index; int old_type = IF_DEF_PARAMS (ifp)->type; struct route_node *rn; - + + if (old_type == OSPF_IFTYPE_LOOPBACK) + { + vty_out (vty, "This is a loopback interface. Can't set network type.%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (strncmp (argv[0], "b", 1) == 0) IF_DEF_PARAMS (ifp)->type = OSPF_IFTYPE_BROADCAST; else if (strncmp (argv[0], "n", 1) == 0) From 49d7af115177d05bd66d3115cbacd56a7591ec5e Mon Sep 17 00:00:00 2001 From: Lu Feng Date: Fri, 21 Feb 2014 08:11:15 +0000 Subject: [PATCH 191/482] ospfd: check the LS-Ack's recentness instead of only comparing the #seq ISSUE: RTA(DR)-----(BackupDR)RTB RTA advertises a new LSA to RTB, and then flushes the LSA (with setting the age of the LSA to MaxAge) within 1 second. Then the LSA is deleted from RTA, while it still exists on RTB with non-MaxAge and can not be flushed any more. FIX: The reason can be explained in below: a) RTA -- new LSA, #seq=1 --> RTB (RTB will send the delayed Ack in 1s) b) RTA -- MaxAge LSA, #seq=1 --> RTB (RTB discards it for the MIN_LS_ARRIVAL) c) RTA <-- Ack for the new LSA, #seq=1 -- RTB (RTA accepts it) In the step c), ospf_ls_ack() compares the #seq of the entry in the LS-Ack with that of local MaxAge LSA. The #seq of the two entries are same. So the Ack is accepted and the LSA is removed from the retransmit-list (while it should not). In RFC2328, section 13.7. Receiving link state acknowledgments: o If the acknowledgment is for the same instance that is <== contained on the list, remove the item from the list and examine the next acknowledgment. Otherwise: where "same instance" does not mean the same #seq. We must call ospf_lsa_more_recent() to check whether the two instances are same. Signed-off-by: Feng Lu Signed-off-by: Christian Franke Signed-off-by: David Lamparter --- ospfd/ospf_packet.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index ab68bf0b..cce56fc6 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -2120,7 +2120,7 @@ ospf_ls_ack (struct ip *iph, struct ospf_header *ospfh, lsr = ospf_ls_retransmit_lookup (nbr, lsa); - if (lsr != NULL && lsr->data->ls_seqnum == lsa->data->ls_seqnum) + if (lsr != NULL && ospf_lsa_more_recent (lsr, lsa) == 0) { #ifdef HAVE_OPAQUE_LSA if (IS_OPAQUE_LSA (lsr->data->type)) From e387dfd18ded3ddfef4c0a9cb896f73831864579 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Mon, 28 Apr 2014 08:04:58 +0000 Subject: [PATCH 192/482] ospfd: fix a reference counting issue introduced by commit 4de8bf0011 Commit 4de8bf0011 added a return statement to a loop iterating over a route_table. That loop uses route_top/route_next. As commit 4de8bf0011 failed to add a route_node_unlock before the return statement, a reference is leaked when this codepath is taken. Signed-off-by: Christian Franke Acked-by: Feng Lu Signed-off-by: David Lamparter --- ospfd/ospf_lsa.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index 109a120b..270c1ea2 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -2856,6 +2856,7 @@ ospf_maxage_lsa_remover (struct thread *thread) if (thread_should_yield (thread)) { OSPF_TIMER_ON (ospf->t_maxage, ospf_maxage_lsa_remover, 0); + route_unlock_node(rn); /* route_top/route_next */ return 0; } From 8afee5c1729e56f74d27ceb1766bea9f943f060c Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Mon, 28 Apr 2014 08:04:59 +0000 Subject: [PATCH 193/482] ospfd: clarify indentation and comments in ospf_lsa_maxage_delete Signed-off-by: Christian Franke Acked-by: Feng Lu Signed-off-by: David Lamparter --- ospfd/ospf_lsa.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index 270c1ea2..36965516 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -2915,9 +2915,9 @@ ospf_lsa_maxage_delete (struct ospf *ospf, struct ospf_lsa *lsa) UNSET_FLAG(lsa->flags, OSPF_LSA_IN_MAXAGE); ospf_lsa_unlock (&lsa); /* maxage_lsa */ rn->info = NULL; - route_unlock_node (rn); /* route_node_lookup */ + route_unlock_node (rn); /* unlock node because lsa is deleted */ } - route_unlock_node (rn); /* route_node_lookup */ + route_unlock_node (rn); /* route_node_lookup */ } } From b4b359a265f1b6272b4eb17c11e9c6ef9817f34b Mon Sep 17 00:00:00 2001 From: Pradosh Mohapatra Date: Mon, 28 Apr 2014 10:58:06 +0000 Subject: [PATCH 194/482] ospfd: For an ABR, ensure the right LSID is MaxAge'd PROBLEM: Accurate garbage collection of maxage LSAs. The global OSPF structure has a maxage_lsa tree - the key to the tree is tuple. Suppose the ABR has multiple areas and has originated some intra-area LSAs. The key for all those LSAs is the same. The code then ends up in a state where all but the first LSA do not get cleaned up from the areas' LSDB. A subsequent event would readvertise those LSAs. PATCH: Since the LSA is going to stick around till it actually gets cleaned up by the maxage_walker, make the LSA pointer as the key. Each distinct LSA that gets maxage'd then gets added to the tree and will get cleaned up correctly. Signed-off-by: Pradosh Mohapatra [CF: Use CHAR_BIT; use uintptr_t; use sizeof(field) instead of sizeof(type)] Signed-off-by: Christian Franke [DL: this must remain a temporary fix! needs to be redone after 0.99.23] Signed-off-by: David Lamparter --- lib/prefix.h | 9 +++++++++ ospfd/ospf_lsa.c | 16 ++++++++++++---- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/lib/prefix.h b/lib/prefix.h index 9ef70ff5..45889e08 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -52,6 +52,7 @@ struct prefix struct in_addr adv_router; } lp; u_char val[8]; + uintptr_t ptr; } u __attribute__ ((aligned (8))); }; @@ -89,6 +90,14 @@ struct prefix_rd u_char val[8] __attribute__ ((aligned (8))); }; +/* Prefix for a generic pointer */ +struct prefix_ptr +{ + u_char family; + u_char prefixlen; + uintptr_t prefix __attribute__ ((aligned (8))); +}; + /* helper to get type safety/avoid casts on calls * (w/o this, functions accepting all prefix types need casts on the caller * side, which strips type safety since the cast will accept any pointer diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index 36965516..0a66b1d4 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -2903,9 +2903,11 @@ void ospf_lsa_maxage_delete (struct ospf *ospf, struct ospf_lsa *lsa) { struct route_node *rn; - struct prefix_ls lsa_prefix; + struct prefix_ptr lsa_prefix; - ls_prefix_set (&lsa_prefix, lsa); + lsa_prefix.family = 0; + lsa_prefix.prefixlen = sizeof(lsa_prefix.prefix) * CHAR_BIT; + lsa_prefix.prefix = (uintptr_t) lsa; if ((rn = route_node_lookup(ospf->maxage_lsa, (struct prefix *)&lsa_prefix))) @@ -2929,7 +2931,7 @@ ospf_lsa_maxage_delete (struct ospf *ospf, struct ospf_lsa *lsa) void ospf_lsa_maxage (struct ospf *ospf, struct ospf_lsa *lsa) { - struct prefix_ls lsa_prefix; + struct prefix_ptr lsa_prefix; struct route_node *rn; /* When we saw a MaxAge LSA flooded to us, we put it on the list @@ -2942,12 +2944,18 @@ ospf_lsa_maxage (struct ospf *ospf, struct ospf_lsa *lsa) return; } - ls_prefix_set (&lsa_prefix, lsa); + lsa_prefix.family = 0; + lsa_prefix.prefixlen = sizeof(lsa_prefix.prefix) * CHAR_BIT; + lsa_prefix.prefix = (uintptr_t) lsa; + if ((rn = route_node_get (ospf->maxage_lsa, (struct prefix *)&lsa_prefix)) != NULL) { if (rn->info != NULL) { + if (IS_DEBUG_OSPF (lsa, LSA_FLOODING)) + zlog_debug ("LSA[%s]: found LSA (%p) in table for LSA %p %d", + dump_lsa_key (lsa), rn->info, lsa, lsa_prefix.prefixlen); route_unlock_node (rn); } else From cbf435cb72b937c9e5bfe38905e05de3755b1021 Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Mon, 28 Apr 2014 11:42:20 +0000 Subject: [PATCH 195/482] ospfd: add debug messages for router lsa-generation Add log messages to lsa_link_broadcast_set so it becomes more apparent why a particular broadcast interface was added as transit or stub interface. Signed-off-by: Christian Franke Acked-by: Feng Lu Signed-off-by: David Lamparter --- ospfd/ospf_lsa.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index 0a66b1d4..967cdb58 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -568,6 +568,9 @@ lsa_link_broadcast_set (struct stream *s, struct ospf_interface *oi) /* Describe Type 3 Link. */ if (oi->state == ISM_Waiting) { + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + zlog_debug ("LSA[Type1]: Interface %s is in state Waiting. " + "Adding stub interface", oi->ifp->name); masklen2ip (oi->address->prefixlen, &mask); id.s_addr = oi->address->u.prefix4.s_addr & mask.s_addr; return link_info_set (s, id, mask, LSA_LINK_TYPE_STUB, 0, @@ -580,12 +583,18 @@ lsa_link_broadcast_set (struct stream *s, struct ospf_interface *oi) IPV4_ADDR_SAME (&oi->address->u.prefix4, &DR (oi))) && ospf_nbr_count (oi, NSM_Full) > 0) { + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + zlog_debug ("LSA[Type1]: Interface %s has a DR. " + "Adding transit interface", oi->ifp->name); return link_info_set (s, DR (oi), oi->address->u.prefix4, LSA_LINK_TYPE_TRANSIT, 0, cost); } /* Describe type 3 link. */ else { + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + zlog_debug ("LSA[Type1]: Interface %s has no DR. " + "Adding stub interface", oi->ifp->name); masklen2ip (oi->address->prefixlen, &mask); id.s_addr = oi->address->u.prefix4.s_addr & mask.s_addr; return link_info_set (s, id, mask, LSA_LINK_TYPE_STUB, 0, From d92a2f39b46f1990052d2db046b47edf7bb21ebb Mon Sep 17 00:00:00 2001 From: "Jorge Boncompte [DTI2]" Date: Wed, 31 Jul 2013 16:36:08 +0000 Subject: [PATCH 196/482] bgpd: use ATTR_FLAG_BIT() for BGP_ATTR_ values * bgp_attr.c: this UNSET_FLAG()s are bogus. I did a quick review and I think that they could not cause any bug anyway. Signed-off-by: Jorge Boncompte [DTI2] Acked-by: Feng Lu Signed-off-by: David Lamparter --- bgpd/bgp_attr.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index cbf2902d..feb073c6 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -645,21 +645,21 @@ bgp_attr_unintern_sub (struct attr *attr) /* aspath refcount shoud be decrement. */ if (attr->aspath) aspath_unintern (&attr->aspath); - UNSET_FLAG(attr->flag, BGP_ATTR_AS_PATH); + UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_AS_PATH)); if (attr->community) community_unintern (&attr->community); - UNSET_FLAG(attr->flag, BGP_ATTR_COMMUNITIES); + UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES)); if (attr->extra) { if (attr->extra->ecommunity) ecommunity_unintern (&attr->extra->ecommunity); - UNSET_FLAG(attr->flag, BGP_ATTR_EXT_COMMUNITIES); + UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES)); if (attr->extra->cluster) cluster_unintern (attr->extra->cluster); - UNSET_FLAG(attr->flag, BGP_ATTR_CLUSTER_LIST); + UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_CLUSTER_LIST)); if (attr->extra->transit) transit_unintern (attr->extra->transit); From 2fdd455cfd1f758b7aa2e6c8e3d185098b93908c Mon Sep 17 00:00:00 2001 From: Pradosh Mohapatra Date: Sat, 7 Sep 2013 07:02:36 +0000 Subject: [PATCH 197/482] bgpd: add 'bgp bestpath as-path multipath-relax' Compute multipath in BGP based on AS_PATH hop count match. If the knob is turned on, it is not required to have an exact match of AS_PATHs (provided other multipath conditions are met, of course). Signed-off-by: Pradosh Mohapatra Reviewed-by: Dinesh G Dutt Signed-off-by: David Lamparter --- bgpd/bgp_route.c | 15 ++++++++++++++- bgpd/bgp_vty.c | 36 ++++++++++++++++++++++++++++++++++++ bgpd/bgpd.c | 3 +++ bgpd/bgpd.h | 1 + doc/bgpd.texi | 6 ++++++ 5 files changed, 60 insertions(+), 1 deletion(-) diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 335543e0..7f68b8d0 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -480,7 +480,20 @@ bgp_info_cmp (struct bgp *bgp, struct bgp_info *new, struct bgp_info *exist, /* 9. Maximum path check. */ if (newm == existm) { - if (new->peer->sort == BGP_PEER_IBGP) + if (bgp_flag_check(bgp, BGP_FLAG_ASPATH_MULTIPATH_RELAX)) + { + + /* + * For the two paths, all comparison steps till IGP metric + * have succeeded - including AS_PATH hop count. Since 'bgp + * bestpath as-path multipath-relax' knob is on, we don't need + * an exact match of AS_PATH. Thus, mark the paths are equal. + * That will trigger both these paths to get into the multipath + * array. + */ + *paths_eq = 1; + } + else if (new->peer->sort == BGP_PEER_IBGP) { if (aspath_cmp (new->attr->aspath, exist->attr->aspath)) *paths_eq = 1; diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 0f288948..bfa1f204 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -1160,6 +1160,38 @@ DEFUN (no_bgp_bestpath_aspath_confed, return CMD_SUCCESS; } +/* "bgp bestpath as-path multipath-relax" configuration. */ +DEFUN (bgp_bestpath_aspath_multipath_relax, + bgp_bestpath_aspath_multipath_relax_cmd, + "bgp bestpath as-path multipath-relax", + "BGP specific commands\n" + "Change the default bestpath selection\n" + "AS-path attribute\n" + "Allow load sharing across routes that have different AS paths (but same length)\n") +{ + struct bgp *bgp; + + bgp = vty->index; + bgp_flag_set (bgp, BGP_FLAG_ASPATH_MULTIPATH_RELAX); + return CMD_SUCCESS; +} + +DEFUN (no_bgp_bestpath_aspath_multipath_relax, + no_bgp_bestpath_aspath_multipath_relax_cmd, + "no bgp bestpath as-path multipath-relax", + NO_STR + "BGP specific commands\n" + "Change the default bestpath selection\n" + "AS-path attribute\n" + "Allow load sharing across routes that have different AS paths (but same length)\n") +{ + struct bgp *bgp; + + bgp = vty->index; + bgp_flag_unset (bgp, BGP_FLAG_ASPATH_MULTIPATH_RELAX); + return CMD_SUCCESS; +} + /* "bgp log-neighbor-changes" configuration. */ DEFUN (bgp_log_neighbor_changes, bgp_log_neighbor_changes_cmd, @@ -9172,6 +9204,10 @@ bgp_vty_init (void) install_element (BGP_NODE, &bgp_bestpath_aspath_confed_cmd); install_element (BGP_NODE, &no_bgp_bestpath_aspath_confed_cmd); + /* "bgp bestpath as-path multipath-relax" commands */ + install_element (BGP_NODE, &bgp_bestpath_aspath_multipath_relax_cmd); + install_element (BGP_NODE, &no_bgp_bestpath_aspath_multipath_relax_cmd); + /* "bgp log-neighbor-changes" commands */ install_element (BGP_NODE, &bgp_log_neighbor_changes_cmd); install_element (BGP_NODE, &no_bgp_log_neighbor_changes_cmd); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index c9a04fff..4b26993e 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -5276,6 +5276,9 @@ bgp_config_write (struct vty *vty) vty_out (vty, " bgp bestpath as-path ignore%s", VTY_NEWLINE); if (bgp_flag_check (bgp, BGP_FLAG_ASPATH_CONFED)) vty_out (vty, " bgp bestpath as-path confed%s", VTY_NEWLINE); + if (bgp_flag_check (bgp, BGP_FLAG_ASPATH_MULTIPATH_RELAX)) { + vty_out (vty, " bgp bestpath as-path multipath-relax%s", VTY_NEWLINE); + } if (bgp_flag_check (bgp, BGP_FLAG_COMPARE_ROUTER_ID)) vty_out (vty, " bgp bestpath compare-routerid%s", VTY_NEWLINE); if (bgp_flag_check (bgp, BGP_FLAG_MED_CONFED) diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 0746f0dd..024fedcf 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -120,6 +120,7 @@ struct bgp #define BGP_FLAG_LOG_NEIGHBOR_CHANGES (1 << 11) #define BGP_FLAG_GRACEFUL_RESTART (1 << 12) #define BGP_FLAG_ASPATH_CONFED (1 << 13) +#define BGP_FLAG_ASPATH_MULTIPATH_RELAX (1 << 14) /* BGP Per AF flags */ u_int16_t af_flags[AFI_MAX][SAFI_MAX]; diff --git a/doc/bgpd.texi b/doc/bgpd.texi index dd37d3ec..24028d6f 100644 --- a/doc/bgpd.texi +++ b/doc/bgpd.texi @@ -124,6 +124,12 @@ sequences should should be taken into account during the BGP best path decision process. @end deffn +@deffn {BGP} {bgp bestpath as-path multipath-relax} {} +This command specifies that BGP decision process should consider paths +of equal AS_PATH length candidates for multipath computation. Without +the knob, the entire AS_PATH must match for multipath computation. +@end deffn + @node BGP route flap dampening @subsection BGP route flap dampening From 689bb66c6a92d238bed1a8b0920438c5a2271966 Mon Sep 17 00:00:00 2001 From: Pradosh Mohapatra Date: Sat, 7 Sep 2013 07:13:37 +0000 Subject: [PATCH 198/482] bgpd: track correct originator-id in reflected routes ISSUE: Suppose route1 and route2 received from route-reflector-client1 and client2 respectively have identical attributes. The current logic of creating the adj-rib-out for a peer threads the 'adv' structures for both routes against the same attribute. This results in 'bgp_update_packet()' to pack those routes in the same UPDATE message with one attr structure formatted. The originator-id is thus set according to the first route's received router id. This is incorrect. PATCH: Fix bgp_announce_check() function to set the originator-id in the advertising attr structure. Also, fix the attribute hash function and compare function to consider originator-id. Otherwise attributes where all fields except the originator-id are identical get merged into one memory location. Signed-off-by: Pradosh Mohapatra Reviewed-by: Scott Feldman Reviewed-by: Ken Yin [DL: whitespace changes dropped] Signed-off-by: David Lamparter --- bgpd/bgp_attr.c | 4 +++- bgpd/bgp_route.c | 11 +++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index feb073c6..a0dfc65d 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -379,6 +379,7 @@ attrhash_key_make (void *p) MIX(extra->aggregator_addr.s_addr); MIX(extra->weight); MIX(extra->mp_nexthop_global_in.s_addr); + MIX(extra->originator_id.s_addr); } if (attr->aspath) @@ -434,7 +435,8 @@ attrhash_cmp (const void *p1, const void *p2) && IPV4_ADDR_SAME (&ae1->mp_nexthop_global_in, &ae2->mp_nexthop_global_in) && ae1->ecommunity == ae2->ecommunity && ae1->cluster == ae2->cluster - && ae1->transit == ae2->transit) + && ae1->transit == ae2->transit + && IPV4_ADDR_SAME (&ae1->originator_id, &ae2->originator_id)) return 1; else if (ae1 || ae2) return 0; diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 7f68b8d0..0428531f 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -965,6 +965,17 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p, attr->local_pref = bgp->default_local_pref; } + /* If originator-id is not set and the route is to be reflected, + set the originator id */ + if (peer && from && peer->sort == BGP_PEER_IBGP && + from->sort == BGP_PEER_IBGP && + (! (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)))) + { + attr->extra = bgp_attr_extra_get(attr); + IPV4_ADDR_COPY(&(attr->extra->originator_id), &(from->remote_id)); + SET_FLAG(attr->flag, BGP_ATTR_ORIGINATOR_ID); + } + /* Remove MED if its an EBGP peer - will get overwritten by route-maps */ if (peer->sort == BGP_PEER_EBGP && attr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC)) From b366b518401e0b0652cd70d297d3fb67b4803db0 Mon Sep 17 00:00:00 2001 From: Boian Bonev Date: Mon, 9 Sep 2013 16:41:35 +0000 Subject: [PATCH 199/482] bgpd: display multipath status in "show ip bgp" The output of "show ip bg" does not show whether and which routes are installed as multipath routes along the best route: BGP table version is 0, local router ID is 10.10.100.209 Status codes: s suppressed, d damped, h history, * valid, > best, i - internal, r RIB-failure, S Stale, R Removed Origin codes: i - IGP, e - EGP, ? - incomplete Network Next Hop Metric LocPrf Weight Path *>i1.0.0.0/24 10.10.100.1 1 111 0 15169 i * i 10.10.100.2 1 111 0 15169 i * i 10.10.100.3 1 111 0 65100 15169 i This patch adds a new status code that is showing exactly which routes are used as multipath: BGP table version is 0, local router ID is 10.10.100.209 Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, i internal, r RIB-failure, S Stale, R Removed Origin codes: i - IGP, e - EGP, ? - incomplete Network Next Hop Metric LocPrf Weight Path *>i1.0.0.0/24 10.10.100.1 1 111 0 15169 i *=i 10.10.100.2 1 111 0 15169 i * i 10.10.100.3 1 111 0 65100 15169 i The inconsistency in the status code legend ("i - internal" vs. "i internal") inherent from old IOS was fixed. It had to be touched anyways. Signed-off-by: Boian Bonev [DL: rewrap long line, clean whitespace in same chunk] Signed-off-by: David Lamparter --- bgpd/bgp_route.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 0428531f..29533c43 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -5651,6 +5651,8 @@ route_vty_short_status_out (struct vty *vty, struct bgp_info *binfo) vty_out (vty, "d"); else if (CHECK_FLAG (binfo->flags, BGP_INFO_SELECTED)) vty_out (vty, ">"); + else if (CHECK_FLAG (binfo->flags, BGP_INFO_MULTIPATH)) + vty_out (vty, "="); else vty_out (vty, " "); @@ -6134,9 +6136,11 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p, #endif /* HAVE_CLOCK_MONOTONIC */ } vty_out (vty, "%s", VTY_NEWLINE); -} - -#define BGP_SHOW_SCODE_HEADER "Status codes: s suppressed, d damped, h history, * valid, > best, i - internal,%s r RIB-failure, S Stale, R Removed%s" +} + +#define BGP_SHOW_SCODE_HEADER "Status codes: s suppressed, d damped, "\ + "h history, * valid, > best, = multipath,%s"\ + " i internal, r RIB-failure, S Stale, R Removed%s" #define BGP_SHOW_OCODE_HEADER "Origin codes: i - IGP, e - EGP, ? - incomplete%s%s" #define BGP_SHOW_HEADER " Network Next Hop Metric LocPrf Weight Path%s" #define BGP_SHOW_DAMP_HEADER " Network From Reuse Path%s" From 6aa136f1eaeb0dfc1e39e6c2cd6380a399ef126f Mon Sep 17 00:00:00 2001 From: Vitaliy Senchyshyn Date: Wed, 2 Oct 2013 10:40:20 +0000 Subject: [PATCH 200/482] bgpd: send notify in OpenSent when stopping manually The issue it fixes is that the notification message is not sent to a second peer when bgp is stopped manually. According to BGP RFC4271, section 8.2.2, regarding the FSM transitions, in OpenSent state: If a ManualStop event (Event 2) is issued in the OpenSent state, the local system: * sends the NOTIFICATION with a Cease, * sets the ConnectRetryTimer to zero, * releases all BGP resources, * drops the TCP connection, * sets the ConnectRetryCounter to zero, and * changes its state to Idle. I've added a check for OpenSent state when the notification is sent from the functions which are called from the CLI commands which directly/indirectly stop/restart BGP. Acked-by: Pradosh Mohapatra Signed-off-by: David Lamparter --- bgpd/bgpd.c | 80 ++++++++++++++++++++++++++++++++--------------------- bgpd/bgpd.h | 2 ++ 2 files changed, 51 insertions(+), 31 deletions(-) diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 4b26993e..5e2a5e19 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -175,7 +175,7 @@ bgp_router_id_set (struct bgp *bgp, struct in_addr *id) { IPV4_ADDR_COPY (&peer->local_id, id); - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_RID_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -205,7 +205,7 @@ bgp_cluster_id_set (struct bgp *bgp, struct in_addr *cluster_id) if (peer->sort != BGP_PEER_IBGP) continue; - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_CLID_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -233,7 +233,7 @@ bgp_cluster_id_unset (struct bgp *bgp) if (peer->sort != BGP_PEER_IBGP) continue; - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_CLID_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -302,7 +302,7 @@ bgp_confederation_id_set (struct bgp *bgp, as_t as) if (peer_sort (peer) == BGP_PEER_EBGP) { peer->local_as = as; - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_CONFED_ID_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -322,7 +322,7 @@ bgp_confederation_id_set (struct bgp *bgp, as_t as) /* Reset the local_as to be our EBGP one */ if (peer_sort (peer) == BGP_PEER_EBGP) peer->local_as = as; - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_CONFED_ID_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -351,7 +351,7 @@ bgp_confederation_id_unset (struct bgp *bgp) if (peer_sort (peer) != BGP_PEER_IBGP) { peer->local_as = bgp->as; - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_CONFED_ID_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -415,7 +415,7 @@ bgp_confederation_peers_add (struct bgp *bgp, as_t as) if (peer->as == as) { peer->local_as = bgp->as; - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_CONFED_PEER_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -471,7 +471,7 @@ bgp_confederation_peers_remove (struct bgp *bgp, as_t as) if (peer->as == as) { peer->local_as = bgp->confed_id; - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_CONFED_PEER_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -911,7 +911,7 @@ peer_as_change (struct peer *peer, as_t as) /* Stop peer. */ if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) { - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_REMOTE_AS_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -1869,7 +1869,7 @@ peer_group_bind (struct bgp *bgp, union sockunion *su, peer_group2peer_config_copy (group, peer, afi, safi); - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_RMAP_BIND; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -1912,7 +1912,7 @@ peer_group_unbind (struct bgp *bgp, struct peer *peer, peer_global_config_reset (peer); } - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_RMAP_UNBIND; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -2095,10 +2095,28 @@ bgp_delete (struct bgp *bgp) bgp_redistribute_unset (bgp, afi, i); for (ALL_LIST_ELEMENTS (bgp->peer, node, next, peer)) - peer_delete (peer); + { + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) + { + /* Send notify to remote peer. */ + bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN); + } + + peer_delete (peer); + } for (ALL_LIST_ELEMENTS (bgp->group, node, next, group)) - peer_group_delete (group); + { + for (ALL_LIST_ELEMENTS (group->peer, node, next, peer)) + { + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) + { + /* Send notify to remote peer. */ + bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN); + } + } + peer_group_delete (group); + } assert (listcount (bgp->rsclient) == 0); @@ -2406,7 +2424,7 @@ peer_flag_modify_action (struct peer *peer, u_int32_t flag) if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT)) peer_nsf_stop (peer); - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN); else @@ -2418,7 +2436,7 @@ peer_flag_modify_action (struct peer *peer, u_int32_t flag) BGP_EVENT_ADD (peer, BGP_Stop); } } - else if (peer->status == Established) + else if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { if (flag == PEER_FLAG_DYNAMIC_CAPABILITY) peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE; @@ -2821,7 +2839,7 @@ peer_update_source_if_set (struct peer *peer, const char *ifname) if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) { - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -2853,7 +2871,7 @@ peer_update_source_if_set (struct peer *peer, const char *ifname) peer->update_if = XSTRDUP (MTYPE_PEER_UPDATE_SOURCE, ifname); - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -2890,7 +2908,7 @@ peer_update_source_addr_set (struct peer *peer, union sockunion *su) if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) { - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -2921,7 +2939,7 @@ peer_update_source_addr_set (struct peer *peer, union sockunion *su) peer->update_source = sockunion_dup (su); - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -2972,7 +2990,7 @@ peer_update_source_unset (struct peer *peer) if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) { - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -3002,7 +3020,7 @@ peer_update_source_unset (struct peer *peer) peer->update_if = NULL; } - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -3441,7 +3459,7 @@ peer_local_as_set (struct peer *peer, as_t as, int no_prepend, int replace_as) if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) { - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_LOCAL_AS_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -3467,7 +3485,7 @@ peer_local_as_set (struct peer *peer, as_t as, int no_prepend, int replace_as) else UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS); - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_LOCAL_AS_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -3498,7 +3516,7 @@ peer_local_as_unset (struct peer *peer) if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) { - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_LOCAL_AS_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -3517,7 +3535,7 @@ peer_local_as_unset (struct peer *peer) UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND); UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS); - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { peer->last_reset = PEER_DOWN_LOCAL_AS_CHANGE; bgp_notify_send (peer, BGP_NOTIFY_CEASE, @@ -3551,8 +3569,8 @@ peer_password_set (struct peer *peer, const char *password) if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) { - if (peer->status == Established) - bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE); + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) + bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE); else BGP_EVENT_ADD (peer, BGP_Stop); @@ -3569,7 +3587,7 @@ peer_password_set (struct peer *peer, const char *password) peer->password = XSTRDUP(MTYPE_PEER_PASSWORD, password); - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE); else BGP_EVENT_ADD (peer, BGP_Stop); @@ -3597,7 +3615,7 @@ peer_password_unset (struct peer *peer) && strcmp (peer->group->conf->password, peer->password) == 0) return BGP_ERR_PEER_GROUP_HAS_THE_FLAG; - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE); else BGP_EVENT_ADD (peer, BGP_Stop); @@ -3620,7 +3638,7 @@ peer_password_unset (struct peer *peer) if (!peer->password) continue; - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE); else BGP_EVENT_ADD (peer, BGP_Stop); @@ -4543,7 +4561,7 @@ peer_clear (struct peer *peer) } peer->v_start = BGP_INIT_START_TIMER; - if (peer->status == Established) + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_ADMIN_RESET); else diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 024fedcf..3d516d35 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -778,6 +778,8 @@ enum bgp_clear_type /* Macros. */ #define BGP_INPUT(P) ((P)->ibuf) #define BGP_INPUT_PNT(P) (STREAM_PNT(BGP_INPUT(P))) +#define BGP_IS_VALID_STATE_FOR_NOTIF(S)\ + (((S) == OpenSent) || ((S) == OpenConfirm) || ((S) == Established)) /* Count prefix size from mask length */ #define PSIZE(a) (((a) + 7) / (8)) From 48fc05fb7e6ee44db9f73f3194bfd4738b7f9dc1 Mon Sep 17 00:00:00 2001 From: Vipin Kumar Date: Thu, 9 Jan 2014 00:31:22 +0000 Subject: [PATCH 201/482] bgpd: fix O_NONBLOCK on outgoing connects BGP was setting sockets to be non-blocking only for the accepted passive peers. As a fix, setting the BGP sockets to be non-blocking even for the active peers. Signed-off-by: Vipin Kumar Reviewed-by: Pradosh Mohapatra Reviewed-by: Dinesh Dutt [DL: patch split, this is item 1.] Signed-off-by: David Lamparter --- bgpd/bgp_network.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index 79d5d27d..bcaaba75 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -306,6 +306,8 @@ bgp_connect (struct peer *peer) if (peer->fd < 0) return -1; + set_nonblocking (peer->fd); + /* If we can get socket for the peer, adjest TTL and make connection. */ if (peer->sort == BGP_PEER_EBGP) { sockopt_ttl (peer->su.sa.sa_family, peer->fd, peer->ttl); From 3374bef0412ac11815779f54321cbc4bf96da909 Mon Sep 17 00:00:00 2001 From: Vipin Kumar Date: Thu, 9 Jan 2014 00:31:22 +0000 Subject: [PATCH 202/482] bgpd: increase TCP socket buffer size BGP does not respond fairly in high scale. As the number of BGP peers and prefixes increase, triggers like interface flaps which lead to BGP peer flaps, cause blockage in bgp_write. BGP does handle the cases of TCP socket buffer full by queuing a write event back, there is no functional issue there as such. Still, increasing the peer socket buffer size should help reduce event queueing in BGP. Signed-off-by: Vipin Kumar Reviewed-by: Pradosh Mohapatra Reviewed-by: Dinesh Dutt [DL: patch split, this is item 3.] Signed-off-by: David Lamparter --- bgpd/bgp_network.c | 30 +++++++++++++++++++++++++++++- bgpd/bgp_network.h | 2 ++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index bcaaba75..d86db3c8 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -122,7 +122,29 @@ bgp_md5_set (struct peer *peer) return ret; } - + +/* Update BGP socket send buffer size */ +static void +bgp_update_sock_send_buffer_size (int fd) +{ + int size = BGP_SOCKET_SNDBUF_SIZE; + int optval; + socklen_t optlen = sizeof(optval); + + if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &optval, &optlen) < 0) + { + zlog_err("getsockopt of SO_SNDBUF failed %s\n", safe_strerror(errno)); + return; + } + if (optval < size) + { + if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)) < 0) + { + zlog_err("Couldn't increase send buffer: %s\n", safe_strerror(errno)); + } + } +} + /* Accept bgp connection. */ static int bgp_accept (struct thread *thread) @@ -153,6 +175,9 @@ bgp_accept (struct thread *thread) } set_nonblocking (bgp_sock); + /* Set socket send buffer size */ + bgp_update_sock_send_buffer_size(bgp_sock); + if (BGP_DEBUG (events, EVENTS)) zlog_debug ("[Event] BGP connection from host %s", inet_sutop (&su, buf)); @@ -308,6 +333,9 @@ bgp_connect (struct peer *peer) set_nonblocking (peer->fd); + /* Set socket send buffer size */ + bgp_update_sock_send_buffer_size(peer->fd); + /* If we can get socket for the peer, adjest TTL and make connection. */ if (peer->sort == BGP_PEER_EBGP) { sockopt_ttl (peer->su.sa.sa_family, peer->fd, peer->ttl); diff --git a/bgpd/bgp_network.h b/bgpd/bgp_network.h index 5bf2e5ff..12768430 100644 --- a/bgpd/bgp_network.h +++ b/bgpd/bgp_network.h @@ -21,6 +21,8 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #ifndef _QUAGGA_BGP_NETWORK_H #define _QUAGGA_BGP_NETWORK_H +#define BGP_SOCKET_SNDBUF_SIZE 65536 + extern int bgp_socket (unsigned short, const char *); extern void bgp_close (void); extern int bgp_connect (struct peer *); From 8da8689d91a6436c17aca5000b1426aaea47e23c Mon Sep 17 00:00:00 2001 From: Pradosh Mohapatra Date: Wed, 11 Sep 2013 03:33:55 +0000 Subject: [PATCH 203/482] bgpd: fix fast external fallover behavior ISSUES 1. When an interface goes down, the zclient callbacks are invoked in the following order: (a) address_delete() that removes the connected address list: ifp->connected, (b) interface_down() that performs "fast external fallover" operation. The operation relies on ifp->connected to look for peers that should be brought down. That's a cyclic dependency. 2. 'ttl-security' configuration handler sets peer->ttl to MAXTTL (so that BGP packets are sent with TTL=255, as per the requirement of ttl-security). This, however, is incompatible with 'fast external fallover' as the fallover operation checks for (ttl == 1) to determine directly connected peers. 3. The current fallover operation does not work for IPv6 address family. PATCH 1. The patch removes the dependency on 'ifp->connected' list for fast fallover. The peer already contains a nexthop structure that reflects the peering address. The nexthop structure has a pointer to the interface (ifp) that peering address resolves to. Everytime the TCP connection succeeds, the ifp is updated. The patch uses this ifp in the interface_down() callback for a match for the peers that should be brought down. 2. The evaluation for directly connected peering is enhanced as 'peer->ttl == 1' OR 'peer->gtsm_hops == 1'. Thus a ttl-security configuration on the peer with one hop is directly connected and should be brought down under 'fast external fallover'. 3. Because of fix (1), IPv6 address family works automatically. Signed-off-by: Pradosh Mohapatra Reviewed-by: Dinesh G Dutt Signed-off-by: David Lamparter --- bgpd/bgp_zebra.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 26b97c2c..0f212321 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -147,12 +147,11 @@ bgp_interface_down (int command, struct zclient *zclient, zebra_size_t length) for (ALL_LIST_ELEMENTS (ifp->connected, node, nnode, c)) bgp_connected_delete (c); - /* Fast external-failover (Currently IPv4 only) */ + /* Fast external-failover */ { struct listnode *mnode; struct bgp *bgp; struct peer *peer; - struct interface *peer_if; for (ALL_LIST_ELEMENTS_RO (bm->bgp, mnode, bgp)) { @@ -161,15 +160,10 @@ bgp_interface_down (int command, struct zclient *zclient, zebra_size_t length) for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) { - if (peer->ttl != 1) + if ((peer->ttl != 1) && (peer->gtsm_hops != 1)) continue; - if (peer->su.sa.sa_family == AF_INET) - peer_if = if_lookup_by_ipv4 (&peer->su.sin.sin_addr); - else - continue; - - if (ifp == peer_if) + if (ifp == peer->nexthop.ifp) BGP_EVENT_ADD (peer, BGP_Stop); } } From ef0b0c3e95a1f30d6f338100c689feef8ad5cd6e Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Mon, 19 May 2014 22:52:04 +0200 Subject: [PATCH 204/482] bgpd: factor out TTL setting TTL/min TTL are set from both bgp_accept() and bgp_connect(). Factor them out so the following change to enable iBGP GTSM becomes more readable. [DL: originally by Dinesh G Dutt , split off from the next commit] Signed-off-by: David Lamparter --- bgpd/bgp_network.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index d86db3c8..93bb1d9b 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -145,6 +145,16 @@ bgp_update_sock_send_buffer_size (int fd) } } +static void +bgp_set_socket_ttl (struct peer *peer, int bgp_sock) +{ + if (peer->sort == BGP_PEER_EBGP) { + sockopt_ttl (peer->su.sa.sa_family, bgp_sock, peer->ttl); + if (peer->gtsm_hops) + sockopt_minttl (peer->su.sa.sa_family, bgp_sock, MAXTTL + 1 - peer->gtsm_hops); + } +} + /* Accept bgp connection. */ static int bgp_accept (struct thread *thread) @@ -198,12 +208,7 @@ bgp_accept (struct thread *thread) return -1; } - /* In case of peer is EBGP, we should set TTL for this connection. */ - if (peer1->sort == BGP_PEER_EBGP) { - sockopt_ttl (peer1->su.sa.sa_family, bgp_sock, peer1->ttl); - if (peer1->gtsm_hops) - sockopt_minttl (peer1->su.sa.sa_family, bgp_sock, MAXTTL + 1 - peer1->gtsm_hops); - } + bgp_set_socket_ttl (peer1, bgp_sock); /* Make dummy peer until read Open packet. */ if (BGP_DEBUG (events, EVENTS)) @@ -336,12 +341,7 @@ bgp_connect (struct peer *peer) /* Set socket send buffer size */ bgp_update_sock_send_buffer_size(peer->fd); - /* If we can get socket for the peer, adjest TTL and make connection. */ - if (peer->sort == BGP_PEER_EBGP) { - sockopt_ttl (peer->su.sa.sa_family, peer->fd, peer->ttl); - if (peer->gtsm_hops) - sockopt_minttl (peer->su.sa.sa_family, peer->fd, MAXTTL + 1 - peer->gtsm_hops); - } + bgp_set_socket_ttl (peer, peer->fd); sockopt_reuseaddr (peer->fd); sockopt_reuseport (peer->fd); From 5f9adb5d26d3af31b00c02084468e9f92b461b01 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Mon, 19 May 2014 23:15:02 +0200 Subject: [PATCH 205/482] bgpd: factor out eBGP multihop check The check for an eBGP multihop configuration is unwieldy; factor it out into a separate function. [DL: originally by Dinesh G Dutt , split off from the next commit] Signed-off-by: David Lamparter --- bgpd/bgpd.c | 66 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 27 deletions(-) diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 5e2a5e19..afd0dbd2 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -4409,14 +4409,42 @@ peer_maximum_prefix_unset (struct peer *peer, afi_t afi, safi_t safi) } return 0; } - + +static int is_ebgp_multihop_configured (struct peer *peer) +{ + struct peer_group *group; + struct listnode *node, *nnode; + struct peer *peer1; + + if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + group = peer->group; + if (group->conf->ttl != 1) + return 1; + + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer1)) + { + if (peer1->sort == BGP_PEER_IBGP) + continue; + + if (peer1->ttl != 1) + return 1; + } + } + else + { + if (peer->ttl != 1) + return 1; + } + return 0; +} + /* Set # of hops between us and BGP peer. */ int peer_ttl_security_hops_set (struct peer *peer, int gtsm_hops) { struct peer_group *group; struct listnode *node, *nnode; - struct peer *peer1; int ret; zlog_debug ("peer_ttl_security_hops_set: set gtsm_hops to %d for %s", gtsm_hops, peer->host); @@ -4432,32 +4460,16 @@ peer_ttl_security_hops_set (struct peer *peer, int gtsm_hops) mess of this configuration parameter, and OpenBGPD got it right. */ - if (peer->gtsm_hops == 0) { - if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) - { - group = peer->group; - if (group->conf->ttl != 1) - return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK; - - for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer1)) - { - if (peer1->sort == BGP_PEER_IBGP) - continue; + if (peer->gtsm_hops == 0) + { + if (is_ebgp_multihop_configured (peer)) + return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK; - if (peer1->ttl != 1) - return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK; - } - } - else - { - if (peer->ttl != 1) - return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK; - } - /* specify MAXTTL on outgoing packets */ - ret = peer_ebgp_multihop_set (peer, MAXTTL); - if (ret != 0) - return ret; - } + /* specify MAXTTL on outgoing packets */ + ret = peer_ebgp_multihop_set (peer, MAXTTL); + if (ret != 0) + return ret; + } peer->gtsm_hops = gtsm_hops; From 5d804b439a4138c77f81de30c64f923e2b5c1340 Mon Sep 17 00:00:00 2001 From: Pradosh Mohapatra Date: Thu, 12 Sep 2013 03:37:07 +0000 Subject: [PATCH 206/482] bgpd: support TTL-security with iBGP Traditionally, ttl-security feature has been associated with EBGP sessions as those identify directly connected external peers. The GTSM RFC (rfc 5082) does not make any restrictions on type of peering. In fact, it is beneficial to support ttl-security for both EBGP and IBGP sessions. Specifically, in data centers, there are directly connected IBGP peerings that will benefit from the protection ttl-security provides. Signed-off-by: Dinesh G Dutt Reviewed-by: Pradosh Mohapatra [DL: function refactoring split out into previous 2 patches. changes: - bgp_set_socket_ttl(): ret type int -> void - is_ebgp_multihop_configured(): stripped peer == NULL check - comments/whitespace] Signed-off-by: David Lamparter --- bgpd/bgp_network.c | 43 ++++++++++++++++++++++++++++++++++++++----- bgpd/bgp_vty.c | 6 ++++++ bgpd/bgpd.c | 31 ++++++++++--------------------- doc/bgpd.texi | 8 ++++++++ 4 files changed, 62 insertions(+), 26 deletions(-) diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index 93bb1d9b..6c7cf54c 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -148,11 +148,44 @@ bgp_update_sock_send_buffer_size (int fd) static void bgp_set_socket_ttl (struct peer *peer, int bgp_sock) { - if (peer->sort == BGP_PEER_EBGP) { - sockopt_ttl (peer->su.sa.sa_family, bgp_sock, peer->ttl); - if (peer->gtsm_hops) - sockopt_minttl (peer->su.sa.sa_family, bgp_sock, MAXTTL + 1 - peer->gtsm_hops); - } + char buf[INET_ADDRSTRLEN]; + int ret; + + /* In case of peer is EBGP, we should set TTL for this connection. */ + if (!peer->gtsm_hops && (peer_sort (peer) == BGP_PEER_EBGP)) + { + ret = sockopt_ttl (peer->su.sa.sa_family, bgp_sock, peer->ttl); + if (ret) + { + zlog_err ("%s: Can't set TxTTL on peer (rtrid %s) socket, err = %d", + __func__, + inet_ntop (AF_INET, &peer->remote_id, buf, sizeof(buf)), + errno); + } + } + else if (peer->gtsm_hops) + { + /* On Linux, setting minttl without setting ttl seems to mess with the + outgoing ttl. Therefore setting both. + */ + ret = sockopt_ttl (peer->su.sa.sa_family, bgp_sock, MAXTTL); + if (ret) + { + zlog_err ("%s: Can't set TxTTL on peer (rtrid %s) socket, err = %d", + __func__, + inet_ntop (AF_INET, &peer->remote_id, buf, sizeof(buf)), + errno); + } + ret = sockopt_minttl (peer->su.sa.sa_family, bgp_sock, + MAXTTL + 1 - peer->gtsm_hops); + if (ret) + { + zlog_err ("%s: Can't set MinTTL on peer (rtrid %s) socket, err = %d", + __func__, + inet_ntop (AF_INET, &peer->remote_id, buf, sizeof(buf)), + errno); + } + } } /* Accept bgp connection. */ diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index bfa1f204..b4d765af 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -7857,6 +7857,12 @@ bgp_show_peer (struct vty *vty, struct peer *p) vty_out (vty, " External BGP neighbor may be up to %d hops away.%s", p->ttl, VTY_NEWLINE); } + else + { + if (p->gtsm_hops > 0) + vty_out (vty, " Internal BGP neighbor may be up to %d hops away.%s", + p->gtsm_hops, VTY_NEWLINE); + } /* Local address. */ if (p->su_local) diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index afd0dbd2..88d13ed8 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -4419,21 +4419,21 @@ static int is_ebgp_multihop_configured (struct peer *peer) if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) { group = peer->group; - if (group->conf->ttl != 1) + if ((peer_sort(peer) != BGP_PEER_IBGP) && + (group->conf->ttl != 1)) return 1; for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer1)) { - if (peer1->sort == BGP_PEER_IBGP) - continue; - - if (peer1->ttl != 1) + if ((peer_sort (peer1) != BGP_PEER_IBGP) && + (peer1->ttl != 1)) return 1; } } else { - if (peer->ttl != 1) + if ((peer_sort(peer) != BGP_PEER_IBGP) && + (peer->ttl != 1)) return 1; } return 0; @@ -4449,9 +4449,6 @@ peer_ttl_security_hops_set (struct peer *peer, int gtsm_hops) zlog_debug ("peer_ttl_security_hops_set: set gtsm_hops to %d for %s", gtsm_hops, peer->host); - if (peer->sort == BGP_PEER_IBGP) - return BGP_ERR_NO_IBGP_WITH_TTLHACK; - /* We cannot configure ttl-security hops when ebgp-multihop is already set. For non peer-groups, the check is simple. For peer-groups, it's slightly messy, because we need to check both the peer-group structure @@ -4466,6 +4463,7 @@ peer_ttl_security_hops_set (struct peer *peer, int gtsm_hops) return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK; /* specify MAXTTL on outgoing packets */ + /* Routine handles iBGP peers correctly */ ret = peer_ebgp_multihop_set (peer, MAXTTL); if (ret != 0) return ret; @@ -4475,7 +4473,7 @@ peer_ttl_security_hops_set (struct peer *peer, int gtsm_hops) if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) { - if (peer->fd >= 0 && peer->sort != BGP_PEER_IBGP) + if (peer->fd >= 0) sockopt_minttl (peer->su.sa.sa_family, peer->fd, MAXTTL + 1 - gtsm_hops); } else @@ -4483,9 +4481,6 @@ peer_ttl_security_hops_set (struct peer *peer, int gtsm_hops) group = peer->group; for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) { - if (peer->sort == BGP_PEER_IBGP) - continue; - peer->gtsm_hops = group->conf->gtsm_hops; /* Change setting of existing peer @@ -4520,9 +4515,6 @@ peer_ttl_security_hops_unset (struct peer *peer) zlog_debug ("peer_ttl_security_hops_unset: set gtsm_hops to zero for %s", peer->host); - if (peer->sort == BGP_PEER_IBGP) - return 0; - /* if a peer-group member, then reset to peer-group default rather than 0 */ if (peer_group_active (peer)) peer->gtsm_hops = peer->group->conf->gtsm_hops; @@ -4532,7 +4524,7 @@ peer_ttl_security_hops_unset (struct peer *peer) opeer = peer; if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) { - if (peer->fd >= 0 && peer->sort != BGP_PEER_IBGP) + if (peer->fd >= 0) sockopt_minttl (peer->su.sa.sa_family, peer->fd, 0); } else @@ -4540,9 +4532,6 @@ peer_ttl_security_hops_unset (struct peer *peer) group = peer->group; for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) { - if (peer->sort == BGP_PEER_IBGP) - continue; - peer->gtsm_hops = 0; if (peer->fd >= 0) @@ -4865,7 +4854,7 @@ bgp_config_write_peer (struct vty *vty, struct bgp *bgp, VTY_NEWLINE); /* ttl-security hops */ - if (peer->sort != BGP_PEER_IBGP && peer->gtsm_hops != 0) + if (peer->gtsm_hops != 0) if (! peer_group_active (peer) || g_peer->gtsm_hops != peer->gtsm_hops) vty_out (vty, " neighbor %s ttl-security hops %d%s", addr, peer->gtsm_hops, VTY_NEWLINE); diff --git a/doc/bgpd.texi b/doc/bgpd.texi index 24028d6f..cb9789bd 100644 --- a/doc/bgpd.texi +++ b/doc/bgpd.texi @@ -366,6 +366,14 @@ Note that replace-as can only be specified if no-prepend is. This command is only allowed for eBGP peers. @end deffn +@deffn {BGP} {neighbor @var{peer} ttl-security hops @var{number}} {} +@deffnx {BGP} {no neighbor @var{peer} ttl-security hops @var{number}} {} +This command enforces Generalized TTL Security Mechanism (GTSM), as +specified in RFC 5082. With this command, only neighbors that are the +specified number of hops away will be allowed to become neighbors. This +command is mututally exclusive with @command{ebgp-multihop}. +@end deffn + @node Peer filtering @subsection Peer filtering From 000e157c852653288c5a1e6d0dee821c1765d315 Mon Sep 17 00:00:00 2001 From: Milan Kocian Date: Fri, 18 Oct 2013 07:59:38 +0000 Subject: [PATCH 207/482] bgpd: Fix condition allowas-in in rsclient code Currently when you set neighbour's 'allowas-in' option on route server side you get redistribution of the prefixes from this neighbour's table into all neighbour's tables which have the same AS number. I think that wanted behaviour is to allow import prefixes from neighbour's tables with the same AS num into neighbour which has 'allowas-in' option set. Signed-off-by: Milan Kocian Signed-off-by: David Lamparter --- bgpd/bgp_route.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 29533c43..46c0c85f 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -1881,7 +1881,7 @@ bgp_update_rsclient (struct peer *rsclient, afi_t afi, safi_t safi, break; /* AS path loop check. */ - if (aspath_loop_check (attr->aspath, rsclient->as) > peer->allowas_in[afi][safi]) + if (aspath_loop_check (attr->aspath, rsclient->as) > rsclient->allowas_in[afi][safi]) { reason = "as-path contains our own AS;"; goto filtered; From 8e998b1eb5fea53f2a2eddd9f7f2b8ab004406f3 Mon Sep 17 00:00:00 2001 From: Michal Sekletar Date: Fri, 16 May 2014 14:13:43 +0000 Subject: [PATCH 208/482] zebra: raise the privileges before calling socket() Because of recent changes when creating AF_NETLINK socket, kernel will cache capabilities of the caller and if file descriptor is used or otherwise handed to another process it will check that current user has necessary capabilities to use the socket. Hence we need to ensure we have necessary capabilities when creating the socket and at the time we use the socket. See: http://www.spinics.net/lists/netdev/msg280198.html Signed-off-by: Michal Sekletar Signed-off-by: David Lamparter --- zebra/rt_netlink.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index ba0b0d7d..6a802f69 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -162,6 +162,12 @@ netlink_socket (struct nlsock *nl, unsigned long groups) int namelen; int save_errno; + if (zserv_privs.change (ZPRIVS_RAISE)) + { + zlog (NULL, LOG_ERR, "Can't raise privileges"); + return -1; + } + sock = socket (AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (sock < 0) { @@ -175,12 +181,6 @@ netlink_socket (struct nlsock *nl, unsigned long groups) snl.nl_groups = groups; /* Bind the socket to the netlink structure for anything. */ - if (zserv_privs.change (ZPRIVS_RAISE)) - { - zlog (NULL, LOG_ERR, "Can't raise privileges"); - return -1; - } - ret = bind (sock, (struct sockaddr *) &snl, sizeof snl); save_errno = errno; if (zserv_privs.change (ZPRIVS_LOWER)) From 66d2ead7df2db9144605c973fcd80b88df33f81b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Luttringer?= Date: Tue, 27 May 2014 19:55:11 +0200 Subject: [PATCH 209/482] vtysh: fix build against readline 6.3 readline 6.3 removes some old deprecated funnily-named types. This updates vtysh to use the new types so it builds again. Reported-by: Joel Teichroeb References: https://bugs.archlinux.org/task/39495 Signed-off-by: David Lamparter --- vtysh/vtysh.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 34c3bd62..89b9b257 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -2212,9 +2212,9 @@ void vtysh_readline_init (void) { /* readline related settings. */ - rl_bind_key ('?', (Function *) vtysh_rl_describe); + rl_bind_key ('?', (rl_command_func_t *) vtysh_rl_describe); rl_completion_entry_function = vtysh_completion_entry_function; - rl_attempted_completion_function = (CPPFunction *)new_completion; + rl_attempted_completion_function = (rl_completion_func_t *)new_completion; } char * From 2c13299a05e5544a5e79c2a970256a21f488a3fa Mon Sep 17 00:00:00 2001 From: Pradosh Mohapatra Date: Sat, 7 Sep 2013 07:07:20 +0000 Subject: [PATCH 210/482] bgpd: don't compare next-hop to router-id While announcing a path to a peer, the code currently compares the path's next-hop with the peer's router-id. This can lead to problems as the router IDs are unique only within an AS. Suppose AS 1 sends route with next-hop 10.1.1.1. It is possible that the speaker has an established BGP peering with a router in AS 2 with router ID 10.1.1.1. The route will not be advertised to that peer in AS 2. The patch removes this check. Signed-off-by: Pradosh Mohapatra Reviewed-by: Dinesh G Dutt Reviewed-by: Shrijeet Mukherjee Signed-off-by: David Lamparter --- bgpd/bgp_route.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 46c0c85f..a919b54e 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -823,16 +823,6 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p, if (from == peer) return 0; - /* If peer's id and route's nexthop are same. draft-ietf-idr-bgp4-23 5.1.3 */ - if (p->family == AF_INET - && IPV4_ADDR_SAME(&peer->remote_id, &riattr->nexthop)) - return 0; -#ifdef HAVE_IPV6 - if (p->family == AF_INET6 - && IPV6_ADDR_SAME(&peer->remote_id, &riattr->nexthop)) - return 0; -#endif - /* Aggregate-address suppress check. */ if (ri->extra && ri->extra->suppress) if (! UNSUPPRESS_MAP_NAME (filter)) From 8c71e481dae11b7ae3f1ef561a989624b2ae84b6 Mon Sep 17 00:00:00 2001 From: Pradosh Mohapatra Date: Wed, 15 Jan 2014 06:57:57 +0000 Subject: [PATCH 211/482] bgpd: efficient NLRI packing for AFs != ipv4-unicast ISSUE: Currently, for non-ipv4-unicast address families where prefixes are encoded in MP_REACH/MP_UNREACH attributes, BGP ends up sending one prefix per UPDATE message. This is quite inefficient. The patch addresses the issue. PATCH: We introduce a scratch buffer in the peer structure that stores the MP_REACH/MP_UNREACH attributes for non-ipv4-unicast families. This enables us to encode multiple prefixes. In the end, the two buffers are merged to create the UPDATE packet. Signed-off-by: Pradosh Mohapatra Reviewed-by: Daniel Walton [DL: removed no longer existing bgp_packet_withdraw prototype] Signed-off-by: David Lamparter --- bgpd/bgp_attr.c | 249 ++++++++++++++++++++++++---------------------- bgpd/bgp_attr.h | 32 ++++-- bgpd/bgp_packet.c | 160 ++++++++++++++++++++--------- bgpd/bgpd.c | 5 +- bgpd/bgpd.h | 6 ++ lib/stream.c | 19 ++++ lib/stream.h | 11 ++ 7 files changed, 310 insertions(+), 172 deletions(-) diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index a0dfc65d..f284758e 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -2058,12 +2058,106 @@ bgp_attr_check (struct peer *peer, struct attr *attr) int stream_put_prefix (struct stream *, struct prefix *); +size_t +bgp_packet_mpattr_start (struct stream *s, afi_t afi, safi_t safi, + struct attr *attr) +{ + size_t sizep; + + /* Set extended bit always to encode the attribute length as 2 bytes */ + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_EXTLEN); + stream_putc (s, BGP_ATTR_MP_REACH_NLRI); + sizep = stream_get_endp (s); + stream_putw (s, 0); /* Marker: Attribute length. */ + stream_putw (s, afi); /* AFI */ + stream_putc (s, safi); /* SAFI */ + + /* Nexthop */ + switch (afi) + { + case AFI_IP: + switch (safi) + { + case SAFI_UNICAST: + case SAFI_MULTICAST: + stream_putc (s, 4); + stream_put_ipv4 (s, attr->nexthop.s_addr); + break; + case SAFI_MPLS_VPN: + stream_putc (s, 12); + stream_putl (s, 0); + stream_putl (s, 0); + stream_put (s, &attr->extra->mp_nexthop_global_in, 4); + break; + default: + break; + } + break; +#ifdef HAVE_IPV6 + case AFI_IP6: + switch (safi) + { + case SAFI_UNICAST: + case SAFI_MULTICAST: + { + unsigned long sizep; + struct attr_extra *attre = attr->extra; + + assert (attr->extra); + stream_putc (s, attre->mp_nexthop_len); + stream_put (s, &attre->mp_nexthop_global, 16); + if (attre->mp_nexthop_len == 32) + stream_put (s, &attre->mp_nexthop_local, 16); + } + default: + break; + } + break; +#endif /*HAVE_IPV6*/ + default: + break; + } + + /* SNPA */ + stream_putc (s, 0); + return sizep; +} + +void +bgp_packet_mpattr_prefix (struct stream *s, afi_t afi, safi_t safi, + struct prefix *p, struct prefix_rd *prd, + u_char *tag) +{ + switch (safi) + { + case SAFI_MPLS_VPN: + /* Tag, RD, Prefix write. */ + stream_putc (s, p->prefixlen + 88); + stream_put (s, tag, 3); + stream_put (s, prd->val, 8); + stream_put (s, &p->u.prefix, PSIZE (p->prefixlen)); + break; + default: + /* Prefix write. */ + stream_put_prefix (s, p); + break; + } +} + +void +bgp_packet_mpattr_end (struct stream *s, size_t sizep) +{ + /* Set MP attribute length. Don't count the (2) bytes used to encode + the attr length */ + stream_putw_at (s, sizep, (stream_get_endp (s) - sizep) - 2); +} + /* Make attribute packet. */ bgp_size_t bgp_packet_attribute (struct bgp *bgp, struct peer *peer, - struct stream *s, struct attr *attr, struct prefix *p, - afi_t afi, safi_t safi, struct peer *from, - struct prefix_rd *prd, u_char *tag) + struct stream *s, struct attr *attr, + struct prefix *p, afi_t afi, safi_t safi, + struct peer *from, struct prefix_rd *prd, u_char *tag) { size_t cp; size_t aspath_sizep; @@ -2071,6 +2165,7 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, int send_as4_path = 0; int send_as4_aggregator = 0; int use32bit = (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV)) ? 1 : 0; + size_t mpattrlen_pos = 0; if (! bgp) bgp = bgp_get_default (); @@ -2078,6 +2173,13 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, /* Remember current pointer. */ cp = stream_get_endp (s); + if (p && !(afi == AFI_IP && safi == SAFI_UNICAST)) + { + mpattrlen_pos = bgp_packet_mpattr_start(s, afi, safi, attr); + bgp_packet_mpattr_prefix(s, afi, safi, p, prd, tag); + bgp_packet_mpattr_end(s, mpattrlen_pos); + } + /* Origin attribute. */ stream_putc (s, BGP_ATTR_FLAG_TRANS); stream_putc (s, BGP_ATTR_ORIGIN); @@ -2286,96 +2388,6 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, } } -#ifdef HAVE_IPV6 - /* If p is IPv6 address put it into attribute. */ - if (p->family == AF_INET6) - { - unsigned long sizep; - struct attr_extra *attre = attr->extra; - - assert (attr->extra); - - stream_putc (s, BGP_ATTR_FLAG_OPTIONAL); - stream_putc (s, BGP_ATTR_MP_REACH_NLRI); - sizep = stream_get_endp (s); - stream_putc (s, 0); /* Marker: Attribute length. */ - stream_putw (s, AFI_IP6); /* AFI */ - stream_putc (s, safi); /* SAFI */ - - stream_putc (s, attre->mp_nexthop_len); - - if (attre->mp_nexthop_len == 16) - stream_put (s, &attre->mp_nexthop_global, 16); - else if (attre->mp_nexthop_len == 32) - { - stream_put (s, &attre->mp_nexthop_global, 16); - stream_put (s, &attre->mp_nexthop_local, 16); - } - - /* SNPA */ - stream_putc (s, 0); - - /* Prefix write. */ - stream_put_prefix (s, p); - - /* Set MP attribute length. */ - stream_putc_at (s, sizep, (stream_get_endp (s) - sizep) - 1); - } -#endif /* HAVE_IPV6 */ - - if (p->family == AF_INET && safi == SAFI_MULTICAST) - { - unsigned long sizep; - - stream_putc (s, BGP_ATTR_FLAG_OPTIONAL); - stream_putc (s, BGP_ATTR_MP_REACH_NLRI); - sizep = stream_get_endp (s); - stream_putc (s, 0); /* Marker: Attribute Length. */ - stream_putw (s, AFI_IP); /* AFI */ - stream_putc (s, SAFI_MULTICAST); /* SAFI */ - - stream_putc (s, 4); - stream_put_ipv4 (s, attr->nexthop.s_addr); - - /* SNPA */ - stream_putc (s, 0); - - /* Prefix write. */ - stream_put_prefix (s, p); - - /* Set MP attribute length. */ - stream_putc_at (s, sizep, (stream_get_endp (s) - sizep) - 1); - } - - if (p->family == AF_INET && safi == SAFI_MPLS_VPN) - { - unsigned long sizep; - - stream_putc (s, BGP_ATTR_FLAG_OPTIONAL); - stream_putc (s, BGP_ATTR_MP_REACH_NLRI); - sizep = stream_get_endp (s); - stream_putc (s, 0); /* Length of this attribute. */ - stream_putw (s, AFI_IP); /* AFI */ - stream_putc (s, SAFI_MPLS_LABELED_VPN); /* SAFI */ - - stream_putc (s, 12); - stream_putl (s, 0); - stream_putl (s, 0); - stream_put (s, &attr->extra->mp_nexthop_global_in, 4); - - /* SNPA */ - stream_putc (s, 0); - - /* Tag, RD, Prefix write. */ - stream_putc (s, p->prefixlen + 88); - stream_put (s, tag, 3); - stream_put (s, prd->val, 8); - stream_put (s, &p->u.prefix, PSIZE (p->prefixlen)); - - /* Set MP attribute length. */ - stream_putc_at (s, sizep, (stream_get_endp (s) - sizep) - 1); - } - /* Extended Communities attribute. */ if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY) && (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES))) @@ -2497,50 +2509,49 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, return stream_get_endp (s) - cp; } -bgp_size_t -bgp_packet_withdraw (struct peer *peer, struct stream *s, struct prefix *p, - afi_t afi, safi_t safi, struct prefix_rd *prd, - u_char *tag) +size_t +bgp_packet_mpunreach_start (struct stream *s, afi_t afi, safi_t safi) { - unsigned long cp; unsigned long attrlen_pnt; - bgp_size_t size; - cp = stream_get_endp (s); - - stream_putc (s, BGP_ATTR_FLAG_OPTIONAL); + /* Set extended bit always to encode the attribute length as 2 bytes */ + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_EXTLEN); stream_putc (s, BGP_ATTR_MP_UNREACH_NLRI); attrlen_pnt = stream_get_endp (s); - stream_putc (s, 0); /* Length of this attribute. */ + stream_putw (s, 0); /* Length of this attribute. */ - stream_putw (s, family2afi (p->family)); + stream_putw (s, afi); + safi = (safi == SAFI_MPLS_VPN) ? SAFI_MPLS_LABELED_VPN : safi; + stream_putc (s, safi); + return attrlen_pnt; +} +void +bgp_packet_mpunreach_prefix (struct stream *s, struct prefix *p, + afi_t afi, safi_t safi, struct prefix_rd *prd, + u_char *tag) +{ if (safi == SAFI_MPLS_VPN) { - /* SAFI */ - stream_putc (s, SAFI_MPLS_LABELED_VPN); - - /* prefix. */ stream_putc (s, p->prefixlen + 88); stream_put (s, tag, 3); stream_put (s, prd->val, 8); stream_put (s, &p->u.prefix, PSIZE (p->prefixlen)); } else - { - /* SAFI */ - stream_putc (s, safi); - - /* prefix */ - stream_put_prefix (s, p); - } + stream_put_prefix (s, p); +} - /* Set MP attribute length. */ - size = stream_get_endp (s) - attrlen_pnt - 1; - stream_putc_at (s, attrlen_pnt, size); +void +bgp_packet_mpunreach_end (struct stream *s, size_t attrlen_pnt) +{ + bgp_size_t size; - return stream_get_endp (s) - cp; + /* Set MP attribute length. Don't count the (2) bytes used to encode + the attr length */ + size = stream_get_endp (s) - attrlen_pnt - 2; + stream_putw_at (s, attrlen_pnt, size); } /* Initialization of attribute. */ diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index df87c863..cdd54674 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -157,13 +157,11 @@ extern struct attr *bgp_attr_default_intern (u_char); extern struct attr *bgp_attr_aggregate_intern (struct bgp *, u_char, struct aspath *, struct community *, int as_set); -extern bgp_size_t bgp_packet_attribute (struct bgp *bgp, struct peer *, - struct stream *, struct attr *, - struct prefix *, afi_t, safi_t, - struct peer *, struct prefix_rd *, u_char *); -extern bgp_size_t bgp_packet_withdraw (struct peer *peer, struct stream *s, - struct prefix *p, afi_t, safi_t, - struct prefix_rd *, u_char *); +extern bgp_size_t bgp_packet_attribute (struct bgp *bgp, struct peer *, + struct stream *, struct attr *, + struct prefix *, afi_t, safi_t, + struct peer *, struct prefix_rd *, + u_char *); extern void bgp_dump_routes_attr (struct stream *, struct attr *, struct prefix *); extern int attrhash_cmp (const void *, const void *); @@ -194,4 +192,24 @@ extern int bgp_mp_reach_parse (struct bgp_attr_parser_args *args, extern int bgp_mp_unreach_parse (struct bgp_attr_parser_args *args, struct bgp_nlri *); +/** + * Set of functions to encode MP_REACH_NLRI and MP_UNREACH_NLRI attributes. + * Typical call sequence is to call _start(), followed by multiple _prefix(), + * one for each NLRI that needs to be encoded into the UPDATE message, and + * finally the _end() function. + */ +extern size_t bgp_packet_mpattr_start(struct stream *s, afi_t afi, safi_t safi, + struct attr *attr); +extern void bgp_packet_mpattr_prefix(struct stream *s, afi_t afi, safi_t safi, + struct prefix *p, struct prefix_rd *prd, + u_char *tag); +extern void bgp_packet_mpattr_end(struct stream *s, size_t sizep); + +extern size_t bgp_packet_mpunreach_start (struct stream *s, afi_t afi, + safi_t safi); +extern void bgp_packet_mpunreach_prefix (struct stream *s, struct prefix *p, + afi_t afi, safi_t safi, struct prefix_rd *prd, + u_char *tag); +extern void bgp_packet_mpunreach_end (struct stream *s, size_t attrlen_pnt); + #endif /* _QUAGGA_BGP_ATTR_H */ diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index d71df082..d5f24170 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -142,16 +142,21 @@ static struct stream * bgp_update_packet (struct peer *peer, afi_t afi, safi_t safi) { struct stream *s; + struct stream *snlri; struct bgp_adj_out *adj; struct bgp_advertise *adv; struct stream *packet; struct bgp_node *rn = NULL; struct bgp_info *binfo = NULL; bgp_size_t total_attr_len = 0; - unsigned long pos; + unsigned long attrlen_pos = 0; + size_t mpattrlen_pos = 0; + size_t mpattr_pos = 0; s = peer->work; stream_reset (s); + snlri = peer->scratch; + stream_reset (snlri); adv = FIFO_HEAD (&peer->sync[afi][safi]->update); @@ -164,39 +169,61 @@ bgp_update_packet (struct peer *peer, afi_t afi, safi_t safi) binfo = adv->binfo; /* When remaining space can't include NLRI and it's length. */ - if (STREAM_REMAIN (s) <= BGP_NLRI_LENGTH + PSIZE (rn->p.prefixlen)) + if (STREAM_CONCAT_REMAIN (s, snlri, STREAM_SIZE(s)) <= + (BGP_NLRI_LENGTH + PSIZE (rn->p.prefixlen))) break; /* If packet is empty, set attribute. */ if (stream_empty (s)) { - struct prefix_rd *prd = NULL; - u_char *tag = NULL; struct peer *from = NULL; - - if (rn->prn) - prd = (struct prefix_rd *) &rn->prn->p; + if (binfo) - { - from = binfo->peer; - if (binfo->extra) - tag = binfo->extra->tag; - } - + from = binfo->peer; + + /* 1: Write the BGP message header - 16 bytes marker, 2 bytes length, + * one byte message type. + */ bgp_packet_set_marker (s, BGP_MSG_UPDATE); - stream_putw (s, 0); - pos = stream_get_endp (s); + + /* 2: withdrawn routes length */ + stream_putw (s, 0); + + /* 3: total attributes length - attrlen_pos stores the position */ + attrlen_pos = stream_get_endp (s); stream_putw (s, 0); - total_attr_len = bgp_packet_attribute (NULL, peer, s, + + /* 4: if there is MP_REACH_NLRI attribute, that should be the first + * attribute, according to draft-ietf-idr-error-handling. Save the + * position. + */ + mpattr_pos = stream_get_endp(s); + + /* 5: Encode all the attributes, except MP_REACH_NLRI attr. */ + total_attr_len = bgp_packet_attribute (NULL, peer, s, adv->baa->attr, - &rn->p, afi, safi, - from, prd, tag); - stream_putw_at (s, pos, total_attr_len); + NULL, afi, safi, + from, NULL, NULL); } if (afi == AFI_IP && safi == SAFI_UNICAST) stream_put_prefix (s, &rn->p); - + else + { + /* Encode the prefix in MP_REACH_NLRI attribute */ + struct prefix_rd *prd = NULL; + u_char *tag = NULL; + + if (rn->prn) + prd = (struct prefix_rd *) &rn->prn->p; + if (binfo && binfo->extra) + tag = binfo->extra->tag; + + if (stream_empty(snlri)) + mpattrlen_pos = bgp_packet_mpattr_start(snlri, afi, safi, + adv->baa->attr); + bgp_packet_mpattr_prefix(snlri, afi, safi, &rn->p, prd, tag); + } if (BGP_DEBUG (update, UPDATE_OUT)) { char buf[INET6_BUFSIZ]; @@ -216,18 +243,28 @@ bgp_update_packet (struct peer *peer, afi_t afi, safi_t safi) adj->attr = bgp_attr_intern (adv->baa->attr); adv = bgp_advertise_clean (peer, adj, afi, safi); - - if (! (afi == AFI_IP && safi == SAFI_UNICAST)) - break; } - + if (! stream_empty (s)) { - bgp_packet_set_size (s); - packet = stream_dup (s); + if (!stream_empty(snlri)) + { + bgp_packet_mpattr_end(snlri, mpattrlen_pos); + total_attr_len += stream_get_endp(snlri); + } + + /* set the total attribute length correctly */ + stream_putw_at (s, attrlen_pos, total_attr_len); + + if (!stream_empty(snlri)) + packet = stream_dupcat(s, snlri, mpattr_pos); + else + packet = stream_dup (s); + bgp_packet_set_size (packet); bgp_packet_add (peer, packet); BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); stream_reset (s); + stream_reset (snlri); return packet; } return NULL; @@ -277,6 +314,15 @@ bgp_update_packet_eor (struct peer *peer, afi_t afi, safi_t safi) } /* Make BGP withdraw packet. */ +/* For ipv4 unicast: + 16-octet marker | 2-octet length | 1-octet type | + 2-octet withdrawn route length | withdrawn prefixes | 2-octet attrlen (=0) +*/ +/* For other afi/safis: + 16-octet marker | 2-octet length | 1-octet type | + 2-octet withdrawn route length (=0) | 2-octet attrlen | + mp_unreach attr type | attr len | afi | safi | withdrawn prefixes +*/ static struct stream * bgp_withdraw_packet (struct peer *peer, afi_t afi, safi_t safi) { @@ -288,6 +334,10 @@ bgp_withdraw_packet (struct peer *peer, afi_t afi, safi_t safi) unsigned long pos; bgp_size_t unfeasible_len; bgp_size_t total_attr_len; + size_t mp_start = 0; + size_t attrlen_pos = 0; + size_t mplen_pos = 0; + u_char first_time = 1; s = peer->work; stream_reset (s); @@ -298,31 +348,38 @@ bgp_withdraw_packet (struct peer *peer, afi_t afi, safi_t safi) adj = adv->adj; rn = adv->rn; - if (STREAM_REMAIN (s) + if (STREAM_REMAIN (s) < (BGP_NLRI_LENGTH + BGP_TOTAL_ATTR_LEN + PSIZE (rn->p.prefixlen))) break; if (stream_empty (s)) { bgp_packet_set_marker (s, BGP_MSG_UPDATE); - stream_putw (s, 0); + stream_putw (s, 0); /* unfeasible routes length */ } + else + first_time = 0; if (afi == AFI_IP && safi == SAFI_UNICAST) stream_put_prefix (s, &rn->p); else { struct prefix_rd *prd = NULL; - + if (rn->prn) prd = (struct prefix_rd *) &rn->prn->p; - pos = stream_get_endp (s); - stream_putw (s, 0); - total_attr_len - = bgp_packet_withdraw (peer, s, &rn->p, afi, safi, prd, NULL); - - /* Set total path attribute length. */ - stream_putw_at (s, pos, total_attr_len); + + /* If first time, format the MP_UNREACH header */ + if (first_time) + { + attrlen_pos = stream_get_endp (s); + /* total attr length = 0 for now. reevaluate later */ + stream_putw (s, 0); + mp_start = stream_get_endp (s); + mplen_pos = bgp_packet_mpunreach_start(s, afi, safi); + } + + bgp_packet_mpunreach_prefix(s, &rn->p, afi, safi, prd, NULL); } if (BGP_DEBUG (update, UPDATE_OUT)) @@ -339,20 +396,26 @@ bgp_withdraw_packet (struct peer *peer, afi_t afi, safi_t safi) bgp_adj_out_remove (rn, adj, peer, afi, safi); bgp_unlock_node (rn); - - if (! (afi == AFI_IP && safi == SAFI_UNICAST)) - break; } if (! stream_empty (s)) { if (afi == AFI_IP && safi == SAFI_UNICAST) { - unfeasible_len + unfeasible_len = stream_get_endp (s) - BGP_HEADER_SIZE - BGP_UNFEASIBLE_LEN; stream_putw_at (s, BGP_HEADER_SIZE, unfeasible_len); stream_putw (s, 0); } + else + { + /* Set the mp_unreach attr's length */ + bgp_packet_mpunreach_end(s, mplen_pos); + + /* Set total path attribute length. */ + total_attr_len = stream_get_endp(s) - mp_start; + stream_putw_at (s, attrlen_pos, total_attr_len); + } bgp_packet_set_size (s); packet = stream_dup (s); bgp_packet_add (peer, packet); @@ -439,10 +502,12 @@ bgp_default_withdraw_send (struct peer *peer, afi_t afi, safi_t safi) struct stream *s; struct stream *packet; struct prefix p; - unsigned long pos; + unsigned long attrlen_pos = 0; unsigned long cp; bgp_size_t unfeasible_len; bgp_size_t total_attr_len; + size_t mp_start = 0; + size_t mplen_pos = 0; if (DISABLE_BGP_ANNOUNCE) return; @@ -455,7 +520,6 @@ bgp_default_withdraw_send (struct peer *peer, afi_t afi, safi_t safi) #endif /* HAVE_IPV6 */ total_attr_len = 0; - pos = 0; if (BGP_DEBUG (update, UPDATE_OUT)) { @@ -490,12 +554,18 @@ bgp_default_withdraw_send (struct peer *peer, afi_t afi, safi_t safi) } else { - pos = stream_get_endp (s); + attrlen_pos = stream_get_endp (s); stream_putw (s, 0); - total_attr_len = bgp_packet_withdraw (peer, s, &p, afi, safi, NULL, NULL); + mp_start = stream_get_endp (s); + mplen_pos = bgp_packet_mpunreach_start(s, afi, safi); + bgp_packet_mpunreach_prefix(s, &p, afi, safi, NULL, NULL); + + /* Set the mp_unreach attr's length */ + bgp_packet_mpunreach_end(s, mplen_pos); /* Set total path attribute length. */ - stream_putw_at (s, pos, total_attr_len); + total_attr_len = stream_get_endp(s) - mp_start; + stream_putw_at (s, attrlen_pos, total_attr_len); } bgp_packet_set_size (s); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 88d13ed8..6a21b11a 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -832,6 +832,7 @@ peer_new (struct bgp *bgp) peer->ibuf = stream_new (BGP_MAX_PACKET_SIZE); peer->obuf = stream_fifo_new (); peer->work = stream_new (BGP_MAX_PACKET_SIZE); + peer->scratch = stream_new (BGP_MAX_PACKET_SIZE); bgp_sync_init (peer); @@ -1272,8 +1273,10 @@ peer_delete (struct peer *peer) stream_fifo_free (peer->obuf); if (peer->work) stream_free (peer->work); + if (peer->scratch) + stream_free(peer->scratch); peer->obuf = NULL; - peer->work = peer->ibuf = NULL; + peer->work = peer->scratch = peer->ibuf = NULL; /* Local and remote addresses. */ if (peer->su_local) diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 3d516d35..688f459f 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -313,6 +313,12 @@ struct peer struct stream_fifo *obuf; struct stream *work; + /* We use a separate stream to encode MP_REACH_NLRI for efficient + * NLRI packing. peer->work stores all the other attributes. The + * actual packet is then constructed by concatenating the two. + */ + struct stream *scratch; + /* Status of the peer. */ int status; int ostatus; diff --git a/lib/stream.c b/lib/stream.c index ccd4623f..9a6fcbcf 100644 --- a/lib/stream.c +++ b/lib/stream.c @@ -154,6 +154,25 @@ stream_dup (struct stream *s) return (stream_copy (new, s)); } +struct stream * +stream_dupcat (struct stream *s1, struct stream *s2, size_t offset) +{ + struct stream *new; + + STREAM_VERIFY_SANE (s1); + STREAM_VERIFY_SANE (s2); + + if ( (new = stream_new (s1->endp + s2->endp)) == NULL) + return NULL; + + memcpy (new->data, s1->data, offset); + memcpy (new->data + offset, s2->data, s2->endp); + memcpy (new->data + offset + s2->endp, s1->data + offset, + (s1->endp - offset)); + new->endp = s1->endp + s2->endp; + return new; +} + size_t stream_resize (struct stream *s, size_t newsize) { diff --git a/lib/stream.h b/lib/stream.h index f10aa6d4..f0c742c0 100644 --- a/lib/stream.h +++ b/lib/stream.h @@ -122,6 +122,9 @@ struct stream_fifo /* number of bytes still to be read */ #define STREAM_READABLE(S) ((S)->endp - (S)->getp) +#define STREAM_CONCAT_REMAIN(S1, S2, size) \ + ((size) - (S1)->endp - (S2)->endp) + /* deprecated macros - do not use in new code */ #define STREAM_PNT(S) stream_pnt((S)) #define STREAM_DATA(S) ((S)->data) @@ -145,6 +148,14 @@ extern size_t stream_get_endp (struct stream *); extern size_t stream_get_size (struct stream *); extern u_char *stream_get_data (struct stream *); +/** + * Create a new stream structure; copy offset bytes from s1 to the new + * stream; copy s2 data to the new stream; copy rest of s1 data to the + * new stream. + */ +extern struct stream *stream_dupcat(struct stream *s1, struct stream *s2, + size_t offset); + extern void stream_set_getp (struct stream *, size_t); extern void stream_set_endp (struct stream *, size_t); extern void stream_forward_getp (struct stream *, size_t); From d9d4ae5157207b9b8bdb167983519a7ffb860f2c Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 13 Jan 2010 00:32:39 +0000 Subject: [PATCH 212/482] bgpd: fix crash when allowas-in is done on inactive peer When allowas-in is changed on a peer that is not up, BGP would crash trying to do route_refresh. If peer is not up, there is no need to do notification or send. Signed-off-by: Stephen Hemminger Acked-by: Feng Lu Signed-off-by: David Lamparter --- bgpd/bgpd.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 6a21b11a..2fe300c5 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -2293,6 +2293,9 @@ peer_change_action (struct peer *peer, afi_t afi, safi_t safi, if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) return; + if (peer->status != Established) + return; + if (type == peer_change_reset) bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE); From 224e7e515747b74dd6d3610570a64b9726a27722 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Ter=C3=A4s?= Date: Thu, 24 Apr 2014 06:40:33 +0000 Subject: [PATCH 213/482] lib: remove redundant and incorrect sys/fcntl.h include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit POSIX defines , is the same thing. However, it should not be used as it's existence can depend on C-library implementation. E.g. musl gives warning if is used. Signed-off-by: Timo Teräs Signed-off-by: David Lamparter --- lib/zebra.h | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/zebra.h b/lib/zebra.h index 780e12eb..3715b342 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -51,7 +51,6 @@ typedef int socklen_t; #ifdef HAVE_STROPTS_H #include #endif /* HAVE_STROPTS_H */ -#include #ifdef HAVE_SYS_SELECT_H #include #endif /* HAVE_SYS_SELECT_H */ From 7b3b98ae3ecdad20cf5cebd2a6532aee17184d95 Mon Sep 17 00:00:00 2001 From: Lu Feng Date: Mon, 14 Apr 2014 08:09:29 +0000 Subject: [PATCH 214/482] ripd & ripngd: avoid the zero interface metric The interface metric is initialized to 0 in the commit db19c85: zebra: set metric for directly connected routes via netlink to 0 Ripd and ripngd must be aware of it and avoid increase the route metric by 0. Signed-off-by: Feng Lu Signed-off-by: David Lamparter --- ripd/ripd.c | 2 +- ripngd/ripngd.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ripd/ripd.c b/ripd/ripd.c index 55a1a75f..01bd69ec 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -433,7 +433,7 @@ rip_rte_process (struct rte *rte, struct sockaddr_in *from, /* If offset-list does not modify the metric use interface's metric. */ if (!ret) - rte->metric += ifp->metric; + rte->metric += ifp->metric ? ifp->metric : 1; if (rte->metric > RIP_METRIC_INFINITY) rte->metric = RIP_METRIC_INFINITY; diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c index 8e97c2f8..318f600e 100644 --- a/ripngd/ripngd.c +++ b/ripngd/ripngd.c @@ -717,7 +717,7 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from, /* If offset-list does not modify the metric use interface's * one. */ if (! ret) - rte->metric += ifp->metric; + rte->metric += ifp->metric ? ifp->metric : 1; if (rte->metric > RIPNG_METRIC_INFINITY) rte->metric = RIPNG_METRIC_INFINITY; From 404fd2974fb53340be6d570b86f2f6f4c0a100c6 Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Wed, 21 Aug 2013 09:39:33 +0000 Subject: [PATCH 215/482] ripd: fix "show ip rip status" documentation The command was mis-named in the documentation as "show ip protocols". Signed-off-by: Joachim Nilsson Signed-off-by: David Lamparter --- doc/ripd.texi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/ripd.texi b/doc/ripd.texi index c6f804af..78d63eed 100644 --- a/doc/ripd.texi +++ b/doc/ripd.texi @@ -560,14 +560,14 @@ for routes redistributed into RIP. @c Exmaple here. -@deffn Command {show ip protocols} {} +@deffn Command {show ip rip status} {} The command displays current RIP status. It includes RIP timer, filtering, version, RIP enabled interface and RIP peer inforation. @end deffn @example @group -ripd> @b{show ip protocols} +ripd> @b{show ip rip status} Routing Protocol is "rip" Sending updates every 30 seconds with +/-50%, next due in 35 seconds Timeout after 180 seconds, garbage collect after 120 seconds From fd6f39a514093973bb0fb39484a0ed9615534c89 Mon Sep 17 00:00:00 2001 From: Lu Feng Date: Wed, 19 Feb 2014 09:05:05 +0000 Subject: [PATCH 216/482] isisd: ignore the unrecognized TLVs When processing LSPDUs, the unrecognized TLVs/sub-TLVs should be silently ignored. In parse_tlvs(), ISIS_WARNING is returned once an unrecognized TLV exists. It breaks the processing in lsp_authentication_check() and lsp_update_data(). So remove it. Signed-off-by: Feng Lu Signed-off-by: David Lamparter --- isisd/isis_tlv.c | 1 - 1 file changed, 1 deletion(-) diff --git a/isisd/isis_tlv.c b/isisd/isis_tlv.c index 2c2415ae..bbfa5d81 100644 --- a/isisd/isis_tlv.c +++ b/isisd/isis_tlv.c @@ -742,7 +742,6 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected, zlog_warn ("ISIS-TLV (%s): unsupported TLV type %d, length %d", areatag, type, length); - retval = ISIS_WARNING; pnt += length; break; } From fdb913aedb5a9807ad60715e8badb4f25d57acea Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 3 Jun 2014 18:42:25 +0200 Subject: [PATCH 217/482] build: Quagga 0.99.23-rc1 this is not a full release version, so neither release notes nor documentation are updated yet. Also, signing the tag with my private GPG key instead of the Quagga one. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index f9b17635..fa9e6e1f 100755 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,7 @@ ## AC_PREREQ(2.53) -AC_INIT(Quagga, 0.99.22, [https://bugzilla.quagga.net]) +AC_INIT(Quagga, 0.99.23-rc1, [https://bugzilla.quagga.net]) AC_CONFIG_SRCDIR(lib/zebra.h) AC_CONFIG_MACRO_DIR([m4]) From 6b0655a25194c7c0331154edaa6124cf783e5e5e Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Wed, 4 Jun 2014 06:53:35 +0200 Subject: [PATCH 218/482] *: nuke ^L (page feed) Quagga sources have inherited a slew of Page Feed (^L, \xC) characters from ancient history. Among other things, these break patchwork's XML-RPC API because \xC is not a valid character in XML documents. Nuke them from high orbit. Patches can be adapted simply by: sed -e 's%^L%%' -i filename.patch (you can type page feeds in some environments with Ctrl-V Ctrl-L) Signed-off-by: David Lamparter --- COPYING | 10 +-- COPYING.LIB | 18 ++--- NEWS | 84 ++++++++++---------- bgpd/bgp_advertise.c | 10 +-- bgpd/bgp_aspath.c | 12 +-- bgpd/bgp_attr.c | 10 +-- bgpd/bgp_clist.c | 8 +- bgpd/bgp_damp.c | 4 +- bgpd/bgp_debug.c | 2 +- bgpd/bgp_dump.c | 8 +- bgpd/bgp_ecommunity.c | 4 +- bgpd/bgp_filter.c | 6 +- bgpd/bgp_fsm.c | 4 +- bgpd/bgp_main.c | 4 +- bgpd/bgp_network.c | 2 +- bgpd/bgp_nexthop.c | 12 +-- bgpd/bgp_packet.c | 6 +- bgpd/bgp_route.c | 68 ++++++++-------- bgpd/bgp_routemap.c | 68 ++++++++-------- bgpd/bgp_snmp.c | 8 +- bgpd/bgp_vty.c | 168 +++++++++++++++++++-------------------- bgpd/bgp_zebra.c | 8 +- bgpd/bgpd.c | 74 ++++++++--------- bgpd/bgpd.h | 2 +- doc/mpls/opaque_lsa.txt | 8 +- lib/command.c | 2 +- lib/distribute.c | 4 +- lib/filter.c | 6 +- lib/getopt.c | 10 +-- lib/getopt1.c | 4 +- lib/if.c | 10 +-- lib/if_rmap.c | 6 +- lib/keychain.c | 8 +- lib/linklist.c | 8 +- lib/log.c | 8 +- lib/memory.c | 10 +-- lib/plist.c | 10 +-- lib/prefix.c | 6 +- lib/privs.c | 10 +-- lib/regex-gnu.h | 12 +-- lib/regex.c | 46 +++++------ lib/routemap.c | 8 +- lib/smux.c | 10 +-- lib/stream.c | 10 +-- lib/table.c | 2 +- lib/thread.c | 12 +-- lib/vty.c | 10 +-- lib/zclient.c | 8 +- ospf6d/ospf6_abr.c | 2 +- ospf6d/ospf6_area.c | 2 +- ospf6d/ospf6_asbr.c | 8 +- ospf6d/ospf6_interface.c | 6 +- ospf6d/ospf6_interface.h | 2 +- ospf6d/ospf6_intra.h | 2 +- ospf6d/ospf6_lsa.c | 6 +- ospf6d/ospf6_lsa.h | 2 +- ospf6d/ospf6_message.c | 4 +- ospf6d/ospf6_neighbor.c | 2 +- ospf6d/ospf6_neighbor.h | 2 +- ospf6d/ospf6_network.h | 2 +- ospf6d/ospf6_route.c | 2 +- ospf6d/ospf6_zebra.c | 4 +- ospf6d/ospf6d.c | 2 +- ospf6d/ospf6d.h | 2 +- ospfclient/COPYING | 10 +-- ospfd/ospf_abr.c | 2 +- ospfd/ospf_asbr.c | 6 +- ospfd/ospf_dump.c | 18 ++--- ospfd/ospf_flood.c | 8 +- ospfd/ospf_ia.c | 2 +- ospfd/ospf_interface.c | 12 +-- ospfd/ospf_ism.c | 4 +- ospfd/ospf_lsa.c | 34 ++++---- ospfd/ospf_lsdb.c | 2 +- ospfd/ospf_main.c | 4 +- ospfd/ospf_nsm.c | 6 +- ospfd/ospf_packet.c | 6 +- ospfd/ospf_snmp.c | 20 ++--- ospfd/ospf_spf.c | 12 +-- ospfd/ospf_vty.c | 50 ++++++------ ospfd/ospf_zebra.c | 2 +- ospfd/ospfd.c | 20 ++--- ripd/rip_debug.c | 2 +- ripd/rip_interface.c | 16 ++-- ripd/rip_main.c | 4 +- ripd/rip_peer.c | 2 +- ripd/rip_routemap.c | 14 ++-- ripd/rip_snmp.c | 6 +- ripd/rip_zebra.c | 8 +- ripd/ripd.c | 20 ++--- ripngd/ripng_debug.c | 2 +- ripngd/ripng_interface.c | 10 +-- ripngd/ripng_main.c | 4 +- ripngd/ripng_peer.c | 2 +- ripngd/ripng_routemap.c | 12 +-- ripngd/ripng_zebra.c | 4 +- ripngd/ripngd.c | 8 +- tests/main.c | 4 +- tests/test-checksum.c | 4 +- tests/test-privs.c | 2 +- zebra/connected.c | 4 +- zebra/if_proc.c | 2 +- zebra/interface.c | 2 +- zebra/irdp_packet.c | 6 +- zebra/kernel_socket.c | 8 +- zebra/main.c | 4 +- zebra/rt_ioctl.c | 2 +- zebra/rt_netlink.c | 2 +- zebra/rtadv.c | 6 +- zebra/test_main.c | 6 +- zebra/zebra_rib.c | 20 ++--- zebra/zebra_routemap.c | 10 +-- zebra/zebra_snmp.c | 6 +- zebra/zebra_vty.c | 2 +- zebra/zserv.c | 16 ++-- 115 files changed, 658 insertions(+), 658 deletions(-) diff --git a/COPYING b/COPYING index a43ea212..b8cf3a1a 100644 --- a/COPYING +++ b/COPYING @@ -55,7 +55,7 @@ patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. - + GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION @@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions: License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) - + These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in @@ -168,7 +168,7 @@ access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. - + 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is @@ -225,7 +225,7 @@ impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. - + 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License @@ -278,7 +278,7 @@ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS - + Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest diff --git a/COPYING.LIB b/COPYING.LIB index 161a3d1d..2b4628e4 100644 --- a/COPYING.LIB +++ b/COPYING.LIB @@ -51,7 +51,7 @@ library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, so that any problems introduced by others will not reflect on the original authors' reputations. - + Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that companies distributing free software will individually obtain patent licenses, thus in effect @@ -98,7 +98,7 @@ works together with the library. Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one. - + GNU LIBRARY GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION @@ -145,7 +145,7 @@ Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. - + 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 @@ -203,7 +203,7 @@ instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. - + Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. @@ -254,7 +254,7 @@ Library will still fall under Section 6.) distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. - + 6. As an exception to the Sections above, you may also compile or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work @@ -308,7 +308,7 @@ restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. - + 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined @@ -349,7 +349,7 @@ subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. - + 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or @@ -401,7 +401,7 @@ conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. - + 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is @@ -435,7 +435,7 @@ SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS - + Appendix: How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest diff --git a/NEWS b/NEWS index 59e237d6..d1529bc4 100644 --- a/NEWS +++ b/NEWS @@ -198,7 +198,7 @@ which the PtP patch introduced. * Chages in ospf6d ** Many bugs are fixed. - + * Changes in zebra-0.92a * Changes in bgpd @@ -215,7 +215,7 @@ which the PtP patch introduced. * Changes in zebra ** Treat kernel type routes as EGP routes. - + * Changes in zebra-0.92 ** Overall security is improved. Default umask is 0077. @@ -272,7 +272,7 @@ router bgp 7675 multiple IP address for an interface. ** Redistribution of loopback interface's address works fine. - + * Changes in zebra-0.91 ** --enable-oldrib configure option is removed. @@ -324,7 +324,7 @@ only supported on GNU/Linux with netlink interface. ** Fix bug of LSA MaxAge flood. ** Fix bug of NSSA codes. - + * Changes in zebra-0.90 ** From this beta release, --enable-unixdomain and --enable-newrib @@ -660,7 +660,7 @@ zebrastart.sh /usr/local/sbin/ospfd -d /usr/local/sbin/bgpd -d /usr/local/bin/vtysh -b - + * Changes in zebra-0.89 * Changes in lib @@ -754,7 +754,7 @@ value. it is available. ** Reflect IPv6 interface's address change to protocol daemons. - + * Changes in zebra-0.88 * Changes in lib @@ -875,7 +875,7 @@ generating MRT compatible dump output. * Changes in vtysh ** VTY shell is now included into the distribution. - + * Changes in zebra-0.87 * Changes in lib @@ -968,7 +968,7 @@ bgp ASN. * Changes in zebra ** Better interface up/down event handle. - + * Changes in zebra-0.86 * Changes in lib @@ -1052,7 +1052,7 @@ fixed. ** Remove client structure when client dies. ** Take care static route when interface goes up/down. - + * Changes in zebra-0.85 * Changes in bgpd @@ -1075,7 +1075,7 @@ drastically improved. * Changes in ripd ** RIPv1 update is done by class-full manner. - + * Changes in zebra-0.84b * Changes in lib @@ -1094,14 +1094,14 @@ consume only one screen size memory. ** Fix debug output string. ** Add RIP peer handling. RIP peer are shown by "show ip protocols". - + * Changes in zebra-0.84a * Changes in bgpd ** Fix serious bug of BGP-4+ peering under IPv6 link-local address. Due to the bug BGP-4+ peering may not be established. - + * Changes in zebra-0.84 * Changes in lib @@ -1180,7 +1180,7 @@ consume only one screen size memory. this command, you have to configure neighbor with "neighbor A.B.C.D soft-reconfiguration inbound" beforehand. - + * Changes in zebra-0.83 * bgpd @@ -1192,7 +1192,7 @@ introduced in zebra-0.82. ** When bgpd send Notify message, don't use thread manager. It is now send to neighbor immediately. - + * Changes in zebra-0.82 ** Solaris 2.6 support is added by Michael Handler @@ -1243,7 +1243,7 @@ draft-ietf-idr-bgp4-cap-neg-04.txt. * Changes in ospf6d ** Many debug feature is added. - + * Changes in zebra-0.81 ** SNMP support is disabled in default.--enable-snmp option is added @@ -1252,7 +1252,7 @@ to configure script. * Changes in bgpd ** Fix FSM bug which introduced in zebra-0.80. - + * Changes in zebra-0.80 * access-list @@ -1515,7 +1515,7 @@ to configure script. From zebra-0.80, ripd will reload it's configuration file when ripd receives HUP signal. Other daemon such as bgpd, ospfd will support HUP signal treatment soon. - + * Changes in zebra-0.79 * Changes in zebra @@ -1551,7 +1551,7 @@ not work for NetBSD-currnet on SparcStation 10. * Changes in ospf6d ** Enclosed KAME specific part with #ifdef #endif - + * Changes in zebra-0.78 * Changes in lib @@ -1596,7 +1596,7 @@ RIP_METRIC_INFINITY with network byte order using htonl (). * Changes in ospf6d ** `ip6' statement in configuration is changed to `ipv6'. - + * Changes in zebra-0.77 * Changes in lib @@ -1667,7 +1667,7 @@ timeout, garbage timer. * Changes in ospf6d ** Redistribute route works. - + * Changes in zebra-0.76 * Changes in lib @@ -1677,7 +1677,7 @@ timeout, garbage timer. ** Include SERVICES file to the distribution ** Update zebra.texi to zebra-0.76. - + * Changes in zebra-0.75 * Changes in lib @@ -1724,7 +1724,7 @@ used when `next-hop-self'. ** Never include a neighbor in Hello packet, when the neighbor goes down. - + * Changes in zebra-0.74 * Changes in lib @@ -1785,7 +1785,7 @@ router. ** LSA data structure is changed. ** Call of log_rotate() is removed. - + * Changes in zebra-0.73 * Changes in lib @@ -1847,7 +1847,7 @@ DEFAULT_LOCAL_PREF(100). ** Clean up logging message. ** Reflect routing information to zebra daemon. - + * Changes in zebra-0.72 * Changes in lib @@ -1868,7 +1868,7 @@ set ipv6 nexthop local -> set ipv6 next-hop local * Changes in ospfd ** Fix bug of multiple `network area' directive crashes. - + * Changes in zebra-0.71 * Changes in lib @@ -1919,7 +1919,7 @@ NOTIFY Malformed AS path is send to the peer. * Chanegs in ospf6d ** Routing table code is rewritten. - + * Changes in zebra-0.70 * Changes in zebra @@ -1940,7 +1940,7 @@ nexthop is calculated by looking up IGP routing table. * Changes in ospfd ** DD null pointer bug is fixed. - + * Changes in zebra-0.69 * Changes in zebra @@ -1990,7 +1990,7 @@ peer. * Changes in ospfd ** LS request and LS update can be send and received. - + * Changes in zebra-0.68 * Changes in lib @@ -2023,7 +2023,7 @@ summary-only mode. * Changes in ospf6d ** router zebra related bug is fixed. - + * Changes in zebra-0.67 * Changes in lib @@ -2065,7 +2065,7 @@ it is fixed. * Changes in ospf6d ** `router zebra' is default behavior. - + * Changes in zebra-0.66 * Changes in zebra @@ -2109,7 +2109,7 @@ routes to the kernel, please configure like below: router zebra no redistribute ripng ! - + * Changes in zebra-0.65 * Changes in lib @@ -2161,7 +2161,7 @@ zebra. * Changes in ospf6d ** Bug fix about network vertex. - + * Changes in zebra-0.64.1. This is bug fix release. @@ -2199,7 +2199,7 @@ not work with previous version, sorry. ** Fix bug of no network IPV6_NETWORK. ** Important bug fix about intra-area-prefix-lsa. - + * Changes in zebra-0.64. * Changes in lib @@ -2238,7 +2238,7 @@ Barcenilla. ** There are many changes. If you have interested in ospf6d please visit ospf6d/README file. - + * Changes in zebra-0.63 first beta package. * Changes in lib @@ -2257,7 +2257,7 @@ visit ospf6d/README file. * Changes in ospf6d ** Now ospf6d can be compiled on both Linux and *BSD system. - + * Changes in zebra-19990420 snapshot ** `make dist' at top directory works now. @@ -2300,7 +2300,7 @@ router bgp ASN ** configure --enable-guile turns on zebra-guile build. ** (router-bgp ASN) allocates real bgp structre. - + * Changes in zebra-19990416 snapshot ** Set version to 0.60 for preparation of beta release. @@ -2331,7 +2331,7 @@ Bligh . * Changes in ospfd ** DR and BDR information is shown by `show ip ospf interface' command. - + * Changes in zebra-19990408 snapshot * Changes in bgpd @@ -2358,7 +2358,7 @@ kad@gibson.skif.net. ** With KAME stack, terminal interface is now bind AF_INET socket instead of AF_INET6 one. - + * Changes in zebra-19990403 snapshot * Changes in bgpd @@ -2377,7 +2377,7 @@ segment. This change is for announcement to gated under iBGP. ** Yasuhiro Ohara's ospf6d codes is imported. It is under development and can't be compiled on any platform. - + * Changes in zebra-19990327 snapshot * Changes in bgpd @@ -2422,7 +2422,7 @@ buffer when the address family is not supported and the length is big * Changes in ospfd ** Now ospfd receive OSPF packet. - + * Changes in zebra-19990319 snapshot * Changes in configuration and libraries @@ -2513,8 +2513,8 @@ several files are included in ospfd directory. ** ospf6d codes are merged from Yasuhiro Ohara 's ospfd work. Now codes are located in ospf6d directory. - + Local variables: mode: outline -paragraph-separate: "[ ]*$" +paragraph-separate: "[ ]*$" end: diff --git a/bgpd/bgp_advertise.c b/bgpd/bgp_advertise.c index 666218fa..e0fa58d4 100644 --- a/bgpd/bgp_advertise.c +++ b/bgpd/bgp_advertise.c @@ -35,7 +35,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_packet.h" #include "bgpd/bgp_fsm.h" #include "bgpd/bgp_mplsvpn.h" - + /* BGP advertise attribute is used for pack same attribute update into one packet. To do that we maintain attribute hash in struct peer. */ @@ -79,7 +79,7 @@ baa_hash_cmp (const void *p1, const void *p2) return attrhash_cmp (baa1->attr, baa2->attr); } - + /* BGP update and withdraw information is stored in BGP advertise structure. This structure is referred from BGP adjacency information. */ @@ -151,7 +151,7 @@ bgp_advertise_unintern (struct hash *hash, struct bgp_advertise_attr *baa) baa_free (baa); } } - + /* BGP adjacency keeps minimal advertisement information. */ static void bgp_adj_out_free (struct bgp_adj_out *adj) @@ -327,7 +327,7 @@ bgp_adj_out_remove (struct bgp_node *rn, struct bgp_adj_out *adj, BGP_ADJ_OUT_DEL (rn, adj); bgp_adj_out_free (adj); } - + void bgp_adj_in_set (struct bgp_node *rn, struct peer *peer, struct attr *attr) { @@ -376,7 +376,7 @@ bgp_adj_in_unset (struct bgp_node *rn, struct peer *peer) bgp_adj_in_remove (rn, adj); bgp_unlock_node (rn); } - + void bgp_sync_init (struct peer *peer) { diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c index a8b078ff..e8559bea 100644 --- a/bgpd/bgp_aspath.c +++ b/bgpd/bgp_aspath.c @@ -34,7 +34,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_aspath.h" #include "bgpd/bgp_debug.h" #include "bgpd/bgp_attr.h" - + /* Attr. Flags and Attr. Type Code. */ #define AS_HEADER_SIZE 2 @@ -90,7 +90,7 @@ static struct hash *ashash; /* Stream for SNMP. See aspath_snmp_pathseg */ static struct stream *snmp_stream; - + /* Callers are required to initialize the memory */ static as_t * assegment_data_new (int num) @@ -308,7 +308,7 @@ assegment_normalise (struct assegment *head) } return head; } - + static struct aspath * aspath_new (void) { @@ -1657,7 +1657,7 @@ aspath_count (void) { return ashash->count; } - + /* Theoretically, one as path can have: @@ -1811,7 +1811,7 @@ aspath_str2aspath (const char *str) return aspath; } - + /* Make hash value by raw aspath data. */ unsigned int aspath_key_make (void *p) @@ -1868,7 +1868,7 @@ aspath_finish (void) if (snmp_stream) stream_free (snmp_stream); } - + /* return and as path value */ const char * aspath_print (struct aspath *as) diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index f284758e..a1fd1654 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -38,7 +38,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_debug.h" #include "bgpd/bgp_packet.h" #include "bgpd/bgp_ecommunity.h" - + /* Attribute strings for logging. */ static const struct message attr_str [] = { @@ -73,7 +73,7 @@ static const struct message attr_flag_str[] = { BGP_ATTR_FLAG_EXTLEN, "Extended Length" }, }; static const size_t attr_flag_str_max = array_size(attr_flag_str); - + static struct hash *cluster_hash; static void * @@ -207,7 +207,7 @@ cluster_finish (void) hash_free (cluster_hash); cluster_hash = NULL; } - + /* Unknown transit attribute. */ static struct hash *transit_hash; @@ -283,7 +283,7 @@ transit_finish (void) hash_free (transit_hash); transit_hash = NULL; } - + /* Attribute hash routines. */ static struct hash *attrhash; @@ -2055,7 +2055,7 @@ bgp_attr_check (struct peer *peer, struct attr *attr) } return BGP_ATTR_PARSE_PROCEED; } - + int stream_put_prefix (struct stream *, struct prefix *); size_t diff --git a/bgpd/bgp_clist.c b/bgpd/bgp_clist.c index 6c9976e3..b91ab81f 100644 --- a/bgpd/bgp_clist.c +++ b/bgpd/bgp_clist.c @@ -30,7 +30,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_aspath.h" #include "bgpd/bgp_regex.h" #include "bgpd/bgp_clist.h" - + /* Lookup master structure for community-list or extcommunity-list. */ struct community_list_master * @@ -262,7 +262,7 @@ community_list_empty_p (struct community_list *list) { return (list->head == NULL && list->tail == NULL) ? 1 : 0; } - + /* Add community-list entry to the list. */ static void community_list_entry_add (struct community_list *list, @@ -329,7 +329,7 @@ community_list_entry_lookup (struct community_list *list, const void *arg, } return NULL; } - + /* Internal function to perform regular expression match for community attribute. */ static int @@ -590,7 +590,7 @@ community_list_dup_check (struct community_list *list, } return 0; } - + /* Set community-list. */ int community_list_set (struct community_list_handler *ch, diff --git a/bgpd/bgp_damp.c b/bgpd/bgp_damp.c index 2820f17c..0ffafb7a 100644 --- a/bgpd/bgp_damp.c +++ b/bgpd/bgp_damp.c @@ -42,7 +42,7 @@ static struct bgp_damp_config *damp = &bgp_damp_cfg; used list. */ #define BGP_DAMP_LIST_ADD(N,A) BGP_INFO_ADD(N,A,no_reuse_list) #define BGP_DAMP_LIST_DEL(N,A) BGP_INFO_DEL(N,A,no_reuse_list) - + /* Calculate reuse list index by penalty value. */ static int bgp_reuse_index (int penalty) @@ -86,7 +86,7 @@ bgp_reuse_list_delete (struct bgp_damp_info *bdi) else damp->reuse_list[bdi->index] = bdi->next; } - + /* Return decayed penalty value. */ int bgp_damp_decay (time_t tdiff, int penalty) diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c index 726dd862..1d097697 100644 --- a/bgpd/bgp_debug.c +++ b/bgpd/bgp_debug.c @@ -290,7 +290,7 @@ bgp_notify_print(struct peer *peer, struct bgp_notify *bgp_notify, code_str, subcode_str, bgp_notify->length, bgp_notify->data ? bgp_notify->data : ""); } - + /* Debug option setting interface. */ unsigned long bgp_debug_option = 0; diff --git a/bgpd/bgp_dump.c b/bgpd/bgp_dump.c index edb725a9..a3c9526f 100644 --- a/bgpd/bgp_dump.c +++ b/bgpd/bgp_dump.c @@ -33,7 +33,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_route.h" #include "bgpd/bgp_attr.h" #include "bgpd/bgp_dump.h" - + enum bgp_dump_type { BGP_DUMP_ALL, @@ -89,7 +89,7 @@ struct bgp_dump bgp_dump_routes; /* Dump whole BGP table is very heavy process. */ struct thread *t_bgp_dump_routes; - + /* Some define for BGP packet dump. */ static FILE * bgp_dump_open_file (struct bgp_dump *bgp_dump) @@ -535,7 +535,7 @@ bgp_dump_packet (struct peer *peer, int type, struct stream *packet) if (type == BGP_MSG_UPDATE) bgp_dump_packet_func (&bgp_dump_updates, peer, packet); } - + static unsigned int bgp_dump_parse_time (const char *str) { @@ -845,7 +845,7 @@ config_write_bgp_dump (struct vty *vty) } return 0; } - + /* Initialize BGP packet dump functionality. */ void bgp_dump_init (void) diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index 5722425e..8a326a8b 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -31,7 +31,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA /* Hash of community attribute. */ static struct hash *ecomhash; - + /* Allocate a new ecommunities. */ static struct ecommunity * ecommunity_new (void) @@ -277,7 +277,7 @@ ecommunity_finish (void) hash_free (ecomhash); ecomhash = NULL; } - + /* Extended Communities token enum. */ enum ecommunity_token { diff --git a/bgpd/bgp_filter.c b/bgpd/bgp_filter.c index 8ee62b01..fd8ece65 100644 --- a/bgpd/bgp_filter.c +++ b/bgpd/bgp_filter.c @@ -84,7 +84,7 @@ struct as_list struct as_filter *head; struct as_filter *tail; }; - + /* ip as-path access-list 10 permit AS1. */ static struct as_list_master as_list_master = @@ -370,7 +370,7 @@ as_list_filter_delete (struct as_list *aslist, struct as_filter *asfilter) if (as_list_master.delete_hook) (*as_list_master.delete_hook) (); } - + static int as_filter_match (struct as_filter *asfilter, struct aspath *aspath) { @@ -412,7 +412,7 @@ as_list_delete_hook (void (*func) (void)) { as_list_master.delete_hook = func; } - + static int as_list_dup_check (struct as_list *aslist, struct as_filter *new) { diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index fba94276..112c34a1 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -43,7 +43,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #ifdef HAVE_SNMP #include "bgpd/bgp_snmp.h" #endif /* HAVE_SNMP */ - + /* BGP FSM (finite state machine) has three types of functions. Type one is thread functions. Type two is event functions. Type three is FSM functions. Timer functions are set by bgp_timer_set @@ -928,7 +928,7 @@ bgp_ignore (struct peer *peer) zlog (peer->log, LOG_DEBUG, "%s [FSM] bgp_ignore called", peer->host); return 0; } - + /* Finite State Machine structure */ static const struct { int (*func) (struct peer *); diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index 1be65043..5026b5ea 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -171,7 +171,7 @@ Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS); exit (status); } - + /* SIGHUP handler. */ void sighup (void) @@ -314,7 +314,7 @@ bgp_exit (int status) exit (status); } - + /* Main routine of bgpd. Treatment of argument and start bgp finite state machine is handled at here. */ int diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index 6c7cf54c..c0527447 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -47,7 +47,7 @@ struct bgp_listener union sockunion su; struct thread *thread; }; - + /* * Set MD5 key for the socket, for the given IPv4 peer address. * If the password is NULL or zero-length, the option will be disabled. diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index e23155c7..5b1d13ac 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -45,7 +45,7 @@ struct bgp_nexthop_cache *zlookup_query (struct in_addr); #ifdef HAVE_IPV6 struct bgp_nexthop_cache *zlookup_query_ipv6 (struct in6_addr *); #endif /* HAVE_IPV6 */ - + /* Only one BGP scan thread are activated at the same time. */ static struct thread *bgp_scan_thread = NULL; @@ -68,7 +68,7 @@ static struct bgp_table *bgp_connected_table[AFI_MAX]; /* BGP nexthop lookup query client. */ struct zclient *zlookup = NULL; - + /* Add nexthop to the end of the list. */ static void bnc_nexthop_add (struct bgp_nexthop_cache *bnc, struct nexthop *nexthop) @@ -109,7 +109,7 @@ bnc_free (struct bgp_nexthop_cache *bnc) bnc_nexthop_free (bnc); XFREE (MTYPE_BGP_NEXTHOP_CACHE, bnc); } - + static int bgp_nexthop_same (struct nexthop *next1, struct nexthop *next2) { @@ -623,7 +623,7 @@ bgp_address_del (struct prefix *p) } } - + struct bgp_connected_ref { unsigned int refcnt; @@ -781,7 +781,7 @@ bgp_nexthop_self (struct attr *attr) return 0; } - + static struct bgp_nexthop_cache * zlookup_read (void) { @@ -1243,7 +1243,7 @@ bgp_multiaccess_check_v4 (struct in_addr nexthop, char *peer) return 0; } - + DEFUN (bgp_scan_time, bgp_scan_time_cmd, "bgp scan-time <5-60>", diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index d5f24170..80651f15 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -49,7 +49,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_vty.h" int stream_put_prefix (struct stream *, struct prefix *); - + /* Set up BGP packet marker and packet type. */ static int bgp_packet_set_marker (struct stream *s, u_char type) @@ -1166,7 +1166,7 @@ bgp_capability_send (struct peer *peer, afi_t afi, safi_t safi, BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); } - + /* RFC1771 6.8 Connection collision detection. */ static int bgp_collision_detect (struct peer *new, struct in_addr remote_id) @@ -2381,7 +2381,7 @@ bgp_capability_receive (struct peer *peer, bgp_size_t size) /* Parse packet. */ return bgp_capability_msg_parse (peer, pnt, size); } - + /* BGP read utility function. */ static int bgp_read_packet (struct peer *peer) diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index a919b54e..f421ca5e 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -59,7 +59,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA /* Extern from bgp_dump.c */ extern const char *bgp_origin_str[]; extern const char *bgp_origin_long_str[]; - + static struct bgp_node * bgp_afi_node_get (struct bgp_table *table, afi_t afi, safi_t safi, struct prefix *p, struct prefix_rd *prd) @@ -89,7 +89,7 @@ bgp_afi_node_get (struct bgp_table *table, afi_t afi, safi_t safi, struct prefix return rn; } - + /* Allocate bgp_info_extra */ static struct bgp_info_extra * bgp_info_extra_new (void) @@ -683,7 +683,7 @@ bgp_cluster_filter (struct peer *peer, struct attr *attr) } return 0; } - + static int bgp_input_modifier (struct peer *peer, struct prefix *p, struct attr *attr, afi_t afi, safi_t safi) @@ -721,7 +721,7 @@ bgp_input_modifier (struct peer *peer, struct prefix *p, struct attr *attr, } return RMAP_PERMIT; } - + static int bgp_export_modifier (struct peer *rsclient, struct peer *peer, struct prefix *p, struct attr *attr, afi_t afi, safi_t safi) @@ -793,7 +793,7 @@ bgp_import_modifier (struct peer *rsclient, struct peer *peer, } return RMAP_PERMIT; } - + static int bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p, struct attr *attr, afi_t afi, safi_t safi) @@ -2468,7 +2468,7 @@ bgp_withdraw (struct peer *peer, struct prefix *p, struct attr *attr, return 0; } - + void bgp_default_originate (struct peer *peer, afi_t afi, safi_t safi, int withdraw) { @@ -2571,7 +2571,7 @@ bgp_default_originate (struct peer *peer, afi_t afi, safi_t safi, int withdraw) bgp_attr_extra_free (&attr); aspath_unintern (&aspath); } - + static void bgp_announce_table (struct peer *peer, afi_t afi, safi_t safi, struct bgp_table *table, int rsclient) @@ -2642,7 +2642,7 @@ bgp_announce_route_all (struct peer *peer) for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) bgp_announce_route (peer, afi, safi); } - + static void bgp_soft_reconfig_table_rsclient (struct peer *rsclient, afi_t afi, safi_t safi, struct bgp_table *table, struct prefix_rd *prd) @@ -2686,7 +2686,7 @@ bgp_soft_reconfig_rsclient (struct peer *rsclient, afi_t afi, safi_t safi) bgp_soft_reconfig_table_rsclient (rsclient, afi, safi, table, &prd); } } - + static void bgp_soft_reconfig_table (struct peer *peer, afi_t afi, safi_t safi, struct bgp_table *table, struct prefix_rd *prd) @@ -2744,7 +2744,7 @@ bgp_soft_reconfig_in (struct peer *peer, afi_t afi, safi_t safi) bgp_soft_reconfig_table (peer, afi, safi, table, &prd); } } - + struct bgp_clear_node_queue { @@ -3038,7 +3038,7 @@ bgp_clear_stale_route (struct peer *peer, afi_t afi, safi_t safi) } } } - + /* Delete all kernel routes. */ void bgp_cleanup_routes (void) @@ -3079,7 +3079,7 @@ bgp_reset (void) access_list_reset (); prefix_list_reset (); } - + /* Parse NLRI stream. Withdraw NLRI is recognized by NULL attr value. */ int @@ -3242,7 +3242,7 @@ bgp_nlri_sanity_check (struct peer *peer, int afi, u_char *pnt, } return 0; } - + static struct bgp_static * bgp_static_new (void) { @@ -4009,7 +4009,7 @@ bgp_static_unset_vpnv4 (struct vty *vty, const char *ip_str, return CMD_SUCCESS; } - + DEFUN (bgp_network, bgp_network_cmd, "network A.B.C.D/M", @@ -4467,7 +4467,7 @@ ALIAS_DEPRECATED (no_ipv6_bgp_network, "AS-Path hopcount limit attribute\n" "AS-Pathlimit TTL, in number of AS-Path hops\n") #endif /* HAVE_IPV6 */ - + /* Aggreagete address: advertise-map Set condition to advertise attribute @@ -5378,7 +5378,7 @@ ALIAS (no_ipv6_aggregate_address_summary_only, "Aggregate prefix\n" "Filter more specific routes from updates\n") #endif /* HAVE_IPV6 */ - + /* Redistribute route treatment. */ void bgp_redistribute_add (struct prefix *p, const struct in_addr *nexthop, @@ -5578,7 +5578,7 @@ bgp_redistribute_withdraw (struct bgp *bgp, afi_t afi, int type) } } } - + /* Static function to display route. */ static void route_vty_out_route (struct prefix *p, struct vty *vty) @@ -7103,7 +7103,7 @@ DEFUN (show_ipv6_mbgp_prefix, return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_MULTICAST, NULL, 1); } #endif - + static int bgp_show_regexp (struct vty *vty, int argc, const char **argv, afi_t afi, @@ -7247,7 +7247,7 @@ DEFUN (show_ipv6_mbgp_regexp, bgp_show_type_regexp); } #endif /* HAVE_IPV6 */ - + static int bgp_show_prefix_list (struct vty *vty, const char *prefix_list_str, afi_t afi, safi_t safi, enum bgp_show_type type) @@ -7362,7 +7362,7 @@ DEFUN (show_ipv6_mbgp_prefix_list, bgp_show_type_prefix_list); } #endif /* HAVE_IPV6 */ - + static int bgp_show_filter_list (struct vty *vty, const char *filter, afi_t afi, safi_t safi, enum bgp_show_type type) @@ -7476,7 +7476,7 @@ DEFUN (show_ipv6_mbgp_filter_list, bgp_show_type_filter_list); } #endif /* HAVE_IPV6 */ - + static int bgp_show_route_map (struct vty *vty, const char *rmap_str, afi_t afi, safi_t safi, enum bgp_show_type type) @@ -7561,7 +7561,7 @@ ALIAS (show_bgp_route_map, "Address family\n" "Display routes matching the route-map\n" "A route-map to match on\n") - + DEFUN (show_ip_bgp_cidr_only, show_ip_bgp_cidr_only_cmd, "show ip bgp cidr-only", @@ -7605,7 +7605,7 @@ DEFUN (show_ip_bgp_ipv4_cidr_only, return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, bgp_show_type_cidr_only, NULL); } - + DEFUN (show_ip_bgp_community_all, show_ip_bgp_community_all_cmd, "show ip bgp community", @@ -7683,7 +7683,7 @@ DEFUN (show_ipv6_mbgp_community_all, bgp_show_type_community_all, NULL); } #endif /* HAVE_IPV6 */ - + static int bgp_show_community (struct vty *vty, const char *view_name, int argc, const char **argv, int exact, afi_t afi, safi_t safi) @@ -8875,7 +8875,7 @@ ALIAS (show_ipv6_mbgp_community_exact, "Do not export to next AS (well-known community)\n" "Exact match of the communities") #endif /* HAVE_IPV6 */ - + static int bgp_show_community_list (struct vty *vty, const char *com, int exact, afi_t afi, safi_t safi) @@ -9062,7 +9062,7 @@ DEFUN (show_ipv6_mbgp_community_list_exact, return bgp_show_community_list (vty, argv[0], 1, AFI_IP6, SAFI_MULTICAST); } #endif /* HAVE_IPV6 */ - + static int bgp_show_prefix_longer (struct vty *vty, const char *prefix, afi_t afi, safi_t safi, enum bgp_show_type type) @@ -9254,7 +9254,7 @@ peer_lookup_in_view (struct vty *vty, const char *view_name, return peer; } - + enum bgp_stats { BGP_STATS_MAXBITLEN = 0, @@ -9602,7 +9602,7 @@ ALIAS (show_bgp_statistics_view, "Address family\n" "Address Family modifier\n" "BGP RIB advertisement statistics\n") - + enum bgp_pcounts { PCOUNT_ADJ_IN = 0, @@ -10142,7 +10142,7 @@ DEFUN (ipv6_mbgp_neighbor_advertised_route, return peer_adj_routes (vty, peer, AFI_IP6, SAFI_MULTICAST, 0); } #endif /* HAVE_IPV6 */ - + DEFUN (show_ip_bgp_view_neighbor_received_routes, show_ip_bgp_view_neighbor_received_routes_cmd, "show ip bgp view WORD neighbors (A.B.C.D|X:X::X:X) received-routes", @@ -10519,7 +10519,7 @@ ALIAS (show_bgp_view_neighbor_received_prefix_filter, "Display information received from a BGP neighbor\n" "Display the prefixlist filter\n") #endif /* HAVE_IPV6 */ - + static int bgp_show_neighbor_route (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi, enum bgp_show_type type) @@ -11671,7 +11671,7 @@ ALIAS (show_bgp_view_ipv6_safi_rsclient_prefix, "IP prefix /, e.g., 3ffe::/16\n") #endif /* HAVE_IPV6 */ - + struct bgp_table *bgp_distance_table; struct bgp_distance @@ -11948,7 +11948,7 @@ DEFUN (no_bgp_distance_source_access_list, bgp_distance_unset (vty, argv[0], argv[1], argv[2]); return CMD_SUCCESS; } - + DEFUN (bgp_damp_set, bgp_damp_set_cmd, "bgp dampening <1-45> <1-20000> <1-20000> <1-255>", @@ -12043,7 +12043,7 @@ DEFUN (show_ip_bgp_flap_statistics, return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, bgp_show_type_flap_statistics, NULL); } - + /* Display specified route of BGP table. */ static int bgp_clear_damp_route (struct vty *vty, const char *view_name, @@ -12207,7 +12207,7 @@ DEFUN (clear_ip_bgp_dampening_address_mask, return bgp_clear_damp_route (vty, NULL, prefix_str, AFI_IP, SAFI_UNICAST, NULL, 0); } - + static int bgp_config_write_network_vpnv4 (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, int *write) diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 40f20765..6dc88b3a 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -100,7 +100,7 @@ o Local extention set as-path exclude : Done */ - + /* 'match peer (A.B.C.D|X:X::X:X)' */ /* Compares the peer specified in the 'match peer' clause with the peer @@ -240,7 +240,7 @@ struct route_map_rule_cmd route_match_ip_address_cmd = route_match_ip_address_compile, route_match_ip_address_free }; - + /* `match ip next-hop IP_ADDRESS' */ /* Match function return 1 if match is success else return zero. */ @@ -292,7 +292,7 @@ struct route_map_rule_cmd route_match_ip_next_hop_cmd = route_match_ip_next_hop_compile, route_match_ip_next_hop_free }; - + /* `match ip route-source ACCESS-LIST' */ /* Match function return 1 if match is success else return zero. */ @@ -350,7 +350,7 @@ struct route_map_rule_cmd route_match_ip_route_source_cmd = route_match_ip_route_source_compile, route_match_ip_route_source_free }; - + /* `match ip address prefix-list PREFIX_LIST' */ static route_map_result_t @@ -390,7 +390,7 @@ struct route_map_rule_cmd route_match_ip_address_prefix_list_cmd = route_match_ip_address_prefix_list_compile, route_match_ip_address_prefix_list_free }; - + /* `match ip next-hop prefix-list PREFIX_LIST' */ static route_map_result_t @@ -437,7 +437,7 @@ struct route_map_rule_cmd route_match_ip_next_hop_prefix_list_cmd = route_match_ip_next_hop_prefix_list_compile, route_match_ip_next_hop_prefix_list_free }; - + /* `match ip route-source prefix-list PREFIX_LIST' */ static route_map_result_t @@ -490,7 +490,7 @@ struct route_map_rule_cmd route_match_ip_route_source_prefix_list_cmd = route_match_ip_route_source_prefix_list_compile, route_match_ip_route_source_prefix_list_free }; - + /* `match metric METRIC' */ /* Match function return 1 if match is success else return zero. */ @@ -555,7 +555,7 @@ struct route_map_rule_cmd route_match_metric_cmd = route_match_metric_compile, route_match_metric_free }; - + /* `match as-path ASPATH' */ /* Match function for as-path match. I assume given object is */ @@ -603,7 +603,7 @@ struct route_map_rule_cmd route_match_aspath_cmd = route_match_aspath_compile, route_match_aspath_free }; - + /* `match community COMMUNIY' */ struct rmap_community { @@ -687,7 +687,7 @@ struct route_map_rule_cmd route_match_community_cmd = route_match_community_compile, route_match_community_free }; - + /* Match function for extcommunity match. */ static route_map_result_t route_match_ecommunity (void *rule, struct prefix *prefix, @@ -736,10 +736,10 @@ struct route_map_rule_cmd route_match_ecommunity_cmd = route_match_ecommunity_compile, route_match_ecommunity_free }; - + /* `match nlri` and `set nlri` are replaced by `address-family ipv4` and `address-family vpnv4'. */ - + /* `match origin' */ static route_map_result_t route_match_origin (void *rule, struct prefix *prefix, @@ -964,7 +964,7 @@ struct route_map_rule_cmd route_set_ip_nexthop_cmd = route_set_ip_nexthop_compile, route_set_ip_nexthop_free }; - + /* `set local-preference LOCAL_PREF' */ /* Set local preference. */ @@ -1031,7 +1031,7 @@ struct route_map_rule_cmd route_set_local_pref_cmd = route_set_local_pref_compile, route_set_local_pref_free, }; - + /* `set weight WEIGHT' */ /* Set weight. */ @@ -1100,7 +1100,7 @@ struct route_map_rule_cmd route_set_weight_cmd = route_set_weight_compile, route_set_weight_free, }; - + /* `set metric METRIC' */ /* Set metric to attribute. */ @@ -1200,7 +1200,7 @@ struct route_map_rule_cmd route_set_metric_cmd = route_set_metric_compile, route_set_metric_free, }; - + /* `set as-path prepend ASPATH' */ /* For AS path prepend mechanism. */ @@ -1256,7 +1256,7 @@ struct route_map_rule_cmd route_set_aspath_prepend_cmd = route_set_aspath_prepend_compile, route_set_aspath_prepend_free, }; - + /* `set as-path exclude ASn' */ /* For ASN exclude mechanism. @@ -1314,7 +1314,7 @@ struct route_map_rule_cmd route_set_aspath_exclude_cmd = route_set_aspath_exclude_compile, route_set_aspath_exclude_free, }; - + /* `set community COMMUNITY' */ struct rmap_com_set { @@ -1438,7 +1438,7 @@ struct route_map_rule_cmd route_set_community_cmd = route_set_community_compile, route_set_community_free, }; - + /* `set comm-list (<1-99>|<100-500>|WORD) delete' */ /* For community set mechanism. */ @@ -1527,7 +1527,7 @@ struct route_map_rule_cmd route_set_community_delete_cmd = route_set_community_delete_compile, route_set_community_delete_free, }; - + /* `set extcommunity rt COMMUNITY' */ /* For community set mechanism. */ @@ -1659,7 +1659,7 @@ struct route_map_rule_cmd route_set_ecommunity_soo_cmd = route_set_ecommunity_soo_compile, route_set_ecommunity_soo_free, }; - + /* `set origin ORIGIN' */ /* For origin set. */ @@ -1713,7 +1713,7 @@ struct route_map_rule_cmd route_set_origin_cmd = route_set_origin_compile, route_set_origin_free, }; - + /* `set atomic-aggregate' */ /* For atomic aggregate set. */ @@ -1754,7 +1754,7 @@ struct route_map_rule_cmd route_set_atomic_aggregate_cmd = route_set_atomic_aggregate_compile, route_set_atomic_aggregate_free, }; - + /* `set aggregator as AS A.B.C.D' */ struct aggregator { @@ -1813,7 +1813,7 @@ struct route_map_rule_cmd route_set_aggregator_as_cmd = route_set_aggregator_as_compile, route_set_aggregator_as_free, }; - + #ifdef HAVE_IPV6 /* `match ipv6 address IP_ACCESS_LIST' */ @@ -1855,7 +1855,7 @@ struct route_map_rule_cmd route_match_ipv6_address_cmd = route_match_ipv6_address_compile, route_match_ipv6_address_free }; - + /* `match ipv6 next-hop IP_ADDRESS' */ static route_map_result_t @@ -1917,7 +1917,7 @@ struct route_map_rule_cmd route_match_ipv6_next_hop_cmd = route_match_ipv6_next_hop_compile, route_match_ipv6_next_hop_free }; - + /* `match ipv6 address prefix-list PREFIX_LIST' */ static route_map_result_t @@ -1957,7 +1957,7 @@ struct route_map_rule_cmd route_match_ipv6_address_prefix_list_cmd = route_match_ipv6_address_prefix_list_compile, route_match_ipv6_address_prefix_list_free }; - + /* `set ipv6 nexthop global IP_ADDRESS' */ /* Set nexthop to object. ojbect must be pointer to struct attr. */ @@ -2021,7 +2021,7 @@ struct route_map_rule_cmd route_set_ipv6_nexthop_global_cmd = route_set_ipv6_nexthop_global_compile, route_set_ipv6_nexthop_global_free }; - + /* `set ipv6 nexthop local IP_ADDRESS' */ /* Set nexthop to object. ojbect must be pointer to struct attr. */ @@ -2086,7 +2086,7 @@ struct route_map_rule_cmd route_set_ipv6_nexthop_local_cmd = route_set_ipv6_nexthop_local_free }; #endif /* HAVE_IPV6 */ - + /* `set vpnv4 nexthop A.B.C.D' */ static route_map_result_t @@ -2142,7 +2142,7 @@ struct route_map_rule_cmd route_set_vpnv4_nexthop_cmd = route_set_vpnv4_nexthop_compile, route_set_vpnv4_nexthop_free }; - + /* `set originator-id' */ /* For origin set. */ @@ -2199,7 +2199,7 @@ struct route_map_rule_cmd route_set_originator_id_cmd = route_set_originator_id_compile, route_set_originator_id_free, }; - + /* Add bgp route map rule. */ static int bgp_route_match_add (struct vty *vty, struct route_map_index *index, @@ -2408,7 +2408,7 @@ bgp_route_map_update (const char *unused) } } } - + DEFUN (match_peer, match_peer_cmd, "match peer (A.B.C.D|X:X::X:X)", @@ -3562,7 +3562,7 @@ ALIAS (no_set_aggregator_as, "AS number\n" "IP address of aggregator\n") - + #ifdef HAVE_IPV6 DEFUN (match_ipv6_address, match_ipv6_address_cmd, @@ -3833,7 +3833,7 @@ ALIAS (no_match_pathlimit_as, "BGP AS-Pathlimit attribute\n" "Match Pathlimit ASN\n") - + /* Initialization of route map. */ void bgp_route_map_init (void) diff --git a/bgpd/bgp_snmp.c b/bgpd/bgp_snmp.c index c8f2aa54..79aaa03a 100644 --- a/bgpd/bgp_snmp.c +++ b/bgpd/bgp_snmp.c @@ -38,7 +38,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_route.h" #include "bgpd/bgp_fsm.h" #include "bgpd/bgp_snmp.h" - + /* BGP4-MIB described in RFC1657. */ #define BGP4MIB 1,3,6,1,2,1,15 @@ -112,7 +112,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #define OCTET_STRING ASN_OCTET_STR #define IPADDRESS ASN_IPADDRESS #define GAUGE32 ASN_UNSIGNED - + /* Declare static local variables for convenience. */ SNMP_LOCAL_VARIABLES @@ -242,7 +242,7 @@ struct variable bgp_variables[] = 3, {6, 1, 14}}, }; - + static u_char * bgpVersion (struct variable *v, oid name[], size_t *length, int exact, size_t *var_len, WriteMethod **write_method) @@ -835,7 +835,7 @@ bgp4PathAttrTable (struct variable *v, oid name[], size_t *length, } return NULL; } - + /* BGP Traps. */ struct trap_object bgpTrapList[] = { diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index b4d765af..3c6973b0 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -314,7 +314,7 @@ DEFUN_DEPRECATED (neighbor_version, { return CMD_SUCCESS; } - + /* "router bgp" commands. */ DEFUN (router_bgp, router_bgp_cmd, @@ -364,7 +364,7 @@ ALIAS (router_bgp, AS_STR "BGP view\n" "view name\n") - + /* "no router bgp" commands. */ DEFUN (no_router_bgp, no_router_bgp_cmd, @@ -405,7 +405,7 @@ ALIAS (no_router_bgp, AS_STR "BGP view\n" "view name\n") - + /* BGP router-id. */ DEFUN (bgp_router_id, @@ -476,7 +476,7 @@ ALIAS (no_bgp_router_id, BGP_STR "Override configured router identifier\n" "Manually configured router identifier\n") - + /* BGP Cluster ID. */ DEFUN (bgp_cluster_id, @@ -546,7 +546,7 @@ ALIAS (no_bgp_cluster_id, BGP_STR "Configure Route-Reflector Cluster-id\n" "Route-Reflector Cluster-id in IP address format\n") - + DEFUN (bgp_confederation_identifier, bgp_confederation_identifier_cmd, "bgp confederation identifier " CMD_AS_RANGE, @@ -596,7 +596,7 @@ ALIAS (no_bgp_confederation_identifier, "AS confederation parameters\n" "AS number\n" "Set routing domain confederation AS\n") - + DEFUN (bgp_confederation_peers, bgp_confederation_peers_cmd, "bgp confederation peers ." CMD_AS_RANGE, @@ -650,7 +650,7 @@ DEFUN (no_bgp_confederation_peers, } return CMD_SUCCESS; } - + /* Maximum-paths configuration */ DEFUN (bgp_maxpaths, bgp_maxpaths_cmd, @@ -793,7 +793,7 @@ bgp_config_write_maxpaths (struct vty *vty, struct bgp *bgp, afi_t afi, return 0; } - + /* BGP timers. */ DEFUN (bgp_timers, @@ -849,7 +849,7 @@ ALIAS (no_bgp_timers, "BGP timers\n" "Keepalive interval\n" "Holdtime\n") - + DEFUN (bgp_client_to_client_reflection, bgp_client_to_client_reflection_cmd, "bgp client-to-client reflection", @@ -906,7 +906,7 @@ DEFUN (no_bgp_always_compare_med, bgp_flag_unset (bgp, BGP_FLAG_ALWAYS_COMPARE_MED); return CMD_SUCCESS; } - + /* "bgp deterministic-med" configuration. */ DEFUN (bgp_deterministic_med, bgp_deterministic_med_cmd, @@ -1037,7 +1037,7 @@ DEFUN (no_bgp_fast_external_failover, bgp_flag_set (bgp, BGP_FLAG_NO_FAST_EXT_FAILOVER); return CMD_SUCCESS; } - + /* "bgp enforce-first-as" configuration. */ DEFUN (bgp_enforce_first_as, bgp_enforce_first_as_cmd, @@ -1065,7 +1065,7 @@ DEFUN (no_bgp_enforce_first_as, bgp_flag_unset (bgp, BGP_FLAG_ENFORCE_FIRST_AS); return CMD_SUCCESS; } - + /* "bgp bestpath compare-routerid" configuration. */ DEFUN (bgp_bestpath_compare_router_id, bgp_bestpath_compare_router_id_cmd, @@ -1095,7 +1095,7 @@ DEFUN (no_bgp_bestpath_compare_router_id, bgp_flag_unset (bgp, BGP_FLAG_COMPARE_ROUTER_ID); return CMD_SUCCESS; } - + /* "bgp bestpath as-path ignore" configuration. */ DEFUN (bgp_bestpath_aspath_ignore, bgp_bestpath_aspath_ignore_cmd, @@ -1127,7 +1127,7 @@ DEFUN (no_bgp_bestpath_aspath_ignore, bgp_flag_unset (bgp, BGP_FLAG_ASPATH_IGNORE); return CMD_SUCCESS; } - + /* "bgp bestpath as-path confed" configuration. */ DEFUN (bgp_bestpath_aspath_confed, bgp_bestpath_aspath_confed_cmd, @@ -1159,7 +1159,7 @@ DEFUN (no_bgp_bestpath_aspath_confed, bgp_flag_unset (bgp, BGP_FLAG_ASPATH_CONFED); return CMD_SUCCESS; } - + /* "bgp bestpath as-path multipath-relax" configuration. */ DEFUN (bgp_bestpath_aspath_multipath_relax, bgp_bestpath_aspath_multipath_relax_cmd, @@ -1191,7 +1191,7 @@ DEFUN (no_bgp_bestpath_aspath_multipath_relax, bgp_flag_unset (bgp, BGP_FLAG_ASPATH_MULTIPATH_RELAX); return CMD_SUCCESS; } - + /* "bgp log-neighbor-changes" configuration. */ DEFUN (bgp_log_neighbor_changes, bgp_log_neighbor_changes_cmd, @@ -1219,7 +1219,7 @@ DEFUN (no_bgp_log_neighbor_changes, bgp_flag_unset (bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES); return CMD_SUCCESS; } - + /* "bgp bestpath med" configuration. */ DEFUN (bgp_bestpath_med, bgp_bestpath_med_cmd, @@ -1317,7 +1317,7 @@ ALIAS (no_bgp_bestpath_med2, "MED attribute\n" "Treat missing MED as the least preferred one\n" "Compare MED among confederation paths\n") - + /* "no bgp default ipv4-unicast". */ DEFUN (no_bgp_default_ipv4_unicast, no_bgp_default_ipv4_unicast_cmd, @@ -1347,7 +1347,7 @@ DEFUN (bgp_default_ipv4_unicast, bgp_flag_unset (bgp, BGP_FLAG_NO_DEFAULT_IPV4); return CMD_SUCCESS; } - + /* "bgp import-check" configuration. */ DEFUN (bgp_network_import_check, bgp_network_import_check_cmd, @@ -1377,7 +1377,7 @@ DEFUN (no_bgp_network_import_check, bgp_flag_unset (bgp, BGP_FLAG_IMPORT_CHECK); return CMD_SUCCESS; } - + DEFUN (bgp_default_local_preference, bgp_default_local_preference_cmd, "bgp default local-preference <0-4294967295>", @@ -1421,7 +1421,7 @@ ALIAS (no_bgp_default_local_preference, "Configure BGP defaults\n" "local preference (higher=more preferred)\n" "Configure default local preference value\n") - + static int peer_remote_as_vty (struct vty *vty, const char *peer_str, const char *as_str, afi_t afi, safi_t safi) @@ -1481,7 +1481,7 @@ DEFUN (neighbor_remote_as, { return peer_remote_as_vty (vty, argv[0], argv[1], AFI_IP, SAFI_UNICAST); } - + DEFUN (neighbor_peer_group, neighbor_peer_group_cmd, "neighbor WORD peer-group", @@ -1586,7 +1586,7 @@ DEFUN (no_neighbor_peer_group_remote_as, } return CMD_SUCCESS; } - + DEFUN (neighbor_local_as, neighbor_local_as_cmd, NEIGHBOR_CMD2 "local-as " CMD_AS_RANGE, @@ -1696,7 +1696,7 @@ ALIAS (no_neighbor_local_as, "AS number used as local AS\n" "Do not prepend local-as to updates from ebgp peers\n" "Do not prepend local-as to updates from ibgp peers\n") - + DEFUN (neighbor_password, neighbor_password_cmd, NEIGHBOR_CMD2 "password LINE", @@ -1734,7 +1734,7 @@ DEFUN (no_neighbor_password, ret = peer_password_unset (peer); return bgp_vty_return (vty, ret); } - + DEFUN (neighbor_activate, neighbor_activate_cmd, NEIGHBOR_CMD2 "activate", @@ -1773,7 +1773,7 @@ DEFUN (no_neighbor_activate, return bgp_vty_return (vty, ret); } - + DEFUN (neighbor_set_peer_group, neighbor_set_peer_group_cmd, NEIGHBOR_CMD "peer-group WORD", @@ -1855,7 +1855,7 @@ DEFUN (no_neighbor_set_peer_group, return bgp_vty_return (vty, ret); } - + static int peer_flag_modify_vty (struct vty *vty, const char *ip_str, u_int16_t flag, int set) @@ -1908,7 +1908,7 @@ DEFUN (no_neighbor_passive, { return peer_flag_unset_vty (vty, argv[0], PEER_FLAG_PASSIVE); } - + /* neighbor shutdown. */ DEFUN (neighbor_shutdown, neighbor_shutdown_cmd, @@ -1930,7 +1930,7 @@ DEFUN (no_neighbor_shutdown, { return peer_flag_unset_vty (vty, argv[0], PEER_FLAG_SHUTDOWN); } - + /* Deprecated neighbor capability route-refresh. */ DEFUN_DEPRECATED (neighbor_capability_route_refresh, neighbor_capability_route_refresh_cmd, @@ -1954,7 +1954,7 @@ DEFUN_DEPRECATED (no_neighbor_capability_route_refresh, { return CMD_SUCCESS; } - + /* neighbor capability dynamic. */ DEFUN (neighbor_capability_dynamic, neighbor_capability_dynamic_cmd, @@ -1978,7 +1978,7 @@ DEFUN (no_neighbor_capability_dynamic, { return peer_flag_unset_vty (vty, argv[0], PEER_FLAG_DYNAMIC_CAPABILITY); } - + /* neighbor dont-capability-negotiate */ DEFUN (neighbor_dont_capability_negotiate, neighbor_dont_capability_negotiate_cmd, @@ -2000,7 +2000,7 @@ DEFUN (no_neighbor_dont_capability_negotiate, { return peer_flag_unset_vty (vty, argv[0], PEER_FLAG_DONT_CAPABILITY); } - + static int peer_af_flag_modify_vty (struct vty *vty, const char *peer_str, afi_t afi, safi_t safi, u_int32_t flag, int set) @@ -2033,7 +2033,7 @@ peer_af_flag_unset_vty (struct vty *vty, const char *peer_str, afi_t afi, { return peer_af_flag_modify_vty (vty, peer_str, afi, safi, flag, 0); } - + /* neighbor capability orf prefix-list. */ DEFUN (neighbor_capability_orf_prefix, neighbor_capability_orf_prefix_cmd, @@ -2089,7 +2089,7 @@ DEFUN (no_neighbor_capability_orf_prefix, return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty), bgp_node_safi (vty), flag); } - + /* neighbor next-hop-self. */ DEFUN (neighbor_nexthop_self, neighbor_nexthop_self_cmd, @@ -2113,7 +2113,7 @@ DEFUN (no_neighbor_nexthop_self, return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty), bgp_node_safi (vty), PEER_FLAG_NEXTHOP_SELF); } - + /* neighbor remove-private-AS. */ DEFUN (neighbor_remove_private_as, neighbor_remove_private_as_cmd, @@ -2139,7 +2139,7 @@ DEFUN (no_neighbor_remove_private_as, bgp_node_safi (vty), PEER_FLAG_REMOVE_PRIVATE_AS); } - + /* neighbor send-community. */ DEFUN (neighbor_send_community, neighbor_send_community_cmd, @@ -2165,7 +2165,7 @@ DEFUN (no_neighbor_send_community, bgp_node_safi (vty), PEER_FLAG_SEND_COMMUNITY); } - + /* neighbor send-community extended. */ DEFUN (neighbor_send_community_type, neighbor_send_community_type_cmd, @@ -2217,7 +2217,7 @@ DEFUN (no_neighbor_send_community_type, (PEER_FLAG_SEND_COMMUNITY | PEER_FLAG_SEND_EXT_COMMUNITY)); } - + /* neighbor soft-reconfig. */ DEFUN (neighbor_soft_reconfiguration, neighbor_soft_reconfiguration_cmd, @@ -2245,7 +2245,7 @@ DEFUN (no_neighbor_soft_reconfiguration, bgp_node_afi (vty), bgp_node_safi (vty), PEER_FLAG_SOFT_RECONFIG); } - + DEFUN (neighbor_route_reflector_client, neighbor_route_reflector_client_cmd, NEIGHBOR_CMD2 "route-reflector-client", @@ -2277,7 +2277,7 @@ DEFUN (no_neighbor_route_reflector_client, bgp_node_safi (vty), PEER_FLAG_REFLECTOR_CLIENT); } - + static int peer_rsclient_set_vty (struct vty *vty, const char *peer_str, int afi, int safi) @@ -2425,7 +2425,7 @@ peer_rsclient_unset_vty (struct vty *vty, const char *peer_str, return CMD_SUCCESS; } - + /* neighbor route-server-client. */ DEFUN (neighbor_route_server_client, neighbor_route_server_client_cmd, @@ -2449,7 +2449,7 @@ DEFUN (no_neighbor_route_server_client, return peer_rsclient_unset_vty (vty, argv[0], bgp_node_afi(vty), bgp_node_safi(vty)); } - + DEFUN (neighbor_nexthop_local_unchanged, neighbor_nexthop_local_unchanged_cmd, NEIGHBOR_CMD2 "nexthop-local unchanged", @@ -2462,7 +2462,7 @@ DEFUN (neighbor_nexthop_local_unchanged, bgp_node_safi (vty), PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED ); } - + DEFUN (no_neighbor_nexthop_local_unchanged, no_neighbor_nexthop_local_unchanged_cmd, NO_NEIGHBOR_CMD2 "nexthop-local unchanged", @@ -2476,7 +2476,7 @@ DEFUN (no_neighbor_nexthop_local_unchanged, bgp_node_safi (vty), PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED ); } - + DEFUN (neighbor_attr_unchanged, neighbor_attr_unchanged_cmd, NEIGHBOR_CMD2 "attribute-unchanged", @@ -2833,7 +2833,7 @@ DEFUN_DEPRECATED (neighbor_transparent_nexthop, bgp_node_safi (vty), PEER_FLAG_NEXTHOP_UNCHANGED); } - + /* EBGP multihop configuration. */ static int peer_ebgp_multihop_set_vty (struct vty *vty, const char *ip_str, @@ -2907,7 +2907,7 @@ ALIAS (no_neighbor_ebgp_multihop, NEIGHBOR_ADDR_STR2 "Allow EBGP neighbors not on directly connected networks\n" "maximum hop count\n") - + /* disable-connected-check */ DEFUN (neighbor_disable_connected_check, neighbor_disable_connected_check_cmd, @@ -2946,7 +2946,7 @@ ALIAS (no_neighbor_disable_connected_check, NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Enforce EBGP neighbors perform multihop\n") - + DEFUN (neighbor_description, neighbor_description_cmd, NEIGHBOR_CMD2 "description .LINE", @@ -3001,7 +3001,7 @@ ALIAS (no_neighbor_description, NEIGHBOR_ADDR_STR2 "Neighbor specific description\n" "Up to 80 characters describing this neighbor\n") - + /* Neighbor update-source. */ static int peer_update_source_vty (struct vty *vty, const char *peer_str, @@ -3056,7 +3056,7 @@ DEFUN (no_neighbor_update_source, { return peer_update_source_vty (vty, argv[0], NULL); } - + static int peer_default_originate_set_vty (struct vty *vty, const char *peer_str, afi_t afi, safi_t safi, @@ -3123,7 +3123,7 @@ ALIAS (no_neighbor_default_originate, "Originate default route to this neighbor\n" "Route-map to specify criteria to originate default\n" "route-map name\n") - + /* Set neighbor's BGP port. */ static int peer_port_vty (struct vty *vty, const char *ip_str, int afi, @@ -3183,7 +3183,7 @@ ALIAS (no_neighbor_port, NEIGHBOR_ADDR_STR "Neighbor's BGP port\n" "TCP port number\n") - + /* neighbor weight. */ static int peer_weight_set_vty (struct vty *vty, const char *ip_str, @@ -3248,7 +3248,7 @@ ALIAS (no_neighbor_weight, NEIGHBOR_ADDR_STR2 "Set default weight for routes from this neighbor\n" "default weight\n") - + /* Override capability negotiation. */ DEFUN (neighbor_override_capability, neighbor_override_capability_cmd, @@ -3270,7 +3270,7 @@ DEFUN (no_neighbor_override_capability, { return peer_flag_unset_vty (vty, argv[0], PEER_FLAG_OVERRIDE_CAPABILITY); } - + DEFUN (neighbor_strict_capability, neighbor_strict_capability_cmd, NEIGHBOR_CMD "strict-capability-match", @@ -3291,7 +3291,7 @@ DEFUN (no_neighbor_strict_capability, { return peer_flag_unset_vty (vty, argv[0], PEER_FLAG_STRICT_CAP_MATCH); } - + static int peer_timers_set_vty (struct vty *vty, const char *ip_str, const char *keep_str, const char *hold_str) @@ -3312,7 +3312,7 @@ peer_timers_set_vty (struct vty *vty, const char *ip_str, return bgp_vty_return (vty, ret); } - + static int peer_timers_unset_vty (struct vty *vty, const char *ip_str) { @@ -3350,7 +3350,7 @@ DEFUN (no_neighbor_timers, { return peer_timers_unset_vty (vty, argv[0]); } - + static int peer_timers_connect_set_vty (struct vty *vty, const char *ip_str, const char *time_str) @@ -3418,7 +3418,7 @@ ALIAS (no_neighbor_timers_connect, "BGP per neighbor timers\n" "BGP connect timer\n" "Connect timer\n") - + static int peer_advertise_interval_vty (struct vty *vty, const char *ip_str, const char *time_str, int set) @@ -3472,7 +3472,7 @@ ALIAS (no_neighbor_advertise_interval, NEIGHBOR_ADDR_STR "Minimum interval between sending BGP routing updates\n" "time in seconds\n") - + /* neighbor interface */ static int peer_interface_vty (struct vty *vty, const char *ip_str, const char *str) @@ -3514,7 +3514,7 @@ DEFUN (no_neighbor_interface, { return peer_interface_vty (vty, argv[0], NULL); } - + /* Set distribute list to the peer. */ static int peer_distribute_set_vty (struct vty *vty, const char *ip_str, @@ -3595,7 +3595,7 @@ DEFUN (no_neighbor_distribute_list, return peer_distribute_unset_vty (vty, argv[0], bgp_node_afi (vty), bgp_node_safi (vty), argv[2]); } - + /* Set prefix list to the peer. */ static int peer_prefix_list_set_vty (struct vty *vty, const char *ip_str, afi_t afi, @@ -3672,7 +3672,7 @@ DEFUN (no_neighbor_prefix_list, return peer_prefix_list_unset_vty (vty, argv[0], bgp_node_afi (vty), bgp_node_safi (vty), argv[2]); } - + static int peer_aslist_set_vty (struct vty *vty, const char *ip_str, afi_t afi, safi_t safi, @@ -3749,7 +3749,7 @@ DEFUN (no_neighbor_filter_list, return peer_aslist_unset_vty (vty, argv[0], bgp_node_afi (vty), bgp_node_safi (vty), argv[2]); } - + /* Set route-map to the peer. */ static int peer_route_map_set_vty (struct vty *vty, const char *ip_str, @@ -3838,7 +3838,7 @@ DEFUN (no_neighbor_route_map, return peer_route_map_unset_vty (vty, argv[0], bgp_node_afi (vty), bgp_node_safi (vty), argv[2]); } - + /* Set unsuppress-map to the peer. */ static int peer_unsuppress_map_set_vty (struct vty *vty, const char *ip_str, afi_t afi, @@ -3897,7 +3897,7 @@ DEFUN (no_neighbor_unsuppress_map, return peer_unsuppress_map_unset_vty (vty, argv[0], bgp_node_afi (vty), bgp_node_safi (vty)); } - + static int peer_maximum_prefix_set_vty (struct vty *vty, const char *ip_str, afi_t afi, safi_t safi, const char *num_str, @@ -4107,7 +4107,7 @@ ALIAS (no_neighbor_maximum_prefix, "Threshold value (%) at which to generate a warning msg\n" "Restart bgp connection after limit is exceeded\n" "Restart interval in minutes") - + /* "neighbor allowas-in" */ DEFUN (neighbor_allowas_in, neighbor_allowas_in_cmd, @@ -4162,7 +4162,7 @@ DEFUN (no_neighbor_allowas_in, return bgp_vty_return (vty, ret); } - + DEFUN (neighbor_ttl_security, neighbor_ttl_security_cmd, NEIGHBOR_CMD2 "ttl-security hops <1-254>", @@ -4198,7 +4198,7 @@ DEFUN (no_neighbor_ttl_security, return bgp_vty_return (vty, peer_ttl_security_hops_unset (peer)); } - + /* Address family configuration. */ DEFUN (address_family_ipv4, address_family_ipv4_cmd, @@ -4282,7 +4282,7 @@ DEFUN (exit_address_family, vty->node = BGP_NODE; return CMD_SUCCESS; } - + /* BGP clear sort. */ enum clear_sort { @@ -4636,7 +4636,7 @@ ALIAS (clear_ip_bgp_as, BGP_STR "Address family\n" "Clear peers with the AS number\n") - + /* Outbound soft-reconfiguration */ DEFUN (clear_ip_bgp_all_soft_out, clear_ip_bgp_all_soft_out_cmd, @@ -5263,7 +5263,7 @@ ALIAS (clear_bgp_as_soft_out, "Address family\n" "Clear peers with the AS number\n" "Soft reconfig outbound update\n") - + /* Inbound soft-reconfiguration */ DEFUN (clear_ip_bgp_all_soft_in, clear_ip_bgp_all_soft_in_cmd, @@ -6225,7 +6225,7 @@ ALIAS (clear_bgp_as_in_prefix_filter, "Clear peers with the AS number\n" "Soft reconfig inbound update\n" "Push out prefix-list ORF and do inbound soft reconfig\n") - + /* Both soft-reconfiguration */ DEFUN (clear_ip_bgp_all_soft, clear_ip_bgp_all_soft_cmd, @@ -6599,7 +6599,7 @@ ALIAS (clear_bgp_as_soft, "Address family\n" "Clear peers with the AS number\n" "Soft reconfig\n") - + /* RS-client soft reconfiguration. */ #ifdef HAVE_IPV6 DEFUN (clear_bgp_all_rsclient, @@ -7278,7 +7278,7 @@ DEFUN (show_ipv6_mbgp_summary, return bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_MULTICAST); } #endif /* HAVE_IPV6 */ - + const char * afi_safi_print (afi_t afi, safi_t safi) { @@ -8188,7 +8188,7 @@ ALIAS (show_ip_bgp_instance_neighbors_peer, "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" "Neighbor to display information about\n") - + /* Show BGP's AS paths internal data. There are both `show ip bgp paths' and `show ip mbgp paths'. Those functions results are the same.*/ @@ -8221,7 +8221,7 @@ DEFUN (show_ip_bgp_ipv4_paths, return CMD_SUCCESS; } - + #include "hash.h" static void @@ -8264,7 +8264,7 @@ DEFUN (show_ip_bgp_attr_info, attr_show_all (vty); return CMD_SUCCESS; } - + static int bgp_write_rsclient_summary (struct vty *vty, struct peer *rsclient, afi_t afi, safi_t safi) @@ -8585,7 +8585,7 @@ ALIAS (show_bgp_instance_ipv6_safi_rsclient_summary, "Summary of all Route Server Clients\n") #endif /* HAVE IPV6 */ - + /* Redistribute VTY commands. */ DEFUN (bgp_redistribute_ipv4, @@ -8799,7 +8799,7 @@ ALIAS (no_bgp_redistribute_ipv4_rmap_metric, "Default metric\n" "Route map reference\n" "Pointer to route-map entries\n") - + #ifdef HAVE_IPV6 DEFUN (bgp_redistribute_ipv6, bgp_redistribute_ipv6_cmd, @@ -9014,7 +9014,7 @@ ALIAS (no_bgp_redistribute_ipv6_rmap_metric, "Route map reference\n" "Pointer to route-map entries\n") #endif /* HAVE_IPV6 */ - + int bgp_config_write_redistribute (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, int *write) @@ -9047,7 +9047,7 @@ bgp_config_write_redistribute (struct vty *vty, struct bgp *bgp, afi_t afi, } return *write; } - + /* BGP node structure. */ static struct cmd_node bgp_node = { @@ -9090,7 +9090,7 @@ static struct cmd_node bgp_vpnv4_node = "%s(config-router-af)# ", 1 }; - + static void community_list_vty (void); void @@ -10231,7 +10231,7 @@ bgp_vty_init (void) /* Community-list. */ community_list_vty (); } - + #include "memory.h" #include "bgp_regex.h" #include "bgp_clist.h" @@ -10630,7 +10630,7 @@ DEFUN (show_ip_community_list_arg, return CMD_SUCCESS; } - + static int extcommunity_list_set_vty (struct vty *vty, int argc, const char **argv, int style, int reject_all_digit_name) @@ -10980,7 +10980,7 @@ DEFUN (show_ip_extcommunity_list_arg, return CMD_SUCCESS; } - + /* Return configuration string of community-list entry. */ static const char * community_list_config_str (struct community_entry *entry) diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 0f212321..f18d916f 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -38,7 +38,7 @@ Boston, MA 02111-1307, USA. */ #include "bgpd/bgp_fsm.h" #include "bgpd/bgp_debug.h" #include "bgpd/bgp_mpath.h" - + /* All information about zebra. */ struct zclient *zclient = NULL; struct in_addr router_id_zebra; @@ -381,7 +381,7 @@ zebra_read_ipv6 (int command, struct zclient *zclient, zebra_size_t length) return 0; } #endif /* HAVE_IPV6 */ - + struct interface * if_lookup_by_ipv4 (struct in_addr *addr) { @@ -943,7 +943,7 @@ bgp_zebra_withdraw (struct prefix *p, struct bgp_info *info, safi_t safi) } #endif /* HAVE_IPV6 */ } - + /* Other routes redistribution into BGP. */ int bgp_redistribute_set (struct bgp *bgp, afi_t afi, int type) @@ -1069,7 +1069,7 @@ bgp_redistribute_metric_unset (struct bgp *bgp, afi_t afi, int type) return 1; } - + void bgp_zclient_reset (void) { diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 2fe300c5..19b96fa9 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -61,7 +61,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #ifdef HAVE_SNMP #include "bgpd/bgp_snmp.h" #endif /* HAVE_SNMP */ - + /* BGP process wide configuration. */ static struct bgp_master bgp_master; @@ -72,7 +72,7 @@ struct bgp_master *bm; /* BGP community-list. */ struct community_list_handler *bgp_clist; - + /* BGP global flag manipulation. */ int bgp_option_set (int flag) @@ -115,7 +115,7 @@ bgp_option_check (int flag) { return CHECK_FLAG (bm->options, flag); } - + /* BGP flag manipulation. */ int bgp_flag_set (struct bgp *bgp, int flag) @@ -136,7 +136,7 @@ bgp_flag_check (struct bgp *bgp, int flag) { return CHECK_FLAG (bgp->flags, flag); } - + /* Internal function to set BGP structure configureation flag. */ static void bgp_config_set (struct bgp *bgp, int config) @@ -155,7 +155,7 @@ bgp_config_check (struct bgp *bgp, int config) { return CHECK_FLAG (bgp->config, config); } - + /* Set BGP router identifier. */ int bgp_router_id_set (struct bgp *bgp, struct in_addr *id) @@ -242,7 +242,7 @@ bgp_cluster_id_unset (struct bgp *bgp) } return 0; } - + /* time_t value that is monotonicly increasing * and uneffected by adjustments to system clock */ @@ -273,7 +273,7 @@ bgp_timers_unset (struct bgp *bgp) return 0; } - + /* BGP confederation configuration. */ int bgp_confederation_id_set (struct bgp *bgp, as_t as) @@ -485,7 +485,7 @@ bgp_confederation_peers_remove (struct bgp *bgp, as_t as) return 0; } - + /* Local preference configuration. */ int bgp_default_local_preference_set (struct bgp *bgp, u_int32_t local_pref) @@ -508,7 +508,7 @@ bgp_default_local_preference_unset (struct bgp *bgp) return 0; } - + /* If peer is RSERVER_CLIENT in at least one address family and is not member of a peer_group for that family, return 1. Used to check wether the peer is included in list bgp->rsclient. */ @@ -1325,7 +1325,7 @@ peer_delete (struct peer *peer) return 0; } - + static int peer_group_cmp (struct peer_group *g1, struct peer_group *g2) { @@ -1926,7 +1926,7 @@ peer_group_unbind (struct bgp *bgp, struct peer *peer, return 0; } - + /* BGP instance creation by `router bgp' commands. */ static struct bgp * bgp_create (as_t *as, const char *name) @@ -2181,7 +2181,7 @@ bgp_free (struct bgp *bgp) } XFREE (MTYPE_BGP, bgp); } - + struct peer * peer_lookup (struct bgp *bgp, union sockunion *su) { @@ -2250,7 +2250,7 @@ peer_lookup_with_open (union sockunion *su, as_t remote_as, } return NULL; } - + /* If peer is configured at least one address family return 1. */ int peer_active (struct peer *peer) @@ -2276,7 +2276,7 @@ peer_active_nego (struct peer *peer) return 1; return 0; } - + /* peer_flag_change_type. */ enum peer_change_type { @@ -2695,7 +2695,7 @@ peer_af_flag_unset (struct peer *peer, afi_t afi, safi_t safi, u_int32_t flag) { return peer_af_flag_modify (peer, afi, safi, flag, 0); } - + /* EBGP multihop configuration. */ int peer_ebgp_multihop_set (struct peer *peer, int ttl) @@ -2794,7 +2794,7 @@ peer_ebgp_multihop_unset (struct peer *peer) } return 0; } - + /* Neighbor description. */ int peer_description_set (struct peer *peer, char *desc) @@ -2817,7 +2817,7 @@ peer_description_unset (struct peer *peer) return 0; } - + /* Neighbor update-source. */ int peer_update_source_if_set (struct peer *peer, const char *ifname) @@ -3037,7 +3037,7 @@ peer_update_source_unset (struct peer *peer) } return 0; } - + int peer_default_originate_set (struct peer *peer, afi_t afi, safi_t safi, const char *rmap) @@ -3142,7 +3142,7 @@ peer_default_originate_unset (struct peer *peer, afi_t afi, safi_t safi) } return 0; } - + int peer_port_set (struct peer *peer, u_int16_t port) { @@ -3156,7 +3156,7 @@ peer_port_unset (struct peer *peer) peer->port = BGP_PORT_DEFAULT; return 0; } - + /* neighbor weight. */ int peer_weight_set (struct peer *peer, u_int16_t weight) @@ -3204,7 +3204,7 @@ peer_weight_unset (struct peer *peer) } return 0; } - + int peer_timers_set (struct peer *peer, u_int32_t keepalive, u_int32_t holdtime) { @@ -3274,7 +3274,7 @@ peer_timers_unset (struct peer *peer) return 0; } - + int peer_timers_connect_set (struct peer *peer, u_int32_t connect) { @@ -3309,7 +3309,7 @@ peer_timers_connect_unset (struct peer *peer) return 0; } - + int peer_advertise_interval_set (struct peer *peer, u_int32_t routeadv) { @@ -3342,7 +3342,7 @@ peer_advertise_interval_unset (struct peer *peer) return 0; } - + /* neighbor interface */ int peer_interface_set (struct peer *peer, const char *str) @@ -3363,7 +3363,7 @@ peer_interface_unset (struct peer *peer) return 0; } - + /* Allow-as in. */ int peer_allowas_in_set (struct peer *peer, afi_t afi, safi_t safi, int allow_num) @@ -3424,7 +3424,7 @@ peer_allowas_in_unset (struct peer *peer, afi_t afi, safi_t safi) } return 0; } - + int peer_local_as_set (struct peer *peer, as_t as, int no_prepend, int replace_as) { @@ -3552,7 +3552,7 @@ peer_local_as_unset (struct peer *peer) } return 0; } - + /* Set password for authenticating with the peer. */ int peer_password_set (struct peer *peer, const char *password) @@ -3657,7 +3657,7 @@ peer_password_unset (struct peer *peer) return 0; } - + /* Set distribute list to the peer. */ int peer_distribute_set (struct peer *peer, afi_t afi, safi_t safi, int direct, @@ -3817,7 +3817,7 @@ peer_distribute_update (struct access_list *access) } } } - + /* Set prefix list to the peer. */ int peer_prefix_list_set (struct peer *peer, afi_t afi, safi_t safi, int direct, @@ -3976,7 +3976,7 @@ peer_prefix_list_update (struct prefix_list *plist) } } } - + int peer_aslist_set (struct peer *peer, afi_t afi, safi_t safi, int direct, const char *name) @@ -4130,7 +4130,7 @@ peer_aslist_update (void) } } } - + /* Set route-map to the peer. */ int peer_route_map_set (struct peer *peer, afi_t afi, safi_t safi, int direct, @@ -4238,7 +4238,7 @@ peer_route_map_unset (struct peer *peer, afi_t afi, safi_t safi, int direct) } return 0; } - + /* Set unsuppress-map to the peer. */ int peer_unsuppress_map_set (struct peer *peer, afi_t afi, safi_t safi, @@ -4320,7 +4320,7 @@ peer_unsuppress_map_unset (struct peer *peer, afi_t afi, safi_t safi) } return 0; } - + int peer_maximum_prefix_set (struct peer *peer, afi_t afi, safi_t safi, u_int32_t max, u_char threshold, @@ -4547,7 +4547,7 @@ peer_ttl_security_hops_unset (struct peer *peer) return peer_ebgp_multihop_unset (opeer); } - + int peer_clear (struct peer *peer) { @@ -4652,7 +4652,7 @@ peer_clear_soft (struct peer *peer, afi_t afi, safi_t safi, } return 0; } - + /* Display peer uptime.*/ /* XXX: why does this function return char * when it takes buffer? */ char * @@ -4697,7 +4697,7 @@ peer_uptime (time_t uptime2, char *buf, size_t len) tm->tm_yday/7, tm->tm_yday - ((tm->tm_yday/7) * 7), tm->tm_hour); return buf; } - + static void bgp_config_write_filter (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi) @@ -5394,7 +5394,7 @@ bgp_master_init (void) bm->start_time = bgp_clock (); } - + void bgp_init (void) { diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 688f459f..a1b1273b 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -854,7 +854,7 @@ extern struct peer *peer_create_accept (struct bgp *); extern char *peer_uptime (time_t, char *, size_t); extern int bgp_config_write (struct vty *); extern void bgp_config_write_family_header (struct vty *, afi_t, safi_t, int *); - + extern void bgp_master_init (void); extern void bgp_init (void); diff --git a/doc/mpls/opaque_lsa.txt b/doc/mpls/opaque_lsa.txt index 7d5c7fed..ae89ee34 100644 --- a/doc/mpls/opaque_lsa.txt +++ b/doc/mpls/opaque_lsa.txt @@ -20,7 +20,7 @@ | (Callback functions) | +----------------------+ - + 2. Self-originated Opaque-LSAs per LSA-type. 2.1 Type-11 (AS-external) Opaque-LSAs @@ -110,7 +110,7 @@ : | +-------------+ : : --- : :..........................................................................: - + 2.2 Type-10 (area-local) Opaque-LSAs struct @@ -204,7 +204,7 @@ : | +-------------+ : : --- : :..........................................................................: - + 2.3 Type-9 (link-local) Opaque-LSAs struct @@ -300,7 +300,7 @@ : +-------------+ : :..........................................................................: - + 3. Internal structures for MPLS-TE parameter management. struct diff --git a/lib/command.c b/lib/command.c index a2373644..7249c653 100644 --- a/lib/command.c +++ b/lib/command.c @@ -183,7 +183,7 @@ print_version (const char *progname) printf ("%s\n", QUAGGA_COPYRIGHT); } - + /* Utility function to concatenate argv argument into a single string with inserting ' ' character between each argument. */ char * diff --git a/lib/distribute.c b/lib/distribute.c index 8d6f6377..ba8043cf 100644 --- a/lib/distribute.c +++ b/lib/distribute.c @@ -34,7 +34,7 @@ struct hash *disthash; /* Hook functions. */ void (*distribute_add_hook) (struct distribute *); void (*distribute_delete_hook) (struct distribute *); - + static struct distribute * distribute_new (void) { @@ -133,7 +133,7 @@ distribute_cmp (const struct distribute *dist1, const struct distribute *dist2) return 1; return 0; } - + /* Set access-list name to the distribute list. */ static struct distribute * distribute_list_set (const char *ifname, enum distribute_type type, diff --git a/lib/filter.c b/lib/filter.c index 69341824..96605c7d 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -110,7 +110,7 @@ static struct access_master access_master_ipv6 = NULL, }; #endif /* HAVE_IPV6 */ - + static struct access_master * access_master_get (afi_t afi) { @@ -208,7 +208,7 @@ filter_match_zebra (struct filter *mfilter, struct prefix *p) else return 0; } - + /* Allocate new access list structure. */ static struct access_list * access_list_new (void) @@ -501,7 +501,7 @@ access_list_filter_delete (struct access_list *access, struct filter *filter) if (master->delete_hook) (*master->delete_hook) (access); } - + /* deny Specify packets to reject permit Specify packets to forward diff --git a/lib/getopt.c b/lib/getopt.c index c784fb6c..064909d3 100644 --- a/lib/getopt.c +++ b/lib/getopt.c @@ -23,7 +23,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ - + /* This tells Alpha OSF/1 not to define a getopt prototype in . Ditto for AIX 3.2 and . */ #ifndef _NO_PROTO @@ -193,7 +193,7 @@ static enum /* Value of POSIXLY_CORRECT environment variable. */ static char *posixly_correct; - + #ifdef __GNU_LIBRARY__ /* We want to avoid inclusion of string.h with non-GNU libraries because there are many ways it can cause trouble. @@ -243,7 +243,7 @@ extern int strlen (const char *); #endif /* __GNUC__ */ #endif /* not __GNU_LIBRARY__ */ - + /* Handle permutation of arguments. */ /* Describe the part of ARGV that contains non-options that have @@ -456,7 +456,7 @@ _getopt_initialize (argc, argv, optstring) return optstring; } - + /* Scan elements of ARGV (whose length is ARGC) for option characters given in OPTSTRING. @@ -986,7 +986,7 @@ getopt (argc, argv, optstring) #endif /* REALLY_NEED_PLAIN_GETOPT */ #endif /* Not ELIDE_CODE. */ - + #ifdef TEST /* Compile with -DTEST to make an executable for use in testing diff --git a/lib/getopt1.c b/lib/getopt1.c index 985f12c5..fa766747 100644 --- a/lib/getopt1.c +++ b/lib/getopt1.c @@ -19,7 +19,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ - + #ifdef HAVE_CONFIG_H #include #endif @@ -95,7 +95,7 @@ getopt_long_only (argc, argv, options, long_options, opt_index) #endif /* Not ELIDE_CODE. */ - + #ifdef TEST #include diff --git a/lib/if.c b/lib/if.c index 6348403b..18e2fb31 100644 --- a/lib/if.c +++ b/lib/if.c @@ -35,7 +35,7 @@ #include "buffer.h" #include "str.h" #include "log.h" - + /* Master list of interfaces. */ struct list *iflist; @@ -45,7 +45,7 @@ struct if_master int (*if_new_hook) (struct interface *); int (*if_delete_hook) (struct interface *); } if_master; - + /* Compare interface names, returning an integer greater than, equal to, or * less than 0, (following the strcmp convention), according to the * relationship between ifp1 and ifp2. Interface names consist of an @@ -513,7 +513,7 @@ DEFUN (no_interface_desc, return CMD_SUCCESS; } - + #ifdef SUNOS_5 /* Need to handle upgrade from SUNWzebra to Quagga. SUNWzebra created * a seperate struct interface for each logical interface, so config @@ -555,7 +555,7 @@ if_sunwzebra_get (const char *name, size_t nlen) return if_get_by_name_len (name, nlen); } #endif /* SUNOS_5 */ - + DEFUN (interface, interface_cmd, "interface IFNAME", @@ -806,7 +806,7 @@ if_indextoname (unsigned int ifindex, char *name) return ifp->name; } #endif - + #if 0 /* this route_table of struct connected's is unused * however, it would be good to use a route_table rather than * a list.. diff --git a/lib/if_rmap.c b/lib/if_rmap.c index 7d049b87..e4a83de8 100644 --- a/lib/if_rmap.c +++ b/lib/if_rmap.c @@ -32,7 +32,7 @@ struct hash *ifrmaphash; /* Hook functions. */ static void (*if_rmap_add_hook) (struct if_rmap *) = NULL; static void (*if_rmap_delete_hook) (struct if_rmap *) = NULL; - + static struct if_rmap * if_rmap_new (void) { @@ -122,7 +122,7 @@ if_rmap_hash_cmp (const void *arg1, const void* arg2) return strcmp (if_rmap1->ifname, if_rmap2->ifname) == 0; } - + static struct if_rmap * if_rmap_set (const char *ifname, enum if_rmap_type type, const char *routemap_name) @@ -273,7 +273,7 @@ ALIAS (no_if_rmap, "Route map for input filtering\n" "Route map for output filtering\n" "Route map interface name\n") - + /* Configuration write function. */ int config_write_if_rmap (struct vty *vty) diff --git a/lib/keychain.c b/lib/keychain.c index 6719cebf..762c4629 100644 --- a/lib/keychain.c +++ b/lib/keychain.c @@ -226,7 +226,7 @@ key_delete (struct keychain *keychain, struct key *key) free (key->string); key_free (key); } - + DEFUN (key_chain, key_chain_cmd, "key chain WORD", @@ -531,7 +531,7 @@ key_lifetime_infinite_set (struct vty *vty, struct key_range *krange, return CMD_SUCCESS; } - + DEFUN (accept_lifetime_day_month_day_month, accept_lifetime_day_month_day_month_cmd, "accept-lifetime HH:MM:SS <1-31> MONTH <1993-2035> HH:MM:SS <1-31> MONTH <1993-2035>", @@ -689,7 +689,7 @@ DEFUN (accept_lifetime_duration_month_day, return key_lifetime_duration_set (vty, &key->accept, argv[0], argv[2], argv[1], argv[3], argv[4]); } - + DEFUN (send_lifetime_day_month_day_month, send_lifetime_day_month_day_month_cmd, "send-lifetime HH:MM:SS <1-31> MONTH <1993-2035> HH:MM:SS <1-31> MONTH <1993-2035>", @@ -847,7 +847,7 @@ DEFUN (send_lifetime_duration_month_day, return key_lifetime_duration_set (vty, &key->send, argv[0], argv[2], argv[1], argv[3], argv[4]); } - + static struct cmd_node keychain_node = { KEYCHAIN_NODE, diff --git a/lib/linklist.c b/lib/linklist.c index 485a80be..370b2fa6 100644 --- a/lib/linklist.c +++ b/lib/linklist.c @@ -23,7 +23,7 @@ #include "linklist.h" #include "memory.h" - + /* Allocate new list. */ struct list * list_new (void) @@ -51,7 +51,7 @@ listnode_free (struct listnode *node) { XFREE (MTYPE_LINK_NODE, node); } - + /* Add new data to the list. */ void listnode_add (struct list *list, void *val) @@ -242,7 +242,7 @@ listnode_lookup (struct list *list, void *data) return node; return NULL; } - + /* Delete the node from list. For ospfd and ospf6d. */ void list_delete_node (struct list *list, struct listnode *node) @@ -258,7 +258,7 @@ list_delete_node (struct list *list, struct listnode *node) list->count--; listnode_free (node); } - + /* ospf_spf.c */ void list_add_node_prev (struct list *list, struct listnode *current, void *val) diff --git a/lib/log.c b/lib/log.c index 55a3b052..1058844b 100644 --- a/lib/log.c +++ b/lib/log.c @@ -69,7 +69,7 @@ const char *zlog_priority[] = }; - + /* For time string format. */ size_t @@ -145,7 +145,7 @@ time_print(FILE *fp, struct timestamp_control *ctl) fprintf(fp, "%s ", ctl->buf); } - + /* va_list version of zlog. */ static void vzlog (struct zlog *zl, int priority, const char *format, va_list args) @@ -619,7 +619,7 @@ _zlog_assert_failed (const char *assertion, const char *file, abort(); } - + /* Open log stream */ struct zlog * openzlog (const char *progname, zlog_proto_t protocol, @@ -756,7 +756,7 @@ zlog_rotate (struct zlog *zl) return 1; } - + /* Message lookup function. */ const char * lookup (const struct message *mes, int key) diff --git a/lib/memory.c b/lib/memory.c index 684ebcff..620bdee5 100644 --- a/lib/memory.c +++ b/lib/memory.c @@ -32,7 +32,7 @@ static void alloc_inc (int); static void alloc_dec (int); static void log_memstats(int log_priority); - + static const struct message mstr [] = { { MTYPE_THREAD, "thread" }, @@ -42,7 +42,7 @@ static const struct message mstr [] = { MTYPE_IF, "interface" }, { 0, NULL }, }; - + /* Fatal memory allocation error occured. */ static void __attribute__ ((noreturn)) zerror (const char *fname, int type, size_t size) @@ -150,7 +150,7 @@ zstrdup (int type, const char *str) alloc_inc (type); return dup; } - + #ifdef MEMORY_LOG static struct { @@ -259,7 +259,7 @@ alloc_dec (int type) { mstat[type].alloc--; } - + /* Looking up memory status from vty interface. */ #include "vector.h" #include "vty.h" @@ -558,7 +558,7 @@ memory_init (void) install_element (ENABLE_NODE, &show_memory_ospf6_cmd); install_element (ENABLE_NODE, &show_memory_isis_cmd); } - + /* Stats querying from users */ /* Return a pointer to a human friendly string describing * the byte count passed in. E.g: diff --git a/lib/plist.c b/lib/plist.c index 0f802a83..7416ebd2 100644 --- a/lib/plist.c +++ b/lib/plist.c @@ -110,7 +110,7 @@ static struct prefix_master prefix_master_orf = NULL, NULL, }; - + static struct prefix_master * prefix_master_get (afi_t afi) { @@ -621,7 +621,7 @@ prefix_list_print (struct prefix_list *plist) } } } - + /* Retrun 1 when plist already include pentry policy. */ static struct prefix_list_entry * prefix_entry_dup_check (struct prefix_list *plist, @@ -1165,7 +1165,7 @@ vty_clear_prefix_list (struct vty *vty, afi_t afi, const char *name, } return CMD_SUCCESS; } - + DEFUN (ip_prefix_list, ip_prefix_list_cmd, "ip prefix-list WORD (deny|permit) (A.B.C.D/M|any)", @@ -1759,7 +1759,7 @@ DEFUN (clear_ip_prefix_list_name_prefix, { return vty_clear_prefix_list (vty, AFI_IP, argv[0], argv[1]); } - + #ifdef HAVE_IPV6 DEFUN (ipv6_prefix_list, ipv6_prefix_list_cmd, @@ -2355,7 +2355,7 @@ DEFUN (clear_ipv6_prefix_list_name_prefix, return vty_clear_prefix_list (vty, AFI_IP6, argv[0], argv[1]); } #endif /* HAVE_IPV6 */ - + /* Configuration write function. */ static int config_write_prefix_afi (afi_t afi, struct vty *vty) diff --git a/lib/prefix.c b/lib/prefix.c index a3b1adf8..dbfdc830 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -27,7 +27,7 @@ #include "sockunion.h" #include "memory.h" #include "log.h" - + /* Maskbit. */ static const u_char maskbit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff}; @@ -186,7 +186,7 @@ prefix6_bit (const struct in6_addr *prefix, const u_char prefixlen) { return prefix_bit((const u_char *) &prefix->s6_addr, prefixlen); } - + /* Address Famiy Identifier to Address Family converter. */ int afi2family (afi_t afi) @@ -494,7 +494,7 @@ prefix_ipv4_any (const struct prefix_ipv4 *p) { return (p->prefix.s_addr == 0 && p->prefixlen == 0); } - + #ifdef HAVE_IPV6 /* Allocate a new ip version 6 route */ diff --git a/lib/privs.c b/lib/privs.c index 69606f57..e182543a 100644 --- a/lib/privs.c +++ b/lib/privs.c @@ -47,7 +47,7 @@ struct _pset { typedef cap_value_t pvalue_t; typedef struct _pset pset_t; typedef cap_t pstorage_t; - + #elif defined (HAVE_SOLARIS_CAPABILITIES) typedef priv_t pvalue_t; typedef priv_set_t pset_t; @@ -56,7 +56,7 @@ typedef priv_set_t *pstorage_t; #error "HAVE_CAPABILITIES defined, but neither LCAPS nor Solaris Capabilties!" #endif /* HAVE_LCAPS */ #endif /* HAVE_CAPABILITIES */ - + /* the default NULL state we report is RAISED, but could be LOWERED if * zprivs_terminate is called and the NULL handler is installed. */ @@ -139,7 +139,7 @@ static struct [ZCAP_FOWNER] = { 1, (pvalue_t []) { PRIV_FILE_OWNER }, }, #endif /* HAVE_SOLARIS_CAPABILITIES */ }; - + #ifdef HAVE_LCAPS /* Linux forms of capabilities methods */ /* convert zebras privileges to system capabilities */ @@ -339,7 +339,7 @@ zprivs_caps_terminate (void) cap_free (zprivs_state.caps); } #elif defined (HAVE_SOLARIS_CAPABILITIES) /* !HAVE_LCAPS */ - + /* Solaris specific capability/privilege methods * * Resources: @@ -556,7 +556,7 @@ zprivs_caps_terminate (void) #error "Neither Solaris nor Linux capabilities, dazed and confused..." #endif /* HAVE_LCAPS */ #endif /* HAVE_CAPABILITIES */ - + int zprivs_change_uid (zebra_privs_ops_t op) { diff --git a/lib/regex-gnu.h b/lib/regex-gnu.h index d88ab92b..4cee464f 100644 --- a/lib/regex-gnu.h +++ b/lib/regex-gnu.h @@ -165,7 +165,7 @@ typedef unsigned long int reg_syntax_t; stored in the pattern buffer, so changing this does not affect already-compiled regexps. */ extern reg_syntax_t re_syntax_options; - + /* Define combinations of the above bits for the standard possibilities. (The [[[ comments delimit what gets put into the Texinfo file, so don't delete them!) */ @@ -234,7 +234,7 @@ extern reg_syntax_t re_syntax_options; | RE_NO_BK_PARENS | RE_NO_BK_REFS \ | RE_NO_BK_VBAR | RE_UNMATCHED_RIGHT_PAREN_ORD) /* [[[end syntaxes]]] */ - + /* Maximum number of duplicates an interval can allow. Some systems (erroneously) define this in other header files, but we want our value, so remove any previous define. */ @@ -309,7 +309,7 @@ typedef enum REG_ESIZE, /* Compiled pattern bigger than 2^16 bytes. */ REG_ERPAREN /* Unmatched ) or \); not returned from regcomp. */ } reg_errcode_t; - + /* This data structure represents a compiled pattern. Before calling the pattern compiler, the fields `buffer', `allocated', `fastmap', `translate', and `no_sub' can be set. After the pattern has been @@ -389,7 +389,7 @@ struct re_pattern_buffer }; typedef struct re_pattern_buffer regex_t; - + /* Type for byte offsets within the string. POSIX mandates this. */ typedef int regoff_t; @@ -420,7 +420,7 @@ typedef struct regoff_t rm_so; /* Byte offset from string's start to substring's start. */ regoff_t rm_eo; /* Byte offset from string's start to substring's end. */ } regmatch_t; - + /* Declarations for routines. */ /* To avoid duplicating every routine declaration -- once with a @@ -532,7 +532,7 @@ extern void regfree _RE_ARGS ((regex_t *__preg)); #endif /* C++ */ #endif /* regex.h */ - + /* Local variables: make-backup-files: t diff --git a/lib/regex.c b/lib/regex.c index a22e03f6..122f4476 100644 --- a/lib/regex.c +++ b/lib/regex.c @@ -209,7 +209,7 @@ init_syntax_once () # define SYNTAX(c) re_syntax_table[c] #endif /* not emacs */ - + /* Get the interface, including the syntax bits. */ #include @@ -279,7 +279,7 @@ init_syntax_once () /* As in Harbison and Steele. */ # define SIGN_EXTEND_CHAR(c) ((((unsigned char) (c)) ^ 128) - 128) #endif - + /* Should we use malloc or alloca? If REGEX_MALLOC is not defined, we use `alloca' instead of `malloc'. This is because using malloc in re_search* or re_match* could cause memory leaks when C-g is used in @@ -388,7 +388,7 @@ static int re_match_2_internal PARAMS ((struct re_pattern_buffer *bufp, int pos, struct re_registers *regs, int stop)); - + /* These are the command codes that appear in compiled regular expressions. Some opcodes are followed by argument bytes. A command code can specify any interpretation whatsoever for its @@ -527,7 +527,7 @@ typedef enum notsyntaxspec #endif /* emacs */ } re_opcode_t; - + /* Common operations on the compiled pattern. */ /* Store NUMBER in two contiguous bytes starting at DESTINATION. */ @@ -604,7 +604,7 @@ extract_number_and_incr (destination, source) # endif /* not EXTRACT_MACROS */ #endif /* DEBUG */ - + /* If DEBUG is defined, Regex prints many voluminous messages about what it is doing (if the variable `debug' is nonzero). If linked with the main program in `iregex.c', you can enter patterns and strings @@ -977,7 +977,7 @@ printchar (c) # define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2) #endif /* not DEBUG */ - + /* Set by `re_set_syntax' to the current regexp syntax to recognize. Can also be assigned to arbitrarily: each pattern buffer stores its own syntax, so it can be changed between regex compilations. */ @@ -1011,7 +1011,7 @@ re_set_syntax (syntax) #ifdef _LIBC weak_alias (__re_set_syntax, re_set_syntax) #endif - + /* This table gives an error message for each of the error codes listed in regex.h. Obviously the order here has to be same as there. POSIX doesn't require that we do anything for REG_NOERROR, @@ -1091,7 +1091,7 @@ static const size_t re_error_msgid_idx[] = REG_ESIZE_IDX, REG_ERPAREN_IDX }; - + /* Avoiding alloca during matching, to placate r_alloc. */ /* Define MATCH_MAY_ALLOCATE unless we need to make sure that the @@ -1129,7 +1129,7 @@ static const size_t re_error_msgid_idx[] = # undef MATCH_MAY_ALLOCATE #endif - + /* Failure stack declarations and macros; both re_compile_fastmap and re_match_2 use a failure stack. These have to be macros because of REGEX_ALLOCATE_STACK. */ @@ -1495,7 +1495,7 @@ typedef struct } /* POP_FAILURE_POINT */ - + /* Structure for per-register (a.k.a. per-group) information. Other register information, such as the starting and ending positions (which are addresses), and the list of @@ -1555,7 +1555,7 @@ typedef union static char reg_unset_dummy; #define REG_UNSET_VALUE (®_unset_dummy) #define REG_UNSET(e) ((e) == REG_UNSET_VALUE) - + /* Subroutine declarations and macros for regex_compile. */ static reg_errcode_t regex_compile _RE_ARGS ((const char *pattern, size_t size, @@ -1809,7 +1809,7 @@ typedef struct || STREQ (string, "punct") || STREQ (string, "graph") \ || STREQ (string, "cntrl") || STREQ (string, "blank")) #endif - + #ifndef MATCH_MAY_ALLOCATE /* If we cannot allocate large objects within re_match_2_internal, @@ -1857,7 +1857,7 @@ regex_grow_registers (num_regs) } #endif /* not MATCH_MAY_ALLOCATE */ - + static boolean group_in_compile_stack _RE_ARGS ((compile_stack_type compile_stack, regnum_t regnum)); @@ -2991,7 +2991,7 @@ regex_compile (pattern, size, syntax, bufp) return REG_NOERROR; } /* regex_compile */ - + /* Subroutines for `regex_compile'. */ /* Store OP at LOC followed by two-byte integer parameter ARG. */ @@ -3178,7 +3178,7 @@ compile_range (p_ptr, pend, translate, syntax, b) return REG_NOERROR; } - + /* re_compile_fastmap computes a ``fastmap'' for the compiled pattern in BUFP. A fastmap records which of the (1 << BYTEWIDTH) possible characters can start a string that matches the pattern. This fastmap @@ -3484,7 +3484,7 @@ re_compile_fastmap (bufp) #ifdef _LIBC weak_alias (__re_compile_fastmap, re_compile_fastmap) #endif - + /* Set REGS to hold NUM_REGS registers, storing them in STARTS and ENDS. Subsequent matches using PATTERN_BUFFER and REGS will use this memory for recording register information. STARTS and ENDS @@ -3522,7 +3522,7 @@ re_set_registers (bufp, regs, num_regs, starts, ends) #ifdef _LIBC weak_alias (__re_set_registers, re_set_registers) #endif - + /* Searching routines. */ /* Like re_search_2, below, but only one string is specified, and @@ -3704,7 +3704,7 @@ re_search_2 (bufp, string1, size1, string2, size2, startpos, range, regs, stop) #ifdef _LIBC weak_alias (__re_search_2, re_search_2) #endif - + /* This converts PTR, a pointer into one of the search strings `string1' and `string2' into an offset from the beginning of that string. */ #define POINTER_TO_OFFSET(ptr) \ @@ -3783,7 +3783,7 @@ weak_alias (__re_search_2, re_search_2) to actually save any registers when none are active. */ #define NO_HIGHEST_ACTIVE_REG (1 << BYTEWIDTH) #define NO_LOWEST_ACTIVE_REG (NO_HIGHEST_ACTIVE_REG + 1) - + /* Matching routines. */ #ifndef emacs /* Emacs never uses this. */ @@ -5248,7 +5248,7 @@ re_match_2_internal (bufp, string1, size1, string2, size2, pos, regs, stop) return -1; /* Failure to match. */ } /* re_match_2 */ - + /* Subroutine definitions for re_match_2. */ @@ -5511,7 +5511,7 @@ bcmp_translate (s1, s2, len, translate) } return 0; } - + /* Entry points for GNU code. */ /* re_compile_pattern is the GNU regular expression compiler: it @@ -5552,7 +5552,7 @@ re_compile_pattern (pattern, length, bufp) #ifdef _LIBC weak_alias (__re_compile_pattern, re_compile_pattern) #endif - + /* Entry points compatible with 4.2 BSD regex library. We don't define them unless specifically requested. */ @@ -5623,7 +5623,7 @@ re_exec (s) } #endif /* _REGEX_RE_COMP */ - + /* POSIX.2 functions. Don't define these for Emacs. */ #ifndef emacs diff --git a/lib/routemap.c b/lib/routemap.c index 4f4e6d62..1e1510eb 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -28,7 +28,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "command.h" #include "vty.h" #include "log.h" - + /* Vector for route match rules. */ static vector route_match_vec; @@ -72,7 +72,7 @@ route_map_rule_delete (struct route_map_rule_list *, static void route_map_index_delete (struct route_map_index *, int); - + /* New route map allocation. Please note route map's name must be specified. */ static struct route_map * @@ -420,7 +420,7 @@ route_map_rule_new (void) new = XCALLOC (MTYPE_ROUTE_MAP_RULE, sizeof (struct route_map_rule)); return new; } - + /* Install rule command to the match list. */ void route_map_install_match (struct route_map_rule_cmd *cmd) @@ -898,7 +898,7 @@ route_map_finish (void) vector_free (route_set_vec); route_set_vec = NULL; } - + /* VTY related functions. */ DEFUN (route_map, route_map_cmd, diff --git a/lib/smux.c b/lib/smux.c index 07466400..70be4928 100644 --- a/lib/smux.c +++ b/lib/smux.c @@ -78,7 +78,7 @@ struct subtree enum smux_event {SMUX_SCHEDULE, SMUX_CONNECT, SMUX_READ}; void smux_event (enum smux_event, int); - + /* SMUX socket. */ int smux_sock = -1; @@ -114,7 +114,7 @@ static struct cmd_node smux_node = /* thread master */ static struct thread_master *master; - + static int oid_compare_part (oid *o1, int o1_len, oid *o2, int o2_len) { @@ -132,7 +132,7 @@ oid_compare_part (oid *o1, int o1_len, oid *o2, int o2_len) return 0; } - + static void smux_oid_dump (const char *prefix, const oid *oid, size_t oid_len) { @@ -1230,7 +1230,7 @@ smux_stop (void) smux_sock = -1; } } - + void @@ -1251,7 +1251,7 @@ smux_event (enum smux_event event, int sock) break; } } - + static int smux_str2oid (const char *str, oid *oid, size_t *oid_len) { diff --git a/lib/stream.c b/lib/stream.c index 9a6fcbcf..0fc3c3b1 100644 --- a/lib/stream.c +++ b/lib/stream.c @@ -196,7 +196,7 @@ stream_resize (struct stream *s, size_t newsize) return s->size; } - + size_t stream_get_getp (struct stream *s) { @@ -285,7 +285,7 @@ stream_forward_endp (struct stream *s, size_t size) s->endp += size; } - + /* Copy from stream to destination. */ void stream_get (void *dst, struct stream *s, size_t size) @@ -492,7 +492,7 @@ stream_get_ipv4 (struct stream *s) return l; } - + /* Copy to source to stream. * * XXX: This uses CHECK_SIZE and hence has funny semantics -> Size will wrap @@ -731,7 +731,7 @@ stream_put_prefix (struct stream *s, struct prefix *p) return psize; } - + /* Read size from fd. */ int stream_read (struct stream *s, int fd, size_t size) @@ -937,7 +937,7 @@ stream_flush (struct stream *s, int fd) return nbytes; } - + /* Stream first in first out queue. */ struct stream_fifo * diff --git a/lib/table.c b/lib/table.c index 19b5d1b1..220e9b81 100644 --- a/lib/table.c +++ b/lib/table.c @@ -29,7 +29,7 @@ static void route_node_delete (struct route_node *); static void route_table_free (struct route_table *); - + /* * route_table_init_with_delegate diff --git a/lib/thread.c b/lib/thread.c index e2a37b14..468edd90 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -45,7 +45,7 @@ extern int agentx_enabled; #include #endif - + /* Recent absolute time of day */ struct timeval recent_time; static struct timeval last_recent_time; @@ -54,9 +54,9 @@ static struct timeval relative_time; static struct timeval relative_time_base; /* init flag */ static unsigned short timers_inited; - + static struct hash *cpu_record = NULL; - + /* Struct timeval's tv_usec one second value. */ #define TIMER_SECOND_MICRO 1000000L @@ -108,7 +108,7 @@ timeval_elapsed (struct timeval a, struct timeval b) return (((a.tv_sec - b.tv_sec) * TIMER_SECOND_MICRO) + (a.tv_usec - b.tv_usec)); } - + #if !defined(HAVE_CLOCK_MONOTONIC) && !defined(__APPLE__) static void quagga_gettimeofday_relative_adjust (void) @@ -247,7 +247,7 @@ recent_relative_time (void) { return relative_time; } - + static unsigned int cpu_record_hash_key (struct cpu_thread_history *a) { @@ -496,7 +496,7 @@ DEFUN(clear_thread_cpu, cpu_record_clear (filter); return CMD_SUCCESS; } - + static int thread_timer_cmp(void *a, void *b) { diff --git a/lib/vty.c b/lib/vty.c index 9908b023..11413576 100644 --- a/lib/vty.c +++ b/lib/vty.c @@ -57,7 +57,7 @@ static void vty_event (enum event, int, struct vty *); /* Extern host structure from command.c */ extern struct host host; - + /* Vector which store each vty structure. */ static vector vtyvec; @@ -89,7 +89,7 @@ static u_char restricted_mode = 0; /* Integrated configuration file path */ char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG; - + /* VTY standard output function. */ int vty_out (struct vty *vty, const char *format, ...) @@ -455,7 +455,7 @@ vty_command (struct vty *vty, char *buf) return ret; } - + static const char telnet_backward_char = 0x08; static const char telnet_space_char = ' '; @@ -2494,7 +2494,7 @@ vty_config_unlock (struct vty *vty) } return vty->config; } - + /* Master of the threads. */ static struct thread_master *master; @@ -2551,7 +2551,7 @@ vty_event (enum event event, int sock, struct vty *vty) break; } } - + DEFUN (config_who, config_who_cmd, "who", diff --git a/lib/zclient.c b/lib/zclient.c index d3165962..20188f6a 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -32,7 +32,7 @@ #include "zclient.h" #include "memory.h" #include "table.h" - + /* Zebra client events. */ enum event {ZCLIENT_SCHEDULE, ZCLIENT_READ, ZCLIENT_CONNECT}; @@ -45,7 +45,7 @@ char *zclient_serv_path = NULL; /* This file local debug flag. */ int zclient_debug = 0; - + /* Allocate zclient structure. */ struct zclient * zclient_new () @@ -413,7 +413,7 @@ zclient_connect (struct thread *t) return zclient_start (zclient); } - + /* * "xdr_encode"-like interface that allows daemon (client) to send * a message to zebra server for a route that needs to be @@ -816,7 +816,7 @@ zebra_interface_address_read (int type, struct stream *s) return ifc; } - + /* Zebra client message read function. */ static int zclient_read (struct thread *thread) diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c index 54404ab8..bb79900e 100644 --- a/ospf6d/ospf6_abr.c +++ b/ospf6d/ospf6_abr.c @@ -787,7 +787,7 @@ ospf6_abr_reimport (struct ospf6_area *oa) } - + /* Display functions */ static char * ospf6_inter_area_prefix_lsa_get_prefix_str (struct ospf6_lsa *lsa, char *buf, diff --git a/ospf6d/ospf6_area.c b/ospf6d/ospf6_area.c index 9a4e30e1..4b4ca130 100644 --- a/ospf6d/ospf6_area.c +++ b/ospf6d/ospf6_area.c @@ -288,7 +288,7 @@ ospf6_area_disable (struct ospf6_area *oa) THREAD_OFF (oa->thread_intra_prefix_lsa); } - + void ospf6_area_show (struct vty *vty, struct ospf6_area *oa) { diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index c414970b..6ba6cdf6 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -151,7 +151,7 @@ ospf6_as_external_lsa_originate (struct ospf6_route *route) ospf6_lsa_originate_process (lsa, ospf6); } - + void ospf6_asbr_lsa_add (struct ospf6_lsa *lsa) { @@ -342,7 +342,7 @@ ospf6_asbr_lsentry_remove (struct ospf6_route *asbr_entry) } - + /* redistribute function */ static void @@ -748,7 +748,7 @@ ospf6_redistribute_show_config (struct vty *vty) } - + /* Routemap Functions */ static route_map_result_t ospf6_routemap_rule_match_address_prefixlist (void *rule, @@ -1168,7 +1168,7 @@ ospf6_routemap_init (void) install_element (RMAP_NODE, &ospf6_routemap_no_set_forwarding_cmd); } - + /* Display functions */ static char * ospf6_as_external_lsa_get_prefix_str (struct ospf6_lsa *lsa, char *buf, diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index 4bc61551..f0ef7909 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -516,7 +516,7 @@ ospf6_interface_state_change (u_char next_state, struct ospf6_interface *oi) } - + /* DR Election, RFC2328 section 9.4 */ #define IS_ELIGIBLE(n) \ @@ -673,7 +673,7 @@ dr_election (struct ospf6_interface *oi) return next_state; } - + /* Interface State Machine */ int interface_up (struct thread *thread) @@ -843,7 +843,7 @@ interface_down (struct thread *thread) return 0; } - + /* show specified interface structure */ static int ospf6_interface_show (struct vty *vty, struct interface *ifp) diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h index 34f75233..95a377fb 100644 --- a/ospf6d/ospf6_interface.h +++ b/ospf6d/ospf6_interface.h @@ -142,7 +142,7 @@ extern const char *ospf6_interface_state_str[]; #define OSPF6_REFERENCE_BANDWIDTH 100000 /* Kbps */ - + /* Function Prototypes */ extern struct ospf6_interface *ospf6_interface_lookup_by_ifindex (int); diff --git a/ospf6d/ospf6_intra.h b/ospf6d/ospf6_intra.h index e909da23..c9660b6a 100644 --- a/ospf6d/ospf6_intra.h +++ b/ospf6d/ospf6_intra.h @@ -153,7 +153,7 @@ struct ospf6_intra_prefix_lsa /* followed by ospf6 prefix(es) */ }; - + #define OSPF6_ROUTER_LSA_SCHEDULE(oa) \ do { \ if (! (oa)->thread_router_lsa \ diff --git a/ospf6d/ospf6_lsa.c b/ospf6d/ospf6_lsa.c index 4aa2b12f..2e615355 100644 --- a/ospf6d/ospf6_lsa.c +++ b/ospf6d/ospf6_lsa.c @@ -658,7 +658,7 @@ ospf6_lsa_unlock (struct ospf6_lsa *lsa) ospf6_lsa_delete (lsa); } - + /* ospf6 lsa expiry */ int ospf6_lsa_expire (struct thread *thread) @@ -744,7 +744,7 @@ ospf6_lsa_refresh (struct thread *thread) return 0; } - + /* Fletcher Checksum -- Refer to RFC1008. */ @@ -790,7 +790,7 @@ ospf6_lsa_terminate (void) { vector_free (ospf6_lsa_handler_vector); } - + static char * ospf6_lsa_handler_name (struct ospf6_lsa_handler *h) { diff --git a/ospf6d/ospf6_lsa.h b/ospf6d/ospf6_lsa.h index aed89df2..a85ca66d 100644 --- a/ospf6d/ospf6_lsa.h +++ b/ospf6d/ospf6_lsa.h @@ -209,7 +209,7 @@ extern struct ospf6_lsa_handler unknown_handler; continue; \ } - + /* Function Prototypes */ extern const char *ospf6_lstype_name (u_int16_t type); extern const char *ospf6_lstype_short_name (u_int16_t type); diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c index 0756ef34..ecc96f71 100644 --- a/ospf6d/ospf6_message.c +++ b/ospf6d/ospf6_message.c @@ -2326,7 +2326,7 @@ ospf6_lsack_send_interface (struct thread *thread) return 0; } - + /* Commands */ DEFUN (debug_ospf6_message, debug_ospf6_message_cmd, @@ -2400,7 +2400,7 @@ ALIAS (debug_ospf6_message, "Debug only receiving message\n" ) - + DEFUN (no_debug_ospf6_message, no_debug_ospf6_message_cmd, "no debug ospf6 message (unknown|hello|dbdesc|lsreq|lsupdate|lsack|all)", diff --git a/ospf6d/ospf6_neighbor.c b/ospf6d/ospf6_neighbor.c index fb209fdf..7f6c6c5c 100644 --- a/ospf6d/ospf6_neighbor.c +++ b/ospf6d/ospf6_neighbor.c @@ -615,7 +615,7 @@ inactivity_timer (struct thread *thread) } - + /* vty functions */ /* show neighbor structure */ static void diff --git a/ospf6d/ospf6_neighbor.h b/ospf6d/ospf6_neighbor.h index 88821898..65c43fd2 100644 --- a/ospf6d/ospf6_neighbor.h +++ b/ospf6d/ospf6_neighbor.h @@ -148,7 +148,7 @@ static const char *ospf6_neighbor_event_string (int event) extern const char *ospf6_neighbor_state_str[]; - + /* Function Prototypes */ int ospf6_neighbor_cmp (void *va, void *vb); void ospf6_neighbor_dbex_init (struct ospf6_neighbor *on); diff --git a/ospf6d/ospf6_network.h b/ospf6d/ospf6_network.h index 0526b3e1..947834d5 100644 --- a/ospf6d/ospf6_network.h +++ b/ospf6d/ospf6_network.h @@ -22,7 +22,7 @@ #ifndef OSPF6_NETWORK_H #define OSPF6_NETWORK_H - + extern int ospf6_sock; extern struct in6_addr allspfrouters6; diff --git a/ospf6d/ospf6_route.c b/ospf6d/ospf6_route.c index 5f1869ac..9e6b33e5 100644 --- a/ospf6d/ospf6_route.c +++ b/ospf6d/ospf6_route.c @@ -780,7 +780,7 @@ ospf6_route_table_delete (struct ospf6_route_table *table) XFREE (MTYPE_OSPF6_ROUTE, table); } - + /* VTY commands */ void ospf6_route_show (struct vty *vty, struct ospf6_route *route) diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c index 8ee63fe6..85f70647 100644 --- a/ospf6d/ospf6_zebra.c +++ b/ospf6d/ospf6_zebra.c @@ -274,7 +274,7 @@ ospf6_zebra_read_ipv6 (int command, struct zclient *zclient, - + DEFUN (show_zebra, show_zebra_cmd, "show zebra", @@ -602,7 +602,7 @@ ospf6_zebra_init (void) } /* Debug */ - + DEFUN (debug_ospf6_zebra_sendrecv, debug_ospf6_zebra_sendrecv_cmd, "debug ospf6 zebra (send|recv)", diff --git a/ospf6d/ospf6d.c b/ospf6d/ospf6d.c index 3fdbda18..3cdd5c11 100644 --- a/ospf6d/ospf6d.c +++ b/ospf6d/ospf6d.c @@ -77,7 +77,7 @@ route_prev (struct route_node *node) return prev; } - + /* show database functions */ DEFUN (show_version_ospf6, show_version_ospf6_cmd, diff --git a/ospf6d/ospf6d.h b/ospf6d/ospf6d.h index 0c86386a..78ae1a14 100644 --- a/ospf6d/ospf6d.h +++ b/ospf6d/ospf6d.h @@ -127,7 +127,7 @@ extern struct thread_master *master; return CMD_SUCCESS; \ } - + /* Function Prototypes */ extern struct route_node *route_prev (struct route_node *node); diff --git a/ospfclient/COPYING b/ospfclient/COPYING index a43ea212..b8cf3a1a 100644 --- a/ospfclient/COPYING +++ b/ospfclient/COPYING @@ -55,7 +55,7 @@ patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. - + GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION @@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions: License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) - + These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in @@ -168,7 +168,7 @@ access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. - + 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is @@ -225,7 +225,7 @@ impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. - + 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License @@ -278,7 +278,7 @@ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS - + Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest diff --git a/ospfd/ospf_abr.c b/ospfd/ospf_abr.c index 4770275d..4bb70b6a 100644 --- a/ospfd/ospf_abr.c +++ b/ospfd/ospf_abr.c @@ -50,7 +50,7 @@ #include "ospfd/ospf_ase.h" #include "ospfd/ospf_zebra.h" #include "ospfd/ospf_dump.h" - + static struct ospf_area_range * ospf_area_range_new (struct prefix_ipv4 *p) { diff --git a/ospfd/ospf_asbr.c b/ospfd/ospf_asbr.c index 7e7c84fd..7e9412c8 100644 --- a/ospfd/ospf_asbr.c +++ b/ospfd/ospf_asbr.c @@ -44,7 +44,7 @@ #include "ospfd/ospf_zebra.h" #include "ospfd/ospf_dump.h" - + /* Remove external route. */ void ospf_external_route_remove (struct ospf *ospf, struct prefix_ipv4 *p) @@ -96,7 +96,7 @@ ospf_external_route_lookup (struct ospf *ospf, return NULL; } - + /* Add an External info for AS-external-LSA. */ struct external_info * ospf_external_info_new (u_char type) @@ -234,7 +234,7 @@ ospf_external_info_find_lsa (struct ospf *ospf, return lsa; } - + /* Update ASBR status. */ void ospf_asbr_status_update (struct ospf *ospf, u_char status) diff --git a/ospfd/ospf_dump.c b/ospfd/ospf_dump.c index 7e11e251..ef023366 100644 --- a/ospfd/ospf_dump.c +++ b/ospfd/ospf_dump.c @@ -143,7 +143,7 @@ unsigned long term_debug_ospf_lsa = 0; unsigned long term_debug_ospf_zebra = 0; unsigned long term_debug_ospf_nssa = 0; - + const char * ospf_redist_string(u_int route_type) @@ -218,7 +218,7 @@ ospf_if_name_string (struct ospf_interface *oi) return buf; } - + void ospf_nbr_state_message (struct ospf_neighbor *nbr, char *buf, size_t size) { @@ -746,7 +746,7 @@ ospf_packet_dump (struct stream *s) stream_set_getp (s, gp); } - + /* [no] debug ospf packet (hello|dd|ls-request|ls-update|ls-ack|all) [send|recv [detail]] @@ -956,7 +956,7 @@ ALIAS (no_debug_ospf_packet, "Packet received\n" "Detail Information\n") - + DEFUN (debug_ospf_ism, debug_ospf_ism_cmd, "debug ospf ism", @@ -1058,7 +1058,7 @@ ALIAS (no_debug_ospf_ism, "ISM Event Information\n" "ISM Timer Information\n") - + DEFUN (debug_ospf_nsm, debug_ospf_nsm_cmd, "debug ospf nsm", @@ -1161,7 +1161,7 @@ ALIAS (no_debug_ospf_nsm, "NSM Event Information\n" "NSM Timer Information\n") - + DEFUN (debug_ospf_lsa, debug_ospf_lsa_cmd, "debug ospf lsa", @@ -1274,7 +1274,7 @@ ALIAS (no_debug_ospf_lsa, "LSA Install/Delete\n" "LSA Refres\n") - + DEFUN (debug_ospf_zebra, debug_ospf_zebra_cmd, "debug ospf zebra", @@ -1366,7 +1366,7 @@ ALIAS (no_debug_ospf_zebra, "OSPF Zebra information\n" "Zebra interface\n" "Zebra redistribute\n") - + DEFUN (debug_ospf_event, debug_ospf_event_cmd, "debug ospf event", @@ -1421,7 +1421,7 @@ DEFUN (no_debug_ospf_nssa, return CMD_SUCCESS; } - + DEFUN (show_debugging_ospf, show_debugging_ospf_cmd, "show debugging ospf", diff --git a/ospfd/ospf_flood.c b/ospfd/ospf_flood.c index 2ebae89a..2c33b00e 100644 --- a/ospfd/ospf_flood.c +++ b/ospfd/ospf_flood.c @@ -49,7 +49,7 @@ #include "ospfd/ospf_dump.h" extern struct zclient *zclient; - + /* Do the LSA acking specified in table 19, Section 13.5, row 2 * This get called from ospf_flood_out_interface. Declared inline * for speed. */ @@ -757,7 +757,7 @@ ospf_flood_through (struct ospf *ospf, return (lsa_ack_flag); } - + /* Management functions for neighbor's Link State Request list. */ void @@ -835,7 +835,7 @@ ospf_ls_request_new (struct lsa_header *lsah) return new; } - + /* Management functions for neighbor's ls-retransmit list. */ unsigned long ospf_ls_retransmit_count (struct ospf_neighbor *nbr) @@ -973,7 +973,7 @@ ospf_ls_retransmit_delete_nbr_as (struct ospf *ospf, struct ospf_lsa *lsa) ospf_ls_retransmit_delete_nbr_if (oi, lsa); } - + /* Sets ls_age to MaxAge and floods throu the area. When we implement ASE routing, there will be anothe function flushing an LSA from the whole domain. */ diff --git a/ospfd/ospf_ia.c b/ospfd/ospf_ia.c index 6a8c3c63..b2d0faeb 100644 --- a/ospfd/ospf_ia.c +++ b/ospfd/ospf_ia.c @@ -193,7 +193,7 @@ ospf_ia_router_route (struct ospf *ospf, struct route_table *rtrs, listnode_add (rn->info, new_or); } - + static int process_summary_lsa (struct ospf_area *area, struct route_table *rt, struct route_table *rtrs, struct ospf_lsa *lsa) diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c index a37dde12..0f02cc82 100644 --- a/ospfd/ospf_interface.c +++ b/ospfd/ospf_interface.c @@ -49,7 +49,7 @@ #include "ospfd/ospf_snmp.h" #endif /* HAVE_SNMP */ - + int ospf_if_get_output_cost (struct ospf_interface *oi) { @@ -334,7 +334,7 @@ ospf_if_free (struct ospf_interface *oi) XFREE (MTYPE_OSPF_IF, oi); } - + /* * check if interface with given address is configured and * return it if yes. special treatment for PtP networks. @@ -489,7 +489,7 @@ ospf_if_lookup_recv_if (struct ospf *ospf, struct in_addr src, return match; } - + void ospf_if_stream_set (struct ospf_interface *oi) { @@ -518,7 +518,7 @@ ospf_if_stream_unset (struct ospf_interface *oi) } } - + static struct ospf_if_params * ospf_new_if_params (void) { @@ -825,7 +825,7 @@ ospf_if_down (struct ospf_interface *oi) return 1; } - + /* Virtual Link related functions. */ struct ospf_vl_data * @@ -1194,7 +1194,7 @@ ospf_vls_in_area (struct ospf_area *area) return c; } - + struct crypt_key * ospf_crypt_key_new () { diff --git a/ospfd/ospf_ism.c b/ospfd/ospf_ism.c index fa7d97f2..9649df8d 100644 --- a/ospfd/ospf_ism.c +++ b/ospfd/ospf_ism.c @@ -44,7 +44,7 @@ #include "ospfd/ospf_flood.h" #include "ospfd/ospf_abr.h" #include "ospfd/ospf_snmp.h" - + /* elect DR and BDR. Refer to RFC2319 section 9.4 */ static struct ospf_neighbor * ospf_dr_election_sub (struct list *routers) @@ -245,7 +245,7 @@ ospf_dr_election (struct ospf_interface *oi) return new_state; } - + int ospf_hello_timer (struct thread *thread) { diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index 967cdb58..fef6b162 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -50,7 +50,7 @@ #include "ospfd/ospf_ase.h" #include "ospfd/ospf_zebra.h" - + u_int32_t get_metric (u_char *metric) { @@ -61,7 +61,7 @@ get_metric (u_char *metric) return m; } - + struct timeval tv_adjust (struct timeval a) { @@ -159,7 +159,7 @@ ospf_lsa_refresh_delay (struct ospf_lsa *lsa) return delay; } - + int get_age (struct ospf_lsa *lsa) { @@ -171,7 +171,7 @@ get_age (struct ospf_lsa *lsa) return age; } - + /* Fletcher Checksum -- Refer to RFC1008. */ /* All the offsets are zero-based. The offsets in the RFC1008 are @@ -205,7 +205,7 @@ ospf_lsa_checksum_valid (struct lsa_header *lsa) } - + /* Create OSPF LSA. */ struct ospf_lsa * ospf_lsa_new () @@ -341,7 +341,7 @@ ospf_lsa_data_free (struct lsa_header *lsah) XFREE (MTYPE_OSPF_LSA_DATA, lsah); } - + /* LSA general functions. */ const char * @@ -393,7 +393,7 @@ lsa_header_set (struct stream *s, u_char options, stream_forward_endp (s, OSPF_LSA_HEADER_SIZE); } - + /* router-LSA related functions. */ /* Get router-LSA flags. */ @@ -746,7 +746,7 @@ ospf_router_lsa_body_set (struct stream *s, struct ospf_area *area) /* Set # of links here. */ stream_putw_at (s, putp, cnt); } - + static int ospf_stub_router_timer (struct thread *t) { @@ -803,7 +803,7 @@ ospf_stub_router_check (struct ospf_area *area) OSPF_AREA_TIMER_ON (area->t_stub_router, ospf_stub_router_timer, area->ospf->stub_router_startup_time); } - + /* Create new router-LSA. */ static struct ospf_lsa * ospf_router_lsa_new (struct ospf_area *area) @@ -1005,7 +1005,7 @@ ospf_router_lsa_update (struct ospf *ospf) return 0; } - + /* network-LSA related functions. */ /* Originate Network-LSA. */ static void @@ -1184,7 +1184,7 @@ ospf_network_lsa_refresh (struct ospf_lsa *lsa) return new; } - + static void stream_put_ospf_metric (struct stream *s, u_int32_t metric_value) { @@ -1343,7 +1343,7 @@ ospf_summary_lsa_refresh (struct ospf *ospf, struct ospf_lsa *lsa) return new; } - + /* summary-ASBR-LSA related functions. */ static void ospf_summary_asbr_lsa_body_set (struct stream *s, struct prefix *p, @@ -2377,7 +2377,7 @@ ospf_external_lsa_refresh (struct ospf *ospf, struct ospf_lsa *lsa, return new; } - + /* LSA installation functions. */ /* Install router-LSA to an area. */ @@ -2802,7 +2802,7 @@ ospf_lsa_install (struct ospf *ospf, struct ospf_interface *oi, return new; } - + int ospf_check_nbr_status (struct ospf *ospf) { @@ -2827,7 +2827,7 @@ ospf_check_nbr_status (struct ospf *ospf) return 1; } - + static int ospf_maxage_lsa_remover (struct thread *thread) @@ -3539,7 +3539,7 @@ ospf_lsa_unique_id (struct ospf *ospf, return id; } - + #define LSA_ACTION_FLOOD_AREA 1 #define LSA_ACTION_FLUSH_AREA 2 @@ -3602,7 +3602,7 @@ ospf_schedule_lsa_flush_area (struct ospf_area *area, struct ospf_lsa *lsa) thread_add_event (master, ospf_lsa_action, data, 0); } - + /* LSA Refreshment functions. */ struct ospf_lsa * ospf_lsa_refresh (struct ospf *ospf, struct ospf_lsa *lsa) diff --git a/ospfd/ospf_lsdb.c b/ospfd/ospf_lsdb.c index aad979a7..f7cf60fd 100644 --- a/ospfd/ospf_lsdb.c +++ b/ospfd/ospf_lsdb.c @@ -31,7 +31,7 @@ #include "ospfd/ospf_asbr.h" #include "ospfd/ospf_lsa.h" #include "ospfd/ospf_lsdb.h" - + struct ospf_lsdb * ospf_lsdb_new () { diff --git a/ospfd/ospf_main.c b/ospfd/ospf_main.c index c68aa4dd..82735b73 100644 --- a/ospfd/ospf_main.c +++ b/ospfd/ospf_main.c @@ -132,7 +132,7 @@ Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS); } exit (status); } - + /* SIGHUP handler. */ static void sighup (void) @@ -174,7 +174,7 @@ struct quagga_signal_t ospf_signals[] = .handler = &sigint, }, }; - + /* OSPFd main routine. */ int main (int argc, char **argv) diff --git a/ospfd/ospf_nsm.c b/ospfd/ospf_nsm.c index bcabd5f7..0e6814e2 100644 --- a/ospfd/ospf_nsm.c +++ b/ospfd/ospf_nsm.c @@ -50,7 +50,7 @@ #include "ospfd/ospf_snmp.h" static void nsm_clear_adj (struct ospf_neighbor *); - + /* OSPF NSM Timer functions. */ static int ospf_inactivity_timer (struct thread *thread) @@ -156,7 +156,7 @@ nsm_should_adj (struct ospf_neighbor *nbr) return 0; } - + /* OSPF NSM functions. */ static int nsm_packet_received (struct ospf_neighbor *nbr) @@ -258,7 +258,7 @@ ospf_db_summary_clear (struct ospf_neighbor *nbr) } } - + /* The area link state database consists of the router-LSAs, network-LSAs and summary-LSAs contained in the area structure, diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index cce56fc6..efdf8263 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -323,7 +323,7 @@ ospf_packet_max (struct ospf_interface *oi) return max; } - + static int ospf_check_md5_digest (struct ospf_interface *oi, struct ospf_header *ospfh) { @@ -438,7 +438,7 @@ ospf_make_md5_digest (struct ospf_interface *oi, struct ospf_packet *op) return OSPF_AUTH_MD5_SIZE; } - + static int ospf_ls_req_timer (struct thread *thread) { @@ -2136,7 +2136,7 @@ ospf_ls_ack (struct ip *iph, struct ospf_header *ospfh, return; } - + static struct stream * ospf_recv_packet (int fd, struct interface **ifp, struct stream *ibuf) { diff --git a/ospfd/ospf_snmp.c b/ospfd/ospf_snmp.c index 1daf0d6a..7f7b1578 100644 --- a/ospfd/ospf_snmp.c +++ b/ospfd/ospf_snmp.c @@ -48,7 +48,7 @@ #include "ospfd/ospf_ism.h" #include "ospfd/ospf_dump.h" #include "ospfd/ospf_snmp.h" - + /* OSPF2-MIB. */ #define OSPF2MIB 1,3,6,1,2,1,14 @@ -204,7 +204,7 @@ #define TIMETICKS ASN_TIMETICKS #define IPADDRESS ASN_IPADDRESS #define STRING ASN_OCTET_STR - + /* Declare static local variables for convenience. */ SNMP_LOCAL_VARIABLES @@ -501,7 +501,7 @@ struct variable ospf_variables[] = {OSPFAREAAGGREGATEEFFECT, INTEGER, RWRITE, ospfAreaAggregateEntry, 3, {14, 1, 6}} }; - + /* The administrative status of OSPF. When OSPF is enbled on at least one interface return 1. */ static int @@ -1407,7 +1407,7 @@ ospfHostEntry (struct variable *v, oid *name, size_t *length, int exact, } return NULL; } - + struct list *ospf_snmp_iflist; struct ospf_snmp_if @@ -1911,7 +1911,7 @@ ospfIfMetricEntry (struct variable *v, oid *name, size_t *length, int exact, } return NULL; } - + struct route_table *ospf_snmp_vl_table; void @@ -2127,7 +2127,7 @@ ospfVirtIfEntry (struct variable *v, oid *name, size_t *length, int exact, } return NULL; } - + static struct ospf_neighbor * ospf_snmp_nbr_lookup (struct ospf *ospf, struct in_addr *nbr_addr, unsigned int *ifindex) @@ -2360,7 +2360,7 @@ ospfNbrEntry (struct variable *v, oid *name, size_t *length, int exact, } return NULL; } - + static u_char * ospfVirtNbrEntry (struct variable *v, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) @@ -2419,7 +2419,7 @@ ospfVirtNbrEntry (struct variable *v, oid *name, size_t *length, int exact, } return NULL; } - + static struct ospf_lsa * ospfExtLsdbLookup (struct variable *v, oid *name, size_t *length, u_char *type, struct in_addr *ls_id, struct in_addr *router_id, int exact) @@ -2572,7 +2572,7 @@ ospfExtLsdbEntry (struct variable *v, oid *name, size_t *length, int exact, } return NULL; } - + static u_char * ospfAreaAggregateEntry (struct variable *v, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) @@ -2608,7 +2608,7 @@ ospfAreaAggregateEntry (struct variable *v, oid *name, size_t *length, } return NULL; } - + /* OSPF Traps. */ #define IFSTATECHANGE 16 #define VIRTIFSTATECHANGE 1 diff --git a/ospfd/ospf_spf.c b/ospfd/ospf_spf.c index bd9564d9..c40fc33e 100644 --- a/ospfd/ospf_spf.c +++ b/ospfd/ospf_spf.c @@ -52,7 +52,7 @@ static void ospf_vertex_free (void *); * dynamically allocated at begin of ospf_spf_calculate */ static struct list vertex_list = { .del = ospf_vertex_free }; - + /* Heap related functions, for the managment of the candidates, to * be used with pqueue. */ static int @@ -90,7 +90,7 @@ update_stat (void *node , int position) /* Set the status of the vertex, when its position changes. */ *(v->stat) = position; } - + static struct vertex_nexthop * vertex_nexthop_new (void) { @@ -134,7 +134,7 @@ ospf_canonical_nexthops_free (struct vertex *root) vertex_nexthop_free (vp->nexthop); } } - + /* TODO: Parent list should be excised, in favour of maintaining only * vertex_nexthop, with refcounts. */ @@ -159,7 +159,7 @@ vertex_parent_free (void *p) { XFREE (MTYPE_OSPF_VERTEX_PARENT, p); } - + static struct vertex * ospf_vertex_new (struct ospf_lsa *lsa) { @@ -276,7 +276,7 @@ ospf_vertex_add_parent (struct vertex *v) listnode_add (vp->parent->children, v); } } - + static void ospf_spf_init (struct ospf_area *area) { @@ -1254,7 +1254,7 @@ ospf_spf_calculate (struct ospf_area *area, struct route_table *new_table, zlog_debug ("ospf_spf_calculate: Stop. %ld vertices", mtype_stats_alloc(MTYPE_OSPF_VERTEX)); } - + /* Timer for SPF calculation. */ static int ospf_spf_calculate_timer (struct thread *thread) diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index ee8c9019..8bfcaa82 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -49,7 +49,7 @@ #include "ospfd/ospf_vty.h" #include "ospfd/ospf_dump.h" - + static const char *ospf_network_type_str[] = { "Null", @@ -61,7 +61,7 @@ static const char *ospf_network_type_str[] = "LOOPBACK" }; - + /* Utility functions. */ static int ospf_str2area_id (const char *str, struct in_addr *area_id, int *format) @@ -94,7 +94,7 @@ ospf_str2area_id (const char *str, struct in_addr *area_id, int *format) return 0; } - + static int str2metric (const char *str, int *metric) { @@ -142,7 +142,7 @@ ospf_oi_count (struct interface *ifp) return i; } - + DEFUN (router_ospf, router_ospf_cmd, "router ospf", @@ -490,7 +490,7 @@ DEFUN (no_ospf_network_area, return CMD_SUCCESS; } - + DEFUN (ospf_area_range, ospf_area_range_cmd, "area (A.B.C.D|<0-4294967295>) range A.B.C.D/M", @@ -634,7 +634,7 @@ ALIAS (no_ospf_area_range, "Advertise this range (default)\n" "User specified metric for this range\n" "Advertised metric for this range\n") - + DEFUN (ospf_area_range_substitute, ospf_area_range_substitute_cmd, "area (A.B.C.D|<0-4294967295>) range A.B.C.D/M substitute A.B.C.D/M", @@ -686,7 +686,7 @@ DEFUN (no_ospf_area_range_substitute, return CMD_SUCCESS; } - + /* Command Handler Logic in VLink stuff is delicate!! ALTER AT YOUR OWN RISK!!!! @@ -1382,7 +1382,7 @@ ALIAS (no_ospf_area_vlink, VLINK_HELPSTR_AUTHTYPE_SIMPLE VLINK_HELPSTR_AUTH_MD5) - + DEFUN (ospf_area_shortcut, ospf_area_shortcut_cmd, "area (A.B.C.D|<0-4294967295>) shortcut (default|enable|disable)", @@ -1450,7 +1450,7 @@ DEFUN (no_ospf_area_shortcut, return CMD_SUCCESS; } - + DEFUN (ospf_area_stub, ospf_area_stub_cmd, "area (A.B.C.D|<0-4294967295>) stub", @@ -1971,7 +1971,7 @@ DEFUN (no_ospf_area_filter_list, return CMD_SUCCESS; } - + DEFUN (ospf_area_authentication_message_digest, ospf_area_authentication_message_digest_cmd, "area (A.B.C.D|<0-4294967295>) authentication message-digest", @@ -2042,7 +2042,7 @@ DEFUN (no_ospf_area_authentication, return CMD_SUCCESS; } - + DEFUN (ospf_abr_type, ospf_abr_type_cmd, "ospf abr-type (cisco|ibm|shortcut|standard)", @@ -2206,7 +2206,7 @@ ALIAS (no_ospf_compatible_rfc1583, NO_STR "OSPF specific commands\n" "Disable the RFC1583Compatibility flag\n") - + static int ospf_timers_spf_set (struct vty *vty, unsigned int delay, unsigned int hold, @@ -2297,7 +2297,7 @@ ALIAS_DEPRECATED (no_ospf_timers_throttle_spf, NO_STR "Adjust routing timers\n" "OSPF SPF timers\n") - + DEFUN (ospf_neighbor, ospf_neighbor_cmd, "neighbor A.B.C.D", @@ -2430,7 +2430,7 @@ ALIAS (no_ospf_neighbor, "Dead Neighbor Polling interval\n" "Seconds\n") - + DEFUN (ospf_refresh_timer, ospf_refresh_timer_cmd, "refresh timer <10-1800>", "Adjust refresh parameters\n" @@ -2548,7 +2548,7 @@ const char *ospf_shortcut_mode_descr_str[] = }; - + static void show_ip_ospf_area (struct vty *vty, struct ospf_area *area) { @@ -2800,7 +2800,7 @@ DEFUN (show_ip_ospf, return CMD_SUCCESS; } - + static void show_ip_ospf_interface_sub (struct vty *vty, struct ospf *ospf, struct interface *ifp) @@ -3429,7 +3429,7 @@ DEFUN (show_ip_ospf_neighbor_int_detail, return CMD_SUCCESS; } - + /* Show functions */ static int show_lsa_summary (struct vty *vty, struct ospf_lsa *lsa, int self) @@ -4302,7 +4302,7 @@ ALIAS (show_ip_ospf_database_type_adv_router, OSPF_LSA_TYPES_DESC "Self-originated link states\n") - + DEFUN (ip_ospf_authentication_args, ip_ospf_authentication_args_addr_cmd, "ip ospf authentication (null|message-digest) A.B.C.D", @@ -6262,7 +6262,7 @@ ALIAS (no_ip_ospf_mtu_ignore, "IP Information\n" "OSPF interface commands\n" "Disable mtu mismatch detection\n") - + DEFUN (ospf_max_metric_router_lsa_admin, ospf_max_metric_router_lsa_admin_cmd, "max-metric router-lsa administrative", @@ -6431,7 +6431,7 @@ config_write_stub_router (struct vty *vty, struct ospf *ospf) } return; } - + static void show_ip_ospf_route_network (struct vty *vty, struct route_table *rt) { @@ -6650,7 +6650,7 @@ DEFUN (show_ip_ospf_route, return CMD_SUCCESS; } - + const char *ospf_abr_type_str[] = { "unknown", @@ -6679,7 +6679,7 @@ area_id2str (char *buf, int length, struct ospf_area *area) sprintf (buf, "%lu", (unsigned long) ntohl (area->area_id.s_addr)); } - + const char *ospf_int_type_str[] = { "unknown", /* should never be used. */ @@ -7113,7 +7113,7 @@ config_write_virtual_link (struct vty *vty, struct ospf *ospf) return 0; } - + static int config_write_ospf_redistribute (struct vty *vty, struct ospf *ospf) { @@ -7406,7 +7406,7 @@ ospf_vty_show_init (void) install_element (ENABLE_NODE, &show_ip_ospf_border_routers_cmd); } - + /* ospfd's interface node. */ static struct cmd_node interface_node = { @@ -7558,7 +7558,7 @@ static struct cmd_node ospf_node = 1 }; - + /* Install OSPF related vty commands. */ void ospf_vty_init (void) diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index b5268a3b..b6d3260b 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -918,7 +918,7 @@ ospf_zebra_read_ipv4 (int command, struct zclient *zclient, return 0; } - + int ospf_distribute_list_out_set (struct ospf *ospf, int type, const char *name) diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index 538bc094..dd57f645 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -53,7 +53,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "ospfd/ospf_route.h" #include "ospfd/ospf_ase.h" - + /* OSPF process wide configuration. */ static struct ospf_master ospf_master; @@ -64,7 +64,7 @@ struct ospf_master *om; extern struct zclient *zclient; extern struct in_addr router_id_zebra; - + static void ospf_remove_vls_through_area (struct ospf *, struct ospf_area *); static void ospf_network_free (struct ospf *, struct ospf_network *); static void ospf_area_free (struct ospf_area *); @@ -76,7 +76,7 @@ static int ospf_network_match_iface (const struct connected *, static void ospf_finish_final (struct ospf *); #define OSPF_EXTERNAL_LSA_ORIGINATE_DELAY 1 - + void ospf_router_id_update (struct ospf *ospf) { @@ -139,7 +139,7 @@ ospf_router_id_update (struct ospf *ospf) ospf_if_update (ospf, ifp); } } - + /* For OSPF area sort by area id. */ static int ospf_area_id_cmp (struct ospf_area *a1, struct ospf_area *a2) @@ -279,7 +279,7 @@ ospf_get () return ospf; } - + /* Handle the second half of deferred shutdown. This is called either * from the deferred-shutdown timer thread, or directly through * ospf_deferred_shutdown_check. @@ -354,7 +354,7 @@ ospf_deferred_shutdown_check (struct ospf *ospf) timeout); return; } - + /* Shut down the entire process */ void ospf_terminate (void) @@ -563,7 +563,7 @@ ospf_finish_final (struct ospf *ospf) XFREE (MTYPE_OSPF_TOP, ospf); } - + /* allocate new OSPF Area object */ static struct ospf_area * ospf_area_new (struct ospf *ospf, struct in_addr area_id) @@ -719,7 +719,7 @@ ospf_area_del_if (struct ospf_area *area, struct ospf_interface *oi) listnode_delete (area->oiflist, oi); } - + /* Config network statement related functions. */ static struct ospf_network * ospf_network_new (struct in_addr area_id, int format) @@ -987,7 +987,7 @@ ospf_remove_vls_through_area (struct ospf *ospf, struct ospf_area *area) ospf_vl_delete (ospf, vl_data); } - + static const struct message ospf_area_type_msg[] = { { OSPF_AREA_DEFAULT, "Default" }, @@ -1350,7 +1350,7 @@ ospf_timers_refresh_unset (struct ospf *ospf) return 1; } - + static struct ospf_nbr_nbma * ospf_nbr_nbma_new (void) { diff --git a/ripd/rip_debug.c b/ripd/rip_debug.c index 662e5843..a267874a 100644 --- a/ripd/rip_debug.c +++ b/ripd/rip_debug.c @@ -27,7 +27,7 @@ unsigned long rip_debug_event = 0; unsigned long rip_debug_packet = 0; unsigned long rip_debug_zebra = 0; - + DEFUN (show_debugging_rip, show_debugging_rip_cmd, "show debugging rip", diff --git a/ripd/rip_interface.c b/ripd/rip_interface.c index 810b71c0..22cef454 100644 --- a/ripd/rip_interface.c +++ b/ripd/rip_interface.c @@ -41,7 +41,7 @@ #include "ripd/ripd.h" #include "ripd/rip_debug.h" #include "ripd/rip_interface.h" - + /* static prototypes */ static void rip_enable_apply (struct interface *); static void rip_passive_interface_apply (struct interface *); @@ -49,7 +49,7 @@ static int rip_if_down(struct interface *ifp); static int rip_enable_if_lookup (const char *ifname); static int rip_enable_network_lookup2 (struct connected *connected); static void rip_enable_apply_all (void); - + struct message ri_version_msg[] = { {RI_RIP_VERSION_1, "1"}, @@ -68,7 +68,7 @@ struct route_table *rip_enable_network; /* Vector to store passive-interface name. */ static int passive_default; /* are we in passive-interface default mode? */ vector Vrip_passive_nondefault; - + /* Join to the RIP version 2 multicast group. */ static int ipv4_multicast_join (int sock, @@ -109,7 +109,7 @@ ipv4_multicast_leave (int sock, return ret; } - + /* Allocate new RIP's interface configuration. */ static struct rip_interface * rip_interface_new (void) @@ -754,7 +754,7 @@ rip_interface_address_delete (int command, struct zclient *zclient, return 0; } - + /* Check interface is enabled by network statement. */ /* Check wether the interface has at least a connected prefix that * is within the ripng_enable_network table. */ @@ -1142,7 +1142,7 @@ rip_clean_network () vector_slot (rip_enable_interface, i) = NULL; } } - + /* Utility function for looking up passive interface settings. */ static int rip_passive_nondefault_lookup (const char *ifname) @@ -1229,7 +1229,7 @@ rip_passive_nondefault_clean (void) } rip_passive_interface_apply_all (); } - + /* RIP enable network or interface configuration. */ DEFUN (rip_network, rip_network_cmd, @@ -1913,7 +1913,7 @@ DEFUN (no_rip_passive_interface, else return rip_passive_nondefault_unset (vty, ifname); } - + /* Write rip configuration of each interface. */ static int rip_interface_config_write (struct vty *vty) diff --git a/ripd/rip_main.c b/ripd/rip_main.c index a512fbc2..e81e61b8 100644 --- a/ripd/rip_main.c +++ b/ripd/rip_main.c @@ -126,7 +126,7 @@ Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS); exit (status); } - + /* SIGHUP handler. */ static void sighup (void) @@ -183,7 +183,7 @@ static struct quagga_signal_t ripd_signals[] = .handler = &sigint, }, }; - + /* Main routine of ripd. */ int main (int argc, char **argv) diff --git a/ripd/rip_peer.c b/ripd/rip_peer.c index fd912eba..6a3add64 100644 --- a/ripd/rip_peer.c +++ b/ripd/rip_peer.c @@ -32,7 +32,7 @@ /* Linked list of RIP peer. */ struct list *peer_list; - + static struct rip_peer * rip_peer_new (void) { diff --git a/ripd/rip_routemap.c b/ripd/rip_routemap.c index cb87ea55..37a986c5 100644 --- a/ripd/rip_routemap.c +++ b/ripd/rip_routemap.c @@ -32,7 +32,7 @@ #include "plist.h" #include "ripd/ripd.h" - + struct rip_metric_modifier { enum @@ -160,7 +160,7 @@ rip_route_map_update (const char *notused) } } } - + /* `match metric METRIC' */ /* Match function return 1 if match is success else return zero. */ static route_map_result_t @@ -323,7 +323,7 @@ static struct route_map_rule_cmd route_match_ip_next_hop_cmd = route_match_ip_next_hop_compile, route_match_ip_next_hop_free }; - + /* `match ip next-hop prefix-list PREFIX_LIST' */ static route_map_result_t @@ -370,7 +370,7 @@ static struct route_map_rule_cmd route_match_ip_next_hop_prefix_list_cmd = route_match_ip_next_hop_prefix_list_compile, route_match_ip_next_hop_prefix_list_free }; - + /* `match ip address IP_ACCESS_LIST' */ /* Match function should return 1 if match is success else return @@ -416,7 +416,7 @@ static struct route_map_rule_cmd route_match_ip_address_cmd = route_match_ip_address_compile, route_match_ip_address_free }; - + /* `match ip address prefix-list PREFIX_LIST' */ static route_map_result_t @@ -507,7 +507,7 @@ struct route_map_rule_cmd route_match_tag_cmd = route_match_tag_compile, route_match_tag_free }; - + /* `set metric METRIC' */ /* Set metric to attribute. */ @@ -718,7 +718,7 @@ static struct route_map_rule_cmd route_set_tag_cmd = route_set_tag_compile, route_set_tag_free }; - + #define MATCH_STR "Match values from routing table\n" #define SET_STR "Set values in destination routing protocol\n" diff --git a/ripd/rip_snmp.c b/ripd/rip_snmp.c index 090ebfae..2df815b0 100644 --- a/ripd/rip_snmp.c +++ b/ripd/rip_snmp.c @@ -33,7 +33,7 @@ #include "smux.h" #include "ripd/ripd.h" - + /* RIPv2-MIB. */ #define RIPV2MIB 1,3,6,1,2,1,23 @@ -76,7 +76,7 @@ #define TIMETICKS ASN_TIMETICKS #define IPADDRESS ASN_IPADDRESS #define STRING ASN_OCTET_STR - + /* Define SNMP local variables. */ SNMP_LOCAL_VARIABLES @@ -149,7 +149,7 @@ struct variable rip_variables[] = }; extern struct thread_master *master; - + static u_char * rip2Globals (struct variable *v, oid name[], size_t *length, int exact, size_t *var_len, WriteMethod **write_method) diff --git a/ripd/rip_zebra.c b/ripd/rip_zebra.c index 199e85e8..1f6ef612 100644 --- a/ripd/rip_zebra.c +++ b/ripd/rip_zebra.c @@ -33,7 +33,7 @@ /* All information about zebra. */ struct zclient *zclient = NULL; - + /* RIPd to zebra command interface. */ void rip_zebra_ipv4_add (struct prefix_ipv4 *p, struct in_addr *nexthop, @@ -196,7 +196,7 @@ rip_routemap_unset (int type, const char *name) return 0; } - + /* Redistribution types */ static struct { int type; @@ -551,7 +551,7 @@ DEFUN (no_rip_redistribute_type_metric_routemap, return CMD_WARNING; } - + /* Default information originate. */ DEFUN (rip_default_information_originate, @@ -597,7 +597,7 @@ DEFUN (no_rip_default_information_originate, return CMD_SUCCESS; } - + /* RIP configuration write function. */ static int config_write_zebra (struct vty *vty) diff --git a/ripd/ripd.c b/ripd/ripd.c index 01bd69ec..dfeb951c 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -61,20 +61,20 @@ long rip_global_route_changes = 0; /* RIP queries. */ long rip_global_queries = 0; - + /* Prototypes. */ static void rip_event (enum rip_event, int); static void rip_output_process (struct connected *, struct sockaddr_in *, int, u_char); static int rip_triggered_update (struct thread *); static int rip_update_jitter (unsigned long); - + /* RIP output routes type. */ enum { rip_all_route, rip_changed_route }; - + /* RIP command strings. */ static const struct message rip_msg[] = { @@ -86,7 +86,7 @@ static const struct message rip_msg[] = {RIP_POLL_ENTRY, "POLL ENTRY"}, {0, NULL}, }; - + /* Utility function to set boradcast option to the socket. */ static int sockopt_broadcast (int sock) @@ -2774,7 +2774,7 @@ rip_request_send (struct sockaddr_in *to, struct interface *ifp, } return sizeof (rip_packet); } - + static int rip_update_jitter (unsigned long time) { @@ -2828,7 +2828,7 @@ rip_event (enum rip_event event, int sock) break; } } - + DEFUN (router_rip, router_rip_cmd, "router rip", @@ -3104,7 +3104,7 @@ ALIAS (no_rip_timers, "Routing information timeout timer. Default is 180.\n" "Garbage collection timer. Default is 120.\n") - + struct route_table *rip_distance_table; struct rip_distance @@ -3372,7 +3372,7 @@ DEFUN (no_rip_distance_source_access_list, rip_distance_unset (vty, argv[0], argv[1], argv[2]); return CMD_SUCCESS; } - + /* Print out routes update time. */ static void rip_vty_out_uptime (struct vty *vty, struct rip_info *rinfo) @@ -3696,7 +3696,7 @@ static struct cmd_node rip_node = "%s(config-router)# ", 1 }; - + /* Distribute-list update functions. */ static void rip_distribute_update (struct distribute *dist) @@ -3787,7 +3787,7 @@ rip_distribute_update_all_wrapper(struct access_list *notused) { rip_distribute_update_all(NULL); } - + /* Delete all added rip route. */ void rip_clean (void) diff --git a/ripngd/ripng_debug.c b/ripngd/ripng_debug.c index 33a7cf69..c1eb39ba 100644 --- a/ripngd/ripng_debug.c +++ b/ripngd/ripng_debug.c @@ -28,7 +28,7 @@ unsigned long ripng_debug_event = 0; unsigned long ripng_debug_packet = 0; unsigned long ripng_debug_zebra = 0; - + DEFUN (show_debugging_ripng, show_debugging_ripng_cmd, "show debugging ripng", diff --git a/ripngd/ripng_interface.c b/ripngd/ripng_interface.c index 6718fff2..8717bfb0 100644 --- a/ripngd/ripng_interface.c +++ b/ripngd/ripng_interface.c @@ -38,7 +38,7 @@ #include "ripngd/ripngd.h" #include "ripngd/ripng_debug.h" - + /* If RFC2133 definition is used. */ #ifndef IPV6_JOIN_GROUP #define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP @@ -505,7 +505,7 @@ ripng_interface_address_delete (int command, struct zclient *zclient, return 0; } - + /* RIPng enable interface vector. */ vector ripng_enable_if; @@ -812,7 +812,7 @@ ripng_enable_apply_all (void) for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) ripng_enable_apply (ifp); } - + /* Clear all network and neighbor configuration */ void ripng_clean_network () @@ -835,7 +835,7 @@ ripng_clean_network () vector_slot (ripng_enable_if, i) = NULL; } } - + /* Vector to store passive-interface name. */ vector Vripng_passive_interface; @@ -1103,7 +1103,7 @@ DEFUN (no_ripng_passive_interface, { return ripng_passive_interface_unset (vty, argv[0]); } - + static struct ripng_interface * ri_new (void) { diff --git a/ripngd/ripng_main.c b/ripngd/ripng_main.c index 7525a267..acc980de 100644 --- a/ripngd/ripng_main.c +++ b/ripngd/ripng_main.c @@ -127,7 +127,7 @@ Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS); } exit (status); } - + /* SIGHUP handler. */ static void sighup (void) @@ -182,7 +182,7 @@ struct quagga_signal_t ripng_signals[] = .handler = &sigint, }, }; - + /* RIPngd main routine. */ int main (int argc, char **argv) diff --git a/ripngd/ripng_peer.c b/ripngd/ripng_peer.c index c04456b8..b12e1460 100644 --- a/ripngd/ripng_peer.c +++ b/ripngd/ripng_peer.c @@ -38,7 +38,7 @@ /* Linked list of RIPng peer. */ struct list *peer_list; - + static struct ripng_peer * ripng_peer_new (void) { diff --git a/ripngd/ripng_routemap.c b/ripngd/ripng_routemap.c index 0f5cca35..f4fadb67 100644 --- a/ripngd/ripng_routemap.c +++ b/ripngd/ripng_routemap.c @@ -29,7 +29,7 @@ #include "sockunion.h" #include "ripngd/ripngd.h" - + struct rip_metric_modifier { enum @@ -42,7 +42,7 @@ struct rip_metric_modifier u_char metric; }; - + static int ripng_route_match_add (struct vty *vty, struct route_map_index *index, const char *command, const char *arg) @@ -130,7 +130,7 @@ ripng_route_set_delete (struct vty *vty, struct route_map_index *index, } return CMD_SUCCESS; } - + /* `match metric METRIC' */ /* Match function return 1 if match is success else return zero. */ static route_map_result_t @@ -184,7 +184,7 @@ static struct route_map_rule_cmd route_match_metric_cmd = route_match_metric_compile, route_match_metric_free }; - + /* `match interface IFNAME' */ /* Match function return 1 if match is success else return zero. */ static route_map_result_t @@ -284,7 +284,7 @@ static struct route_map_rule_cmd route_match_tag_cmd = route_match_tag_compile, route_match_tag_free }; - + /* `set metric METRIC' */ /* Set metric to attribute. */ @@ -496,7 +496,7 @@ static struct route_map_rule_cmd route_set_tag_cmd = route_set_tag_compile, route_set_tag_free }; - + #define MATCH_STR "Match values from routing table\n" #define SET_STR "Set values in destination routing protocol\n" diff --git a/ripngd/ripng_zebra.c b/ripngd/ripng_zebra.c index 8e766062..68f37be3 100644 --- a/ripngd/ripng_zebra.c +++ b/ripngd/ripng_zebra.c @@ -41,7 +41,7 @@ int ripng_interface_add (int, struct zclient *, zebra_size_t); int ripng_interface_delete (int, struct zclient *, zebra_size_t); int ripng_interface_address_add (int, struct zclient *, zebra_size_t); int ripng_interface_address_delete (int, struct zclient *, zebra_size_t); - + void ripng_zebra_ipv6_add (struct prefix_ipv6 *p, struct in6_addr *nexthop, unsigned int ifindex, u_char metric) @@ -206,7 +206,7 @@ ripng_redistribute_routemap_unset (int type) ripng->route_map[type].name = NULL; ripng->route_map[type].map = NULL; } - + /* Redistribution types */ static struct { int type; diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c index 318f600e..941c3a06 100644 --- a/ripngd/ripngd.c +++ b/ripngd/ripngd.c @@ -95,7 +95,7 @@ ripng_info_free (struct ripng_info *rinfo) { XFREE (MTYPE_RIPNG_ROUTE, rinfo); } - + /* Create ripng socket. */ static int ripng_make_socket (void) @@ -1886,7 +1886,7 @@ ripng_request (struct interface *ifp) NULL, ifp); } - + static int ripng_update_jitter (int time) { @@ -1928,7 +1928,7 @@ ripng_event (enum ripng_event event, int sock) break; } } - + /* Print out routes update time. */ static void @@ -2767,7 +2767,7 @@ ripng_distribute_update_all_wrapper (struct access_list *notused) { ripng_distribute_update_all(NULL); } - + /* delete all the added ripng routes. */ void ripng_clean() diff --git a/tests/main.c b/tests/main.c index 2d8cb0c5..5e7bdcb1 100644 --- a/tests/main.c +++ b/tests/main.c @@ -95,8 +95,8 @@ Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS); } exit (status); } - - + + /* main routine. */ int main (int argc, char **argv) diff --git a/tests/test-checksum.c b/tests/test-checksum.c index fc4eb02d..921b58c4 100644 --- a/tests/test-checksum.c +++ b/tests/test-checksum.c @@ -24,7 +24,7 @@ typedef uint16_t testoff_t; /* Fletcher Checksum -- Refer to RFC1008. */ #define MODX 4102 - + /* Accumulator phase of checksum */ static struct acc_vals @@ -263,7 +263,7 @@ struct reductions_t { { .name = "isisd-mody", .f = reduce_isisd_mody }, { NULL, NULL }, }; - + /* The original ospfd checksum */ static u_int16_t ospfd_checksum (u_char *buffer, testsz_t len, testoff_t off) diff --git a/tests/test-privs.c b/tests/test-privs.c index a888ea0f..beae81f6 100644 --- a/tests/test-privs.c +++ b/tests/test-privs.c @@ -74,7 +74,7 @@ Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS); } exit (status); } - + struct thread_master *master; /* main routine. */ int diff --git a/zebra/connected.c b/zebra/connected.c index 4802f2ba..c4f87f4c 100644 --- a/zebra/connected.c +++ b/zebra/connected.c @@ -36,7 +36,7 @@ #include "zebra/interface.h" #include "zebra/connected.h" extern struct zebra_t zebrad; - + /* communicate the withdrawal of a connected address */ static void connected_withdraw (struct connected *ifc) @@ -96,7 +96,7 @@ connected_announce (struct interface *ifp, struct connected *ifc) #endif } } - + /* If same interface address is already exist... */ struct connected * connected_check (struct interface *ifp, struct prefix *p) diff --git a/zebra/if_proc.c b/zebra/if_proc.c index 3aec530b..2dbc4726 100644 --- a/zebra/if_proc.c +++ b/zebra/if_proc.c @@ -198,7 +198,7 @@ interface_list_proc () fclose(fp); return 0; } - + #if defined(HAVE_IPV6) && defined(HAVE_PROC_NET_IF_INET6) #ifndef _PATH_PROC_NET_IF_INET6 diff --git a/zebra/interface.c b/zebra/interface.c index 155ee029..7e1d3dd8 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -1206,7 +1206,7 @@ ALIAS (no_bandwidth_if, NO_STR "Set bandwidth informational parameter\n" "Bandwidth in kilobits\n") - + static int ip_address_install (struct vty *vty, struct interface *ifp, const char *addr_str, const char *peer_str, diff --git a/zebra/irdp_packet.c b/zebra/irdp_packet.c index 50525043..0d31050c 100644 --- a/zebra/irdp_packet.c +++ b/zebra/irdp_packet.c @@ -184,7 +184,7 @@ parse_irdp_packet(char *p, inet_ntoa (src)); } } - + static int irdp_recvmsg (int sock, u_char *buf, int size, int *ifindex) { @@ -222,7 +222,7 @@ irdp_recvmsg (int sock, u_char *buf, int size, int *ifindex) return ret; } - + int irdp_read_raw(struct thread *r) { struct interface *ifp; @@ -264,7 +264,7 @@ int irdp_read_raw(struct thread *r) return ret; } - + void send_packet(struct interface *ifp, struct stream *s, diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index 37b2ae23..3dbeb98b 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -252,7 +252,7 @@ af_check (int family) #endif /* HAVE_IPV6 */ return 0; } - + /* Dump routing table flag for debug purpose. */ static void rtm_flag_dump (int flag) @@ -557,7 +557,7 @@ ifm_read (struct if_msghdr *ifm) return 0; } - + /* Address read from struct ifa_msghdr. */ static void ifam_read_mesg (struct ifa_msghdr *ifm, @@ -751,7 +751,7 @@ ifam_read (struct ifa_msghdr *ifam) return 0; } - + /* Interface function for reading kernel routing table information. */ static int rtm_read_mesg (struct rt_msghdr *rtm, @@ -1128,7 +1128,7 @@ rtm_write (int message, return ZEBRA_ERR_NOERROR; } - + #include "thread.h" #include "zebra/zserv.h" diff --git a/zebra/main.c b/zebra/main.c index 523b1911..d7f2a108 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -151,7 +151,7 @@ usage (char *progname, int status) exit (status); } - + /* SIGHUP handler. */ static void sighup (void) @@ -203,7 +203,7 @@ struct quagga_signal_t zebra_signals[] = .handler = &sigint, }, }; - + /* Main startup routine. */ int main (int argc, char **argv) diff --git a/zebra/rt_ioctl.c b/zebra/rt_ioctl.c index 404a7c69..e175d1e2 100644 --- a/zebra/rt_ioctl.c +++ b/zebra/rt_ioctl.c @@ -332,7 +332,7 @@ kernel_delete_ipv4 (struct prefix *p, struct rib *rib) { return kernel_ioctl_ipv4 (SIOCDELRT, p, rib, AF_INET); } - + #ifdef HAVE_IPV6 /* Below is hack for GNU libc definition and Linux 2.1.X header. */ diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 6a802f69..452ea642 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -1848,7 +1848,7 @@ kernel_delete_ipv6_old (struct prefix_ipv6 *dest, struct in6_addr *gate, dest->prefixlen, gate, index, flags, table); } #endif /* HAVE_IPV6 */ - + /* Interface address modification. */ static int netlink_address (int cmd, int family, struct interface *ifp, diff --git a/zebra/rtadv.c b/zebra/rtadv.c index 91d7b324..21ca6da9 100644 --- a/zebra/rtadv.c +++ b/zebra/rtadv.c @@ -66,7 +66,7 @@ static void rtadv_event (enum rtadv_event, int); static int if_join_all_router (int, struct interface *); static int if_leave_all_router (int, struct interface *); - + /* Structure which hold status of router advertisement. */ struct rtadv { @@ -80,7 +80,7 @@ struct rtadv }; struct rtadv *rtadv = NULL; - + static struct rtadv * rtadv_new (void) { @@ -580,7 +580,7 @@ rtadv_make_socket (void) return sock; } - + static struct rtadv_prefix * rtadv_prefix_new (void) { diff --git a/zebra/test_main.c b/zebra/test_main.c index c6951729..f98bb419 100644 --- a/zebra/test_main.c +++ b/zebra/test_main.c @@ -103,7 +103,7 @@ usage (char *progname, int status) exit (status); } - + static unsigned int test_ifindex = 0; /* testrib commands */ @@ -149,7 +149,7 @@ test_cmd_init (void) { install_element (INTERFACE_NODE, &test_interface_state_cmd); } - + /* SIGHUP handler. */ static void sighup (void) @@ -195,7 +195,7 @@ struct quagga_signal_t zebra_signals[] = .handler = &sigint, }, }; - + /* Main startup routine. */ int main (int argc, char **argv) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 8835ef37..dc7e1ca1 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -71,7 +71,7 @@ static const struct [ZEBRA_ROUTE_BABEL] = {ZEBRA_ROUTE_BABEL, 95}, /* no entry/default: 150 */ }; - + /* Vector for routing table. */ static vector vrf_vector; @@ -176,7 +176,7 @@ vrf_static_table (afi_t afi, safi_t safi, u_int32_t id) return vrf->stable[afi][safi]; } - + /* * nexthop_type_to_str */ @@ -1090,7 +1090,7 @@ nexthop_active_update (struct route_node *rn, struct rib *rib, int set) return rib->nexthop_active_num; } - + static void rib_install_kernel (struct route_node *rn, struct rib *rib) @@ -2283,7 +2283,7 @@ rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p, route_unlock_node (rn); return 0; } - + /* Install static route into rib. */ static void static_install_ipv4 (struct prefix *p, struct static_ipv4 *si) @@ -2596,7 +2596,7 @@ static_delete_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, return 1; } - + #ifdef HAVE_IPV6 static int rib_bogus_ipv6 (int type, struct prefix_ipv6 *p, @@ -2857,7 +2857,7 @@ rib_delete_ipv6 (int type, int flags, struct prefix_ipv6 *p, route_unlock_node (rn); return 0; } - + /* Install static route into rib. */ static void static_install_ipv6 (struct prefix *p, struct static_ipv6 *si) @@ -3155,7 +3155,7 @@ static_delete_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate, return 1; } #endif /* HAVE_IPV6 */ - + /* RIB update function. */ void rib_update (void) @@ -3176,7 +3176,7 @@ rib_update (void) rib_queue_add (&zebrad, rn); } - + /* Remove all routes which comes from non main table. */ static void rib_weed_table (struct route_table *table) @@ -3205,7 +3205,7 @@ rib_weed_tables (void) rib_weed_table (vrf_table (AFI_IP, SAFI_UNICAST, 0)); rib_weed_table (vrf_table (AFI_IP6, SAFI_UNICAST, 0)); } - + /* Delete self installed routes after zebra is relaunched. */ static void rib_sweep_table (struct route_table *table) @@ -3301,7 +3301,7 @@ rib_close (void) rib_close_table (vrf_table (AFI_IP, SAFI_UNICAST, 0)); rib_close_table (vrf_table (AFI_IP6, SAFI_UNICAST, 0)); } - + /* Routing information base initialize. */ void rib_init (void) diff --git a/zebra/zebra_routemap.c b/zebra/zebra_routemap.c index 39c7e1bf..b0dca088 100644 --- a/zebra/zebra_routemap.c +++ b/zebra/zebra_routemap.c @@ -123,7 +123,7 @@ zebra_route_set_delete (struct vty *vty, struct route_map_index *index, return CMD_SUCCESS; } - + /* `match interface IFNAME' */ /* Match function return 1 if match is success else return zero. */ static route_map_result_t @@ -467,7 +467,7 @@ static struct route_map_rule_cmd route_match_ip_next_hop_cmd = route_match_ip_next_hop_compile, route_match_ip_next_hop_free }; - + /* `match ip next-hop prefix-list PREFIX_LIST' */ static route_map_result_t @@ -525,7 +525,7 @@ static struct route_map_rule_cmd route_match_ip_next_hop_prefix_list_cmd = route_match_ip_next_hop_prefix_list_compile, route_match_ip_next_hop_prefix_list_free }; - + /* `match ip address IP_ACCESS_LIST' */ /* Match function should return 1 if match is success else return @@ -571,7 +571,7 @@ static struct route_map_rule_cmd route_match_ip_address_cmd = route_match_ip_address_compile, route_match_ip_address_free }; - + /* `match ip address prefix-list PREFIX_LIST' */ static route_map_result_t @@ -612,7 +612,7 @@ static struct route_map_rule_cmd route_match_ip_address_prefix_list_cmd = route_match_ip_address_prefix_list_free }; - + /* `set src A.B.C.D' */ /* Set src. */ diff --git a/zebra/zebra_snmp.c b/zebra/zebra_snmp.c index e06e1443..f0d3e7e5 100644 --- a/zebra/zebra_snmp.c +++ b/zebra/zebra_snmp.c @@ -34,7 +34,7 @@ #include "zebra/rib.h" #include "zebra/zserv.h" - + #define IPFWMIB 1,3,6,1,2,1,4,24 /* ipForwardTable */ @@ -78,7 +78,7 @@ #define ROWSTATUS ASN_INTEGER #define IPADDRESS ASN_IPADDRESS #define OBJECTIDENTIFIER ASN_OBJECT_ID - + extern struct zebra_t zebrad; oid ipfw_oid [] = { IPFWMIB }; @@ -130,7 +130,7 @@ struct variable zebra_variables[] = {IPCIDRROUTESTATUS, ROWSTATUS, RONLY, ipCidrTable, 3, {4, 1, 16}} }; - + static u_char * ipFwNumber (struct variable *v, oid objid[], size_t *objid_len, int exact, size_t *val_len, WriteMethod **write_method) diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index f4946f46..baa60db9 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -1210,7 +1210,7 @@ DEFUN (show_ip_mroute, return CMD_SUCCESS; } - + #ifdef HAVE_IPV6 /* General fucntion for IPv6 static route. */ static int diff --git a/zebra/zserv.c b/zebra/zserv.c index 55ac6e4f..ca17c2c6 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -42,7 +42,7 @@ #include "zebra/redistribute.h" #include "zebra/debug.h" #include "zebra/ipforward.h" - + /* Event list of zebra. */ enum event { ZEBRA_SERV, ZEBRA_READ, ZEBRA_WRITE }; @@ -51,7 +51,7 @@ extern struct zebra_t zebrad; static void zebra_event (enum event event, int sock, struct zserv *client); extern struct zebra_privs_t zserv_privs; - + static void zebra_client_close (struct zserv *client); static int @@ -661,7 +661,7 @@ zsend_ipv4_import_lookup (struct zserv *client, struct prefix_ipv4 *p) return zebra_server_send_message(client); } - + /* Router-id is updated. Send ZEBRA_ROUTER_ID_ADD to client. */ int zsend_router_id_update (struct zserv *client, struct prefix *p) @@ -690,7 +690,7 @@ zsend_router_id_update (struct zserv *client, struct prefix *p) return zebra_server_send_message(client); } - + /* Register zebra server interface information. Send current all interface and address information. */ static int @@ -1539,7 +1539,7 @@ zebra_serv_un (const char *path) zebra_event (ZEBRA_SERV, sock, NULL); } - + static void zebra_event (enum event event, int sock, struct zserv *client) @@ -1558,7 +1558,7 @@ zebra_event (enum event event, int sock, struct zserv *client) break; } } - + /* Display default rtm_table for all clients. */ DEFUN (show_table, show_table_cmd, @@ -1658,7 +1658,7 @@ static struct cmd_node table_node = "", /* This node has no interface. */ 1 }; - + /* Only display ip forwarding is enabled or not. */ DEFUN (show_ip_forwarding, show_ip_forwarding_cmd, @@ -1779,7 +1779,7 @@ static struct cmd_node forwarding_node = 1 }; - + /* Initialisation of zebra and installation of commands. */ void zebra_init (void) From 2c32ee5f04191c6ffae9c19621548bc72b00e3ba Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 24 Jun 2014 07:12:01 +0200 Subject: [PATCH 219/482] doc: update NEWS for 0.99.23 changes --- NEWS | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/NEWS b/NEWS index d1529bc4..57cc99f0 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,29 @@ Note: this file lists major user-visible changes only. +* Changes in Quagga 0.99.23 + +Known issues: +- [bgpd] setting an extcommunity in a route map on a route that already has + an extcommunity attribute will cause bgpd to crash. This issue will be + fixed in a followup minor release. + +User-visible changes: +- [lib] Performance enhancements on hashes and timers. +- [bgpd] New feature: iBGP TTL security. +- [bgpd] New feature: relaxed bestpath criteria for multipath and improved + display of multipath routes in "show ip bgp". Scripts parsing this output + may need to be updated. +- [bgpd] Multiprotocol peerings over IPv6 now try to find a more appropriate + IPv4 nexthop by looking at the interface. +- [ospf6d] A large amount of changes has been merged for ospf6d. Careful + evaluation prior to deployment is recommended. +- [zebra] Recursive route support has been overhauled. Scripts parsing + "show ip route" output may need adaptation. +- [zebra] IPv6 address management has been improved regarding tentative + addresses. This is visible in that a freshly configured address will not + immediately be marked as usable. +- [*] a lot of bugs have been fixed, please refer to the git log + * Changes in Quagga 0.99.22 - [bgpd] The semantics of default-originate route-map have changed. From a4b5665f76d9e907a547c85c9c4a7a656c568b9d Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 24 Jun 2014 07:14:20 +0200 Subject: [PATCH 220/482] release: 0.99.23 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index fa9e6e1f..220124ab 100755 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,7 @@ ## AC_PREREQ(2.53) -AC_INIT(Quagga, 0.99.23-rc1, [https://bugzilla.quagga.net]) +AC_INIT(Quagga, 0.99.23, [https://bugzilla.quagga.net]) AC_CONFIG_SRCDIR(lib/zebra.h) AC_CONFIG_MACRO_DIR([m4]) From 2aa640bd78b64821bde9a53ecdd1e96e91b20ae4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Ter=C3=A4s?= Date: Tue, 20 May 2014 08:57:26 +0300 Subject: [PATCH 221/482] bgpd: fix route-map comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Timo Teräs --- bgpd/bgp_routemap.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 6dc88b3a..cb5ccffd 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -93,7 +93,7 @@ o Cisco route-map tag : (This will not be implemented by bgpd) weight : Done -o Local extention +o Local extensions set ipv6 next-hop global: Done set ipv6 next-hop local : Done @@ -1248,7 +1248,7 @@ route_set_aspath_prepend_free (void *rule) aspath_free (aspath); } -/* Set metric rule structure. */ +/* Set as-path prepend rule structure. */ struct route_map_rule_cmd route_set_aspath_prepend_cmd = { "as-path prepend", @@ -1705,7 +1705,7 @@ route_set_origin_free (void *rule) XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); } -/* Set metric rule structure. */ +/* Set origin rule structure. */ struct route_map_rule_cmd route_set_origin_cmd = { "origin", @@ -2191,7 +2191,7 @@ route_set_originator_id_free (void *rule) XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); } -/* Set metric rule structure. */ +/* Set originator-id rule structure. */ struct route_map_rule_cmd route_set_originator_id_cmd = { "originator-id", From 9e7a53c179f6897128b24435452b5d3d0f8c715a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Ter=C3=A4s?= Date: Thu, 24 Apr 2014 10:22:37 +0300 Subject: [PATCH 222/482] bgpd: implement "next-hop-self all" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As specified in: http://www.cisco.com/c/en/us/td/docs/ios-xml/ios/iproute_bgp/command/irg-cr-book/bgp-m1.html#wp4972925610 This allows overriding next-hop for ibgp learned routes on an RR for reflected routes. Especially useful for using iBGP in DMVPN setups. See: http://blog.ipspace.net/2014/04/changes-in-ibgp-next-hop-processing.html Signed-off-by: Timo Teräs --- bgpd/bgp_route.c | 3 ++- bgpd/bgp_vty.c | 30 +++++++++++++++++++++++------- bgpd/bgpd.c | 5 ++++- bgpd/bgpd.h | 1 + doc/bgpd.texi | 8 +++++--- 5 files changed, 35 insertions(+), 12 deletions(-) diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index f421ca5e..232a6a1c 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -976,7 +976,8 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p, } /* next-hop-set */ - if (transparent || reflect + if (transparent + || (reflect && ! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_SELF_ALL)) || (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_UNCHANGED) && ((p->family == AF_INET && attr->nexthop.s_addr) #ifdef HAVE_IPV6 diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 3c6973b0..a818fe7a 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -2093,25 +2093,41 @@ DEFUN (no_neighbor_capability_orf_prefix, /* neighbor next-hop-self. */ DEFUN (neighbor_nexthop_self, neighbor_nexthop_self_cmd, - NEIGHBOR_CMD2 "next-hop-self", + NEIGHBOR_CMD2 "next-hop-self {all}", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 - "Disable the next hop calculation for this neighbor\n") + "Disable the next hop calculation for this neighbor\n" + "Apply also to ibgp-learned routes when acting as a route reflector\n") { - return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty), - bgp_node_safi (vty), PEER_FLAG_NEXTHOP_SELF); + u_int32_t flags = PEER_FLAG_NEXTHOP_SELF, unset = 0; + int rc; + + /* Check if "all" is specified */ + if (argv[1] != NULL) + flags |= PEER_FLAG_NEXTHOP_SELF_ALL; + else + unset |= PEER_FLAG_NEXTHOP_SELF_ALL; + + rc = peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), flags); + if ( rc == CMD_SUCCESS && unset ) + rc = peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), unset); + return rc; } DEFUN (no_neighbor_nexthop_self, no_neighbor_nexthop_self_cmd, - NO_NEIGHBOR_CMD2 "next-hop-self", + NO_NEIGHBOR_CMD2 "next-hop-self {all}", NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2 - "Disable the next hop calculation for this neighbor\n") + "Disable the next hop calculation for this neighbor\n" + "Apply also to ibgp-learned routes when acting as a route reflector\n") { return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty), - bgp_node_safi (vty), PEER_FLAG_NEXTHOP_SELF); + bgp_node_safi (vty), + PEER_FLAG_NEXTHOP_SELF|PEER_FLAG_NEXTHOP_SELF_ALL); } /* neighbor remove-private-AS. */ diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 19b96fa9..4d374cc1 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -2355,6 +2355,7 @@ static const struct peer_flag_action peer_af_flag_action_list[] = { PEER_FLAG_ORF_PREFIX_SM, 1, peer_change_reset }, { PEER_FLAG_ORF_PREFIX_RM, 1, peer_change_reset }, { PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED, 0, peer_change_reset_out }, + { PEER_FLAG_NEXTHOP_SELF_ALL, 1, peer_change_reset_out }, { 0, 0, 0 } }; @@ -4990,7 +4991,9 @@ bgp_config_write_peer (struct vty *vty, struct bgp *bgp, /* Nexthop self. */ if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_NEXTHOP_SELF) && ! peer->af_group[afi][safi]) - vty_out (vty, " neighbor %s next-hop-self%s", addr, VTY_NEWLINE); + vty_out (vty, " neighbor %s next-hop-self%s%s", addr, + peer_af_flag_check (peer, afi, safi, PEER_FLAG_NEXTHOP_SELF_ALL) ? + " all" : "", VTY_NEWLINE); /* Remove private AS. */ if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_REMOVE_PRIVATE_AS) diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index a1b1273b..eae803de 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -412,6 +412,7 @@ struct peer #define PEER_FLAG_MAX_PREFIX (1 << 14) /* maximum prefix */ #define PEER_FLAG_MAX_PREFIX_WARNING (1 << 15) /* maximum prefix warning-only */ #define PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED (1 << 16) /* leave link-local nexthop unchanged */ +#define PEER_FLAG_NEXTHOP_SELF_ALL (1 << 17) /* next-hop-self all */ /* MD5 password */ char *password; diff --git a/doc/bgpd.texi b/doc/bgpd.texi index cb9789bd..de709707 100644 --- a/doc/bgpd.texi +++ b/doc/bgpd.texi @@ -299,10 +299,12 @@ This command is deprecated and may be removed in a future release. Its use should be avoided. @end deffn -@deffn {BGP} {neighbor @var{peer} next-hop-self} {} -@deffnx {BGP} {no neighbor @var{peer} next-hop-self} {} +@deffn {BGP} {neighbor @var{peer} next-hop-self [all]} {} +@deffnx {BGP} {no neighbor @var{peer} next-hop-self [all]} {} This command specifies an announced route's nexthop as being equivalent -to the address of the bgp router. +to the address of the bgp router if it is learned via eBGP. +If the optional keyword @code{all} is specified the modifiation is done +also for routes learned via iBGP. @end deffn @deffn {BGP} {neighbor @var{peer} update-source @var{}} {} From b304dcb8abc4e5b93f86a4024990980746e730be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Ter=C3=A4s?= Date: Tue, 20 May 2014 09:04:49 +0300 Subject: [PATCH 223/482] bgpd: route-map: share aspath object compilation code where possible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Timo Teräs --- bgpd/bgp_routemap.c | 72 +++++++++++++++------------------------------ 1 file changed, 24 insertions(+), 48 deletions(-) diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index cb5ccffd..a0b1fb42 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -101,6 +101,26 @@ o Local extensions */ + /* generic as path object to be shared in multiple rules */ + +static void * +route_aspath_compile (const char *arg) +{ + struct aspath *aspath; + + aspath = aspath_str2aspath (arg); + if (! aspath) + return NULL; + return aspath; +} + +static void +route_aspath_free (void *rule) +{ + struct aspath *aspath = rule; + aspath_free (aspath); +} + /* 'match peer (A.B.C.D|X:X::X:X)' */ /* Compares the peer specified in the 'match peer' clause with the peer @@ -1228,33 +1248,13 @@ route_set_aspath_prepend (void *rule, struct prefix *prefix, route_map_object_t return RMAP_OKAY; } -/* Compile function for as-path prepend. */ -static void * -route_set_aspath_prepend_compile (const char *arg) -{ - struct aspath *aspath; - - aspath = aspath_str2aspath (arg); - if (! aspath) - return NULL; - return aspath; -} - -/* Compile function for as-path prepend. */ -static void -route_set_aspath_prepend_free (void *rule) -{ - struct aspath *aspath = rule; - aspath_free (aspath); -} - /* Set as-path prepend rule structure. */ struct route_map_rule_cmd route_set_aspath_prepend_cmd = { "as-path prepend", route_set_aspath_prepend, - route_set_aspath_prepend_compile, - route_set_aspath_prepend_free, + route_aspath_compile, + route_aspath_free, }; /* `set as-path exclude ASn' */ @@ -1282,37 +1282,13 @@ route_set_aspath_exclude (void *rule, struct prefix *dummy, route_map_object_t t 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, + route_aspath_compile, + route_aspath_free, }; /* `set community COMMUNITY' */ From c460e5720c1101a6da53e5b753b736ac2c7981af Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Wed, 4 Jun 2014 00:54:58 +0200 Subject: [PATCH 224/482] bgpd: fix some bgp_update_main() attribute leaks bgp_update_main() wasn't doing anything to release attribute values set from route maps for two of its error paths. To fix, pull up the appropriate cleanup from further down and apply it here. bgp_update_rsclient() doesn't have the issue since it immediately does bgp_attr_intern() on the results from bgp_{export,import}_modifier. Signed-off-by: David Lamparter --- bgpd/bgp_route.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index f421ca5e..ed8464d2 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -713,11 +713,8 @@ bgp_input_modifier (struct peer *peer, struct prefix *p, struct attr *attr, peer->rmap_type = 0; if (ret == RMAP_DENYMATCH) - { - /* Free newly generated AS path and community by route-map. */ - bgp_attr_flush (attr); - return RMAP_DENY; - } + /* caller has multiple error paths with bgp_attr_flush() */ + return RMAP_DENY; } return RMAP_PERMIT; } @@ -2143,10 +2140,14 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr, new_attr.extra = &new_extra; bgp_attr_dup (&new_attr, attr); - /* Apply incoming route-map. */ + /* Apply incoming route-map. + * NB: new_attr may now contain newly allocated values from route-map "set" + * commands, so we need bgp_attr_flush in the error paths, until we intern + * the attr (which takes over the memory references) */ if (bgp_input_modifier (peer, p, &new_attr, afi, safi) == RMAP_DENY) { reason = "route-map;"; + bgp_attr_flush (&new_attr); goto filtered; } @@ -2160,6 +2161,7 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr, && ! CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)) { reason = "non-connected next-hop;"; + bgp_attr_flush (&new_attr); goto filtered; } @@ -2170,6 +2172,7 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr, || bgp_nexthop_self (&new_attr)) { reason = "martian next-hop;"; + bgp_attr_flush (&new_attr); goto filtered; } } From 73d78ea0153fd36a300be5fec2ef0fca34a67477 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Wed, 4 Jun 2014 00:58:47 +0200 Subject: [PATCH 225/482] bgpd: remove duplicate route-map extcommunity code route_set_ecommunity_rt and _soo share almost all of their code. Let's remove one of the redundant copies. Signed-off-by: David Lamparter --- bgpd/bgp_routemap.c | 59 +++++++-------------------------------------- 1 file changed, 9 insertions(+), 50 deletions(-) diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 6dc88b3a..eb5e80f8 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -1530,10 +1530,10 @@ struct route_map_rule_cmd route_set_community_delete_cmd = /* `set extcommunity rt COMMUNITY' */ -/* For community set mechanism. */ +/* For community set mechanism. Used by _rt and _soo. */ static route_map_result_t -route_set_ecommunity_rt (void *rule, struct prefix *prefix, - route_map_object_t type, void *object) +route_set_ecommunity (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) { struct ecommunity *ecom; struct ecommunity *new_ecom; @@ -1578,9 +1578,9 @@ route_set_ecommunity_rt_compile (const char *arg) return ecommunity_intern (ecom); } -/* Free function for set community. */ +/* Free function for set community. Used by _rt and _soo */ static void -route_set_ecommunity_rt_free (void *rule) +route_set_ecommunity_free (void *rule) { struct ecommunity *ecom = rule; ecommunity_unintern (&ecom); @@ -1590,46 +1590,13 @@ route_set_ecommunity_rt_free (void *rule) struct route_map_rule_cmd route_set_ecommunity_rt_cmd = { "extcommunity rt", - route_set_ecommunity_rt, + route_set_ecommunity, route_set_ecommunity_rt_compile, - route_set_ecommunity_rt_free, + route_set_ecommunity_free, }; /* `set extcommunity soo COMMUNITY' */ -/* For community set mechanism. */ -static route_map_result_t -route_set_ecommunity_soo (void *rule, struct prefix *prefix, - route_map_object_t type, void *object) -{ - struct ecommunity *ecom, *old_ecom, *new_ecom; - struct bgp_info *bgp_info; - - if (type == RMAP_BGP) - { - ecom = rule; - bgp_info = object; - - if (! ecom) - return RMAP_OKAY; - - old_ecom = (bgp_attr_extra_get (bgp_info->attr))->ecommunity; - - if (old_ecom) - new_ecom = ecommunity_merge (ecommunity_dup (old_ecom), ecom); - else - new_ecom = ecommunity_dup (ecom); - - bgp_info->attr->extra->ecommunity = ecommunity_intern (new_ecom); - - if (old_ecom) - ecommunity_unintern (&old_ecom); - - bgp_info->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES); - } - return RMAP_OKAY; -} - /* Compile function for set community. */ static void * route_set_ecommunity_soo_compile (const char *arg) @@ -1643,21 +1610,13 @@ route_set_ecommunity_soo_compile (const char *arg) return ecommunity_intern (ecom); } -/* Free function for set community. */ -static void -route_set_ecommunity_soo_free (void *rule) -{ - struct ecommunity *ecom = rule; - ecommunity_unintern (&ecom); -} - /* Set community rule structure. */ struct route_map_rule_cmd route_set_ecommunity_soo_cmd = { "extcommunity soo", - route_set_ecommunity_soo, + route_set_ecommunity, route_set_ecommunity_soo_compile, - route_set_ecommunity_soo_free, + route_set_ecommunity_free, }; /* `set origin ORIGIN' */ From 27bf90a14670283a899b96c56dd23f8413e0973e Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Wed, 4 Jun 2014 00:59:01 +0200 Subject: [PATCH 226/482] bgpd: fix double free after extcommunity set (BZ#799) The route-map extcommunity set code was incorrectly assuming that it owns the intern'd struct ecommunity reference. In reality, the intern'd reference belongs to bgp_update_receive() and we're not supposed to touch it in the route-map code. Instead, like all the other set commands, we use a on-heap but non-intern'd ecommunity to set the new value. This is then either intern'd in bgp_update_main/_rsclient() through bgp_attr_intern(), or free'd through bgp_attr_flush(). This fixes Bugzilla #799, which is that bgpd otherwise crashes with a double free. The ecommunity got unintern'd first in the route-map set command, then in bgp_update_receive(). Debugged-by: Milan Kocian Reported-by: Florian S Signed-off-by: David Lamparter --- bgpd/bgp_routemap.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index eb5e80f8..36d177d2 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -1552,14 +1552,19 @@ route_set_ecommunity (void *rule, struct prefix *prefix, old_ecom = (bgp_attr_extra_get (bgp_info->attr))->ecommunity; if (old_ecom) - new_ecom = ecommunity_merge (ecommunity_dup (old_ecom), ecom); + { + new_ecom = ecommunity_merge (ecommunity_dup (old_ecom), ecom); + + /* old_ecom->refcnt = 1 => owned elsewhere, e.g. bgp_update_receive() + * ->refcnt = 0 => set by a previous route-map statement */ + if (!old_ecom->refcnt) + ecommunity_free (&old_ecom); + } else new_ecom = ecommunity_dup (ecom); - bgp_info->attr->extra->ecommunity = ecommunity_intern (new_ecom); - - if (old_ecom) - ecommunity_unintern (&old_ecom); + /* will be intern()'d or attr_flush()'d by bgp_update_main() */ + bgp_info->attr->extra->ecommunity = new_ecom; bgp_info->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES); } From f80f838b2f54738937ef1281b237710132195c44 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Wed, 4 Jun 2014 01:00:51 +0200 Subject: [PATCH 227/482] bgpd: fix memory leak on malformed attribute When bgp_attr_parse returns BGP_ATTR_PARSE_ERROR, it may already have parsed and allocated some attributes before hitting that error. Free the attr's data before returning. Signed-off-by: David Lamparter --- bgpd/bgp_packet.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index 80651f15..65c6cac1 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -1720,7 +1720,10 @@ bgp_update_receive (struct peer *peer, bgp_size_t size) attr_parse_ret = bgp_attr_parse (peer, &attr, attribute_len, &mp_update, &mp_withdraw); if (attr_parse_ret == BGP_ATTR_PARSE_ERROR) - return -1; + { + bgp_attr_unintern_sub (&attr); + return -1; + } } /* Logging the attribute. */ From bb02b82354a80f74706efc5e4c914b3f89fb033e Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Wed, 4 Jun 2014 01:01:00 +0200 Subject: [PATCH 228/482] bgpd: fix IP endianness in debug message inet_ntop expects network byte order. Signed-off-by: David Lamparter --- bgpd/bgp_attr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index a1fd1654..f9fde9f0 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -1110,7 +1110,7 @@ bgp_attr_nexthop (struct bgp_attr_parser_args *args) if (IPV4_NET0 (nexthop_h) || IPV4_NET127 (nexthop_h) || IPV4_CLASS_DE (nexthop_h)) { char buf[INET_ADDRSTRLEN]; - inet_ntop (AF_INET, &nexthop_h, buf, INET_ADDRSTRLEN); + inet_ntop (AF_INET, &nexthop_n, buf, INET_ADDRSTRLEN); zlog (peer->log, LOG_ERR, "Martian nexthop %s", buf); return bgp_attr_malformed (args, BGP_NOTIFY_UPDATE_INVAL_NEXT_HOP, From f57000c0dbdd0e30e71b6651022392f284201e19 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Wed, 4 Jun 2014 01:01:10 +0200 Subject: [PATCH 229/482] bgpd: don't send NOTIFY twice for malformed attrs Most of the attribute parsing functions were already sending a notify, let's clean up the code to make it happen only once. Signed-off-by: David Lamparter --- bgpd/bgp_attr.c | 34 ++++++++++++++++++++++------------ bgpd/bgp_attr.h | 3 +++ 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index f9fde9f0..fcf82551 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -794,7 +794,7 @@ bgp_attr_malformed (struct bgp_attr_parser_args *args, u_char subcode, return BGP_ATTR_PARSE_WITHDRAW; /* default to reset */ - return BGP_ATTR_PARSE_ERROR; + return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; } /* Find out what is wrong with the path attribute flag bits and log the error. @@ -1483,7 +1483,7 @@ bgp_mp_reach_parse (struct bgp_attr_parser_args *args, { zlog_info ("%s: %s sent invalid length, %lu", __func__, peer->host, (unsigned long)length); - return BGP_ATTR_PARSE_ERROR; + return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; } /* Load AFI, SAFI. */ @@ -1497,7 +1497,7 @@ bgp_mp_reach_parse (struct bgp_attr_parser_args *args, { zlog_info ("%s: %s, MP nexthop length, %u, goes past end of attribute", __func__, peer->host, attre->mp_nexthop_len); - return BGP_ATTR_PARSE_ERROR; + return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; } /* Nexthop length check. */ @@ -1540,14 +1540,14 @@ bgp_mp_reach_parse (struct bgp_attr_parser_args *args, default: zlog_info ("%s: (%s) Wrong multiprotocol next hop length: %d", __func__, peer->host, attre->mp_nexthop_len); - return BGP_ATTR_PARSE_ERROR; + return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; } if (!LEN_LEFT) { zlog_info ("%s: (%s) Failed to read SNPA and NLRI(s)", __func__, peer->host); - return BGP_ATTR_PARSE_ERROR; + return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; } { @@ -1563,7 +1563,7 @@ bgp_mp_reach_parse (struct bgp_attr_parser_args *args, { zlog_info ("%s: (%s) Failed to read NLRI", __func__, peer->host); - return BGP_ATTR_PARSE_ERROR; + return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; } if (safi != SAFI_MPLS_LABELED_VPN) @@ -1573,7 +1573,7 @@ bgp_mp_reach_parse (struct bgp_attr_parser_args *args, { zlog_info ("%s: (%s) NLRI doesn't pass sanity check", __func__, peer->host); - return BGP_ATTR_PARSE_ERROR; + return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; } } @@ -1605,7 +1605,7 @@ bgp_mp_unreach_parse (struct bgp_attr_parser_args *args, #define BGP_MP_UNREACH_MIN_SIZE 3 if ((length > STREAM_READABLE(s)) || (length < BGP_MP_UNREACH_MIN_SIZE)) - return BGP_ATTR_PARSE_ERROR; + return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; afi = stream_getw (s); safi = stream_getc (s); @@ -1616,7 +1616,7 @@ bgp_mp_unreach_parse (struct bgp_attr_parser_args *args, { ret = bgp_nlri_sanity_check (peer, afi, stream_pnt (s), withdraw_len); if (ret < 0) - return BGP_ATTR_PARSE_ERROR; + return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; } mp_withdraw->afi = afi; @@ -1913,6 +1913,14 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, break; } + if (ret == BGP_ATTR_PARSE_ERROR_NOTIFYPLS) + { + bgp_notify_send (peer, + BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MAL_ATTR); + ret = BGP_ATTR_PARSE_ERROR; + } + /* If hard error occured immediately return to the caller. */ if (ret == BGP_ATTR_PARSE_ERROR) { @@ -1920,9 +1928,6 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, "%s: Attribute %s, parse error", peer->host, LOOKUP (attr_str, type)); - bgp_notify_send (peer, - BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_MAL_ATTR); if (as4_path) aspath_unintern (&as4_path); return ret; @@ -1979,9 +1984,14 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, * all attributes first, including these 32bit ones, and now, * afterwards, we look what and if something is to be done for as4. */ + /* actually... this doesn't ever return failure currently, but + * better safe than sorry */ if (bgp_attr_munge_as4_attrs (peer, attr, as4_path, as4_aggregator, &as4_aggregator_addr)) { + bgp_notify_send (peer, + BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MAL_ATTR); if (as4_path) aspath_unintern (&as4_path); return BGP_ATTR_PARSE_ERROR; diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index cdd54674..cb401e71 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -136,6 +136,9 @@ typedef enum { BGP_ATTR_PARSE_PROCEED = 0, BGP_ATTR_PARSE_ERROR = -1, BGP_ATTR_PARSE_WITHDRAW = -2, + + /* only used internally, send notify + convert to BGP_ATTR_PARSE_ERROR */ + BGP_ATTR_PARSE_ERROR_NOTIFYPLS = -3, } bgp_attr_parse_ret_t; /* Prototypes. */ From 342a31bfda21616209366679ac522471e5772a2f Mon Sep 17 00:00:00 2001 From: Lu Feng Date: Wed, 25 Jun 2014 07:43:15 +0000 Subject: [PATCH 230/482] ripd: use only one constant for derivation RIP_MAX_RTE is defined in ripd.h as 25 but is in fact the result of a formula. More over it is not used in the code: the code itself includes the fomula. This makes it un-clear for maintenance. Signed-off-by: Feng Lu Reviewed-by: Alain Ritoux Signed-off-by: David Lamparter --- ripd/ripd.c | 2 +- ripd/ripd.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ripd/ripd.c b/ripd/ripd.c index dfeb951c..8a7fef87 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -2169,7 +2169,7 @@ rip_output_process (struct connected *ifc, struct sockaddr_in *to, /* Reset stream and RTE counter. */ stream_reset (s); - rtemax = (RIP_PACKET_MAXSIZ - 4) / 20; + rtemax = RIP_MAX_RTE; /* Get RIP interface. */ ri = ifc->ifp->info; diff --git a/ripd/ripd.h b/ripd/ripd.h index 45b07b9c..0fc2fd37 100644 --- a/ripd/ripd.h +++ b/ripd/ripd.h @@ -49,7 +49,7 @@ #define RIP_RTE_SIZE 20 /* Max count of routing table entry in one rip packet. */ -#define RIP_MAX_RTE 25 +#define RIP_MAX_RTE ((RIP_PACKET_MAXSIZ - RIP_HEADER_SIZE) / RIP_RTE_SIZE) /* RIP version 2 multicast address. */ #ifndef INADDR_RIP_GROUP From 8b16ed74fa61523c3348d2584b66a56a8ad4e350 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sun, 6 Jul 2014 22:33:48 +0200 Subject: [PATCH 231/482] tests/bgpd: don't hardcode error number (fix f57000c) f57000c ("bgpd: don't send NOTIFY twice for malformed attrs") introduces BGP_ATTR_PARSE_ERROR_NOTIFYPLS as additional error code that implies the caller should sent a NOTIFY and convert it to BGP_ATTR_PARSE_ERROR. Sadly, the latter was hardcoded in bgp_mp_attr_test.c, which now didn't consider the new value to be an error. Make the testcase treat all nonzero values as error without discern. Signed-off-by: David Lamparter --- tests/bgp_mp_attr_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/bgp_mp_attr_test.c b/tests/bgp_mp_attr_test.c index 177c1ad8..aa8e485d 100644 --- a/tests/bgp_mp_attr_test.c +++ b/tests/bgp_mp_attr_test.c @@ -478,7 +478,7 @@ parse_test (struct peer *peer, struct test_segment *t, int type) printf ("parsed?: %s\n", ret ? "no" : "yes"); - if (ret != t->parses) + if ((ret == 0) != (t->parses == 0)) failed++; if (tty) From 28a8cfcbc3a5cc74bb3b87981b878f8b4edc2dd6 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sun, 29 Jun 2014 13:48:18 +0200 Subject: [PATCH 232/482] isisd: don't require IPv4 for adjacency This was precluding isisd from IPv6-only operation; no adjacency would come up unless there was IPv4 in parallel. Reported-by: Martin Winter Signed-off-by: David Lamparter --- isisd/isis_pdu.c | 89 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 79 insertions(+), 10 deletions(-) diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c index 8a92789f..5f18135e 100644 --- a/isisd/isis_pdu.c +++ b/isisd/isis_pdu.c @@ -402,6 +402,7 @@ process_p2p_hello (struct isis_circuit *circuit) u_int32_t expected = 0, found = 0, auth_tlv_offset = 0; uint16_t pdu_len; struct tlvs tlvs; + int v4_usable = 0, v6_usable = 0; if (isis->debugs & DEBUG_ADJ_PACKETS) { @@ -518,11 +519,44 @@ process_p2p_hello (struct isis_circuit *circuit) /* * check if it's own interface ip match iih ip addrs */ - if ((found & TLVFLAG_IPV4_ADDR) == 0 || - ip_match (circuit->ip_addrs, tlvs.ipv4_addrs) == 0) + if (found & TLVFLAG_IPV4_ADDR) + { + if (ip_match (circuit->ip_addrs, tlvs.ipv4_addrs)) + v4_usable = 1; + else + zlog_warn ("ISIS-Adj: IPv4 addresses present but no overlap " + "in P2P IIH from %s\n", circuit->interface->name); + } +#ifndef HAVE_IPV6 + else /* !(found & TLVFLAG_IPV4_ADDR) */ + zlog_warn ("ISIS-Adj: no IPv4 in P2P IIH from %s " + "(this isisd has no IPv6)\n", circuit->interface->name); + +#else + if (found & TLVFLAG_IPV6_ADDR) + { + /* TBA: check that we have a linklocal ourselves? */ + struct listnode *node; + struct prefix_ipv6 *ip; + for (ALL_LIST_ELEMENTS_RO (tlvs.ipv6_addrs, node, ip)) + if (IN6_IS_ADDR_LINKLOCAL (ip)) + { + v6_usable = 1; + break; + } + + if (!v6_usable) + zlog_warn ("ISIS-Adj: IPv6 addresses present but no link-local " + "in P2P IIH from %s\n", circuit->interface->name); + } + + if (!(found & (TLVFLAG_IPV4_ADDR | TLVFLAG_IPV6_ADDR))) + zlog_warn ("ISIS-Adj: neither IPv4 nor IPv6 addr in P2P IIH from %s\n", + circuit->interface->name); +#endif + + if (!v6_usable && !v4_usable) { - zlog_warn ("ISIS-Adj: No usable IP interface addresses " - "in LAN IIH from %s\n", circuit->interface->name); free_tlvs (&tlvs); return ISIS_WARNING; } @@ -859,6 +893,7 @@ process_lan_hello (int level, struct isis_circuit *circuit, u_char * ssnpa) struct tlvs tlvs; u_char *snpa; struct listnode *node; + int v4_usable = 0, v6_usable = 0; if (isis->debugs & DEBUG_ADJ_PACKETS) { @@ -1045,15 +1080,49 @@ process_lan_hello (int level, struct isis_circuit *circuit, u_char * ssnpa) /* * check if it's own interface ip match iih ip addrs */ - if ((found & TLVFLAG_IPV4_ADDR) == 0 || - ip_match (circuit->ip_addrs, tlvs.ipv4_addrs) == 0) + if (found & TLVFLAG_IPV4_ADDR) { - zlog_debug ("ISIS-Adj: No usable IP interface addresses " - "in LAN IIH from %s\n", circuit->interface->name); - retval = ISIS_WARNING; - goto out; + if (ip_match (circuit->ip_addrs, tlvs.ipv4_addrs)) + v4_usable = 1; + else + zlog_warn ("ISIS-Adj: IPv4 addresses present but no overlap " + "in LAN IIH from %s\n", circuit->interface->name); + } +#ifndef HAVE_IPV6 + else /* !(found & TLVFLAG_IPV4_ADDR) */ + zlog_warn ("ISIS-Adj: no IPv4 in LAN IIH from %s " + "(this isisd has no IPv6)\n", circuit->interface->name); + +#else + if (found & TLVFLAG_IPV6_ADDR) + { + /* TBA: check that we have a linklocal ourselves? */ + struct listnode *node; + struct prefix_ipv6 *ip; + for (ALL_LIST_ELEMENTS_RO (tlvs.ipv6_addrs, node, ip)) + if (IN6_IS_ADDR_LINKLOCAL (ip)) + { + v6_usable = 1; + break; + } + + if (!v6_usable) + zlog_warn ("ISIS-Adj: IPv6 addresses present but no link-local " + "in LAN IIH from %s\n", circuit->interface->name); } + if (!(found & (TLVFLAG_IPV4_ADDR | TLVFLAG_IPV6_ADDR))) + zlog_warn ("ISIS-Adj: neither IPv4 nor IPv6 addr in LAN IIH from %s\n", + circuit->interface->name); +#endif + + if (!v6_usable && !v4_usable) + { + free_tlvs (&tlvs); + return ISIS_WARNING; + } + + adj = isis_adj_lookup (hdr.source_id, circuit->u.bc.adjdb[level - 1]); if ((adj == NULL) || (memcmp(adj->snpa, ssnpa, ETH_ALEN)) || (adj->level != level)) From 16ffb26fbbf8b3d1fee7a14eb401ecb02eed5058 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Ter=C3=A4s?= Date: Tue, 29 Jul 2014 09:41:54 +0000 Subject: [PATCH 233/482] *: fix detection and usage of sys/cdefs.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This header is non-standard (though present on many systems) and there is no standard for what it should or should not define. Remove it where it is not really needed. But add also a configure check, so it can be used if available but otherwise fallback to defining the needed macroes. Signed-off-by: Timo Teräs Signed-off-by: David Lamparter --- configure.ac | 2 +- isisd/include-netbsd/iso.h | 2 +- lib/queue.h | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index 220124ab..3dba3b35 100755 --- a/configure.ac +++ b/configure.ac @@ -442,7 +442,7 @@ dnl Check other header files. dnl ------------------------- AC_CHECK_HEADERS([stropts.h sys/ksym.h sys/times.h sys/select.h \ sys/types.h linux/version.h netdb.h asm/types.h \ - sys/param.h limits.h signal.h \ + sys/cdefs.h sys/param.h limits.h signal.h \ sys/socket.h netinet/in.h time.h sys/time.h]) dnl Utility macro to avoid retyping includes all the time diff --git a/isisd/include-netbsd/iso.h b/isisd/include-netbsd/iso.h index 1a80aec8..42b9bc88 100644 --- a/isisd/include-netbsd/iso.h +++ b/isisd/include-netbsd/iso.h @@ -192,7 +192,7 @@ extern struct protosw isosw[]; #else /* user utilities definitions from the iso library */ -#ifdef SUNOS_5 +#ifndef HAVE_SYS_CDEFS_H #define __P(x) x #define __BEGIN_DECLS #define __END_DECLS diff --git a/lib/queue.h b/lib/queue.h index 70cffab1..48b363e2 100644 --- a/lib/queue.h +++ b/lib/queue.h @@ -33,8 +33,6 @@ #ifndef _SYS_QUEUE_H_ #define _SYS_QUEUE_H_ -#include - /* * This file defines four types of data structures: singly-linked lists, * singly-linked tail queues, lists and tail queues. From c299ed717eea4dbf7ca3581bcba05ff09f79276c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Ter=C3=A4s?= Date: Tue, 29 Jul 2014 09:41:55 +0000 Subject: [PATCH 234/482] zebra: fix struct msghdr initializers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit struct msghdr field orders are not strictly specified in POSIX. Improve portability by using designated initializer. This fixes build against musl c-library where struct msghdr is POSIX compliant (Linux kernel and glibc definitions are non-conforming). As the result is also more readable, struct iovec initilizers were also converted. Signed-off-by: Timo Teräs Signed-off-by: David Lamparter --- zebra/rt_netlink.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 452ea642..95a82fd2 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -282,9 +282,17 @@ netlink_parse_info (int (*filter) (struct sockaddr_nl *, struct nlmsghdr *), while (1) { char buf[NL_PKT_BUF_SIZE]; - struct iovec iov = { buf, sizeof buf }; + struct iovec iov = { + .iov_base = buf, + .iov_len = sizeof buf + }; struct sockaddr_nl snl; - struct msghdr msg = { (void *) &snl, sizeof snl, &iov, 1, NULL, 0, 0 }; + struct msghdr msg = { + .msg_name = (void *) &snl, + .msg_namelen = sizeof snl, + .msg_iov = &iov, + .msg_iovlen = 1 + }; struct nlmsghdr *h; status = recvmsg (nl->sock, &msg, 0); @@ -1312,8 +1320,16 @@ netlink_talk (struct nlmsghdr *n, struct nlsock *nl) { int status; struct sockaddr_nl snl; - struct iovec iov = { (void *) n, n->nlmsg_len }; - struct msghdr msg = { (void *) &snl, sizeof snl, &iov, 1, NULL, 0, 0 }; + struct iovec iov = { + .iov_base = (void *) n, + .iov_len = n->nlmsg_len + }; + struct msghdr msg = { + .msg_name = (void *) &snl, + .msg_namelen = sizeof snl, + .msg_iov = &iov, + .msg_iovlen = 1, + }; int save_errno; memset (&snl, 0, sizeof snl); From 3ef0b877f08344aa52367794aa4ec32b12becd6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Ter=C3=A4s?= Date: Tue, 29 Jul 2014 09:41:56 +0000 Subject: [PATCH 235/482] build: do not assume glibc on linux MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The whole IPv6 stack detection could need refactoring. But this fixes the linux check to not assume glibc. Fixes build against musl c-library. Signed-off-by: Timo Teräs Signed-off-by: David Lamparter --- configure.ac | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 3dba3b35..f1df482b 100755 --- a/configure.ac +++ b/configure.ac @@ -1204,21 +1204,22 @@ dnl ---------- if test "$zebra_cv_linux_ipv6" = "yes";then AC_MSG_CHECKING(for GNU libc >= 2.1) AC_DEFINE(HAVE_IPV6,1,Linux IPv6) + AC_DEFINE(LINUX_IPV6,1,Linux IPv6 stack) + AC_EGREP_CPP(yes, [ #include #if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1 yes #endif], [glibc=yes - AC_DEFINE(LINUX_IPV6,1,Linux IPv6 stack) AC_MSG_RESULT(yes)], AC_MSG_RESULT(no) ) RIPNGD="ripngd" OSPF6D="ospf6d" if test "$glibc" != "yes"; then - INCLUDES="-I/usr/inet6/include" if test x`ls /usr/inet6/lib/libinet6.a 2>/dev/null` != x;then + INCLUDES="-I/usr/inet6/include" LIB_IPV6="-L/usr/inet6/lib -linet6" fi fi From 4c005e3f65a1f5b4592b1ebbac392cbb1a710998 Mon Sep 17 00:00:00 2001 From: John Glotzer Date: Mon, 4 Aug 2014 19:39:23 +0000 Subject: [PATCH 236/482] bgpd: memmove needed in community_del_val In bgpd/bgp_community_del_val memcpy is used for potentially overlapping regions which is *not* safe. It may "work" in some cases but is not guaranteed to work in all cases. The case that I saw fail was on an x86_64 architecture with the number of bytes being moved/copied equal to 8. The way the code is written the uint32_t pointers will always differ by 1, which is equivalent to a memcpy/memmove of regions that are 4 bytes away from one another. So the code failed while copying an 8 byte region to an address that is 4 bytes lower i.e. overlapping regions. Interestingly, the same architecture had no problems with a 12 byte copy. When the code failed the communities were [200,300,400] and a call was made to delete the 200 community. The result of this was an array that looked like [400,400] which was uniquified to [400]. Of course the expected result should have been [300, 400]. One additional point - in our production environment memmove would not *link* without including but in an isolated quagga git repo this #include does not seem to be required and I see memmove is used in vtysh.c without this #include either. Signed-off-by: David Lamparter --- bgpd/bgp_community.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bgpd/bgp_community.c b/bgpd/bgp_community.c index fc1bef88..1bd2dd84 100644 --- a/bgpd/bgp_community.c +++ b/bgpd/bgp_community.c @@ -78,7 +78,7 @@ community_del_val (struct community *com, u_int32_t *val) c = com->size -i -1; if (c > 0) - memcpy (com->val + i, com->val + (i + 1), c * sizeof (*val)); + memmove (com->val + i, com->val + (i + 1), c * sizeof (*val)); com->size--; From ad2f92b6b07883f6a2a26499eab1776933185960 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Mon, 18 Aug 2014 18:05:25 +0200 Subject: [PATCH 237/482] isisd: type mix-up in 28a8cfc "don't require IPv4" Whoops, these are in6_addrs, not prefix_ipv6... funnily enough, it does the right thing either way, if it compiles, which it only does on Linux because IN6_IS_ADDR_LINKLOCAL contains a cast to the right type. On BSD there is no such cast, hence it explodes on trying to compile, trying to access struct members of in6_addrs while operating on prefix_ipv6... Fixes: 28a8cfc ("isisd: don't require IPv4 for adjacency") Signed-off-by: David Lamparter --- isisd/isis_pdu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c index 5f18135e..e0208fa4 100644 --- a/isisd/isis_pdu.c +++ b/isisd/isis_pdu.c @@ -537,7 +537,7 @@ process_p2p_hello (struct isis_circuit *circuit) { /* TBA: check that we have a linklocal ourselves? */ struct listnode *node; - struct prefix_ipv6 *ip; + struct in6_addr *ip; for (ALL_LIST_ELEMENTS_RO (tlvs.ipv6_addrs, node, ip)) if (IN6_IS_ADDR_LINKLOCAL (ip)) { @@ -1098,7 +1098,7 @@ process_lan_hello (int level, struct isis_circuit *circuit, u_char * ssnpa) { /* TBA: check that we have a linklocal ourselves? */ struct listnode *node; - struct prefix_ipv6 *ip; + struct in6_addr *ip; for (ALL_LIST_ELEMENTS_RO (tlvs.ipv6_addrs, node, ip)) if (IN6_IS_ADDR_LINKLOCAL (ip)) { From 90444ca35e3037ed43ec695428f0ef6d82f9a320 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 1 Jul 2014 16:14:05 +0200 Subject: [PATCH 238/482] lib: unset ZEBRA_IFA_PEER if no dst addr present (BZ#801) On OpenBSD, carp interfaces claim to be PtP interfaces with a 0.0.0.0/0 peer address. We process those in zebra and try to send them to clients, at which point they get encoded as all-0. The client code, however, decodes that to a NULL pointer instead of 0.0.0.0. This later turns into a SEGV when CONNECTED_PREFIX sees that ZEBRA_IFA_PEER is set and tries to access the peer prefix. This is a band-aid fix for stable/0.99.23, a long-term solution needs some conceptual improvements on the entire thing. (The usefulness of a PtP-to-0.0.0.0/0 is a separate question; at this point dropping the peer prefix seems the least intrusive solution.) Reported-by: Laurent Lavaud Signed-off-by: David Lamparter --- lib/zclient.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/zclient.c b/lib/zclient.c index 20188f6a..3b5477e9 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -805,6 +805,16 @@ zebra_interface_address_read (int type, struct stream *s) ifc->flags = ifc_flags; if (ifc->destination) ifc->destination->prefixlen = ifc->address->prefixlen; + else if (CHECK_FLAG(ifc->flags, ZEBRA_IFA_PEER)) + { + /* carp interfaces on OpenBSD with 0.0.0.0/0 as "peer" */ + char buf[BUFSIZ]; + prefix2str (ifc->address, buf, sizeof(buf)); + zlog_warn("warning: interface %s address %s " + "with peer flag set, but no peer address!", + ifp->name, buf); + UNSET_FLAG(ifc->flags, ZEBRA_IFA_PEER); + } } } else From b397cf4f0fc484c5ebfc8a680090055c8e6cbe32 Mon Sep 17 00:00:00 2001 From: Lu Feng Date: Fri, 18 Jul 2014 06:13:18 +0000 Subject: [PATCH 239/482] ripd: add ECMP support * Each node in the routing table is changed into a list, holding the multiple equal-cost paths. * If one of the multiple entries gets less-preferred (greater metric or greater distance), it will be directly deleted instead of starting a garbage-collection timer for it. The garbage-collection timer is started only when the last entry in the list gets INFINITY. * Some new functions are used to maintain the ECMP list. And hence rip_rte_process(), rip_redistribute_add() and rip_timeout() are significantly simplified. * rip_zebra_ipv4_add() and rip_zebra_ipv4_delete() now can share the common code. The common part is moved to rip_zebra_ipv4_send(). Signed-off-by: Feng Lu Reviewed-by: Alain Ritoux Signed-off-by: David Lamparter --- ripd/rip_interface.c | 38 +-- ripd/rip_zebra.c | 90 +++--- ripd/ripd.c | 661 ++++++++++++++++++++++++------------------- ripd/ripd.h | 8 +- 4 files changed, 442 insertions(+), 355 deletions(-) diff --git a/ripd/rip_interface.c b/ripd/rip_interface.c index 22cef454..bdd319e1 100644 --- a/ripd/rip_interface.c +++ b/ripd/rip_interface.c @@ -577,37 +577,15 @@ rip_if_down(struct interface *ifp) struct route_node *rp; struct rip_info *rinfo; struct rip_interface *ri = NULL; + struct list *list = NULL; + struct listnode *listnode = NULL, *nextnode = NULL; if (rip) - { - for (rp = route_top (rip->table); rp; rp = route_next (rp)) - if ((rinfo = rp->info) != NULL) - { - /* Routes got through this interface. */ - if (rinfo->ifindex == ifp->ifindex && - rinfo->type == ZEBRA_ROUTE_RIP && - rinfo->sub_type == RIP_ROUTE_RTE) - { - rip_zebra_ipv4_delete ((struct prefix_ipv4 *) &rp->p, - &rinfo->nexthop, - rinfo->metric); - - rip_redistribute_delete (rinfo->type,rinfo->sub_type, - (struct prefix_ipv4 *)&rp->p, - rinfo->ifindex); - } - else - { - /* All redistributed routes but static and system */ - if ((rinfo->ifindex == ifp->ifindex) && - /* (rinfo->type != ZEBRA_ROUTE_STATIC) && */ - (rinfo->type != ZEBRA_ROUTE_SYSTEM)) - rip_redistribute_delete (rinfo->type,rinfo->sub_type, - (struct prefix_ipv4 *)&rp->p, - rinfo->ifindex); - } - } - } - + for (rp = route_top (rip->table); rp; rp = route_next (rp)) + if ((list = rp->info) != NULL) + for (ALL_LIST_ELEMENTS (list, listnode, nextnode, rinfo)) + if (rinfo->ifindex == ifp->ifindex) + rip_ecmp_delete (rinfo); + ri = ifp->info; if (ri->running) diff --git a/ripd/rip_zebra.c b/ripd/rip_zebra.c index 1f6ef612..8b1c64d6 100644 --- a/ripd/rip_zebra.c +++ b/ripd/rip_zebra.c @@ -23,7 +23,9 @@ #include "command.h" #include "prefix.h" +#include "table.h" #include "stream.h" +#include "memory.h" #include "routemap.h" #include "zclient.h" #include "log.h" @@ -34,12 +36,18 @@ /* All information about zebra. */ struct zclient *zclient = NULL; -/* RIPd to zebra command interface. */ -void -rip_zebra_ipv4_add (struct prefix_ipv4 *p, struct in_addr *nexthop, - u_int32_t metric, u_char distance) +/* Send ECMP routes to zebra. */ +static void +rip_zebra_ipv4_send (struct route_node *rp, u_char cmd) { + static struct in_addr **nexthops = NULL; + static unsigned int nexthops_len = 0; + + struct list *list = (struct list *)rp->info; struct zapi_ipv4 api; + struct listnode *listnode = NULL; + struct rip_info *rinfo = NULL; + int count = 0; if (zclient->redist[ZEBRA_ROUTE_RIP]) { @@ -47,48 +55,64 @@ rip_zebra_ipv4_add (struct prefix_ipv4 *p, struct in_addr *nexthop, api.flags = 0; api.message = 0; api.safi = SAFI_UNICAST; + + if (nexthops_len < listcount (list)) + { + nexthops_len = listcount (list); + nexthops = XREALLOC (MTYPE_TMP, nexthops, + nexthops_len * sizeof (struct in_addr *)); + } + SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP); - api.nexthop_num = 1; - api.nexthop = &nexthop; + for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo)) + { + nexthops[count++] = &rinfo->nexthop; + if (cmd == ZEBRA_IPV4_ROUTE_ADD) + SET_FLAG (rinfo->flags, RIP_RTF_FIB); + else + UNSET_FLAG (rinfo->flags, RIP_RTF_FIB); + } + + api.nexthop = nexthops; + api.nexthop_num = count; api.ifindex_num = 0; + + rinfo = listgetdata (listhead (list)); + SET_FLAG (api.message, ZAPI_MESSAGE_METRIC); - api.metric = metric; + api.metric = rinfo->metric; - if (distance && distance != ZEBRA_RIP_DISTANCE_DEFAULT) - { - SET_FLAG (api.message, ZAPI_MESSAGE_DISTANCE); - api.distance = distance; - } + if (rinfo->distance && rinfo->distance != ZEBRA_RIP_DISTANCE_DEFAULT) + { + SET_FLAG (api.message, ZAPI_MESSAGE_DISTANCE); + api.distance = rinfo->distance; + } + + zapi_ipv4_route (cmd, zclient, + (struct prefix_ipv4 *)&rp->p, &api); - zapi_ipv4_route (ZEBRA_IPV4_ROUTE_ADD, zclient, p, &api); + if (IS_RIP_DEBUG_ZEBRA) + zlog_debug ("%s: %s/%d nexthops %d", + (cmd == ZEBRA_IPV4_ROUTE_ADD) ? \ + "Install into zebra" : "Delete from zebra", + inet_ntoa (rp->p.u.prefix4), rp->p.prefixlen, count); rip_global_route_changes++; } } +/* Add/update ECMP routes to zebra. */ void -rip_zebra_ipv4_delete (struct prefix_ipv4 *p, struct in_addr *nexthop, - u_int32_t metric) +rip_zebra_ipv4_add (struct route_node *rp) { - struct zapi_ipv4 api; - - if (zclient->redist[ZEBRA_ROUTE_RIP]) - { - api.type = ZEBRA_ROUTE_RIP; - api.flags = 0; - api.message = 0; - api.safi = SAFI_UNICAST; - SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP); - api.nexthop_num = 1; - api.nexthop = &nexthop; - api.ifindex_num = 0; - SET_FLAG (api.message, ZAPI_MESSAGE_METRIC); - api.metric = metric; - - zapi_ipv4_route (ZEBRA_IPV4_ROUTE_DELETE, zclient, p, &api); + rip_zebra_ipv4_send (rp, ZEBRA_IPV4_ROUTE_ADD); +} - rip_global_route_changes++; - } +/* Delete ECMP routes from zebra. */ +void +rip_zebra_ipv4_delete (struct route_node *rp) +{ + rip_zebra_ipv4_send (rp, ZEBRA_IPV4_ROUTE_DELETE); } /* Zebra route add and delete treatment. */ diff --git a/ripd/ripd.c b/ripd/ripd.c index 8a7fef87..b00241c9 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -138,8 +138,13 @@ rip_garbage_collect (struct thread *t) rp = rinfo->rp; /* Unlock route_node. */ - rp->info = NULL; - route_unlock_node (rp); + listnode_delete (rp->info, rinfo); + if (list_isempty ((struct list *)rp->info)) + { + list_free (rp->info); + rp->info = NULL; + route_unlock_node (rp); + } /* Free RIP routing information. */ rip_info_free (rinfo); @@ -147,36 +152,149 @@ rip_garbage_collect (struct thread *t) return 0; } -/* Timeout RIP routes. */ -static int -rip_timeout (struct thread *t) +static void rip_timeout_update (struct rip_info *rinfo); + +/* Add new route to the ECMP list. + * RETURN: the new entry added in the list + */ +struct rip_info * +rip_ecmp_add (struct rip_info *rinfo_new) { - struct rip_info *rinfo; - struct route_node *rn; + struct route_node *rp = rinfo_new->rp; + struct rip_info *rinfo = NULL; + struct list *list = NULL; - rinfo = THREAD_ARG (t); - rinfo->t_timeout = NULL; + if (rp->info == NULL) + rp->info = list_new (); + list = (struct list *)rp->info; + + rinfo = rip_info_new (); + memcpy (rinfo, rinfo_new, sizeof (struct rip_info)); + listnode_add (list, rinfo); + + if (rip_route_rte (rinfo)) + { + rip_timeout_update (rinfo); + rip_zebra_ipv4_add (rp); + } + + /* Set the route change flag on the first entry. */ + rinfo = listgetdata (listhead (list)); + SET_FLAG (rinfo->flags, RIP_RTF_CHANGED); + + /* Signal the output process to trigger an update (see section 2.5). */ + rip_event (RIP_TRIGGERED_UPDATE, 0); - rn = rinfo->rp; + return rinfo; +} + +/* Replace the ECMP list with the new route. + * RETURN: the new entry added in the list + */ +struct rip_info * +rip_ecmp_replace (struct rip_info *rinfo_new) +{ + struct route_node *rp = rinfo_new->rp; + struct list *list = (struct list *)rp->info; + struct rip_info *rinfo = NULL, *tmp_rinfo = NULL; + struct listnode *node = NULL, *nextnode = NULL; + + if (list == NULL || listcount (list) == 0) + return rip_ecmp_add (rinfo_new); + + /* Get the first entry */ + rinfo = listgetdata (listhead (list)); + + /* Learnt route replaced by a local one. Delete it from zebra. */ + if (rip_route_rte (rinfo) && !rip_route_rte (rinfo_new)) + if (CHECK_FLAG (rinfo->flags, RIP_RTF_FIB)) + rip_zebra_ipv4_delete (rp); + + /* Re-use the first entry, and delete the others. */ + for (ALL_LIST_ELEMENTS (list, node, nextnode, tmp_rinfo)) + if (tmp_rinfo != rinfo) + { + RIP_TIMER_OFF (tmp_rinfo->t_timeout); + RIP_TIMER_OFF (tmp_rinfo->t_garbage_collect); + list_delete_node (list, node); + rip_info_free (tmp_rinfo); + } - /* - The garbage-collection timer is set for 120 seconds. */ - RIP_TIMER_ON (rinfo->t_garbage_collect, rip_garbage_collect, - rip->garbage_time); + RIP_TIMER_OFF (rinfo->t_timeout); + RIP_TIMER_OFF (rinfo->t_garbage_collect); + memcpy (rinfo, rinfo_new, sizeof (struct rip_info)); - rip_zebra_ipv4_delete ((struct prefix_ipv4 *)&rn->p, &rinfo->nexthop, - rinfo->metric); - /* - The metric for the route is set to 16 (infinity). This causes - the route to be removed from service. */ - rinfo->metric = RIP_METRIC_INFINITY; - rinfo->flags &= ~RIP_RTF_FIB; + if (rip_route_rte (rinfo)) + { + rip_timeout_update (rinfo); + /* The ADD message implies an update. */ + rip_zebra_ipv4_add (rp); + } - /* - The route change flag is to indicate that this entry has been - changed. */ - rinfo->flags |= RIP_RTF_CHANGED; + /* Set the route change flag. */ + SET_FLAG (rinfo->flags, RIP_RTF_CHANGED); - /* - The output process is signalled to trigger a response. */ + /* Signal the output process to trigger an update (see section 2.5). */ rip_event (RIP_TRIGGERED_UPDATE, 0); + return rinfo; +} + +/* Delete one route from the ECMP list. + * RETURN: + * null - the entry is freed, and other entries exist in the list + * the entry - the entry is the last one in the list; its metric is set + * to INFINITY, and the garbage collector is started for it + */ +struct rip_info * +rip_ecmp_delete (struct rip_info *rinfo) +{ + struct route_node *rp = rinfo->rp; + struct list *list = (struct list *)rp->info; + + RIP_TIMER_OFF (rinfo->t_timeout); + + if (listcount (list) > 1) + { + /* Some other ECMP entries still exist. Just delete this entry. */ + RIP_TIMER_OFF (rinfo->t_garbage_collect); + listnode_delete (list, rinfo); + if (rip_route_rte (rinfo) && CHECK_FLAG (rinfo->flags, RIP_RTF_FIB)) + /* The ADD message implies the update. */ + rip_zebra_ipv4_add (rp); + rip_info_free (rinfo); + rinfo = NULL; + } + else + { + assert (rinfo == listgetdata (listhead (list))); + + /* This is the only entry left in the list. We must keep it in + * the list for garbage collection time, with INFINITY metric. */ + + rinfo->metric = RIP_METRIC_INFINITY; + RIP_TIMER_ON (rinfo->t_garbage_collect, + rip_garbage_collect, rip->garbage_time); + + if (rip_route_rte (rinfo) && CHECK_FLAG (rinfo->flags, RIP_RTF_FIB)) + rip_zebra_ipv4_delete (rp); + } + + /* Set the route change flag on the first entry. */ + rinfo = listgetdata (listhead (list)); + SET_FLAG (rinfo->flags, RIP_RTF_CHANGED); + + /* Signal the output process to trigger an update (see section 2.5). */ + rip_event (RIP_TRIGGERED_UPDATE, 0); + + return rinfo; +} + +/* Timeout RIP routes. */ +static int +rip_timeout (struct thread *t) +{ + rip_ecmp_delete ((struct rip_info *)THREAD_ARG (t)); return 0; } @@ -365,13 +483,13 @@ rip_rte_process (struct rte *rte, struct sockaddr_in *from, int ret; struct prefix_ipv4 p; struct route_node *rp; - struct rip_info *rinfo, rinfotmp; + struct rip_info *rinfo = NULL, newinfo; struct rip_interface *ri; struct in_addr *nexthop; - u_char oldmetric; int same = 0; - int route_reuse = 0; unsigned char old_dist, new_dist; + struct list *list = NULL; + struct listnode *node = NULL; /* Make prefix structure. */ memset (&p, 0, sizeof (struct prefix_ipv4)); @@ -389,21 +507,20 @@ rip_rte_process (struct rte *rte, struct sockaddr_in *from, if (ret < 0) return; + memset (&newinfo, 0, sizeof (newinfo)); + newinfo.type = ZEBRA_ROUTE_RIP; + newinfo.sub_type = RIP_ROUTE_RTE; + newinfo.nexthop = rte->nexthop; + newinfo.from = from->sin_addr; + newinfo.ifindex = ifp->ifindex; + newinfo.metric = rte->metric; + newinfo.metric_out = rte->metric; /* XXX */ + newinfo.tag = ntohs (rte->tag); /* XXX */ + /* Modify entry according to the interface routemap. */ if (ri->routemap[RIP_FILTER_IN]) { int ret; - struct rip_info newinfo; - - memset (&newinfo, 0, sizeof (newinfo)); - newinfo.type = ZEBRA_ROUTE_RIP; - newinfo.sub_type = RIP_ROUTE_RTE; - newinfo.nexthop = rte->nexthop; - newinfo.from = from->sin_addr; - newinfo.ifindex = ifp->ifindex; - newinfo.metric = rte->metric; - newinfo.metric_out = rte->metric; /* XXX */ - newinfo.tag = ntohs (rte->tag); /* XXX */ /* The object should be of the type of rip_info */ ret = route_map_apply (ri->routemap[RIP_FILTER_IN], @@ -455,8 +572,62 @@ rip_rte_process (struct rte *rte, struct sockaddr_in *from, /* Get index for the prefix. */ rp = route_node_get (rip->table, (struct prefix *) &p); + newinfo.rp = rp; + newinfo.nexthop = *nexthop; + newinfo.metric = rte->metric; + newinfo.tag = ntohs (rte->tag); + newinfo.distance = rip_distance_apply (&newinfo); + + new_dist = newinfo.distance ? newinfo.distance : ZEBRA_RIP_DISTANCE_DEFAULT; + /* Check to see whether there is already RIP route on the table. */ - rinfo = rp->info; + if ((list = rp->info) != NULL) + for (ALL_LIST_ELEMENTS_RO (list, node, rinfo)) + { + /* Need to compare with redistributed entry or local entry */ + if (!rip_route_rte (rinfo)) + break; + + if (IPV4_ADDR_SAME (&rinfo->from, &from->sin_addr) && + IPV4_ADDR_SAME (&rinfo->nexthop, nexthop)) + break; + + if (!listnextnode (node)) + { + /* Not found in the list */ + + if (rte->metric > rinfo->metric) + { + /* New route has a greater metric. Discard it. */ + route_unlock_node (rp); + return; + } + + if (rte->metric < rinfo->metric) + /* New route has a smaller metric. Replace the ECMP list + * with the new one in below. */ + break; + + /* Metrics are same. We compare the distances. */ + old_dist = rinfo->distance ? \ + rinfo->distance : ZEBRA_RIP_DISTANCE_DEFAULT; + + if (new_dist > old_dist) + { + /* New route has a greater distance. Discard it. */ + route_unlock_node (rp); + return; + } + + if (new_dist < old_dist) + /* New route has a smaller distance. Replace the ECMP list + * with the new one in below. */ + break; + + /* Metrics and distances are both same. Keep "rinfo" null and + * the new route is added in the ECMP list in below. */ + } + } if (rinfo) { @@ -474,13 +645,6 @@ rip_rte_process (struct rte *rte, struct sockaddr_in *from, if (rinfo->type != ZEBRA_ROUTE_RIP && rinfo->metric != RIP_METRIC_INFINITY) { - /* Fill in a minimaly temporary rip_info structure, for a future - rip_distance_apply() use) */ - memset (&rinfotmp, 0, sizeof (rinfotmp)); - IPV4_ADDR_COPY (&rinfotmp.from, &from->sin_addr); - rinfotmp.rp = rinfo->rp; - new_dist = rip_distance_apply (&rinfotmp); - new_dist = new_dist ? new_dist : ZEBRA_RIP_DISTANCE_DEFAULT; old_dist = rinfo->distance; /* Only routes directly connected to an interface (nexthop == 0) * may have a valid NULL distance */ @@ -488,88 +652,30 @@ rip_rte_process (struct rte *rte, struct sockaddr_in *from, old_dist = old_dist ? old_dist : ZEBRA_RIP_DISTANCE_DEFAULT; /* If imported route does not have STRICT precedence, mark it as a ghost */ - if (new_dist > old_dist - || rte->metric == RIP_METRIC_INFINITY) - { - route_unlock_node (rp); - return; - } - else - { - RIP_TIMER_OFF (rinfo->t_timeout); - RIP_TIMER_OFF (rinfo->t_garbage_collect); - - rp->info = NULL; - if (rip_route_rte (rinfo)) - rip_zebra_ipv4_delete ((struct prefix_ipv4 *)&rp->p, - &rinfo->nexthop, rinfo->metric); - rip_info_free (rinfo); - rinfo = NULL; - route_reuse = 1; - } + if (new_dist <= old_dist && rte->metric != RIP_METRIC_INFINITY) + rip_ecmp_replace (&newinfo); + + route_unlock_node (rp); + return; } } if (!rinfo) { + if (rp->info) + route_unlock_node (rp); + /* Now, check to see whether there is already an explicit route for the destination prefix. If there is no such route, add this route to the routing table, unless the metric is infinity (there is no point in adding a route which unusable). */ if (rte->metric != RIP_METRIC_INFINITY) - { - rinfo = rip_info_new (); - - /* - Setting the destination prefix and length to those in - the RTE. */ - rinfo->rp = rp; - - /* - Setting the metric to the newly calculated metric (as - described above). */ - rinfo->metric = rte->metric; - rinfo->tag = ntohs (rte->tag); - - /* - Set the next hop address to be the address of the router - from which the datagram came or the next hop address - specified by a next hop RTE. */ - IPV4_ADDR_COPY (&rinfo->nexthop, nexthop); - IPV4_ADDR_COPY (&rinfo->from, &from->sin_addr); - rinfo->ifindex = ifp->ifindex; - - /* - Initialize the timeout for the route. If the - garbage-collection timer is running for this route, stop it - (see section 2.3 for a discussion of the timers). */ - rip_timeout_update (rinfo); - - /* - Set the route change flag. */ - rinfo->flags |= RIP_RTF_CHANGED; - - /* - Signal the output process to trigger an update (see section - 2.5). */ - rip_event (RIP_TRIGGERED_UPDATE, 0); - - /* Finally, route goes into the kernel. */ - rinfo->type = ZEBRA_ROUTE_RIP; - rinfo->sub_type = RIP_ROUTE_RTE; - - /* Set distance value. */ - rinfo->distance = rip_distance_apply (rinfo); - - rp->info = rinfo; - rip_zebra_ipv4_add (&p, &rinfo->nexthop, rinfo->metric, - rinfo->distance); - rinfo->flags |= RIP_RTF_FIB; - } - - /* Unlock temporary lock, i.e. same behaviour */ - if (route_reuse) - route_unlock_node (rp); + rip_ecmp_add (&newinfo); } else { /* Route is there but we are not sure the route is RIP or not. */ - rinfo = rp->info; /* If there is an existing route, compare the next hop address to the address of the router from which the datagram came. @@ -578,16 +684,8 @@ rip_rte_process (struct rte *rte, struct sockaddr_in *from, same = (IPV4_ADDR_SAME (&rinfo->from, &from->sin_addr) && (rinfo->ifindex == ifp->ifindex)); - if (same) - rip_timeout_update (rinfo); - - - /* Fill in a minimaly temporary rip_info structure, for a future - rip_distance_apply() use) */ - memset (&rinfotmp, 0, sizeof (rinfotmp)); - IPV4_ADDR_COPY (&rinfotmp.from, &from->sin_addr); - rinfotmp.rp = rinfo->rp; - + old_dist = rinfo->distance ? \ + rinfo->distance : ZEBRA_RIP_DISTANCE_DEFAULT; /* Next, compare the metrics. If the datagram is from the same router as the existing route, and the new metric is different @@ -599,95 +697,51 @@ rip_rte_process (struct rte *rte, struct sockaddr_in *from, || (rte->metric < rinfo->metric) || ((same) && (rinfo->metric == rte->metric) - && ntohs (rte->tag) != rinfo->tag) - || (rinfo->distance > rip_distance_apply (&rinfotmp)) - || ((rinfo->distance != rip_distance_apply (rinfo)) && same)) + && (newinfo.tag != rinfo->tag)) + || (old_dist > new_dist) + || ((old_dist != new_dist) && same)) { - /* - Adopt the route from the datagram. That is, put the - new metric in, and adjust the next hop address (if - necessary). */ - oldmetric = rinfo->metric; - rinfo->metric = rte->metric; - rinfo->tag = ntohs (rte->tag); - IPV4_ADDR_COPY (&rinfo->from, &from->sin_addr); - rinfo->ifindex = ifp->ifindex; - rinfo->distance = rip_distance_apply (rinfo); - - /* Should a new route to this network be established - while the garbage-collection timer is running, the - new route will replace the one that is about to be - deleted. In this case the garbage-collection timer - must be cleared. */ - - if (oldmetric == RIP_METRIC_INFINITY && - rinfo->metric < RIP_METRIC_INFINITY) + if (listcount (list) == 1) { - rinfo->type = ZEBRA_ROUTE_RIP; - rinfo->sub_type = RIP_ROUTE_RTE; - - RIP_TIMER_OFF (rinfo->t_garbage_collect); - - if (!IPV4_ADDR_SAME (&rinfo->nexthop, nexthop)) - IPV4_ADDR_COPY (&rinfo->nexthop, nexthop); - - rip_zebra_ipv4_add (&p, nexthop, rinfo->metric, - rinfo->distance); - rinfo->flags |= RIP_RTF_FIB; + if (newinfo.metric != RIP_METRIC_INFINITY) + rip_ecmp_replace (&newinfo); + else + rip_ecmp_delete (rinfo); } - - /* Update nexthop and/or metric value. */ - if (oldmetric != RIP_METRIC_INFINITY) + else { - rip_zebra_ipv4_delete (&p, &rinfo->nexthop, oldmetric); - rip_zebra_ipv4_add (&p, nexthop, rinfo->metric, - rinfo->distance); - rinfo->flags |= RIP_RTF_FIB; - - if (!IPV4_ADDR_SAME (&rinfo->nexthop, nexthop)) - IPV4_ADDR_COPY (&rinfo->nexthop, nexthop); - } + if (newinfo.metric < rinfo->metric) + rip_ecmp_replace (&newinfo); + else if (newinfo.metric > rinfo->metric) + rip_ecmp_delete (rinfo); + else if (new_dist < old_dist) + rip_ecmp_replace (&newinfo); + else if (new_dist > old_dist) + rip_ecmp_delete (rinfo); + else + { + int update = CHECK_FLAG (rinfo->flags, RIP_RTF_FIB) ? 1 : 0; - /* - Set the route change flag and signal the output process - to trigger an update. */ - rinfo->flags |= RIP_RTF_CHANGED; - rip_event (RIP_TRIGGERED_UPDATE, 0); + assert (newinfo.metric != RIP_METRIC_INFINITY); - /* - If the new metric is infinity, start the deletion - process (described above); */ - if (rinfo->metric == RIP_METRIC_INFINITY) - { - /* If the new metric is infinity, the deletion process - begins for the route, which is no longer used for - routing packets. Note that the deletion process is - started only when the metric is first set to - infinity. If the metric was already infinity, then a - new deletion process is not started. */ - if (oldmetric != RIP_METRIC_INFINITY) - { - /* - The garbage-collection timer is set for 120 seconds. */ - RIP_TIMER_ON (rinfo->t_garbage_collect, - rip_garbage_collect, rip->garbage_time); RIP_TIMER_OFF (rinfo->t_timeout); + RIP_TIMER_OFF (rinfo->t_garbage_collect); + memcpy (rinfo, &newinfo, sizeof (struct rip_info)); + rip_timeout_update (rinfo); + + if (update) + rip_zebra_ipv4_add (rp); - /* - The metric for the route is set to 16 - (infinity). This causes the route to be removed - from service. */ - rip_zebra_ipv4_delete (&p, &rinfo->nexthop, oldmetric); - rinfo->flags &= ~RIP_RTF_FIB; - - /* - The route change flag is to indicate that this - entry has been changed. */ - /* - The output process is signalled to trigger a - response. */ - ; /* Above processes are already done previously. */ + /* - Set the route change flag on the first entry. */ + rinfo = listgetdata (listhead (list)); + SET_FLAG (rinfo->flags, RIP_RTF_CHANGED); + rip_event (RIP_TRIGGERED_UPDATE, 0); } } - else - { - /* otherwise, re-initialize the timeout. */ - rip_timeout_update (rinfo); - } } + else /* same & no change */ + rip_timeout_update (rinfo); + /* Unlock tempolary lock of the route. */ route_unlock_node (rp); } @@ -1520,8 +1574,9 @@ rip_redistribute_add (int type, int sub_type, struct prefix_ipv4 *p, unsigned int metric, unsigned char distance) { int ret; - struct route_node *rp; - struct rip_info *rinfo; + struct route_node *rp = NULL; + struct rip_info *rinfo = NULL, newinfo; + struct list *list = NULL; /* Redistribute route */ ret = rip_destination_check (p->prefix); @@ -1530,10 +1585,21 @@ rip_redistribute_add (int type, int sub_type, struct prefix_ipv4 *p, rp = route_node_get (rip->table, (struct prefix *) p); - rinfo = rp->info; + memset (&newinfo, 0, sizeof (struct rip_info)); + newinfo.type = type; + newinfo.sub_type = sub_type; + newinfo.ifindex = ifindex; + newinfo.metric = 1; + newinfo.external_metric = metric; + newinfo.distance = distance; + newinfo.rp = rp; + if (nexthop) + newinfo.nexthop = *nexthop; - if (rinfo) + if ((list = rp->info) != NULL && listcount (list) != 0) { + rinfo = listgetdata (listhead (list)); + if (rinfo->type == ZEBRA_ROUTE_CONNECT && rinfo->sub_type == RIP_ROUTE_INTERFACE && rinfo->metric != RIP_METRIC_INFINITY) @@ -1555,35 +1621,11 @@ rip_redistribute_add (int type, int sub_type, struct prefix_ipv4 *p, } } - RIP_TIMER_OFF (rinfo->t_timeout); - RIP_TIMER_OFF (rinfo->t_garbage_collect); - - if (rip_route_rte (rinfo)) - rip_zebra_ipv4_delete ((struct prefix_ipv4 *)&rp->p, &rinfo->nexthop, - rinfo->metric); - rp->info = NULL; - rip_info_free (rinfo); - - route_unlock_node (rp); + rinfo = rip_ecmp_replace (&newinfo); + route_unlock_node (rp); } - - rinfo = rip_info_new (); - - rinfo->type = type; - rinfo->sub_type = sub_type; - rinfo->ifindex = ifindex; - rinfo->metric = 1; - rinfo->external_metric = metric; - rinfo->distance = distance; - rinfo->rp = rp; - - if (nexthop) - rinfo->nexthop = *nexthop; - - rinfo->flags |= RIP_RTF_FIB; - rp->info = rinfo; - - rinfo->flags |= RIP_RTF_CHANGED; + else + rinfo = rip_ecmp_add (&newinfo); if (IS_RIP_DEBUG_EVENT) { if (!nexthop) @@ -1596,7 +1638,6 @@ rip_redistribute_add (int type, int sub_type, struct prefix_ipv4 *p, ifindex2ifname(ifindex)); } - rip_event (RIP_TRIGGERED_UPDATE, 0); } @@ -1616,27 +1657,33 @@ rip_redistribute_delete (int type, int sub_type, struct prefix_ipv4 *p, rp = route_node_lookup (rip->table, (struct prefix *) p); if (rp) { - rinfo = rp->info; + struct list *list = rp->info; - if (rinfo != NULL - && rinfo->type == type - && rinfo->sub_type == sub_type - && rinfo->ifindex == ifindex) - { - /* Perform poisoned reverse. */ - rinfo->metric = RIP_METRIC_INFINITY; - RIP_TIMER_ON (rinfo->t_garbage_collect, - rip_garbage_collect, rip->garbage_time); - RIP_TIMER_OFF (rinfo->t_timeout); - rinfo->flags |= RIP_RTF_CHANGED; + if (list != NULL && listcount (list) != 0) + { + rinfo = listgetdata (listhead (list)); + if (rinfo != NULL + && rinfo->type == type + && rinfo->sub_type == sub_type + && rinfo->ifindex == ifindex) + { + /* Perform poisoned reverse. */ + rinfo->metric = RIP_METRIC_INFINITY; + RIP_TIMER_ON (rinfo->t_garbage_collect, + rip_garbage_collect, rip->garbage_time); + RIP_TIMER_OFF (rinfo->t_timeout); + rinfo->flags |= RIP_RTF_CHANGED; - if (IS_RIP_DEBUG_EVENT) - zlog_debug ("Poisone %s/%d on the interface %s with an infinity metric [delete]", - inet_ntoa(p->prefix), p->prefixlen, - ifindex2ifname(ifindex)); + if (IS_RIP_DEBUG_EVENT) + zlog_debug ("Poisone %s/%d on the interface %s with an " + "infinity metric [delete]", + inet_ntoa(p->prefix), p->prefixlen, + ifindex2ifname(ifindex)); - rip_event (RIP_TRIGGERED_UPDATE, 0); - } + rip_event (RIP_TRIGGERED_UPDATE, 0); + } + } + route_unlock_node (rp); } } @@ -1718,7 +1765,7 @@ rip_request_process (struct rip_packet *packet, int size, rp = route_node_lookup (rip->table, (struct prefix *) &p); if (rp) { - rinfo = rp->info; + rinfo = listgetdata (listhead ((struct list *)rp->info)); rte->metric = htonl (rinfo->metric); route_unlock_node (rp); } @@ -2153,6 +2200,8 @@ rip_output_process (struct connected *ifc, struct sockaddr_in *to, int num = 0; int rtemax; int subnetted = 0; + struct list *list = NULL; + struct listnode *listnode = NULL; /* Logging output event. */ if (IS_RIP_DEBUG_EVENT) @@ -2210,8 +2259,9 @@ rip_output_process (struct connected *ifc, struct sockaddr_in *to, } for (rp = route_top (rip->table); rp; rp = route_next (rp)) - if ((rinfo = rp->info) != NULL) + if ((list = rp->info) != NULL && listcount (list) != 0) { + rinfo = listgetdata (listhead (list)); /* For RIPv1, if we are subnetted, output subnets in our network */ /* that have the same mask as the output "interface". For other */ /* networks, only the classfull version is output. */ @@ -2270,11 +2320,22 @@ rip_output_process (struct connected *ifc, struct sockaddr_in *to, * (in order to handle the case when multiple subnets are * configured on the same interface). */ - if (rinfo->type == ZEBRA_ROUTE_RIP && - rinfo->ifindex == ifc->ifp->ifindex) - continue; - if (rinfo->type == ZEBRA_ROUTE_CONNECT && + int suppress = 0; + struct rip_info *tmp_rinfo = NULL; + + for (ALL_LIST_ELEMENTS_RO (list, listnode, tmp_rinfo)) + if (tmp_rinfo->type == ZEBRA_ROUTE_RIP && + tmp_rinfo->ifindex == ifc->ifp->ifindex) + { + suppress = 1; + break; + } + + if (!suppress && rinfo->type == ZEBRA_ROUTE_CONNECT && prefix_match((struct prefix *)p, ifc->address)) + suppress = 1; + + if (suppress) continue; } @@ -2368,12 +2429,15 @@ rip_output_process (struct connected *ifc, struct sockaddr_in *to, * (in order to handle the case when multiple subnets are * configured on the same interface). */ - if (rinfo->type == ZEBRA_ROUTE_RIP && - rinfo->ifindex == ifc->ifp->ifindex) - rinfo->metric_out = RIP_METRIC_INFINITY; - if (rinfo->type == ZEBRA_ROUTE_CONNECT && + struct rip_info *tmp_rinfo = NULL; + + for (ALL_LIST_ELEMENTS_RO (list, listnode, tmp_rinfo)) + if (tmp_rinfo->type == ZEBRA_ROUTE_RIP && + tmp_rinfo->ifindex == ifc->ifp->ifindex) + rinfo->metric_out = RIP_METRIC_INFINITY; + if (tmp_rinfo->type == ZEBRA_ROUTE_CONNECT && prefix_match((struct prefix *)p, ifc->address)) - rinfo->metric_out = RIP_METRIC_INFINITY; + rinfo->metric_out = RIP_METRIC_INFINITY; } /* Prepare preamble, auth headers, if needs be */ @@ -2593,12 +2657,18 @@ static void rip_clear_changed_flag (void) { struct route_node *rp; - struct rip_info *rinfo; + struct rip_info *rinfo = NULL; + struct list *list = NULL; + struct listnode *listnode = NULL; for (rp = route_top (rip->table); rp; rp = route_next (rp)) - if ((rinfo = rp->info) != NULL) - if (rinfo->flags & RIP_RTF_CHANGED) - rinfo->flags &= ~RIP_RTF_CHANGED; + if ((list = rp->info) != NULL) + for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo)) + { + UNSET_FLAG (rinfo->flags, RIP_RTF_CHANGED); + /* This flag can be set only on the first entry. */ + break; + } } /* Triggered update interval timer. */ @@ -2663,14 +2733,16 @@ void rip_redistribute_withdraw (int type) { struct route_node *rp; - struct rip_info *rinfo; + struct rip_info *rinfo = NULL; + struct list *list = NULL; if (!rip) return; for (rp = route_top (rip->table); rp; rp = route_next (rp)) - if ((rinfo = rp->info) != NULL) + if ((list = rp->info) != NULL) { + rinfo = listgetdata (listhead (list)); if (rinfo->type == type && rinfo->sub_type != RIP_ROUTE_INTERFACE) { @@ -2983,12 +3055,15 @@ static void rip_update_default_metric (void) { struct route_node *np; - struct rip_info *rinfo; + struct rip_info *rinfo = NULL; + struct list *list = NULL; + struct listnode *listnode = NULL; for (np = route_top (rip->table); np; np = route_next (np)) - if ((rinfo = np->info) != NULL) - if (rinfo->type != ZEBRA_ROUTE_RIP && rinfo->type != ZEBRA_ROUTE_CONNECT) - rinfo->metric = rip->default_metric; + if ((list = np->info) != NULL) + for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo)) + if (rinfo->type != ZEBRA_ROUTE_RIP && rinfo->type != ZEBRA_ROUTE_CONNECT) + rinfo->metric = rip->default_metric; } #endif @@ -3427,7 +3502,9 @@ DEFUN (show_ip_rip, "Show RIP routes\n") { struct route_node *np; - struct rip_info *rinfo; + struct rip_info *rinfo = NULL; + struct list *list = NULL; + struct listnode *listnode = NULL; if (! rip) return CMD_SUCCESS; @@ -3440,7 +3517,8 @@ DEFUN (show_ip_rip, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); for (np = route_top (rip->table); np; np = route_next (np)) - if ((rinfo = np->info) != NULL) + if ((list = np->info) != NULL) + for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo)) { int len; @@ -3794,27 +3872,30 @@ rip_clean (void) { int i; struct route_node *rp; - struct rip_info *rinfo; + struct rip_info *rinfo = NULL; + struct list *list = NULL; + struct listnode *listnode = NULL; if (rip) { /* Clear RIP routes */ for (rp = route_top (rip->table); rp; rp = route_next (rp)) - if ((rinfo = rp->info) != NULL) - { - if (rinfo->type == ZEBRA_ROUTE_RIP && - rinfo->sub_type == RIP_ROUTE_RTE) - rip_zebra_ipv4_delete ((struct prefix_ipv4 *)&rp->p, - &rinfo->nexthop, rinfo->metric); - - RIP_TIMER_OFF (rinfo->t_timeout); - RIP_TIMER_OFF (rinfo->t_garbage_collect); - - rp->info = NULL; - route_unlock_node (rp); - - rip_info_free (rinfo); - } + if ((list = rp->info) != NULL) + { + rinfo = listgetdata (listhead (list)); + if (rip_route_rte (rinfo)) + rip_zebra_ipv4_delete (rp); + + for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo)) + { + RIP_TIMER_OFF (rinfo->t_timeout); + RIP_TIMER_OFF (rinfo->t_garbage_collect); + rip_info_free (rinfo); + } + list_delete (list); + rp->info = NULL; + route_unlock_node (rp); + } /* Cancel RIP related timers. */ RIP_TIMER_OFF (rip->t_update); diff --git a/ripd/ripd.h b/ripd/ripd.h index 0fc2fd37..0f0e2160 100644 --- a/ripd/ripd.h +++ b/ripd/ripd.h @@ -401,8 +401,8 @@ extern void rip_redistribute_add (int, int, struct prefix_ipv4 *, unsigned int, struct in_addr *, unsigned int, unsigned char); extern void rip_redistribute_delete (int, int, struct prefix_ipv4 *, unsigned int); extern void rip_redistribute_withdraw (int); -extern void rip_zebra_ipv4_add (struct prefix_ipv4 *, struct in_addr *, u_int32_t, u_char); -extern void rip_zebra_ipv4_delete (struct prefix_ipv4 *, struct in_addr *, u_int32_t); +extern void rip_zebra_ipv4_add (struct route_node *); +extern void rip_zebra_ipv4_delete (struct route_node *); extern void rip_interface_multicast_set (int, struct connected *); extern void rip_distribute_update_interface (struct interface *); extern void rip_if_rmap_update_interface (struct interface *); @@ -429,6 +429,10 @@ extern void rip_redistribute_clean (void); extern void rip_ifaddr_add (struct interface *, struct connected *); extern void rip_ifaddr_delete (struct interface *, struct connected *); +extern struct rip_info *rip_ecmp_add (struct rip_info *); +extern struct rip_info *rip_ecmp_replace (struct rip_info *); +extern struct rip_info *rip_ecmp_delete (struct rip_info *); + /* There is only one rip strucutre. */ extern struct rip *rip; From 0b74a0a5db7bcf65bf68c44b547b02b1310b5cdb Mon Sep 17 00:00:00 2001 From: Lu Feng Date: Fri, 18 Jul 2014 06:13:19 +0000 Subject: [PATCH 240/482] ripd: allow to enable/disable the ECMP feature Introduce a new command "[no] allow-ecmp" to enable/disable the ECMP feature in RIP. By default, ECMP is not allowed. Once ECMP is disabled, only one route entry can exist in the list. * rip_zebra.c: adjust a debugging information, which shows the number of nexthops according to whether ECMP is enabled. * ripd.c: rip_ecmp_add() will reject the new route if ECMP is not allowed and some entry already exists. A new configurable command "allow-ecmp" is added to control whether ECMP is allowed. When ECMP is disabled, rip_ecmp_disable() is called to remove the multiple nexthops. * ripd.h: Add a new member "ecmp" to "struct rip", indicating whether ECMP is allowed or not. Signed-off-by: Feng Lu Reviewed-by: Alain Ritoux Signed-off-by: David Lamparter --- ripd/rip_zebra.c | 16 ++++++--- ripd/ripd.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++- ripd/ripd.h | 3 ++ 3 files changed, 102 insertions(+), 5 deletions(-) diff --git a/ripd/rip_zebra.c b/ripd/rip_zebra.c index 8b1c64d6..b005ece9 100644 --- a/ripd/rip_zebra.c +++ b/ripd/rip_zebra.c @@ -92,10 +92,18 @@ rip_zebra_ipv4_send (struct route_node *rp, u_char cmd) (struct prefix_ipv4 *)&rp->p, &api); if (IS_RIP_DEBUG_ZEBRA) - zlog_debug ("%s: %s/%d nexthops %d", - (cmd == ZEBRA_IPV4_ROUTE_ADD) ? \ - "Install into zebra" : "Delete from zebra", - inet_ntoa (rp->p.u.prefix4), rp->p.prefixlen, count); + { + if (rip->ecmp) + zlog_debug ("%s: %s/%d nexthops %d", + (cmd == ZEBRA_IPV4_ROUTE_ADD) ? \ + "Install into zebra" : "Delete from zebra", + inet_ntoa (rp->p.u.prefix4), rp->p.prefixlen, count); + else + zlog_debug ("%s: %s/%d", + (cmd == ZEBRA_IPV4_ROUTE_ADD) ? \ + "Install into zebra" : "Delete from zebra", + inet_ntoa (rp->p.u.prefix4), rp->p.prefixlen); + } rip_global_route_changes++; } diff --git a/ripd/ripd.c b/ripd/ripd.c index b00241c9..c69ef7fc 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -155,7 +155,8 @@ rip_garbage_collect (struct thread *t) static void rip_timeout_update (struct rip_info *rinfo); /* Add new route to the ECMP list. - * RETURN: the new entry added in the list + * RETURN: the new entry added in the list, or NULL if it is not the first + * entry and ECMP is not allowed. */ struct rip_info * rip_ecmp_add (struct rip_info *rinfo_new) @@ -168,6 +169,11 @@ rip_ecmp_add (struct rip_info *rinfo_new) rp->info = list_new (); list = (struct list *)rp->info; + /* If ECMP is not allowed and some entry already exists in the list, + * do nothing. */ + if (listcount (list) && !rip->ecmp) + return NULL; + rinfo = rip_info_new (); memcpy (rinfo, rinfo_new, sizeof (struct rip_info)); listnode_add (list, rinfo); @@ -3448,6 +3454,80 @@ DEFUN (no_rip_distance_source_access_list, return CMD_SUCCESS; } +/* Update ECMP routes to zebra when ECMP is disabled. */ +static void +rip_ecmp_disable (void) +{ + struct route_node *rp; + struct rip_info *rinfo, *tmp_rinfo; + struct list *list; + struct listnode *node, *nextnode; + + if (!rip) + return; + + for (rp = route_top (rip->table); rp; rp = route_next (rp)) + if ((list = rp->info) != NULL && listcount (list) > 1) + { + rinfo = listgetdata (listhead (list)); + if (!rip_route_rte (rinfo)) + continue; + + /* Drop all other entries, except the first one. */ + for (ALL_LIST_ELEMENTS (list, node, nextnode, tmp_rinfo)) + if (tmp_rinfo != rinfo) + { + RIP_TIMER_OFF (tmp_rinfo->t_timeout); + RIP_TIMER_OFF (tmp_rinfo->t_garbage_collect); + list_delete_node (list, node); + rip_info_free (tmp_rinfo); + } + + /* Update zebra. */ + rip_zebra_ipv4_add (rp); + + /* Set the route change flag. */ + SET_FLAG (rinfo->flags, RIP_RTF_CHANGED); + + /* Signal the output process to trigger an update. */ + rip_event (RIP_TRIGGERED_UPDATE, 0); + } +} + +DEFUN (rip_allow_ecmp, + rip_allow_ecmp_cmd, + "allow-ecmp", + "Allow Equal Cost MultiPath\n") +{ + if (rip->ecmp) + { + vty_out (vty, "ECMP is already enabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + rip->ecmp = 1; + zlog_info ("ECMP is enabled."); + return CMD_SUCCESS; +} + +DEFUN (no_rip_allow_ecmp, + no_rip_allow_ecmp_cmd, + "no allow-ecmp", + NO_STR + "Allow Equal Cost MultiPath\n") +{ + if (!rip->ecmp) + { + vty_out (vty, "ECMP is already disabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + rip->ecmp = 0; + zlog_info ("ECMP is disabled."); + rip_ecmp_disable (); + return CMD_SUCCESS; +} + /* Print out routes update time. */ static void rip_vty_out_uptime (struct vty *vty, struct rip_info *rinfo) @@ -3755,6 +3835,10 @@ config_write_rip (struct vty *vty) rdistance->access_list ? rdistance->access_list : "", VTY_NEWLINE); + /* ECMP configuration. */ + if (rip->ecmp) + vty_out (vty, " allow-ecmp%s", VTY_NEWLINE); + /* RIP static route configuration. */ for (rn = route_top (rip->route); rn; rn = route_next (rn)) if (rn->info) @@ -4092,6 +4176,8 @@ rip_init (void) install_element (RIP_NODE, &no_rip_distance_source_cmd); install_element (RIP_NODE, &rip_distance_source_access_list_cmd); install_element (RIP_NODE, &no_rip_distance_source_access_list_cmd); + install_element (RIP_NODE, &rip_allow_ecmp_cmd); + install_element (RIP_NODE, &no_rip_allow_ecmp_cmd); /* Debug related init. */ rip_debug_init (); diff --git a/ripd/ripd.h b/ripd/ripd.h index 0f0e2160..4f40e79a 100644 --- a/ripd/ripd.h +++ b/ripd/ripd.h @@ -143,6 +143,9 @@ struct rip u_char distance; struct route_table *distance_table; + /* RIP ECMP flag */ + unsigned int ecmp; + /* For redistribute route map. */ struct { From 621e2aaf33d8ab73bf44b0eea3f3900135d34996 Mon Sep 17 00:00:00 2001 From: Lu Feng Date: Fri, 11 Jul 2014 07:52:15 +0000 Subject: [PATCH 241/482] zebra: fix rtnh_len in the rt_netlink messages for multipath case In _netlink_route_build_multipath(): - Each time when appending a IPv4 gateway in the message, rtnh_len is increased by sizeof (struct rtattr) + 4, where we should use "bytelen" instead of the hard coding "4". - As what done for IPv4, we should increase rtnh_len accordingly along with adding a IPv6 gateway, or else the IPv6 gateways will be lost. Signed-off-by: Feng Lu Reviewed-by: Alain Ritoux Signed-off-by: David Lamparter --- zebra/rt_netlink.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 95a82fd2..f3cdcdc3 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -1561,7 +1561,7 @@ _netlink_route_build_multipath( { rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_GATEWAY, &nexthop->gate.ipv4, bytelen); - rtnh->rtnh_len += sizeof (struct rtattr) + 4; + rtnh->rtnh_len += sizeof (struct rtattr) + bytelen; if (nexthop->src.ipv4.s_addr) *src = &nexthop->src; @@ -1580,6 +1580,7 @@ _netlink_route_build_multipath( { rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_GATEWAY, &nexthop->gate.ipv6, bytelen); + rtnh->rtnh_len += sizeof (struct rtattr) + bytelen; if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("netlink_route_multipath() (%s): " From 3493b7731b750cbc62f00be94b624a08ccccf0b2 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Mon, 18 Nov 2013 23:04:27 +0100 Subject: [PATCH 242/482] lib: unstupidify thread debug information the library's thread scheduling functions keep track of the thread function's name, so far so good. However, copying the compiler-provided constant into a buffer inside the thread structure is plain useless. Also, strip_funcname() was trying to support something that never happens. Instead, let's use some bytes here to track where threads are scheduled from. Another commit will print that information on crashes. Ripping out useless stuff: -64 bytes in the thread structure Re-add as const ptr: +8 bytes Extra debug info: +12 bytes Signed-off-by: David Lamparter --- lib/thread.c | 84 ++++++++++++++++++++++------------------------------ lib/thread.h | 41 +++++++++++++------------ 2 files changed, 58 insertions(+), 67 deletions(-) diff --git a/lib/thread.c b/lib/thread.c index 468edd90..9de5f94f 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -267,7 +267,7 @@ cpu_record_hash_alloc (struct cpu_thread_history *a) struct cpu_thread_history *new; new = XCALLOC (MTYPE_THREAD_STATS, sizeof (struct cpu_thread_history)); new->func = a->func; - strcpy(new->funcname, a->funcname); + new->funcname = a->funcname; return new; } @@ -334,7 +334,7 @@ cpu_record_print(struct vty *vty, thread_type filter) void *args[3] = {&tmp, vty, &filter}; memset(&tmp, 0, sizeof tmp); - strcpy(tmp.funcname, "TOTAL"); + tmp.funcname = "TOTAL"; tmp.types = filter; #ifdef HAVE_RUSAGE @@ -582,7 +582,6 @@ thread_add_unuse (struct thread_master *m, struct thread *thread) assert (thread->prev == NULL); assert (thread->type == THREAD_UNUSED); thread_list_add (&m->unuse, thread); - /* XXX: Should we deallocate funcname here? */ } /* Free all unused thread. */ @@ -663,35 +662,13 @@ thread_timer_remain_second (struct thread *thread) return 0; } -/* Trim blankspace and "()"s */ -void -strip_funcname (char *dest, const char *funcname) -{ - char buff[FUNCNAME_LEN]; - char tmp, *e, *b = buff; - - strncpy(buff, funcname, sizeof(buff)); - buff[ sizeof(buff) -1] = '\0'; - e = buff +strlen(buff) -1; - - /* Wont work for funcname == "Word (explanation)" */ - - while (*b == ' ' || *b == '(') - ++b; - while (*e == ' ' || *e == ')') - --e; - e++; - - tmp = *e; - *e = '\0'; - strcpy (dest, b); - *e = tmp; -} +#define debugargdef const char *funcname, const char *schedfrom, int fromln +#define debugargpass funcname, schedfrom, fromln /* Get new thread. */ static struct thread * thread_get (struct thread_master *m, u_char type, - int (*func) (struct thread *), void *arg, const char* funcname) + int (*func) (struct thread *), void *arg, debugargdef) { struct thread *thread = thread_trim_head (&m->unuse); @@ -707,7 +684,9 @@ thread_get (struct thread_master *m, u_char type, thread->arg = arg; thread->index = -1; - strip_funcname (thread->funcname, funcname); + thread->funcname = funcname; + thread->schedfrom = schedfrom; + thread->schedfrom_line = fromln; return thread; } @@ -715,7 +694,8 @@ thread_get (struct thread_master *m, u_char type, /* Add new read thread. */ struct thread * funcname_thread_add_read (struct thread_master *m, - int (*func) (struct thread *), void *arg, int fd, const char* funcname) + int (*func) (struct thread *), void *arg, int fd, + debugargdef) { struct thread *thread; @@ -727,7 +707,7 @@ funcname_thread_add_read (struct thread_master *m, return NULL; } - thread = thread_get (m, THREAD_READ, func, arg, funcname); + thread = thread_get (m, THREAD_READ, func, arg, debugargpass); FD_SET (fd, &m->readfd); thread->u.fd = fd; thread_list_add (&m->read, thread); @@ -738,7 +718,8 @@ funcname_thread_add_read (struct thread_master *m, /* Add new write thread. */ struct thread * funcname_thread_add_write (struct thread_master *m, - int (*func) (struct thread *), void *arg, int fd, const char* funcname) + int (*func) (struct thread *), void *arg, int fd, + debugargdef) { struct thread *thread; @@ -750,7 +731,7 @@ funcname_thread_add_write (struct thread_master *m, return NULL; } - thread = thread_get (m, THREAD_WRITE, func, arg, funcname); + thread = thread_get (m, THREAD_WRITE, func, arg, debugargpass); FD_SET (fd, &m->writefd); thread->u.fd = fd; thread_list_add (&m->write, thread); @@ -763,8 +744,8 @@ funcname_thread_add_timer_timeval (struct thread_master *m, int (*func) (struct thread *), int type, void *arg, - struct timeval *time_relative, - const char* funcname) + struct timeval *time_relative, + debugargdef) { struct thread *thread; struct pqueue *queue; @@ -776,7 +757,7 @@ funcname_thread_add_timer_timeval (struct thread_master *m, assert (time_relative); queue = ((type == THREAD_TIMER) ? m->timer : m->background); - thread = thread_get (m, type, func, arg, funcname); + thread = thread_get (m, type, func, arg, debugargpass); /* Do we need jitter here? */ quagga_get_relative (NULL); @@ -793,7 +774,8 @@ funcname_thread_add_timer_timeval (struct thread_master *m, struct thread * funcname_thread_add_timer (struct thread_master *m, int (*func) (struct thread *), - void *arg, long timer, const char* funcname) + void *arg, long timer, + debugargdef) { struct timeval trel; @@ -803,14 +785,15 @@ funcname_thread_add_timer (struct thread_master *m, trel.tv_usec = 0; return funcname_thread_add_timer_timeval (m, func, THREAD_TIMER, arg, - &trel, funcname); + &trel, debugargpass); } /* Add timer event thread with "millisecond" resolution */ struct thread * funcname_thread_add_timer_msec (struct thread_master *m, int (*func) (struct thread *), - void *arg, long timer, const char* funcname) + void *arg, long timer, + debugargdef) { struct timeval trel; @@ -820,15 +803,15 @@ funcname_thread_add_timer_msec (struct thread_master *m, trel.tv_usec = 1000*(timer % 1000); return funcname_thread_add_timer_timeval (m, func, THREAD_TIMER, - arg, &trel, funcname); + arg, &trel, debugargpass); } /* Add a background thread, with an optional millisec delay */ struct thread * funcname_thread_add_background (struct thread_master *m, int (*func) (struct thread *), - void *arg, long delay, - const char *funcname) + void *arg, long delay, + debugargdef) { struct timeval trel; @@ -846,19 +829,20 @@ funcname_thread_add_background (struct thread_master *m, } return funcname_thread_add_timer_timeval (m, func, THREAD_BACKGROUND, - arg, &trel, funcname); + arg, &trel, debugargpass); } /* Add simple event thread. */ struct thread * funcname_thread_add_event (struct thread_master *m, - int (*func) (struct thread *), void *arg, int val, const char* funcname) + int (*func) (struct thread *), void *arg, int val, + debugargdef) { struct thread *thread; assert (m != NULL); - thread = thread_get (m, THREAD_EVENT, func, arg, funcname); + thread = thread_get (m, THREAD_EVENT, func, arg, debugargpass); thread->u.val = val; thread_list_add (&m->event, thread); @@ -1253,7 +1237,7 @@ thread_call (struct thread *thread) struct cpu_thread_history tmp; tmp.func = thread->func; - strcpy(tmp.funcname, thread->funcname); + tmp.funcname = thread->funcname; thread->hist = hash_get (cpu_record, &tmp, (void * (*) (void *))cpu_record_hash_alloc); @@ -1301,7 +1285,7 @@ funcname_thread_execute (struct thread_master *m, int (*func)(struct thread *), void *arg, int val, - const char* funcname) + debugargdef) { struct thread dummy; @@ -1313,7 +1297,11 @@ funcname_thread_execute (struct thread_master *m, dummy.func = func; dummy.arg = arg; dummy.u.val = val; - strip_funcname (dummy.funcname, funcname); + + dummy.funcname = funcname; + dummy.schedfrom = schedfrom; + dummy.schedfrom_line = fromln; + thread_call (&dummy); return NULL; diff --git a/lib/thread.h b/lib/thread.h index dbf5f25b..a088b472 100644 --- a/lib/thread.h +++ b/lib/thread.h @@ -64,9 +64,6 @@ struct thread_master typedef unsigned char thread_type; -/* ISO C99 maximum function name length is 63 */ -#define FUNCNAME_LEN 64 - /* Thread itself. */ struct thread { @@ -85,7 +82,9 @@ struct thread int index; /* used for timers to store position in queue */ struct timeval real; struct cpu_thread_history *hist; /* cache pointer to cpu_history */ - char funcname[FUNCNAME_LEN]; + const char *funcname; + const char *schedfrom; + int schedfrom_line; }; struct cpu_thread_history @@ -100,7 +99,7 @@ struct cpu_thread_history struct time_stats cpu; #endif thread_type types; - char funcname[FUNCNAME_LEN]; + const char *funcname; }; /* Clocks supported by Quagga */ @@ -165,15 +164,17 @@ enum quagga_clkid { #define THREAD_WRITE_OFF(thread) THREAD_OFF(thread) #define THREAD_TIMER_OFF(thread) THREAD_OFF(thread) -#define thread_add_read(m,f,a,v) funcname_thread_add_read(m,f,a,v,#f) -#define thread_add_write(m,f,a,v) funcname_thread_add_write(m,f,a,v,#f) -#define thread_add_timer(m,f,a,v) funcname_thread_add_timer(m,f,a,v,#f) -#define thread_add_timer_msec(m,f,a,v) funcname_thread_add_timer_msec(m,f,a,v,#f) -#define thread_add_event(m,f,a,v) funcname_thread_add_event(m,f,a,v,#f) -#define thread_execute(m,f,a,v) funcname_thread_execute(m,f,a,v,#f) +#define debugargdef const char *funcname, const char *schedfrom, int fromln + +#define thread_add_read(m,f,a,v) funcname_thread_add_read(m,f,a,v,#f,__FILE__,__LINE__) +#define thread_add_write(m,f,a,v) funcname_thread_add_write(m,f,a,v,#f,__FILE__,__LINE__) +#define thread_add_timer(m,f,a,v) funcname_thread_add_timer(m,f,a,v,#f,__FILE__,__LINE__) +#define thread_add_timer_msec(m,f,a,v) funcname_thread_add_timer_msec(m,f,a,v,#f,__FILE__,__LINE__) +#define thread_add_event(m,f,a,v) funcname_thread_add_event(m,f,a,v,#f,__FILE__,__LINE__) +#define thread_execute(m,f,a,v) funcname_thread_execute(m,f,a,v,#f,__FILE__,__LINE__) /* The 4th arg to thread_add_background is the # of milliseconds to delay. */ -#define thread_add_background(m,f,a,v) funcname_thread_add_background(m,f,a,v,#f) +#define thread_add_background(m,f,a,v) funcname_thread_add_background(m,f,a,v,#f,__FILE__,__LINE__) /* Prototypes. */ extern struct thread_master *thread_master_create (void); @@ -181,27 +182,29 @@ extern void thread_master_free (struct thread_master *); extern struct thread *funcname_thread_add_read (struct thread_master *, int (*)(struct thread *), - void *, int, const char*); + void *, int, debugargdef); extern struct thread *funcname_thread_add_write (struct thread_master *, int (*)(struct thread *), - void *, int, const char*); + void *, int, debugargdef); extern struct thread *funcname_thread_add_timer (struct thread_master *, int (*)(struct thread *), - void *, long, const char*); + void *, long, debugargdef); extern struct thread *funcname_thread_add_timer_msec (struct thread_master *, int (*)(struct thread *), - void *, long, const char*); + void *, long, debugargdef); extern struct thread *funcname_thread_add_event (struct thread_master *, int (*)(struct thread *), - void *, int, const char*); + void *, int, debugargdef); extern struct thread *funcname_thread_add_background (struct thread_master *, int (*func)(struct thread *), void *arg, long milliseconds_to_delay, - const char *funcname); + debugargdef); extern struct thread *funcname_thread_execute (struct thread_master *, int (*)(struct thread *), - void *, int, const char *); + void *, int, debugargdef); +#undef debugargdef + extern void thread_cancel (struct thread *); extern unsigned int thread_cancel_event (struct thread_master *, void *); extern struct thread *thread_fetch (struct thread_master *, struct thread *); From 615f9f18fc025757a255f936748fc1e86e922783 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Mon, 18 Nov 2013 23:52:02 +0100 Subject: [PATCH 243/482] lib: include thread information in backtraces now that we know what thread we're currently executing, let's add that information to SEGV / assert backtraces. Signed-off-by: David Lamparter --- lib/log.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ lib/log.h | 2 ++ lib/thread.c | 4 ++++ lib/thread.h | 4 ++++ 4 files changed, 55 insertions(+) diff --git a/lib/log.c b/lib/log.c index 1058844b..04f8fab6 100644 --- a/lib/log.c +++ b/lib/log.c @@ -425,6 +425,40 @@ zlog_signal(int signo, const char *action NULL #endif ); + + s = buf; + if (!thread_current) + s = str_append (LOC, "no thread information available\n"); + else + { + s = str_append (LOC, "in thread "); + s = str_append (LOC, thread_current->funcname); + s = str_append (LOC, " scheduled from "); + s = str_append (LOC, thread_current->schedfrom); + s = str_append (LOC, ":"); + s = num_append (LOC, thread_current->schedfrom_line); + s = str_append (LOC, "\n"); + } + +#define DUMP(FD) write(FD, buf, s-buf); + /* If no file logging configured, try to write to fallback log file. */ + if (logfile_fd >= 0) + DUMP(logfile_fd) + if (!zlog_default) + DUMP(STDERR_FILENO) + else + { + if (PRI <= zlog_default->maxlvl[ZLOG_DEST_STDOUT]) + DUMP(STDOUT_FILENO) + /* Remove trailing '\n' for monitor and syslog */ + *--s = '\0'; + if (PRI <= zlog_default->maxlvl[ZLOG_DEST_MONITOR]) + vty_log_fixed(buf,s-buf); + if (PRI <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG]) + syslog_sigsafe(PRI|zlog_default->facility,msgstart,s-msgstart); + } +#undef DUMP + #undef PRI #undef LOC } @@ -604,6 +638,16 @@ PLOG_FUNC(plog_debug, LOG_DEBUG) #undef PLOG_FUNC +void zlog_thread_info (int log_level) +{ + if (thread_current) + zlog(NULL, log_level, "Current thread function %s, scheduled from " + "file %s, line %u", thread_current->funcname, + thread_current->schedfrom, thread_current->schedfrom_line); + else + zlog(NULL, log_level, "Current thread not known/applicable"); +} + void _zlog_assert_failed (const char *assertion, const char *file, unsigned int line, const char *function) @@ -616,6 +660,7 @@ _zlog_assert_failed (const char *assertion, const char *file, zlog(NULL, LOG_CRIT, "Assertion `%s' failed in file %s, line %u, function %s", assertion,file,line,(function ? function : "?")); zlog_backtrace(LOG_CRIT); + zlog_thread_info(LOG_CRIT); abort(); } diff --git a/lib/log.h b/lib/log.h index cf247a83..f3b43ad1 100644 --- a/lib/log.h +++ b/lib/log.h @@ -132,6 +132,8 @@ extern void plog_notice (struct zlog *, const char *format, ...) extern void plog_debug (struct zlog *, const char *format, ...) PRINTF_ATTRIBUTE(2, 3); +extern void zlog_thread_info (int log_level); + /* Set logging level for the given destination. If the log_level argument is ZLOG_DISABLED, then the destination is disabled. This function should not be used for file logging (use zlog_set_file diff --git a/lib/thread.c b/lib/thread.c index 9de5f94f..9c3ee823 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -1217,6 +1217,8 @@ thread_getrusage (RUSAGE_T *r) #endif /* HAVE_CLOCK_MONOTONIC */ } +struct thread *thread_current = NULL; + /* We check thread consumed time. If the system has getrusage, we'll use that to get in-depth stats on the performance of the thread in addition to wall clock time stats from gettimeofday. */ @@ -1246,7 +1248,9 @@ thread_call (struct thread *thread) GETRUSAGE (&before); thread->real = before.real; + thread_current = thread; (*thread->func) (thread); + thread_current = NULL; GETRUSAGE (&after); diff --git a/lib/thread.h b/lib/thread.h index a088b472..9743a22d 100644 --- a/lib/thread.h +++ b/lib/thread.h @@ -234,4 +234,8 @@ extern unsigned long thread_consumed_time(RUSAGE_T *after, RUSAGE_T *before, extern struct timeval recent_time; /* Similar to recent_time, but a monotonically increasing time value */ extern struct timeval recent_relative_time (void); + +/* only for use in logging functions! */ +extern struct thread *thread_current; + #endif /* _ZEBRA_THREAD_H */ From d4a8607d12e1d3f655055647f1633ec154685545 Mon Sep 17 00:00:00 2001 From: Paul Jakma Date: Fri, 19 Oct 2012 12:02:42 +0100 Subject: [PATCH 244/482] HACKING: remove an unneeded escape --- HACKING.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HACKING.tex b/HACKING.tex index a49113fb..c868e7ca 100644 --- a/HACKING.tex +++ b/HACKING.tex @@ -207,7 +207,7 @@ \section{COMMIT MESSAGES} detect loss of frob. * frob.h: (struct frob) Add DOWN state flag. -* frob.c: (frob\_change) set/clear DOWN appropriately on state change. +* frob.c: (frob_change) set/clear DOWN appropriately on state change. * bar.c: (barinate) Check frob for DOWN state. \end{verbatim}\end{quote} From ea55500409651b0f8fd2c8a02fdbf245acc96dd8 Mon Sep 17 00:00:00 2001 From: Steve Hill Date: Tue, 28 Jul 2009 16:36:14 -0400 Subject: [PATCH 245/482] lib: Improve error reporting from broken config files * command.h: (config_from_file) Add variable to interface for line number reporting. * command.c: (config_from_file) Set & increment 'line_num' while parsing. * vty.c: (vty_read_file) Report parse errors in the correct order to stderr, with added line numbers. --- lib/command.c | 4 +++- lib/command.h | 2 +- lib/vty.c | 23 ++++++++++++++++------- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/lib/command.c b/lib/command.c index 7249c653..1087ceb8 100644 --- a/lib/command.c +++ b/lib/command.c @@ -2762,13 +2762,15 @@ cmd_execute_command_strict (vector vline, struct vty *vty, /* Configration make from file. */ int -config_from_file (struct vty *vty, FILE *fp) +config_from_file (struct vty *vty, FILE *fp, unsigned int *line_num) { int ret; + *line_num = 0; vector vline; while (fgets (vty->buf, VTY_BUFSIZ, fp)) { + ++(*line_num); vline = cmd_make_strvec (vty->buf); /* In case of comment line */ diff --git a/lib/command.h b/lib/command.h index e47c4255..8dc50d0d 100644 --- a/lib/command.h +++ b/lib/command.h @@ -518,7 +518,7 @@ extern void cmd_free_strvec (vector); extern vector cmd_describe_command (vector, struct vty *, int *status); extern char **cmd_complete_command (vector, struct vty *, int *status); extern const char *cmd_prompt (enum node_type); -extern int config_from_file (struct vty *, FILE *); +extern int config_from_file (struct vty *, FILE *, unsigned int *line_num); extern enum node_type node_parent (enum node_type); extern int cmd_execute_command (vector, struct vty *, struct cmd_element **, int); extern int cmd_execute_command_strict (vector, struct vty *, struct cmd_element **); diff --git a/lib/vty.c b/lib/vty.c index 11413576..488f8d5f 100644 --- a/lib/vty.c +++ b/lib/vty.c @@ -2229,28 +2229,37 @@ vty_read_file (FILE *confp) { int ret; struct vty *vty; + unsigned int line_num = 0; vty = vty_new (); - vty->fd = 0; /* stdout */ - vty->type = VTY_TERM; + vty->fd = dup(STDERR_FILENO); /* vty_close() will close this */ + if (vty->fd < 0) + { + /* Fine, we couldn't make a new fd. vty_close doesn't close stdout. */ + vty->fd = STDOUT_FILENO; + } + vty->type = VTY_FILE; vty->node = CONFIG_NODE; /* Execute configuration file */ - ret = config_from_file (vty, confp); + ret = config_from_file (vty, confp, &line_num); + + /* Flush any previous errors before printing messages below */ + buffer_flush_all (vty->obuf, vty->fd); if ( !((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO)) ) { switch (ret) { case CMD_ERR_AMBIGUOUS: - fprintf (stderr, "Ambiguous command.\n"); + fprintf (stderr, "*** Error reading config: Ambiguous command.\n"); break; case CMD_ERR_NO_MATCH: - fprintf (stderr, "There is no such command.\n"); + fprintf (stderr, "*** Error reading config: There is no such command.\n"); break; } - fprintf (stderr, "Error occured during reading below line.\n%s\n", - vty->buf); + fprintf (stderr, "*** Error occured processing line %u, below:\n%s\n", + line_num, vty->buf); vty_close (vty); exit (1); } From 010ebbbca6396f272cc2d50d147dd922dda68213 Mon Sep 17 00:00:00 2001 From: Paul Jakma Date: Tue, 16 Sep 2014 11:53:49 +0100 Subject: [PATCH 246/482] Add missing GPL headers, and copyright claims that certainly apply. * Fix (a subset of)? files with non-trivial code that are missing GPL headers. * A few copyright claims added which I am certain apply, but which I had missed out on the original commits. NB: Copyright claims are not exclusive and the addition of any copyright claim should not be read as implying a lack of any further claims, or denying the validity of any other claims. All those with claims of copyright over any portion of Quagga are welcome to submit them, ideally as patches to update copyright strings in files. --- lib/memtypes.awk | 21 ++++++++++++++++++++- lib/str.c | 18 ++++++++++++++++++ lib/zassert.h | 17 +++++++++++++++++ ospfclient/ospfclient.c | 18 ++++++++++++++++++ tests/aspath_test.c | 21 +++++++++++++++++++++ tests/bgp_capability_test.c | 21 +++++++++++++++++++++ tests/bgp_mp_attr_test.c | 21 +++++++++++++++++++++ tests/ecommunity_test.c | 20 ++++++++++++++++++++ tests/test-buffer.c | 21 +++++++++++++++++++++ tests/test-checksum.c | 21 +++++++++++++++++++++ tests/test-memory.c | 19 +++++++++++++++++++ tests/test-sig.c | 19 +++++++++++++++++++ tests/test-stream.c | 22 ++++++++++++++++++++++ zebra/ioctl_null.c | 21 +++++++++++++++++++++ zebra/kernel_null.c | 21 +++++++++++++++++++++ zebra/misc_null.c | 21 +++++++++++++++++++++ zebra/redistribute_null.c | 21 +++++++++++++++++++++ 17 files changed, 342 insertions(+), 1 deletion(-) diff --git a/lib/memtypes.awk b/lib/memtypes.awk index 5429f6e8..bd13327d 100644 --- a/lib/memtypes.awk +++ b/lib/memtypes.awk @@ -1,4 +1,23 @@ -# $Id: memtypes.awk,v 1.4 2006/03/30 14:30:19 paul Exp $ +### +# Copyright (C) Paul Jakma 2005 +# +# This file is part of Quagga. +# +# Quagga is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2, or (at your option) any +# later version. +# +# Quagga is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Quagga; see the file COPYING. If not, write to the Free +# Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +### # # Scan a file of memory definitions (see eg memtypes.c) and generate # a corresponding header file with an enum of the MTYPE's and declarations diff --git a/lib/str.c b/lib/str.c index 4ab71e19..d8f039a0 100644 --- a/lib/str.c +++ b/lib/str.c @@ -16,6 +16,24 @@ Copyright (C) 1996, 1997, 1998, 2001, 2002 Free Software Foundation, Inc. */ +/* + * This file is part of Quagga. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ #include diff --git a/lib/zassert.h b/lib/zassert.h index 79126760..bf0a851b 100644 --- a/lib/zassert.h +++ b/lib/zassert.h @@ -1,5 +1,22 @@ /* * $Id: zassert.h,v 1.2 2004/12/03 18:01:04 ajs Exp $ + * + * This file is part of Quagga. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. */ #ifndef _QUAGGA_ASSERT_H diff --git a/ospfclient/ospfclient.c b/ospfclient/ospfclient.c index 3608ebac..1de7644f 100644 --- a/ospfclient/ospfclient.c +++ b/ospfclient/ospfclient.c @@ -1,3 +1,21 @@ +/* This file is part of Quagga. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + /* * Simple program to demonstrate how OSPF API can be used. This * application retrieves the LSDB from the OSPF daemon and then diff --git a/tests/aspath_test.c b/tests/aspath_test.c index 7fdb5e22..0e57c535 100644 --- a/tests/aspath_test.c +++ b/tests/aspath_test.c @@ -1,3 +1,24 @@ +/* + * Copyright (C) 2005 Sun Microsystems, Inc. + * + * This file is part of Quagga. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + #include #include "vty.h" diff --git a/tests/bgp_capability_test.c b/tests/bgp_capability_test.c index 65c6a700..96f18f01 100644 --- a/tests/bgp_capability_test.c +++ b/tests/bgp_capability_test.c @@ -1,3 +1,24 @@ +/* + * Copyright (C) 2007 Sun Microsystems, Inc. + * + * This file is part of Quagga. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + #include #include "vty.h" diff --git a/tests/bgp_mp_attr_test.c b/tests/bgp_mp_attr_test.c index aa8e485d..5f0e733d 100644 --- a/tests/bgp_mp_attr_test.c +++ b/tests/bgp_mp_attr_test.c @@ -1,3 +1,24 @@ +/* + * Copyright (C) 2008 Sun Microsystems, Inc. + * + * This file is part of Quagga. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + #include #include "vty.h" diff --git a/tests/ecommunity_test.c b/tests/ecommunity_test.c index 87f20f28..cd1681d5 100644 --- a/tests/ecommunity_test.c +++ b/tests/ecommunity_test.c @@ -1,3 +1,23 @@ +/* + * Copyright (C) 2007 Sun Microsystems, Inc. + * + * This file is part of Quagga. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ #include #include "vty.h" diff --git a/tests/test-buffer.c b/tests/test-buffer.c index b310776f..e95d6fb8 100644 --- a/tests/test-buffer.c +++ b/tests/test-buffer.c @@ -1,3 +1,24 @@ +/* + * Copyright (C) 2004 Paul Jakma + * + * This file is part of Quagga. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + #include #include #include diff --git a/tests/test-checksum.c b/tests/test-checksum.c index 921b58c4..9672e95a 100644 --- a/tests/test-checksum.c +++ b/tests/test-checksum.c @@ -1,3 +1,24 @@ +/* + * Copyright (C) 2008 Sun Microsystems, Inc. + * + * This file is part of Quagga. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + #include #include #include diff --git a/tests/test-memory.c b/tests/test-memory.c index 2971160b..807249ea 100644 --- a/tests/test-memory.c +++ b/tests/test-memory.c @@ -1,3 +1,22 @@ +/* + * This file is part of Quagga. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + #include #include diff --git a/tests/test-sig.c b/tests/test-sig.c index df023fac..7415d7ae 100644 --- a/tests/test-sig.c +++ b/tests/test-sig.c @@ -1,3 +1,22 @@ +/* + * This file is part of Quagga. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + #include #include #include "lib/log.h" diff --git a/tests/test-stream.c b/tests/test-stream.c index 785ce588..5997b47d 100644 --- a/tests/test-stream.c +++ b/tests/test-stream.c @@ -1,3 +1,25 @@ +/* Simple stream test. + * + * Copyright (C) 2006 Sun Microsystems, Inc. + * + * This file is part of Quagga. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + #include #include #include diff --git a/zebra/ioctl_null.c b/zebra/ioctl_null.c index 5d046d36..5a8be991 100644 --- a/zebra/ioctl_null.c +++ b/zebra/ioctl_null.c @@ -1,3 +1,24 @@ +/* + * Copyright (C) 2006 Sun Microsystems, Inc. + * + * This file is part of Quagga. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + #include #include "zebra/rib.h" diff --git a/zebra/kernel_null.c b/zebra/kernel_null.c index cdb6e23f..29c7881b 100644 --- a/zebra/kernel_null.c +++ b/zebra/kernel_null.c @@ -1,5 +1,26 @@ /* NULL kernel methods for testing. */ +/* + * Copyright (C) 2006 Sun Microsystems, Inc. + * + * This file is part of Quagga. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + #include #include diff --git a/zebra/misc_null.c b/zebra/misc_null.c index 06807267..b4416e63 100644 --- a/zebra/misc_null.c +++ b/zebra/misc_null.c @@ -1,3 +1,24 @@ +/* + * Copyright (C) 2006 Sun Microsystems, Inc. + * + * This file is part of Quagga. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + #include #include "prefix.h" diff --git a/zebra/redistribute_null.c b/zebra/redistribute_null.c index 54198c8e..c45ebe1d 100644 --- a/zebra/redistribute_null.c +++ b/zebra/redistribute_null.c @@ -1,3 +1,24 @@ +/* + * Copyright (C) 2006 Sun Microsystems, Inc. + * + * This file is part of Quagga. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Quagga; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + #include #include "zebra/rib.h" #include "zebra/zserv.h" From 7aa9dcef80b2ce50ecaa77653d87c8b84e009c49 Mon Sep 17 00:00:00 2001 From: Paul Jakma Date: Fri, 19 Sep 2014 14:42:23 +0100 Subject: [PATCH 247/482] Fix most compiler warnings in default GCC build. Fix lots of warnings. Some const and type-pun breaks strict-aliasing warnings left but much reduced. * bgp_advertise.h: (struct bgp_advertise_fifo) is functionally identical to (struct fifo), so just use that. Makes it clearer the beginning of (struct bgp_advertise) is compatible with with (struct fifo), which seems to be enough for gcc. Add a BGP_ADV_FIFO_HEAD macro to contain the right cast to try shut up type-punning breaks strict aliasing warnings. * bgp_packet.c: Use BGP_ADV_FIFO_HEAD. (bgp_route_refresh_receive) fix an interesting logic error in (!ok || (ret != BLAH)) where ret is only well-defined if ok. * bgp_vty.c: Peer commands should use bgp_vty_return to set their return. * jhash.{c,h}: Can take const on * args without adding issues & fix warnings. * libospf.h: LSA sequence numbers use the unsigned range of values, and constants need to be set to unsigned, or it causes warnings in ospf6d. * md5.h: signedness of caddr_t is implementation specific, change to an explicit (uint_8 *), fix sign/unsigned comparison warnings. * vty.c: (vty_log_fixed) const on level is well-intentioned, but not going to fly given iov_base. * workqueue.c: ALL_LIST_ELEMENTS_RO tests for null pointer, which is always true for address of static variable. Correct but pointless warning in this case, but use a 2nd pointer to shut it up. * ospf6_route.h: Add a comment about the use of (struct prefix) to stuff 2 different 32 bit IDs into in (struct ospf6_route), and the resulting type-pun strict-alias breakage warnings this causes. Need to use 2 different fields to fix that warning? general: * remove unused variables, other than a few cases where they serve a sufficiently useful documentary purpose (e.g. for code that needs fixing), or they're required dummies. In those cases, try mark them as unused. * Remove dead code that can't be reached. * Quite a few 'no ...' forms of vty commands take arguments, but do not check the argument matches the command being negated. E.g., should 'distance X ' succeed if previously 'distance Y ' was set? Or should it be required that the distance match the previously configured distance for the prefix? Ultimately, probably better to be strict about this. However, changing from slack to strict might expose problems in command aliases and tools. * Fix uninitialised use of variables. * Fix sign/unsigned comparison warnings by making signedness of types consistent. * Mark functions as static where their use is restricted to the same compilation unit. * Add required headers * Move constants defined in headers into code. * remove dead, unused functions that have no debug purpose. --- bgpd/bgp_advertise.h | 17 ++++++----------- bgpd/bgp_attr.c | 1 - bgpd/bgp_mplsvpn.c | 6 +++--- bgpd/bgp_nexthop.c | 35 +++++++++++++++++++++-------------- bgpd/bgp_packet.c | 27 ++++++++++++--------------- bgpd/bgp_route.c | 23 ++++++++--------------- bgpd/bgp_routemap.c | 14 +++++--------- bgpd/bgp_vty.c | 25 +++++++------------------ lib/command.c | 4 ++-- lib/if.c | 4 +--- lib/jhash.c | 6 +++--- lib/jhash.h | 4 ++-- lib/libospf.h | 4 ++-- lib/md5.c | 2 +- lib/md5.h | 2 +- lib/table.c | 3 --- lib/vty.c | 4 ++-- lib/vty.h | 2 +- lib/workqueue.c | 12 ++++++++---- ospf6d/ospf6_abr.h | 2 ++ ospf6d/ospf6_area.c | 2 -- ospf6d/ospf6_asbr.c | 7 +------ ospf6d/ospf6_interface.c | 1 - ospf6d/ospf6_lsa.c | 2 +- ospf6d/ospf6_neighbor.c | 25 +++++++++++++++++++++++++ ospf6d/ospf6_neighbor.h | 25 ------------------------- ospf6d/ospf6_network.c | 18 ++++-------------- ospf6d/ospf6_network.h | 6 ------ ospf6d/ospf6_route.h | 5 +++++ ospf6d/ospf6_spf.c | 3 ++- ospf6d/ospf6_top.c | 6 ++---- ospfd/ospf_api.c | 8 ++++---- ospfd/ospf_vty.c | 1 - ripd/ripd.c | 13 ------------- zebra/irdp_interface.c | 3 --- zebra/router-id.c | 33 +++++++++++++++++++-------------- zebra/rt_netlink.c | 8 ++++---- zebra/rt_netlink.h | 4 ++-- 38 files changed, 156 insertions(+), 211 deletions(-) diff --git a/bgpd/bgp_advertise.h b/bgpd/bgp_advertise.h index 4ebde907..2cf2a29b 100644 --- a/bgpd/bgp_advertise.h +++ b/bgpd/bgp_advertise.h @@ -21,13 +21,6 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #ifndef _QUAGGA_BGP_ADVERTISE_H #define _QUAGGA_BGP_ADVERTISE_H -/* BGP advertise FIFO. */ -struct bgp_advertise_fifo -{ - struct bgp_advertise *next; - struct bgp_advertise *prev; -}; - /* BGP advertise attribute. */ struct bgp_advertise_attr { @@ -44,7 +37,7 @@ struct bgp_advertise_attr struct bgp_advertise { /* FIFO for advertisement. */ - struct bgp_advertise_fifo fifo; + struct fifo fifo; /* Link list for same attribute advertise. */ struct bgp_advertise *next; @@ -97,11 +90,13 @@ struct bgp_adj_in /* BGP advertisement list. */ struct bgp_synchronize { - struct bgp_advertise_fifo update; - struct bgp_advertise_fifo withdraw; - struct bgp_advertise_fifo withdraw_low; + struct fifo update; + struct fifo withdraw; + struct fifo withdraw_low; }; +#define BGP_ADV_FIFO_HEAD(F) ((struct bgp_advertise *)FIFO_HEAD(F)) + /* BGP adjacency linked list. */ #define BGP_INFO_ADD(N,A,TYPE) \ do { \ diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index fcf82551..b62a4f82 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -2110,7 +2110,6 @@ bgp_packet_mpattr_start (struct stream *s, afi_t afi, safi_t safi, case SAFI_UNICAST: case SAFI_MULTICAST: { - unsigned long sizep; struct attr_extra *attre = attr->extra; assert (attr->extra); diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index b0cf2a98..3d2dadef 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -84,7 +84,6 @@ bgp_nlri_parse_vpnv4 (struct peer *peer, struct attr *attr, struct prefix p; int psize; int prefixlen; - u_int32_t label; u_int16_t type; struct rd_as rd_as; struct rd_ip rd_ip; @@ -117,8 +116,9 @@ bgp_nlri_parse_vpnv4 (struct peer *peer, struct attr *attr, zlog_err ("prefix length is less than 88: %d", prefixlen); return -1; } - - label = decode_label (pnt); + + /* XXX: Not doing anything with the label */ + decode_label (pnt); /* Copyr label to prefix. */ tagpnt = pnt;; diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index 5b1d13ac..6218e670 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -789,9 +789,9 @@ zlookup_read (void) uint16_t length; u_char marker; u_char version; - uint16_t command; - int nbytes; - struct in_addr raddr; + uint16_t command __attribute__((unused)); + int nbytes __attribute__((unused)); + struct in_addr raddr __attribute__((unused)); uint32_t metric; int i; u_char nexthop_num; @@ -801,6 +801,7 @@ zlookup_read (void) s = zlookup->ibuf; stream_reset (s); + /* nbytes not being checked */ nbytes = stream_read (s, zlookup->sock, 2); length = stream_getw (s); @@ -814,9 +815,11 @@ zlookup_read (void) __func__, zlookup->sock, marker, version); return NULL; } - + + /* XXX: not checking command */ command = stream_getw (s); + /* XXX: not doing anything with raddr */ raddr.s_addr = stream_get_ipv4 (s); metric = stream_getl (s); nexthop_num = stream_getc (s); @@ -901,8 +904,6 @@ zlookup_read_ipv6 (void) struct stream *s; uint16_t length; u_char version, marker; - uint16_t command; - int nbytes; struct in6_addr raddr; uint32_t metric; int i; @@ -913,10 +914,11 @@ zlookup_read_ipv6 (void) s = zlookup->ibuf; stream_reset (s); - nbytes = stream_read (s, zlookup->sock, 2); + /* XXX: ignoring nbytes, see also zread_lookup */ + stream_read (s, zlookup->sock, 2); length = stream_getw (s); - nbytes = stream_read (s, zlookup->sock, length - 2); + stream_read (s, zlookup->sock, length - 2); marker = stream_getc (s); version = stream_getc (s); @@ -926,9 +928,11 @@ zlookup_read_ipv6 (void) __func__, zlookup->sock, marker, version); return NULL; } - - command = stream_getw (s); + /* XXX: ignoring command */ + stream_getw (s); + + /* XXX: not actually doing anything with raddr */ stream_get (&raddr, s, 16); metric = stream_getl (s); @@ -1014,10 +1018,10 @@ bgp_import_check (struct prefix *p, u_int32_t *igpmetric, { struct stream *s; int ret; - u_int16_t length, command; + u_int16_t length, command __attribute__((unused)); u_char version, marker; - int nbytes; - struct in_addr addr; + int nbytes __attribute__((unused)); + struct in_addr addr __attribute__((unused)); struct in_addr nexthop; u_int32_t metric = 0; u_char nexthop_num; @@ -1063,6 +1067,7 @@ bgp_import_check (struct prefix *p, u_int32_t *igpmetric, stream_reset (s); /* Fetch length. */ + /* XXX: not using nbytes */ nbytes = stream_read (s, zlookup->sock, 2); length = stream_getw (s); @@ -1077,9 +1082,11 @@ bgp_import_check (struct prefix *p, u_int32_t *igpmetric, __func__, zlookup->sock, marker, version); return 0; } - + + /* XXX: not using command */ command = stream_getw (s); + /* XXX: not using addr */ addr.s_addr = stream_get_ipv4 (s); metric = stream_getl (s); nexthop_num = stream_getc (s); diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index 65c6cac1..0fab1b08 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -158,7 +158,7 @@ bgp_update_packet (struct peer *peer, afi_t afi, safi_t safi) snlri = peer->scratch; stream_reset (snlri); - adv = FIFO_HEAD (&peer->sync[afi][safi]->update); + adv = BGP_ADV_FIFO_HEAD (&peer->sync[afi][safi]->update); while (adv) { @@ -331,7 +331,6 @@ bgp_withdraw_packet (struct peer *peer, afi_t afi, safi_t safi) struct bgp_adj_out *adj; struct bgp_advertise *adv; struct bgp_node *rn; - unsigned long pos; bgp_size_t unfeasible_len; bgp_size_t total_attr_len; size_t mp_start = 0; @@ -342,7 +341,7 @@ bgp_withdraw_packet (struct peer *peer, afi_t afi, safi_t safi) s = peer->work; stream_reset (s); - while ((adv = FIFO_HEAD (&peer->sync[afi][safi]->withdraw)) != NULL) + while ((adv = BGP_ADV_FIFO_HEAD (&peer->sync[afi][safi]->withdraw)) != NULL) { assert (adv->rn); adj = adv->adj; @@ -595,7 +594,7 @@ bgp_write_packet (struct peer *peer) for (afi = AFI_IP; afi < AFI_MAX; afi++) for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) { - adv = FIFO_HEAD (&peer->sync[afi][safi]->withdraw); + adv = BGP_ADV_FIFO_HEAD (&peer->sync[afi][safi]->withdraw); if (adv) { s = bgp_withdraw_packet (peer, afi, safi); @@ -607,7 +606,7 @@ bgp_write_packet (struct peer *peer) for (afi = AFI_IP; afi < AFI_MAX; afi++) for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) { - adv = FIFO_HEAD (&peer->sync[afi][safi]->update); + adv = BGP_ADV_FIFO_HEAD (&peer->sync[afi][safi]->update); if (adv) { if (adv->binfo && adv->binfo->uptime < peer->synctime) @@ -663,7 +662,7 @@ bgp_write_proceed (struct peer *peer) for (afi = AFI_IP; afi < AFI_MAX; afi++) for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) - if ((adv = FIFO_HEAD (&peer->sync[afi][safi]->update)) != NULL) + if ((adv = BGP_ADV_FIFO_HEAD (&peer->sync[afi][safi]->update)) != NULL) if (adv->binfo->uptime < peer->synctime) return 1; @@ -2036,7 +2035,6 @@ bgp_route_refresh_receive (struct peer *peer, bgp_size_t size) { afi_t afi; safi_t safi; - u_char reserved; struct stream *s; /* If peer does not have the capability, send notification. */ @@ -2064,7 +2062,8 @@ bgp_route_refresh_receive (struct peer *peer, bgp_size_t size) /* Parse packet. */ afi = stream_getw (s); - reserved = stream_getc (s); + /* reserved byte */ + stream_getc (s); safi = stream_getc (s); if (BGP_DEBUG (normal, NORMAL)) @@ -2116,8 +2115,8 @@ bgp_route_refresh_receive (struct peer *peer, bgp_size_t size) if (orf_type == ORF_TYPE_PREFIX || orf_type == ORF_TYPE_PREFIX_OLD) { - u_char *p_pnt = stream_pnt (s); - u_char *p_end = stream_pnt (s) + orf_len; + uint8_t *p_pnt = stream_pnt (s); + uint8_t *p_end = stream_pnt (s) + orf_len; struct orf_prefix orfp; u_char common = 0; u_int32_t seq; @@ -2157,7 +2156,7 @@ bgp_route_refresh_receive (struct peer *peer, bgp_size_t size) prefix_bgp_orf_remove_all (name); break; } - ok = ((p_end - p_pnt) >= sizeof(u_int32_t)) ; + ok = ((size_t)(p_end - p_pnt) >= sizeof(u_int32_t)) ; if (ok) { memcpy (&seq, p_pnt, sizeof (u_int32_t)); @@ -2209,8 +2208,8 @@ bgp_route_refresh_receive (struct peer *peer, bgp_size_t size) ret = prefix_bgp_orf_set (name, afi, &orfp, (common & ORF_COMMON_PART_DENY ? 0 : 1 ), (common & ORF_COMMON_PART_REMOVE ? 0 : 1)); - - if (!ok || (ret != CMD_SUCCESS)) + + if (!ok || (ok && ret != CMD_SUCCESS)) { if (BGP_DEBUG (normal, NORMAL)) zlog_debug ("%s Received misformatted prefixlist ORF." @@ -2246,11 +2245,9 @@ bgp_capability_msg_parse (struct peer *peer, u_char *pnt, bgp_size_t length) struct capability_mp_data mpc; struct capability_header *hdr; u_char action; - struct bgp *bgp; afi_t afi; safi_t safi; - bgp = peer->bgp; end = pnt + length; while (pnt < end) diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 04cbb8ab..e7357e54 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -4525,20 +4525,11 @@ bgp_aggregate_route (struct bgp *bgp, struct prefix *p, struct bgp_info *rinew, struct aspath *asmerge = NULL; struct community *community = NULL; struct community *commerge = NULL; - struct in_addr nexthop; - u_int32_t med = 0; struct bgp_info *ri; struct bgp_info *new; int first = 1; unsigned long match = 0; - /* Record adding route's nexthop and med. */ - if (rinew) - { - nexthop = rinew->attr->nexthop; - med = rinew->attr->med; - } - /* ORIGIN attribute: If at least one route among routes that are aggregated has ORIGIN with the value INCOMPLETE, then the aggregated route must have the ORIGIN attribute with the value @@ -4566,11 +4557,7 @@ bgp_aggregate_route (struct bgp *bgp, struct prefix *p, struct bgp_info *rinew, continue; if (! rinew && first) - { - nexthop = ri->attr->nexthop; - med = ri->attr->med; - first = 0; - } + first = 0; #ifdef AGGREGATE_NEXTHOP_CHECK if (! IPV4_ADDR_SAME (&ri->attr->nexthop, &nexthop) @@ -11773,7 +11760,13 @@ bgp_distance_unset (struct vty *vty, const char *distance_str, } bdistance = rn->info; - + + if (bdistance->distance != distance) + { + vty_out (vty, "Distance does not match configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (bdistance->access_list) free (bdistance->access_list); bgp_distance_free (bdistance); diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index c498f584..06b08592 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -1174,7 +1174,6 @@ route_set_metric (void *rule, struct prefix *prefix, static void * route_set_metric_compile (const char *arg) { - u_int32_t metric; unsigned long larg; char *endptr = NULL; @@ -1185,7 +1184,6 @@ route_set_metric_compile (const char *arg) larg = strtoul (arg, &endptr, 10); if (*endptr != '\0' || errno || larg > UINT32_MAX) return NULL; - metric = larg; } else { @@ -1199,7 +1197,6 @@ route_set_metric_compile (const char *arg) larg = strtoul (arg+1, &endptr, 10); if (*endptr != '\0' || errno || larg > UINT32_MAX) return NULL; - metric = larg; } return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); @@ -1802,22 +1799,21 @@ static route_map_result_t route_match_ipv6_next_hop (void *rule, struct prefix *prefix, route_map_object_t type, void *object) { - struct in6_addr *addr; + struct in6_addr *addr = rule; struct bgp_info *bgp_info; if (type == RMAP_BGP) { - addr = rule; bgp_info = object; if (!bgp_info->attr->extra) return RMAP_NOMATCH; - if (IPV6_ADDR_SAME (&bgp_info->attr->extra->mp_nexthop_global, rule)) + if (IPV6_ADDR_SAME (&bgp_info->attr->extra->mp_nexthop_global, addr)) return RMAP_MATCH; if (bgp_info->attr->extra->mp_nexthop_len == 32 && - IPV6_ADDR_SAME (&bgp_info->attr->extra->mp_nexthop_local, rule)) + IPV6_ADDR_SAME (&bgp_info->attr->extra->mp_nexthop_local, addr)) return RMAP_MATCH; return RMAP_NOMATCH; @@ -3430,7 +3426,7 @@ DEFUN (set_aggregator_as, "IP address of aggregator\n") { int ret; - as_t as; + as_t as __attribute__((unused)); /* dummy for VTY_GET_INTEGER_RANGE */ struct in_addr address; char *argstr; @@ -3464,7 +3460,7 @@ DEFUN (no_set_aggregator_as, "AS number of aggregator\n") { int ret; - as_t as; + as_t as __attribute__((unused)); /* dummy for VTY_GET_INTEGER_RANGE */ struct in_addr address; char *argstr; diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index a818fe7a..ca44774a 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -576,7 +576,7 @@ DEFUN (no_bgp_confederation_identifier, "AS number\n") { struct bgp *bgp; - as_t as; + as_t as __attribute__((unused)); /* Dummy for VTY_GET_INTEGER_RANGE */ bgp = vty->index; @@ -3205,7 +3205,6 @@ static int peer_weight_set_vty (struct vty *vty, const char *ip_str, const char *weight_str) { - int ret; struct peer *peer; unsigned long weight; @@ -3215,9 +3214,7 @@ peer_weight_set_vty (struct vty *vty, const char *ip_str, VTY_GET_INTEGER_RANGE("weight", weight, weight_str, 0, 65535); - ret = peer_weight_set (peer, weight); - - return CMD_SUCCESS; + return bgp_vty_return (vty, peer_weight_set (peer, weight)); } static int @@ -3229,9 +3226,7 @@ peer_weight_unset_vty (struct vty *vty, const char *ip_str) if (! peer) return CMD_WARNING; - peer_weight_unset (peer); - - return CMD_SUCCESS; + return bgp_vty_return (vty, peer_weight_unset (peer)); } DEFUN (neighbor_weight, @@ -3371,7 +3366,6 @@ static int peer_timers_connect_set_vty (struct vty *vty, const char *ip_str, const char *time_str) { - int ret; struct peer *peer; u_int32_t connect; @@ -3381,24 +3375,19 @@ peer_timers_connect_set_vty (struct vty *vty, const char *ip_str, VTY_GET_INTEGER_RANGE ("Connect time", connect, time_str, 0, 65535); - ret = peer_timers_connect_set (peer, connect); - - return CMD_SUCCESS; + return bgp_vty_return (vty, peer_timers_connect_set (peer, connect)); } static int peer_timers_connect_unset_vty (struct vty *vty, const char *ip_str) { - int ret; struct peer *peer; peer = peer_and_group_lookup_vty (vty, ip_str); if (! peer) return CMD_WARNING; - ret = peer_timers_connect_unset (peer); - - return CMD_SUCCESS; + return bgp_vty_return (vty, peer_timers_connect_unset (peer)); } DEFUN (neighbor_timers_connect, @@ -3455,7 +3444,7 @@ peer_advertise_interval_vty (struct vty *vty, const char *ip_str, else ret = peer_advertise_interval_unset (peer); - return CMD_SUCCESS; + return bgp_vty_return (vty, ret); } DEFUN (neighbor_advertise_interval, @@ -3505,7 +3494,7 @@ peer_interface_vty (struct vty *vty, const char *ip_str, const char *str) else ret = peer_interface_unset (peer); - return CMD_SUCCESS; + return bgp_vty_return (vty, ret); } DEFUN (neighbor_interface, diff --git a/lib/command.c b/lib/command.c index 1087ceb8..d1af7fa2 100644 --- a/lib/command.c +++ b/lib/command.c @@ -1411,7 +1411,7 @@ cmd_matcher_read_keywords(struct cmd_matcher *matcher, const char *word; int keyword_argc; const char **keyword_argv; - enum matcher_rv rv; + enum matcher_rv rv = MATCHER_NO_MATCH; keyword_mask = 0; while (1) @@ -1642,7 +1642,7 @@ cmd_element_match(struct cmd_element *cmd_element, { struct cmd_matcher matcher; unsigned int token_index; - enum matcher_rv rv; + enum matcher_rv rv = MATCHER_NO_MATCH; cmd_matcher_init(&matcher, cmd_element, filter, vline, index, match_type, match); diff --git a/lib/if.c b/lib/if.c index 18e2fb31..3a1f9b41 100644 --- a/lib/if.c +++ b/lib/if.c @@ -309,8 +309,6 @@ struct interface * if_lookup_prefix (struct prefix *prefix) { struct listnode *node; - struct prefix addr; - int bestlen = 0; struct listnode *cnode; struct interface *ifp; struct connected *c; @@ -453,7 +451,7 @@ static void if_dump (const struct interface *ifp) { struct listnode *node; - struct connected *c; + struct connected *c __attribute__((unused)); for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, c)) zlog_info ("Interface %s index %d metric %d mtu %d " diff --git a/lib/jhash.c b/lib/jhash.c index 071fed6e..6154c346 100644 --- a/lib/jhash.c +++ b/lib/jhash.c @@ -42,10 +42,10 @@ * the input key. */ u_int32_t -jhash (void *key, u_int32_t length, u_int32_t initval) +jhash (const void *key, u_int32_t length, u_int32_t initval) { u_int32_t a, b, c, len; - u_int8_t *k = key; + const u_int8_t *k = key; len = length; a = b = JHASH_GOLDEN_RATIO; @@ -105,7 +105,7 @@ jhash (void *key, u_int32_t length, u_int32_t initval) * The length parameter here is the number of u_int32_ts in the key. */ u_int32_t -jhash2 (u_int32_t * k, u_int32_t length, u_int32_t initval) +jhash2 (const u_int32_t *k, u_int32_t length, u_int32_t initval) { u_int32_t a, b, c, len; diff --git a/lib/jhash.h b/lib/jhash.h index 44dd1b56..985ac94e 100644 --- a/lib/jhash.h +++ b/lib/jhash.h @@ -24,12 +24,12 @@ * of bytes. No alignment or length assumptions are made about * the input key. */ -extern u_int32_t jhash(void *key, u_int32_t length, u_int32_t initval); +extern u_int32_t jhash(const void *key, u_int32_t length, u_int32_t initval); /* A special optimized version that handles 1 or more of u_int32_ts. * The length parameter here is the number of u_int32_ts in the key. */ -extern u_int32_t jhash2(u_int32_t *k, u_int32_t length, u_int32_t initval); +extern u_int32_t jhash2(const u_int32_t *k, u_int32_t length, u_int32_t initval); /* A special ultra-optimized versions that knows they are hashing exactly * 3, 2 or 1 word(s). diff --git a/lib/libospf.h b/lib/libospf.h index 856c76df..2796209d 100644 --- a/lib/libospf.h +++ b/lib/libospf.h @@ -47,8 +47,8 @@ #define OSPF_LSA_MAXAGE_DIFF 900 #define OSPF_LS_INFINITY 0xffffff #define OSPF_DEFAULT_DESTINATION 0x00000000 /* 0.0.0.0 */ -#define OSPF_INITIAL_SEQUENCE_NUMBER 0x80000001 -#define OSPF_MAX_SEQUENCE_NUMBER 0x7fffffff +#define OSPF_INITIAL_SEQUENCE_NUMBER 0x80000001U +#define OSPF_MAX_SEQUENCE_NUMBER 0x7fffffffU /* OSPF Interface Types */ #define OSPF_IFTYPE_NONE 0 diff --git a/lib/md5.c b/lib/md5.c index 2fc36e17..ce459bbe 100644 --- a/lib/md5.c +++ b/lib/md5.c @@ -306,7 +306,7 @@ unsigned char* text; /* pointer to data stream */ int text_len; /* length of data stream */ unsigned char* key; /* pointer to authentication key */ int key_len; /* length of authentication key */ -caddr_t digest; /* caller digest to be filled in */ +uint8_t * digest; /* caller digest to be filled in */ { MD5_CTX context; diff --git a/lib/md5.h b/lib/md5.h index a03bf22a..4e5ffbd9 100644 --- a/lib/md5.h +++ b/lib/md5.h @@ -84,6 +84,6 @@ do { \ /* From RFC 2104 */ void hmac_md5(unsigned char* text, int text_len, unsigned char* key, - int key_len, caddr_t digest); + int key_len, uint8_t *digest); #endif /* ! _LIBZEBRA_MD5_H_*/ diff --git a/lib/table.c b/lib/table.c index 220e9b81..bd7023c1 100644 --- a/lib/table.c +++ b/lib/table.c @@ -626,11 +626,8 @@ route_table_get_next_internal (const struct route_table *table, struct prefix *p) { struct route_node *node, *tmp_node; - u_char prefixlen; int cmp; - prefixlen = p->prefixlen; - node = table->top; while (node) diff --git a/lib/vty.c b/lib/vty.c index 488f8d5f..3f08b9e9 100644 --- a/lib/vty.c +++ b/lib/vty.c @@ -2458,7 +2458,7 @@ vty_log (const char *level, const char *proto_str, /* Async-signal-safe version of vty_log for fixed strings. */ void -vty_log_fixed (const char *buf, size_t len) +vty_log_fixed (char *buf, size_t len) { unsigned int i; struct iovec iov[2]; @@ -2467,7 +2467,7 @@ vty_log_fixed (const char *buf, size_t len) if (!vtyvec) return; - iov[0].iov_base = (void *)buf; + iov[0].iov_base = buf; iov[0].iov_len = len; iov[1].iov_base = (void *)"\r\n"; iov[1].iov_len = 2; diff --git a/lib/vty.h b/lib/vty.h index 1798585e..4d6048c9 100644 --- a/lib/vty.h +++ b/lib/vty.h @@ -242,6 +242,6 @@ extern void vty_hello (struct vty *); /* Send a fixed-size message to all vty terminal monitors; this should be an async-signal-safe function. */ -extern void vty_log_fixed (const char *buf, size_t len); +extern void vty_log_fixed (char *buf, size_t len); #endif /* _ZEBRA_VTY_H */ diff --git a/lib/workqueue.c b/lib/workqueue.c index 61643bf8..e09d009f 100644 --- a/lib/workqueue.c +++ b/lib/workqueue.c @@ -30,7 +30,11 @@ #include "log.h" /* master list of work_queues */ -static struct list work_queues; +static struct list _work_queues; +/* pointer primarily to avoid an otherwise harmless warning on + * ALL_LIST_ELEMENTS_RO + */ +static struct list *work_queues = &_work_queues; #define WORK_QUEUE_MIN_GRANULARITY 1 @@ -78,7 +82,7 @@ work_queue_new (struct thread_master *m, const char *queue_name) new->items->del = (void (*)(void *)) work_queue_item_free; - listnode_add (&work_queues, new); + listnode_add (work_queues, new); new->cycles.granularity = WORK_QUEUE_MIN_GRANULARITY; @@ -96,7 +100,7 @@ work_queue_free (struct work_queue *wq) /* list_delete frees items via callback */ list_delete (wq->items); - listnode_delete (&work_queues, wq); + listnode_delete (work_queues, wq); XFREE (MTYPE_WORK_QUEUE_NAME, wq->name); XFREE (MTYPE_WORK_QUEUE, wq); @@ -187,7 +191,7 @@ DEFUN(show_work_queues, "Name", VTY_NEWLINE); - for (ALL_LIST_ELEMENTS_RO ((&work_queues), node, wq)) + for (ALL_LIST_ELEMENTS_RO (work_queues, node, wq)) { vty_out (vty,"%c %8d %5d %8ld %7d %6d %6u %s%s", (CHECK_FLAG (wq->flags, WQ_UNPLUGGED) ? ' ' : 'P'), diff --git a/ospf6d/ospf6_abr.h b/ospf6d/ospf6_abr.h index 816f5964..e5e2660b 100644 --- a/ospf6d/ospf6_abr.h +++ b/ospf6d/ospf6_abr.h @@ -24,6 +24,8 @@ /* for struct ospf6_route */ #include "ospf6_route.h" +/* for struct ospf6_prefix */ +#include "ospf6_proto.h" /* Debug option */ extern unsigned char conf_debug_ospf6_abr; diff --git a/ospf6d/ospf6_area.c b/ospf6d/ospf6_area.c index 4b4ca130..9b704221 100644 --- a/ospf6d/ospf6_area.c +++ b/ospf6d/ospf6_area.c @@ -518,13 +518,11 @@ DEFUN (no_area_filter_list, "Filter networks sent from this area\n") { struct ospf6_area *area; - struct prefix_list *plist; OSPF6_CMD_AREA_GET (argv[0], area); argc--; argv++; - plist = prefix_list_lookup (AFI_IP6, argv[0]); if (strncmp (argv[1], "in", 2) == 0) { if (PREFIX_NAME_IN (area)) diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index 6ba6cdf6..b1620d4a 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -58,18 +58,13 @@ ospf6_as_external_lsa_originate (struct ospf6_route *route) { char buffer[OSPF6_MAX_LSASIZE]; struct ospf6_lsa_header *lsa_header; - struct ospf6_lsa *old, *lsa; + struct ospf6_lsa *lsa; struct ospf6_external_info *info = route->route_option; struct ospf6_as_external_lsa *as_external_lsa; char buf[64]; caddr_t p; - /* find previous LSA */ - old = ospf6_lsdb_lookup (htons (OSPF6_LSTYPE_AS_EXTERNAL), - route->path.origin.id, ospf6->router_id, - ospf6->lsdb); - if (IS_OSPF6_DEBUG_ASBR || IS_OSPF6_DEBUG_ORIGINATE (AS_EXTERNAL)) { prefix2str (&route->prefix, buf, sizeof (buf)); diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index f0ef7909..772caff7 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -1283,7 +1283,6 @@ DEFUN (no_ipv6_ospf6_cost, { struct ospf6_interface *oi; struct interface *ifp; - unsigned long int lcost; ifp = (struct interface *) vty->index; assert (ifp); diff --git a/ospf6d/ospf6_lsa.c b/ospf6d/ospf6_lsa.c index 2e615355..e57306bc 100644 --- a/ospf6d/ospf6_lsa.c +++ b/ospf6d/ospf6_lsa.c @@ -493,7 +493,7 @@ ospf6_lsa_show_internal (struct vty *vty, struct ospf6_lsa *lsa) vty_out (vty, "Flag: %x %s", lsa->flag, VNL); vty_out (vty, "Lock: %d %s", lsa->lock, VNL); vty_out (vty, "ReTx Count: %d%s", lsa->retrans_count, VNL); - vty_out (vty, "Threads: Expire: %x, Refresh: %x %s", + vty_out (vty, "Threads: Expire: 0x%p, Refresh: 0x%p %s", lsa->expire, lsa->refresh, VNL); vty_out (vty, "%s", VNL); return; diff --git a/ospf6d/ospf6_neighbor.c b/ospf6d/ospf6_neighbor.c index 7f6c6c5c..f20c83b9 100644 --- a/ospf6d/ospf6_neighbor.c +++ b/ospf6d/ospf6_neighbor.c @@ -46,6 +46,31 @@ const char *ospf6_neighbor_state_str[] = { "None", "Down", "Attempt", "Init", "Twoway", "ExStart", "ExChange", "Loading", "Full", NULL }; +static const char *ospf6_neighbor_event_str[] = + { + "NoEvent", + "HelloReceived", + "2-WayReceived", + "NegotiationDone", + "ExchangeDone", + "LoadingDone", + "AdjOK?", + "SeqNumberMismatch", + "BadLSReq", + "1-WayReceived", + "InactivityTimer", + }; + +static const char * +ospf6_neighbor_event_string (int event) +{ + #define OSPF6_NEIGHBOR_UNKNOWN_EVENT_STRING "UnknownEvent" + + if (event < OSPF6_NEIGHBOR_EVENT_MAX_EVENT) + return ospf6_neighbor_event_str[event]; + return OSPF6_NEIGHBOR_UNKNOWN_EVENT_STRING; +} + int ospf6_neighbor_cmp (void *va, void *vb) { diff --git a/ospf6d/ospf6_neighbor.h b/ospf6d/ospf6_neighbor.h index 65c43fd2..93ffa289 100644 --- a/ospf6d/ospf6_neighbor.h +++ b/ospf6d/ospf6_neighbor.h @@ -122,33 +122,8 @@ struct ospf6_neighbor #define OSPF6_NEIGHBOR_EVENT_INACTIVITY_TIMER 10 #define OSPF6_NEIGHBOR_EVENT_MAX_EVENT 11 -static const char *ospf6_neighbor_event_str[] = - { - "NoEvent", - "HelloReceived", - "2-WayReceived", - "NegotiationDone", - "ExchangeDone", - "LoadingDone", - "AdjOK?", - "SeqNumberMismatch", - "BadLSReq", - "1-WayReceived", - "InactivityTimer", - }; - -static const char *ospf6_neighbor_event_string (int event) -{ - #define OSPF6_NEIGHBOR_UNKNOWN_EVENT_STRING "UnknownEvent" - - if (event < OSPF6_NEIGHBOR_EVENT_MAX_EVENT) - return ospf6_neighbor_event_str[event]; - return OSPF6_NEIGHBOR_UNKNOWN_EVENT_STRING; -} - extern const char *ospf6_neighbor_state_str[]; - /* Function Prototypes */ int ospf6_neighbor_cmp (void *va, void *vb); void ospf6_neighbor_dbex_init (struct ospf6_neighbor *on); diff --git a/ospf6d/ospf6_network.c b/ospf6d/ospf6_network.c index 74cfbec7..e0be38b3 100644 --- a/ospf6d/ospf6_network.c +++ b/ospf6d/ospf6_network.c @@ -37,18 +37,8 @@ int ospf6_sock; struct in6_addr allspfrouters6; struct in6_addr alldrouters6; -/* setsockopt ReUseAddr to on */ -void -ospf6_set_reuseaddr (void) -{ - u_int on = 0; - if (setsockopt (ospf6_sock, SOL_SOCKET, SO_REUSEADDR, &on, - sizeof (u_int)) < 0) - zlog_warn ("Network: set SO_REUSEADDR failed: %s", safe_strerror (errno)); -} - /* setsockopt MulticastLoop to off */ -void +static void ospf6_reset_mcastloop (void) { u_int off = 0; @@ -58,13 +48,13 @@ ospf6_reset_mcastloop (void) safe_strerror (errno)); } -void +static void ospf6_set_pktinfo (void) { setsockopt_ipv6_pktinfo (ospf6_sock, 1); } -void +static void ospf6_set_transport_class (void) { #ifdef IPTOS_PREC_INTERNETCONTROL @@ -72,7 +62,7 @@ ospf6_set_transport_class (void) #endif } -void +static void ospf6_set_checksum (void) { int offset = 12; diff --git a/ospf6d/ospf6_network.h b/ospf6d/ospf6_network.h index 947834d5..127bf45c 100644 --- a/ospf6d/ospf6_network.h +++ b/ospf6d/ospf6_network.h @@ -28,12 +28,6 @@ extern int ospf6_sock; extern struct in6_addr allspfrouters6; extern struct in6_addr alldrouters6; -/* Function Prototypes */ -extern void ospf6_set_reuseaddr (void); -extern void ospf6_reset_mcastloop (void); -extern void ospf6_set_pktinfo (void); -extern void ospf6_set_checksum (void); - extern int ospf6_serv_sock (void); extern void ospf6_sso (u_int ifindex, struct in6_addr *group, int option); diff --git a/ospf6d/ospf6_route.h b/ospf6d/ospf6_route.h index b384824c..c0dcf9f1 100644 --- a/ospf6d/ospf6_route.h +++ b/ospf6d/ospf6_route.h @@ -122,6 +122,10 @@ struct ospf6_route /* Destination Type */ u_char type; + /* XXX: It would likely be better to use separate struct in_addr's + * for the advertising router-ID and prefix IDs, instead of stuffing them + * into one. See also XXX below. + */ /* Destination ID */ struct prefix prefix; @@ -235,6 +239,7 @@ extern const char *ospf6_path_type_substr[OSPF6_PATH_TYPE_MAX]; sizeof (struct ospf6_nexthop) * OSPF6_MULTI_PATH_LIMIT) == 0) #define ospf6_route_is_best(r) (CHECK_FLAG ((r)->flag, OSPF6_ROUTE_BEST)) +/* XXX: This gives GCC heartburn aboutbreaking aliasing rules. */ #define ospf6_linkstate_prefix_adv_router(x) \ (*(u_int32_t *)(&(x)->u.prefix6.s6_addr[0])) #define ospf6_linkstate_prefix_id(x) \ diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c index 3ef5485f..d0e9101b 100644 --- a/ospf6d/ospf6_spf.c +++ b/ospf6d/ospf6_spf.c @@ -40,6 +40,7 @@ #include "ospf6_intra.h" #include "ospf6_interface.h" #include "ospf6d.h" +#include "ospf6_abr.h" unsigned char conf_debug_ospf6_spf = 0; @@ -391,7 +392,7 @@ static const char *ospf6_spf_reason_str[] = void ospf6_spf_reason_string (unsigned int reason, char *buf, int size) { - int bit; + size_t bit; int len = 0; if (!buf) diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c index 71912701..e4e6f17a 100644 --- a/ospf6d/ospf6_top.c +++ b/ospf6d/ospf6_top.c @@ -180,6 +180,7 @@ ospf6_delete (struct ospf6 *o) } static void +__attribute__((unused)) ospf6_enable (struct ospf6 *o) { struct listnode *node, *nnode; @@ -219,7 +220,7 @@ ospf6_disable (struct ospf6 *o) } } -int +static int ospf6_maxage_remover (struct thread *thread) { struct ospf6 *o = (struct ospf6 *) THREAD_ARG (thread); @@ -471,14 +472,11 @@ DEFUN (no_ospf6_interface_area, "OSPF6 area ID in IPv4 address notation\n" ) { - struct ospf6 *o; struct ospf6_interface *oi; struct ospf6_area *oa; struct interface *ifp; u_int32_t area_id; - o = (struct ospf6 *) vty->index; - ifp = if_lookup_by_name (argv[0]); if (ifp == NULL) { diff --git a/ospfd/ospf_api.c b/ospfd/ospf_api.c index fae942ec..cbb234ae 100644 --- a/ospfd/ospf_api.c +++ b/ospfd/ospf_api.c @@ -464,7 +464,7 @@ new_msg_register_event (u_int32_t seqnum, struct lsa_filter_type *filter) { u_char buf[OSPF_API_MAX_MSG_SIZE]; struct msg_register_event *emsg; - int len; + size_t len; emsg = (struct msg_register_event *) buf; len = sizeof (struct msg_register_event) + @@ -483,7 +483,7 @@ new_msg_sync_lsdb (u_int32_t seqnum, struct lsa_filter_type *filter) { u_char buf[OSPF_API_MAX_MSG_SIZE]; struct msg_sync_lsdb *smsg; - int len; + size_t len; smsg = (struct msg_sync_lsdb *) buf; len = sizeof (struct msg_sync_lsdb) + @@ -504,7 +504,7 @@ new_msg_originate_request (u_int32_t seqnum, struct in_addr area_id, struct lsa_header *data) { struct msg_originate_request *omsg; - int omsglen; + size_t omsglen; char buf[OSPF_API_MAX_MSG_SIZE]; omsg = (struct msg_originate_request *) buf; @@ -630,7 +630,7 @@ new_msg_lsa_change_notify (u_char msgtype, { u_char buf[OSPF_API_MAX_MSG_SIZE]; struct msg_lsa_change_notify *nmsg; - int len; + size_t len; assert (data); diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 8bfcaa82..97fcffd1 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -4034,7 +4034,6 @@ static void show_ip_ospf_database_maxage (struct vty *vty, struct ospf *ospf) { struct route_node *rn; - struct ospf_lsa *lsa; vty_out (vty, "%s MaxAge Link States:%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); diff --git a/ripd/ripd.c b/ripd/ripd.c index c69ef7fc..9d355ecb 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -1737,16 +1737,6 @@ rip_request_process (struct rip_packet *packet, int size, ntohs (rte->family) == 0 && ntohl (rte->metric) == RIP_METRIC_INFINITY) { - struct prefix_ipv4 saddr; - - /* saddr will be used for determining which routes to split-horizon. - Since the source address we'll pick will be on the same subnet as the - destination, for the purpose of split-horizoning, we'll - pretend that "from" is our source address. */ - saddr.family = AF_INET; - saddr.prefixlen = IPV4_MAX_BITLEN; - saddr.prefix = from->sin_addr; - /* All route with split horizon */ rip_output_process (ifc, from, rip_all_route, packet->version); } @@ -3262,7 +3252,6 @@ rip_distance_unset (struct vty *vty, const char *distance_str, { int ret; struct prefix_ipv4 p; - u_char distance; struct route_node *rn; struct rip_distance *rdistance; @@ -3273,8 +3262,6 @@ rip_distance_unset (struct vty *vty, const char *distance_str, return CMD_WARNING; } - distance = atoi (distance_str); - rn = route_node_lookup (rip_distance_table, (struct prefix *)&p); if (! rn) { diff --git a/zebra/irdp_interface.c b/zebra/irdp_interface.c index 6403830b..43c63a83 100644 --- a/zebra/irdp_interface.c +++ b/zebra/irdp_interface.c @@ -638,7 +638,6 @@ DEFUN (no_ip_irdp_address_preference, { struct listnode *node, *nnode; struct in_addr ip; - int pref; int ret; struct interface *ifp; struct zebra_if *zi; @@ -657,8 +656,6 @@ DEFUN (no_ip_irdp_address_preference, if (!ret) return CMD_WARNING; - pref = atoi(argv[1]); - for (ALL_LIST_ELEMENTS (irdp->AdvPrefList, node, nnode, adv)) { if(adv->ip.s_addr == ip.s_addr ) diff --git a/zebra/router-id.c b/zebra/router-id.c index b738027e..94a29419 100644 --- a/zebra/router-id.c +++ b/zebra/router-id.c @@ -41,8 +41,13 @@ #include "zebra/router-id.h" #include "zebra/redistribute.h" -static struct list rid_all_sorted_list; -static struct list rid_lo_sorted_list; +/* 2nd pointer type used primarily to quell a warning on + * ALL_LIST_ELEMENTS_RO + */ +static struct list _rid_all_sorted_list; +static struct list _rid_lo_sorted_list; +static struct list *rid_all_sorted_list = &_rid_all_sorted_list; +static struct list *rid_lo_sorted_list = &_rid_lo_sorted_list; static struct prefix rid_user_assigned; /* master zebra server structure */ @@ -86,15 +91,15 @@ router_id_get (struct prefix *p) if (rid_user_assigned.u.prefix4.s_addr) p->u.prefix4.s_addr = rid_user_assigned.u.prefix4.s_addr; - else if (!list_isempty (&rid_lo_sorted_list)) + else if (!list_isempty (rid_lo_sorted_list)) { - node = listtail (&rid_lo_sorted_list); + node = listtail (rid_lo_sorted_list); c = listgetdata (node); p->u.prefix4.s_addr = c->address->u.prefix4.s_addr; } - else if (!list_isempty (&rid_all_sorted_list)) + else if (!list_isempty (rid_all_sorted_list)) { - node = listtail (&rid_all_sorted_list); + node = listtail (rid_all_sorted_list); c = listgetdata (node); p->u.prefix4.s_addr = c->address->u.prefix4.s_addr; } @@ -131,9 +136,9 @@ router_id_add_address (struct connected *ifc) if (!strncmp (ifc->ifp->name, "lo", 2) || !strncmp (ifc->ifp->name, "dummy", 5)) - l = &rid_lo_sorted_list; + l = rid_lo_sorted_list; else - l = &rid_all_sorted_list; + l = rid_all_sorted_list; if (!router_id_find_node (l, ifc)) listnode_add_sort (l, ifc); @@ -164,9 +169,9 @@ router_id_del_address (struct connected *ifc) if (!strncmp (ifc->ifp->name, "lo", 2) || !strncmp (ifc->ifp->name, "dummy", 5)) - l = &rid_lo_sorted_list; + l = rid_lo_sorted_list; else - l = &rid_all_sorted_list; + l = rid_all_sorted_list; if ((c = router_id_find_node (l, ifc))) listnode_delete (l, c); @@ -240,12 +245,12 @@ router_id_init (void) install_element (CONFIG_NODE, &router_id_cmd); install_element (CONFIG_NODE, &no_router_id_cmd); - memset (&rid_all_sorted_list, 0, sizeof (rid_all_sorted_list)); - memset (&rid_lo_sorted_list, 0, sizeof (rid_lo_sorted_list)); + memset (rid_all_sorted_list, 0, sizeof (rid_all_sorted_list)); + memset (rid_lo_sorted_list, 0, sizeof (rid_lo_sorted_list)); memset (&rid_user_assigned, 0, sizeof (rid_user_assigned)); - rid_all_sorted_list.cmp = router_id_cmp; - rid_lo_sorted_list.cmp = router_id_cmp; + rid_all_sorted_list->cmp = router_id_cmp; + rid_lo_sorted_list->cmp = router_id_cmp; rid_user_assigned.family = AF_INET; rid_user_assigned.prefixlen = 32; diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index f3cdcdc3..12dbd1ad 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -1246,9 +1246,9 @@ netlink_route_read (void) /* Utility function comes from iproute2. Authors: Alexey Kuznetsov, */ int -addattr_l (struct nlmsghdr *n, int maxlen, int type, void *data, int alen) +addattr_l (struct nlmsghdr *n, size_t maxlen, int type, void *data, int alen) { - int len; + size_t len; struct rtattr *rta; len = RTA_LENGTH (alen); @@ -1288,9 +1288,9 @@ rta_addattr_l (struct rtattr *rta, int maxlen, int type, void *data, int alen) /* Utility function comes from iproute2. Authors: Alexey Kuznetsov, */ int -addattr32 (struct nlmsghdr *n, int maxlen, int type, int data) +addattr32 (struct nlmsghdr *n, size_t maxlen, int type, int data) { - int len; + size_t len; struct rtattr *rta; len = RTA_LENGTH (4); diff --git a/zebra/rt_netlink.h b/zebra/rt_netlink.h index 452b3974..0facd49e 100644 --- a/zebra/rt_netlink.h +++ b/zebra/rt_netlink.h @@ -27,9 +27,9 @@ #define NL_PKT_BUF_SIZE 8192 extern int -addattr32 (struct nlmsghdr *n, int maxlen, int type, int data); +addattr32 (struct nlmsghdr *n, size_t maxlen, int type, int data); extern int -addattr_l (struct nlmsghdr *n, int maxlen, int type, void *data, int alen); +addattr_l (struct nlmsghdr *n, size_t maxlen, int type, void *data, int alen); extern int rta_addattr_l (struct rtattr *rta, int maxlen, int type, void *data, int alen); From b2dd59ee0e74926278e128846624f5c93288223b Mon Sep 17 00:00:00 2001 From: Paul Jakma Date: Fri, 19 Sep 2014 15:34:48 +0100 Subject: [PATCH 248/482] bgpd.c: Remove unused store to variable --- bgpd/bgpd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 4d374cc1..3a9bc480 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -1028,9 +1028,9 @@ peer_remote_as (struct bgp *bgp, union sockunion *su, as_t *as, if (bgp_flag_check (bgp, BGP_FLAG_NO_DEFAULT_IPV4) && afi == AFI_IP && safi == SAFI_UNICAST) - peer = peer_create (su, bgp, local_as, *as, 0, 0); + peer_create (su, bgp, local_as, *as, 0, 0); else - peer = peer_create (su, bgp, local_as, *as, afi, safi); + peer_create (su, bgp, local_as, *as, afi, safi); } return 0; From 7bef33cbf5027189bd55e4890a07a6bef8277f93 Mon Sep 17 00:00:00 2001 From: Paul Jakma Date: Fri, 19 Sep 2014 15:35:15 +0100 Subject: [PATCH 249/482] ospf6_lsdb: trivial, make it clear that showfunc is set before deref. --- ospf6d/ospf6_lsdb.c | 27 +++++++++++++++++---------- ospf6d/ospf6_lsdb.h | 13 ++++++++----- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/ospf6d/ospf6_lsdb.c b/ospf6d/ospf6_lsdb.c index 707afc67..b5a4587c 100644 --- a/ospf6d/ospf6_lsdb.c +++ b/ospf6d/ospf6_lsdb.c @@ -485,22 +485,29 @@ ospf6_lsdb_maxage_remover (struct ospf6_lsdb *lsdb) } void -ospf6_lsdb_show (struct vty *vty, int level, +ospf6_lsdb_show (struct vty *vty, enum ospf_lsdb_show_level level, u_int16_t *type, u_int32_t *id, u_int32_t *adv_router, struct ospf6_lsdb *lsdb) { struct ospf6_lsa *lsa; void (*showfunc) (struct vty *, struct ospf6_lsa *) = NULL; - if (level == OSPF6_LSDB_SHOW_LEVEL_NORMAL) - showfunc = ospf6_lsa_show_summary; - else if (level == OSPF6_LSDB_SHOW_LEVEL_DETAIL) - showfunc = ospf6_lsa_show; - else if (level == OSPF6_LSDB_SHOW_LEVEL_INTERNAL) - showfunc = ospf6_lsa_show_internal; - else if (level == OSPF6_LSDB_SHOW_LEVEL_DUMP) - showfunc = ospf6_lsa_show_dump; - + switch (level) + { + case OSPF6_LSDB_SHOW_LEVEL_DETAIL: + showfunc = ospf6_lsa_show; + break; + case OSPF6_LSDB_SHOW_LEVEL_INTERNAL: + showfunc = ospf6_lsa_show_internal; + break; + case OSPF6_LSDB_SHOW_LEVEL_DUMP: + showfunc = ospf6_lsa_show_dump; + break; + case OSPF6_LSDB_SHOW_LEVEL_NORMAL: + default: + showfunc = ospf6_lsa_show_summary; + } + if (type && id && adv_router) { lsa = ospf6_lsdb_lookup (*type, *id, *adv_router, lsdb); diff --git a/ospf6d/ospf6_lsdb.h b/ospf6d/ospf6_lsdb.h index a124adbf..425f6153 100644 --- a/ospf6d/ospf6_lsdb.h +++ b/ospf6d/ospf6_lsdb.h @@ -66,12 +66,15 @@ extern struct ospf6_lsa *ospf6_lsdb_type_next (u_int16_t type, extern void ospf6_lsdb_remove_all (struct ospf6_lsdb *lsdb); extern void ospf6_lsdb_lsa_unlock (struct ospf6_lsa *lsa); -#define OSPF6_LSDB_SHOW_LEVEL_NORMAL 0 -#define OSPF6_LSDB_SHOW_LEVEL_DETAIL 1 -#define OSPF6_LSDB_SHOW_LEVEL_INTERNAL 2 -#define OSPF6_LSDB_SHOW_LEVEL_DUMP 3 +enum ospf_lsdb_show_level { + OSPF6_LSDB_SHOW_LEVEL_NORMAL = 0, + OSPF6_LSDB_SHOW_LEVEL_DETAIL, + OSPF6_LSDB_SHOW_LEVEL_INTERNAL, + OSPF6_LSDB_SHOW_LEVEL_DUMP, +}; -extern void ospf6_lsdb_show (struct vty *vty, int level, u_int16_t *type, +extern void ospf6_lsdb_show (struct vty *vty, + enum ospf_lsdb_show_level level, u_int16_t *type, u_int32_t *id, u_int32_t *adv_router, struct ospf6_lsdb *lsdb); From 16f1606382b77ac6b951ea0de15384fcbc1df73f Mon Sep 17 00:00:00 2001 From: Paul Jakma Date: Fri, 19 Sep 2014 15:35:54 +0100 Subject: [PATCH 250/482] ripng_nexthop: remove unused store to variable --- ripngd/ripng_nexthop.c | 1 - 1 file changed, 1 deletion(-) diff --git a/ripngd/ripng_nexthop.c b/ripngd/ripng_nexthop.c index 05f190e2..b966af00 100644 --- a/ripngd/ripng_nexthop.c +++ b/ripngd/ripng_nexthop.c @@ -209,7 +209,6 @@ ripng_rte_send(struct list *ripng_rte_list, struct interface *ifp, if (ret >= 0 && IS_RIPNG_DEBUG_SEND) ripng_packet_dump ((struct ripng_packet *)STREAM_DATA (s), stream_get_endp (s), "SEND"); - num = 0; stream_reset (s); } } From 18f420e9f99e7f6557cf5877673cd6e71ac32192 Mon Sep 17 00:00:00 2001 From: Paul Jakma Date: Fri, 19 Sep 2014 16:55:46 +0100 Subject: [PATCH 251/482] lib/plist: Add some required parentheses, according to clang-analyzer --- lib/plist.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/plist.c b/lib/plist.c index 7416ebd2..61075560 100644 --- a/lib/plist.c +++ b/lib/plist.c @@ -733,16 +733,16 @@ vty_prefix_list_install (struct vty *vty, afi_t afi, const char *name, #endif /* HAVE_IPV6 */ /* ge and le check. */ - if (genum && genum <= p.prefixlen) + if (genum && (genum <= p.prefixlen)) return vty_invalid_prefix_range (vty, prefix); - if (lenum && lenum <= p.prefixlen) + if (lenum && (lenum <= p.prefixlen)) return vty_invalid_prefix_range (vty, prefix); - if (lenum && genum > lenum) + if (lenum && (genum > lenum)) return vty_invalid_prefix_range (vty, prefix); - if (genum && lenum == (afi == AFI_IP ? 32 : 128)) + if (genum && (lenum == (afi == AFI_IP ? 32 : 128))) lenum = 0; /* Get prefix_list with name. */ From b166ea2dda9f04a8b75e0bf5adb7064580695f22 Mon Sep 17 00:00:00 2001 From: Joakim Tjernlund Date: Thu, 25 Jun 2009 16:40:06 +0100 Subject: [PATCH 252/482] [lib] Add support for backtrace on more platforms * lib/sigevent.c: (program_counter) extend to support more platforms. Joint effort with Paul Jakma. --- configure.ac | 12 ++++++++++++ lib/sigevent.c | 32 ++++++++++++++++++++++++++++---- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index f1df482b..9d188284 100755 --- a/configure.ac +++ b/configure.ac @@ -533,6 +533,18 @@ AC_CHECK_HEADERS([ucontext.h], [], [], QUAGGA_INCLUDES ]) +m4_define([UCONTEXT_INCLUDES], +[#include ])dnl + +AC_CHECK_MEMBERS([ucontext_t.uc_mcontext.uc_regs], + [], [], [UCONTEXT_INCLUDES]) +AC_CHECK_MEMBERS([ucontext_t.uc_mcontext.regs], + [AC_CHECK_MEMBERS([ucontext_t.uc_mcontext.regs.nip], + [], [], [UCONTEXT_INCLUDES])], + [], [UCONTEXT_INCLUDES]) +AC_CHECK_MEMBERS([ucontext_t.uc_mcontext.gregs], + [], [], [UCONTEXT_INCLUDES]) + m4_define([QUAGGA_INCLUDES], QUAGGA_INCLUDES [#if HAVE_SYS_UN_H diff --git a/lib/sigevent.c b/lib/sigevent.c index 7d08fd97..c80a7290 100644 --- a/lib/sigevent.c +++ b/lib/sigevent.c @@ -175,11 +175,35 @@ program_counter(void *context) { #ifdef HAVE_UCONTEXT_H #ifdef GNU_LINUX -#ifdef REG_EIP - if (context) - return (void *)(((ucontext_t *)context)->uc_mcontext.gregs[REG_EIP]); -#endif /* REG_EIP */ + /* these are from GNU libc, rather than Linux, strictly speaking */ +# if defined(REG_EIP) +# define REG_INDEX REG_EIP +# elif defined(REG_RIP) +# define REG_INDEX REG_RIP +# elif defined(__powerpc__) +# define REG_INDEX 32 +# endif +#elif defined(SUNOS_5) /* !GNU_LINUX */ +# define REG_INDEX REG_PC #endif /* GNU_LINUX */ + +#ifdef REG_INDEX +# ifdef HAVE_UCONTEXT_T_UC_MCONTEXT_GREGS +# define REGS gregs[REG_INDEX] +# elif defined(HAVE_UCONTEXT_T_UC_MCONTEXT_UC_REGS) +# define REGS uc_regs->gregs[REG_INDEX] +# endif /* HAVE_UCONTEXT_T_UC_MCONTEXT_GREGS */ +#endif /* REG_INDEX */ + +#ifdef REGS + if (context) + return (void *)(((ucontext_t *)context)->uc_mcontext.REGS); +#elif defined(HAVE_UCONTEXT_T_UC_MCONTEXT_REGS__NIP) + /* older Linux / struct pt_regs ? */ + if (context) + return (void *)(((ucontext_t *)context)->uc_mcontext.regs->nip); +#endif /* REGS */ + #endif /* HAVE_UCONTEXT_H */ return NULL; } From 055086f70febc30fdfd94bb4406e9075d6934cd8 Mon Sep 17 00:00:00 2001 From: Paul Jakma Date: Tue, 23 Sep 2014 15:23:01 +0100 Subject: [PATCH 253/482] bgpd: well-known attr check only run for v4/uni, which could cause a crash. * ANVL testing by Martin Winter threw up a crash in bgpd in aspath_dup called from bgp_packet_attribute, if attr->aspath was NULL, on an IPv6 UPDATE. This root cause is that the checks for well-known, mandatory attributes were being applied only if an UPDATE contained the IPv4 NLRI and the peer was configured for v4/unicast (i.e. not deconfigured). This is something inherited from GNU Zebra, and never noticed before. * bgp_attr.c: (bgp_attr_parse) Move the well-known mandatory attribute check to here, so that it can be run immediately after all attributes are parsed, and before any further processing of attributes that might assume the existence of WK/M attributes (e.g. AS4-Path). (bgp_attr_munge_as4_attrs) Missing AS_PATH shouldn't happen here anymore, but retain a check anyway for robustness - it's definitely a hard error though. * bgp_attr.h: (bgp_attr_check) No longer needs to be exported, make static. * bgp_packet.c: (bgp_update_receive) Responsibility for well-known check now in bgp_attr_parse. --- bgpd/bgp_attr.c | 101 +++++++++++++++++++++++++++------------------- bgpd/bgp_attr.h | 1 - bgpd/bgp_packet.c | 11 ----- 3 files changed, 59 insertions(+), 54 deletions(-) diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index b62a4f82..69ca786d 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -1282,7 +1282,17 @@ bgp_attr_munge_as4_attrs (struct peer *const peer, int ignore_as4_path = 0; struct aspath *newpath; struct attr_extra *attre = attr->extra; - + + if (!attr->aspath) + { + /* NULL aspath shouldn't be possible as bgp_attr_parse should have + * checked that all well-known, mandatory attributes were present. + * + * Can only be a problem with peer itself - hard error + */ + return BGP_ATTR_PARSE_ERROR; + } + if (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV)) { /* peer can do AS4, so we ignore AS4_PATH and AS4_AGGREGATOR @@ -1362,12 +1372,9 @@ bgp_attr_munge_as4_attrs (struct peer *const peer, /* need to reconcile NEW_AS_PATH and AS_PATH */ if (!ignore_as4_path && (attr->flag & (ATTR_FLAG_BIT( BGP_ATTR_AS4_PATH)))) { - if (!attr->aspath) - return BGP_ATTR_PARSE_PROCEED; - - newpath = aspath_reconcile_as4 (attr->aspath, as4_path); - aspath_unintern (&attr->aspath); - attr->aspath = aspath_intern (newpath); + newpath = aspath_reconcile_as4 (attr->aspath, as4_path); + aspath_unintern (&attr->aspath); + attr->aspath = aspath_intern (newpath); } return BGP_ATTR_PARSE_PROCEED; } @@ -1726,6 +1733,39 @@ bgp_attr_unknown (struct bgp_attr_parser_args *args) return BGP_ATTR_PARSE_PROCEED; } +/* Well-known attribute check. */ +static int +bgp_attr_check (struct peer *peer, struct attr *attr) +{ + u_char type = 0; + + if (! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_ORIGIN))) + type = BGP_ATTR_ORIGIN; + + if (! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_AS_PATH))) + type = BGP_ATTR_AS_PATH; + + if (! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP))) + type = BGP_ATTR_NEXT_HOP; + + if (peer->sort == BGP_PEER_IBGP + && ! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF))) + type = BGP_ATTR_LOCAL_PREF; + + if (type) + { + zlog (peer->log, LOG_WARNING, + "%s Missing well-known attribute %d.", + peer->host, type); + bgp_notify_send_with_data (peer, + BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MISS_ATTR, + &type, 1); + return BGP_ATTR_PARSE_ERROR; + } + return BGP_ATTR_PARSE_PROCEED; +} + /* Read attribute of update packet. This function is called from bgp_update_receive() in bgp_packet.c. */ bgp_attr_parse_ret_t @@ -1958,7 +1998,6 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, return BGP_ATTR_PARSE_ERROR; } } - /* Check final read pointer is same as end pointer. */ if (BGP_INPUT_PNT (peer) != endp) { @@ -1972,7 +2011,18 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, aspath_unintern (&as4_path); return BGP_ATTR_PARSE_ERROR; } - + + /* Check all mandatory well-known attributes are present */ + { + bgp_attr_parse_ret_t ret; + if ((ret = bgp_attr_check (peer, attr)) < 0) + { + if (as4_path) + aspath_unintern (&as4_path); + return ret; + } + } + /* * At this place we can see whether we got AS4_PATH and/or * AS4_AGGREGATOR from a 16Bit peer and act accordingly. @@ -2033,39 +2083,6 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, return BGP_ATTR_PARSE_PROCEED; } -/* Well-known attribute check. */ -int -bgp_attr_check (struct peer *peer, struct attr *attr) -{ - u_char type = 0; - - if (! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_ORIGIN))) - type = BGP_ATTR_ORIGIN; - - if (! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_AS_PATH))) - type = BGP_ATTR_AS_PATH; - - if (! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP))) - type = BGP_ATTR_NEXT_HOP; - - if (peer->sort == BGP_PEER_IBGP - && ! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF))) - type = BGP_ATTR_LOCAL_PREF; - - if (type) - { - zlog (peer->log, LOG_WARNING, - "%s Missing well-known attribute %d.", - peer->host, type); - bgp_notify_send_with_data (peer, - BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_MISS_ATTR, - &type, 1); - return BGP_ATTR_PARSE_ERROR; - } - return BGP_ATTR_PARSE_PROCEED; -} - int stream_put_prefix (struct stream *, struct prefix *); size_t diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index cb401e71..b59fa8e4 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -147,7 +147,6 @@ extern void bgp_attr_finish (void); extern bgp_attr_parse_ret_t bgp_attr_parse (struct peer *, struct attr *, bgp_size_t, struct bgp_nlri *, struct bgp_nlri *); -extern int bgp_attr_check (struct peer *, struct attr *); extern struct attr_extra *bgp_attr_extra_get (struct attr *); extern void bgp_attr_extra_free (struct attr *); extern void bgp_attr_dup (struct attr *, struct attr *); diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index 0fab1b08..35a22c1e 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -1775,18 +1775,7 @@ bgp_update_receive (struct peer *peer, bgp_size_t size) bgp_nlri_parse (peer, NULL, &withdraw); if (update.length) - { - /* We check well-known attribute only for IPv4 unicast - update. */ - ret = bgp_attr_check (peer, &attr); - if (ret < 0) - { - bgp_attr_unintern_sub (&attr); - return -1; - } - bgp_nlri_parse (peer, NLRI_ATTR_ARG, &update); - } if (mp_update.length && mp_update.afi == AFI_IP From f6444e4f6e1664f49f7552f894c8c94e45dd3c35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cochard-Labb=C3=A9?= Date: Thu, 9 Oct 2014 10:28:21 +0100 Subject: [PATCH 254/482] FreeBSD has changed its SOCK_RAW for being truly raw. --- lib/zebra.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/zebra.h b/lib/zebra.h index 3715b342..b289a19e 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -358,7 +358,8 @@ struct in_pktinfo * OpenBSD: network byte order, apart from older versions which are as per * *BSD */ -#if defined(__NetBSD__) || defined(__FreeBSD__) \ +#if defined(__NetBSD__) \ + || (defined(__FreeBSD__) && (__FreeBSD_version < 1100030)) \ || (defined(__OpenBSD__) && (OpenBSD < 200311)) \ || (defined(__APPLE__)) \ || (defined(SUNOS_5) && defined(WORDS_BIGENDIAN)) From 7a6eec54eaffa82f4f03363314bb81c400eb2a66 Mon Sep 17 00:00:00 2001 From: Paul Jakma Date: Thu, 9 Oct 2014 10:51:41 +0100 Subject: [PATCH 255/482] zebra: Build the test client, can be useful, and add IPv6 to testrib.conf --- zebra/Makefile.am | 4 ++-- zebra/testrib.conf | 24 ++++++++++++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/zebra/Makefile.am b/zebra/Makefile.am index 96f7bef3..0591a555 100644 --- a/zebra/Makefile.am +++ b/zebra/Makefile.am @@ -59,8 +59,8 @@ EXTRA_DIST = if_ioctl.c if_ioctl_solaris.c if_netlink.c if_proc.c \ ioctl.c ioctl_solaris.c \ GNOME-SMI GNOME-PRODUCT-ZEBRA-MIB -#client : client_main.o ../lib/libzebra.la -# $(CC) -g -o client client_main.o ../liblzebra.la $(LIBS) $(LIB_IPV6) +client : client_main.o ../lib/libzebra.la + $(CC) -g -o client client_main.o ../liblzebra.la $(LIBS) $(LIB_IPV6) quaggaconfdir = $(sysconfdir) diff --git a/zebra/testrib.conf b/zebra/testrib.conf index 75ba0ccd..0df7dc24 100644 --- a/zebra/testrib.conf +++ b/zebra/testrib.conf @@ -11,45 +11,65 @@ debug zebra kernel ! interface eth0 ip address 10.0.0.1/24 + ipv6 address 1::0:1/64 state up ! interface eth1 ip address 10.0.1.1/24 + ipv6 address 1::1:1/64 ! interface eth2 ip address 10.0.2.1/24 + ipv6 address 1::2:1/64 ! +! Unnumbered interface foo1 ip address 192.168.1.1/32 + ipv6 address 2::1:1/128 ! interface foo0 ip address 192.168.1.1/32 ip address 192.168.1.1/24 label foo + ipv6 address 2::1:1/128 state up ! -ip route 1.1.1.0/24 1.1.2.2 ! statics that should be subsumed by connected routes, according to interface ! state ip route 10.0.0.0/24 10.0.1.254 ip route 10.0.1.0/24 10.0.2.254 ip route 10.0.2.0/24 10.0.0.254 +ipv6 route 1::0:0/64 1::1:f +ipv6 route 1::1:0/64 1::2:f +ipv6 route 1::2:0/64 1::0:f + +! null route +ip route 10.1.0.1/32 null0 +ipv6 route 100::1:1/128 null0 -! normalish route +! normalish routes ip route 1.1.2.0/24 10.0.0.2 +ipv6 route 80::/64 1::0:e + ! different admin distances ip route 1.1.0.2/32 10.0.0.3 10 ip route 1.1.0.2/32 10.0.0.4 20 ip route 1.1.0.2/32 10.0.1.3 30 +ipv6 route 90::1/128 1::0:a 10 +ipv6 route 90::1/128 1::0:b 20 +ipv6 route 90::1/128 1::1:c 30 + ! multiple-nexthop + distance ip route 1.1.0.2/32 10.0.0.5 10 +ipv6 route 90::1/128 1::0:d 10 ! a recursive route, potentially. ip route 1.1.3.0/24 10.0.0.2 ! double recursive, potentially ip route 1.1.0.1/32 1.1.3.1 ! +ip route 1.1.1.0/24 1.1.2.2 line vty exec-timeout 0 0 From 384d7ad98c109e92eaf65bf10a3256e5657639c3 Mon Sep 17 00:00:00 2001 From: Paul Jakma Date: Thu, 9 Oct 2014 16:09:10 +0100 Subject: [PATCH 256/482] docs: defines.texi include seems to want to be after setfilename * quagga.texi: I'm getting warnings about stuff in defines.texi not being defined when building quagga.info. Seems to be fixed by moving the include of defines.texi to the end of the header. Also, the Texinfo docs suggest setfilename must go first. --- doc/quagga.texi | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/doc/quagga.texi b/doc/quagga.texi index b4105ac9..af82e515 100644 --- a/doc/quagga.texi +++ b/doc/quagga.texi @@ -1,11 +1,10 @@ \input texinfo @c -*- texinfo -*- -@c Set variables - sourced from defines.texi -@include defines.texi @c %**start of header -@setchapternewpage odd -@settitle @uref{http://www.quagga.net,,@value{PACKAGE_NAME}} @setfilename quagga.info +@settitle @uref{http://www.quagga.net,,@value{PACKAGE_NAME}} +@c Set variables - sourced from defines.texi +@include defines.texi @c %**end of header @c automake will automatically generate version.texi From 969d3550a8cbb07f8b4d5ebe8dde5064f8260140 Mon Sep 17 00:00:00 2001 From: Lu Feng Date: Tue, 21 Oct 2014 06:24:07 +0000 Subject: [PATCH 257/482] zebra: route_unlock_node is missing in "show ip[v6] route " commands Signed-off-by: Feng Lu Acked-by: Vincent Jardin --- zebra/zebra_vty.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index baa60db9..1d12ac5f 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -998,6 +998,8 @@ DEFUN (show_ip_route_prefix, if (! rn || rn->p.prefixlen != p.prefixlen) { vty_out (vty, "%% Network not in table%s", VTY_NEWLINE); + if (rn) + route_unlock_node (rn); return CMD_WARNING; } @@ -1897,6 +1899,8 @@ DEFUN (show_ipv6_route_prefix, if (! rn || rn->p.prefixlen != p.prefixlen) { vty_out (vty, "%% Network not in table%s", VTY_NEWLINE); + if (rn) + route_unlock_node (rn); return CMD_WARNING; } From ea2a598411cc7bd20456849e56bbc9e93c9916e7 Mon Sep 17 00:00:00 2001 From: Joakim Tjernlund Date: Thu, 26 Nov 2009 12:23:07 +0000 Subject: [PATCH 258/482] ospfd: invalid MD5 auth_key? This looks fishy in ospf_make_md5_digest() if (list_isempty (OSPF_IF_PARAM (oi, auth_crypt))) auth_key = (const u_int8_t *) ""; ... MD5Update(&ctx, auth_key, OSPF_AUTH_MD5_SIZE); auth_key points to a "" string of len 1 which is a lot smaller that OSPF_AUTH_MD5_SIZE. Is this intentional to get some random data or just a plain bug? Anyone using MD5 should have a closer look and decide what to do. Acked-by: Feng Lu --- ospfd/ospf_packet.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index efdf8263..17260eb1 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -383,7 +383,7 @@ static int ospf_make_md5_digest (struct ospf_interface *oi, struct ospf_packet *op) { struct ospf_header *ospfh; - unsigned char digest[OSPF_AUTH_MD5_SIZE]; + unsigned char digest[OSPF_AUTH_MD5_SIZE] = {0}; MD5_CTX ctx; void *ibuf; u_int32_t t; @@ -410,7 +410,7 @@ ospf_make_md5_digest (struct ospf_interface *oi, struct ospf_packet *op) /* Get MD5 Authentication key from auth_key list. */ if (list_isempty (OSPF_IF_PARAM (oi, auth_crypt))) - auth_key = (const u_int8_t *) ""; + auth_key = (const u_int8_t *) digest; else { ck = listgetdata (listtail(OSPF_IF_PARAM (oi, auth_crypt))); From bdd8cd70a042473477f9144c9cedb8dde11ba2c1 Mon Sep 17 00:00:00 2001 From: Yasuhiro Ohara Date: Thu, 17 Dec 2009 05:41:17 +0000 Subject: [PATCH 259/482] Bug in ospf6_lsa_compare() This fix is probably correct on 32bit systems, but i think it will not work on 64bit systems. sizeof(signed long) would be 8 and therefore the cast from u_int32_t will map all the values to non-negative part of long int. You would like to use int (like in ospfd) and change the type of seqnuma, seqnumb to that. The type int32_t would be even more proper, but sizeof(int) is 4 on relevant platforms. Signed-off: Ondrej Zajicek Acked-by: Feng Lu Acked-by: Yasuhiro Ohara --- ospf6d/ospf6_lsa.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ospf6d/ospf6_lsa.c b/ospf6d/ospf6_lsa.c index e57306bc..8eeb9959 100644 --- a/ospf6d/ospf6_lsa.c +++ b/ospf6d/ospf6_lsa.c @@ -306,7 +306,7 @@ ospf6_lsa_premature_aging (struct ospf6_lsa *lsa) int ospf6_lsa_compare (struct ospf6_lsa *a, struct ospf6_lsa *b) { - int seqnuma, seqnumb; + int32_t seqnuma, seqnumb; u_int16_t cksuma, cksumb; u_int16_t agea, ageb; @@ -314,8 +314,8 @@ ospf6_lsa_compare (struct ospf6_lsa *a, struct ospf6_lsa *b) assert (b && b->header); assert (OSPF6_LSA_IS_SAME (a, b)); - seqnuma = (int) ntohl (a->header->seqnum); - seqnumb = (int) ntohl (b->header->seqnum); + seqnuma = (int32_t) ntohl (a->header->seqnum); + seqnumb = (int32_t) ntohl (b->header->seqnum); /* compare by sequence number */ if (seqnuma > seqnumb) From 3790eb0d3f0bbb24b9c6be97f547cec144ee05d1 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 13 Jan 2010 00:32:43 +0000 Subject: [PATCH 260/482] stream: remove unused stream_read_unblock The one place this was being used in BGP is now gone, can remove deprecated interface. Acked-by: Feng Lu --- lib/stream.c | 26 -------------------------- lib/stream.h | 4 ---- 2 files changed, 30 deletions(-) diff --git a/lib/stream.c b/lib/stream.c index 0fc3c3b1..c6f20c85 100644 --- a/lib/stream.c +++ b/lib/stream.c @@ -754,32 +754,6 @@ stream_read (struct stream *s, int fd, size_t size) return nbytes; } -/* Read size from fd. */ -int -stream_read_unblock (struct stream *s, int fd, size_t size) -{ - int nbytes; - int val; - - STREAM_VERIFY_SANE(s); - - if (STREAM_WRITEABLE (s) < size) - { - STREAM_BOUND_WARN (s, "put"); - return 0; - } - - val = fcntl (fd, F_GETFL, 0); - fcntl (fd, F_SETFL, val|O_NONBLOCK); - nbytes = read (fd, s->data + s->endp, size); - fcntl (fd, F_SETFL, val); - - if (nbytes > 0) - s->endp += nbytes; - - return nbytes; -} - ssize_t stream_read_try(struct stream *s, int fd, size_t size) { diff --git a/lib/stream.h b/lib/stream.h index f0c742c0..1fc382d6 100644 --- a/lib/stream.h +++ b/lib/stream.h @@ -193,10 +193,6 @@ extern u_int32_t stream_get_ipv4 (struct stream *); Use stream_read_try instead. */ extern int stream_read (struct stream *, int, size_t); -/* Deprecated: all file descriptors should already be non-blocking. - Will be removed. Use stream_read_try instead. */ -extern int stream_read_unblock (struct stream *, int, size_t); - /* Read up to size bytes into the stream. Return code: >0: number of bytes read From 6b274d90fa9b0c9f43e3ca9494cd78df1ccad14e Mon Sep 17 00:00:00 2001 From: Joakim Tjernlund Date: Tue, 9 Mar 2010 06:42:30 +0000 Subject: [PATCH 261/482] ospfd: Don't leave stale RouterLSA's when changing areaID Signed-off-by: Joakim Tjernlund Acked-by: Feng Lu --- ospfd/ospfd.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index dd57f645..c55bdaeb 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -827,7 +827,10 @@ ospf_network_unset (struct ospf *ospf, struct prefix_ipv4 *p, } if (found == 0) - ospf_if_free (oi); + { + ospf_if_free (oi); + ospf_area_check_free (ospf, area_id); + } } /* Update connected redistribute. */ From f80ba04074f1211d857d08d6deddc41d029be1c7 Mon Sep 17 00:00:00 2001 From: Vincent JARDIN Date: Mon, 27 Oct 2014 13:03:14 +0000 Subject: [PATCH 262/482] Handy guidelines to contribute Explain how to be a nice contributor in a handy way. Signed-off-by: Vincent JARDIN Acked-by: Paul Jakma --- HACKING.tex | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/HACKING.tex b/HACKING.tex index c868e7ca..54931d12 100644 --- a/HACKING.tex +++ b/HACKING.tex @@ -105,6 +105,68 @@ \section{GUIDELINES FOR HACKING ON QUAGGA} See also section~\ref{sec:dll-versioning} below regarding SHARED LIBRARY VERSIONING. +\section{YOUR FIRST CONTRIBUTIONS} + +Routing protocols can be very complex sometimes. Then, working with an +Opensource community can be complex too, but usually friendly with +anyone who is ready to be willing to do it properly. + +\begin{itemize} + + \item First, start doing simple tasks. Quagga's patchwork is a good place + to start with. Pickup some patches, apply them on your git trie, + review them and send your ack't or review comments. Then, a + maintainer will apply the patch if ack't or the author will + have to provide a new update. It help a lot to drain the + patchwork queues. + See \url{http://patchwork.quagga.net/project/quagga/list/} + + \item The more you'll review patches from patchwork, the more the + Quagga's maintainers will be willing to consider some patches you will + be sending. + + \item start using git clone, pwclient \url{http://patchwork.quagga.net/help/pwclient/} + +\begin{verbatim} +$ pwclient list -s new +ID State Name +-- ----- ---- +179 New [quagga-dev,6648] Re: quagga on FreeBSD 4.11 (gcc-2.95) +181 New [quagga-dev,6660] proxy-arp patch +[...] + +$ pwclient git-am 1046 +\end{verbatim} + +\end{itemize} + +\section{HANDY GUIDELINES FOR MAINTAINERS} + +Get your cloned trie: +\begin{verbatim} + git clone vjardin@git.sv.gnu.org:/srv/git/quagga.git +\end{verbatim} + +Apply some ack't patches: +\begin{verbatim} + pwclient git-am 1046 + Applying patch #1046 using 'git am' + Description: [quagga-dev,11595] zebra: route_unlock_node is missing in "show ip[v6] route " commands + Applying: zebra: route_unlock_node is missing in "show ip[v6] route " commands +\end{verbatim} + +Run a quick review. If the ack't was not done properly, you know who you have +to blame. + +Push the patches: +\begin{verbatim} + git push +\end{verbatim} + +Set the patch to accepted on patchwork +\begin{verbatim} + pwclient update -s Accepted 1046 +\end{verbatim} \section{COMPILE-TIME CONDITIONAL CODE} From 9562a7774b76df050d3e01632c6203796dc72c87 Mon Sep 17 00:00:00 2001 From: Paul Jakma Date: Tue, 21 Oct 2014 10:59:45 +0100 Subject: [PATCH 263/482] mrlg: Remove obsolete version. * mrlg.cgi: The version we shipped was very much out of date, remove it. * mrlg.txt: Add file pointing to the official MRLG site. --- tools/mrlg.cgi | 395 ------------------------------------------------- tools/mrlg.txt | 5 + 2 files changed, 5 insertions(+), 395 deletions(-) delete mode 100755 tools/mrlg.cgi create mode 100644 tools/mrlg.txt diff --git a/tools/mrlg.cgi b/tools/mrlg.cgi deleted file mode 100755 index ac468eef..00000000 --- a/tools/mrlg.cgi +++ /dev/null @@ -1,395 +0,0 @@ -#!/usr/bin/perl -## -## Zebra Looking Glass version 1.0 -## 01 FEB 2000 -## Copyright (C) 2000 John W. Fraizer III -## *All* copyright notices must remain in place to use this code. -## -## The latest version of this code is available at: -## ftp://ftp.enterzone.net/looking-glass/ -## -## -## This file is part of GNU Zebra. -## -## GNU Zebra is free software; you can redistribute it and/or modify it -## under the terms of the GNU General Public License as published by the -## Free Software Foundation; either version 2, or (at your option) any -## later version. -## -## GNU Zebra is distributed in the hope that it will be useful, but -## WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -## General Public License for more details. -## -## You should have received a copy of the GNU General Public License -## along with GNU Zebra; see the file COPYING. If not, write to the -## Free Software Foundation, Inc., 59 Temple Place - Suite 330, -## Boston, MA 02111-1307, USA. - -require 5.002; -use POSIX; -use Net::Telnet (); - - - -## Set the URL for your site. -$url="http://www.sample.com/mrlg.cgi"; - -## Set your router variables in sub set_router and modify the selections in Main to match. - - -############################################################ -#Main -############################################################ -{ - -## Set the router default -@Form{'router'} = "router1"; - -## Get the form results now so we can override the default router -get_form(); - -print "Content-type: text/html\n\n"; - -print ' - - -Multi-Router Looking Glass for Zebra - - - - -

Multi-Router Looking Glass for Zebra

-Copyright 2000 - John Fraizer, EnterZone Inc. -
-'; - -print ' - -'; -print "
\n"; -print "Router: -

-Query: -
-show ip bgp
-show ip bgp summary
-show ip route
-show interface
-show ipv6 bgp
-show ipv6 bgp summary
-show ipv6 route
-
-Argument: -
-'; - -## Set up the address, pw and ports, etc for the selected router. -set_router(); - -## Set up which command is to be executed (and then execute it!) -set_command(); - - -print ' -

-
- -Multi-Router Looking Glass for Zebra version 1.0
-Written by: John Fraizer - -EnterZone, Inc
-Source code: ftp://ftp.enterzone.net/looking-glass/ - - -'; - -## All done! - -exit (0); -} - - -############################################################ -sub get_form -############################################################ -{ - - #read STDIN - read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); - - # Split the name-value pairs - @pairs = split(/&/, $buffer); - - # For each name-value pair: - foreach $pair (@pairs) - { - - # Split the pair up into individual variables. - local($name, $value) = split(/=/, $pair); - - # Decode the form encoding on the name and value variables. - $name =~ tr/+/ /; - $name =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; - - $value =~ tr/+/ /; - $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; - - # If they try to include server side includes, erase them, so they - # aren't a security risk if the html gets returned. Another - # security hole plugged up. - $value =~ s///g; - - @Form{$name} = $value ; - - } - -} - -############################################################ -sub set_router -############################################################ - -## $server is the IP address of the router running zebra -## $login_pass is the password of the router -## $bgpd is the port that bgpd will answer on -## $zebra is the port that zebra will answer on -## if $zebra is "", it will disable sh ip route and sh int for that router. -## if $full_tables is set to "1" for a router, full BGP and IP ROUTE table dumps will be allowed via the looking glass. -## This is a BAD thing to do if you have multiple full views on a router. That's why the option is there. - -{ -if ($Form{'router'} eq 'router1') - { -$server = '10.1.1.1'; -$login_pass = 'zebra'; -$bgpd = "2605"; -$zebra = ""; -$full_tables=1; - - } - -elsif ($Form{'router'} eq 'router2') - { -$server = '10.1.1.2'; -$login_pass = 'zebra'; -$bgpd = "2605"; -$zebra = "2601"; - } - -elsif ($Form{'router'} eq 'router3') - { -$server = '10.1.1.3'; -$login_pass = 'zebra'; -$bgpd = "2605"; -$zebra = "2601"; -$full_tables=1; - } - -elsif ($Form{'router'} eq 'router4') - { -$server = '10.1.1.4'; -$login_pass = 'zebra'; -$bgpd = "2605"; -$zebra = "2601"; - } - - -} - - -############################################################ -sub set_command -############################################################ -{ -if ($Form{'query'} eq '1') - { - sh_ip_bgp('ip'); - } - -elsif ($Form{'query'} eq '2') - { - sh_ip_bgp_sum('ip'); - } - -if ($Form{'query'} eq '3') - { - sh_ip_route('ip'); - } - -if ($Form{'query'} eq '4') - { - sh_int(); - } -if ($Form{'query'} eq '5') - { - sh_ip_bgp('ipv6'); - } -if ($Form{'query'} eq '6') - { - sh_ip_bgp_sum('ipv6'); - } -if ($Form{'query'} eq '7') - { - sh_ip_route('ipv6'); - } -} -############################################################ -sub sh_ip_bgp -############################################################ -{ -my $protocol = shift(@_); -$port = $bgpd; -if ($protocol ne 'ip' && $protocol ne 'ipv6') - { - print "Invalid protocol: $protocol\n"; - print "protocol must be 'ip' or 'ipv6'\n\n"; - return; - } -$command = "show $protocol bgp $Form{'arg'}"; -if ($Form{'arg'} eq '') - { - if ($full_tables eq '1') - { - execute_command(); - } - else - { - print "Sorry. Displaying the FULL routing table would put too much load on the router!\n\n"; - } - } -else - { - execute_command(); - } -} - -############################################################ -sub sh_ip_bgp_sum -############################################################ -{ - my $protocol = shift(@_); - $port = $bgpd; - if ($protocol ne 'ip' && $protocol ne 'ipv6') - { - print "Invalid protocol: $protocol\n"; - print "protocol must be 'ip' or 'ipv6'\n\n"; - return; - } - $command = "show $protocol bgp summary"; - execute_command(); -} - -############################################################ -sub sh_ip_route -############################################################ -{ - -if ($zebra eq '') - { - print "Sorry. The show ip route command is disabled for this router." - } -else - { - - $port = $zebra; - my $protocol = shift(@_); - if ($protocol ne 'ip' && $protocol ne 'ipv6') - { - print "Invalid protocol: $protocol\n"; - print "protocol must be 'ip' or 'ipv6'\n\n"; - return; - } - $command = "show $protocol route $Form{'arg'}"; - if ($Form{'arg'} eq '') - { - if ($full_tables eq '1') - { - execute_command(); - } - else - { - print "Sorry. Displaying the FULL routing table would put too much load on the router!\n\n"; - } - } - else - { - execute_command(); - } - } -} - -############################################################ -sub sh_int -############################################################ -{ -if ($zebra eq '') - { - print "Sorry. The show interface command is disabled for this router." - } -else - { - $port = $zebra; - $command = "show interface $Form{'arg'}"; - execute_command(); - } -} - - - -############################################################ -sub execute_command -############################################################ -## This code is based on: -## -## Zebra interactive console -## Copyright (C) 2000 Vladimir B. Grebenschikov -## - - -{ - -print "Executing command = $command"; - -# my $port = ($opt_z ? 'zebra' : 0) || -# ($opt_b ? 'bgpd' : 0) || -# ($opt_o ? 'ospfd' : 0) || -# ($opt_r ? 'ripd' : 0) || 'bgpd'; - -my $cmd = $command; - - - my $t = new Net::Telnet (Timeout => 10, - Prompt => '/[\>\#] $/', - Port => $port); - - $t->open ($server); - - $t->cmd ($login_pass); - - if ($cmd) - { - docmd ($t, $cmd); - } - -} - -############################################################ -sub docmd -############################################################ -{ - my ($t, $cmd) = @_; - my @lines = $t->cmd ($cmd); - print "
\n";
-  print join ('', grep (!/[\>\#] $/, @lines)), "\n";
-  print "
"; -} - - - diff --git a/tools/mrlg.txt b/tools/mrlg.txt new file mode 100644 index 00000000..0ebf7ee6 --- /dev/null +++ b/tools/mrlg.txt @@ -0,0 +1,5 @@ +The Multi-Router Looking Glass (MRLG) CGI script now lives at: + + http://mrlg.op-sec.us/ + +Please obtain the latest version from there. From aed1b556cf2f55680ae09d7ad1a1f22729dea8c5 Mon Sep 17 00:00:00 2001 From: Paul Jakma Date: Tue, 21 Oct 2014 16:59:01 +0100 Subject: [PATCH 264/482] bgpd: Fixes for recent well-known-attr check patch. * bgp_attr.c: Recent patch to tighten well-known attr checks and apply that to all AFIs has some breakage with MP-extensions and GR, which needs to be fixed. (bgp_attr_check) Graceful Restart EoR can be an empty UPDATE for IPv4/uni. MP-Ext allow UPDATE with just MP_UNREACH_NLRI. Check for these and return proceed. NEXT_HOP becomes optional, if MP_REACH_NLRI is present and there's no v4 NLTI, update NEXT_HOP check accordingly. Print the missing attr in string form in the log message. (bgp_attr_parse) AS_PATH need not be there, so bgp_attr_munge_as4_attrs call needs to be conditional on that. --- bgpd/bgp_attr.c | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 69ca786d..da17e82e 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -1739,15 +1739,32 @@ bgp_attr_check (struct peer *peer, struct attr *attr) { u_char type = 0; + /* BGP Graceful-Restart End-of-RIB for IPv4 unicast is signaled as an + * empty UPDATE. */ + if (CHECK_FLAG (peer->cap, PEER_CAP_RESTART_RCV) && !attr->flag) + return BGP_ATTR_PARSE_PROCEED; + + /* "An UPDATE message that contains the MP_UNREACH_NLRI is not required + to carry any other path attributes.", though if MP_REACH_NLRI or NLRI + are present, it should. Check for any other attribute being present + instead. + */ + if (attr->flag == ATTR_FLAG_BIT (BGP_ATTR_MP_UNREACH_NLRI)) + return BGP_ATTR_PARSE_PROCEED; + if (! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_ORIGIN))) type = BGP_ATTR_ORIGIN; if (! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_AS_PATH))) type = BGP_ATTR_AS_PATH; - - if (! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP))) + + /* RFC 2858 makes Next-Hop optional/ignored, if MP_REACH_NLRI is present and + * NLRI is empty. We can't easily check NLRI empty here though. + */ + if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_MP_REACH_NLRI)) + && !CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP))) type = BGP_ATTR_NEXT_HOP; - + if (peer->sort == BGP_PEER_IBGP && ! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF))) type = BGP_ATTR_LOCAL_PREF; @@ -1755,8 +1772,8 @@ bgp_attr_check (struct peer *peer, struct attr *attr) if (type) { zlog (peer->log, LOG_WARNING, - "%s Missing well-known attribute %d.", - peer->host, type); + "%s Missing well-known attribute %d / %s", + peer->host, type, LOOKUP (attr_str, type)); bgp_notify_send_with_data (peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_MISS_ATTR, @@ -2033,10 +2050,14 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, * So, to be defensive, we are not relying on any order and read * all attributes first, including these 32bit ones, and now, * afterwards, we look what and if something is to be done for as4. + * + * It is possible to not have AS_PATH, e.g. GR EoR and sole + * MP_UNREACH_NLRI. */ /* actually... this doesn't ever return failure currently, but * better safe than sorry */ - if (bgp_attr_munge_as4_attrs (peer, attr, as4_path, + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_AS_PATH)) + && bgp_attr_munge_as4_attrs (peer, attr, as4_path, as4_aggregator, &as4_aggregator_addr)) { bgp_notify_send (peer, From 773224404cb33b2dbd3d8d8d2572013603995ce4 Mon Sep 17 00:00:00 2001 From: Dinesh G Dutt Date: Tue, 30 Sep 2014 12:39:24 -0700 Subject: [PATCH 265/482] zebra: Set link-detect on by default Signed-off-by: Roopa Prabhu Reviewed-by: Dinesh G Dutt Reviewed-by: Scott Feldman --- lib/if.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/if.c b/lib/if.c index 3a1f9b41..2b46a1bb 100644 --- a/lib/if.c +++ b/lib/if.c @@ -132,6 +132,9 @@ if_create (const char *name, int namelen) ifp->connected = list_new (); ifp->connected->del = (void (*) (void *)) connected_free; + /* Enable Link-detection by default */ + SET_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION); + if (if_master.if_new_hook) (*if_master.if_new_hook) (ifp); From 93b344f3b14390e3952ea9025ac5996ae9131148 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 28 Oct 2014 14:52:49 +0100 Subject: [PATCH 266/482] Revert "zebra: Set link-detect on by default" This reverts commit 773224404cb33b2dbd3d8d8d2572013603995ce4. This patch is nontrivial but wasn't passed along on the mailing list; this is a revert purely on procedural reasons. Signed-off-by: David Lamparter --- lib/if.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/if.c b/lib/if.c index 2b46a1bb..3a1f9b41 100644 --- a/lib/if.c +++ b/lib/if.c @@ -132,9 +132,6 @@ if_create (const char *name, int namelen) ifp->connected = list_new (); ifp->connected->del = (void (*) (void *)) connected_free; - /* Enable Link-detection by default */ - SET_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION); - if (if_master.if_new_hook) (*if_master.if_new_hook) (ifp); From 59135bde25441cd39cea0389467eb206fc9030c9 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Thu, 30 Oct 2014 06:19:15 +0100 Subject: [PATCH 267/482] build: fix 9562a77... (mrlg removal) mrlg.cgi was removed in 9562a77 "mrlg: Remove obsolete version." but the file was still listed in Makefile.am. Fixes: 9562a77 ("mrlg: Remove obsolete version.") Signed-off-by: David Lamparter --- Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index 6916470b..b6082535 100644 --- a/Makefile.am +++ b/Makefile.am @@ -11,7 +11,7 @@ DIST_SUBDIRS = lib zebra bgpd ripd ripngd ospfd ospf6d babeld \ EXTRA_DIST = aclocal.m4 SERVICES TODO REPORTING-BUGS INSTALL.quagga.txt \ update-autotools \ vtysh/Makefile.in vtysh/Makefile.am \ - tools/mrlg.cgi tools/rrcheck.pl tools/rrlookup.pl tools/zc.pl \ + tools/rrcheck.pl tools/rrlookup.pl tools/zc.pl \ tools/zebra.el tools/multiple-bgpd.sh \ fpm/fpm.h From c68f6d9dbb9f910d3ee82e099655fff7c12ef856 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Thu, 30 Oct 2014 06:42:00 +0100 Subject: [PATCH 268/482] tests: fix tests for 055086f (well-known attr check) Fix tests/aspathtest.c by including an ORIGIN attribute in the testcases. After 055086f "bgpd: well-known attr check only run for v4/uni, which could cause a crash," we're now checking for it and tests are failing due to that. Note that test #11 ("4b AS4_PATH w/o AS_PATH") is no longer accepted as OK since the function now checks for the existence of an AS_PATH attr. Fixes: 055086f ("bgpd: well-known attr check only run for v4/uni"...) Signed-off-by: David Lamparter --- tests/aspath_test.c | 76 ++++++++++++++++++++++++++++----------------- 1 file changed, 48 insertions(+), 28 deletions(-) diff --git a/tests/aspath_test.c b/tests/aspath_test.c index 0e57c535..71a31022 100644 --- a/tests/aspath_test.c +++ b/tests/aspath_test.c @@ -453,6 +453,13 @@ static struct test_segment { }, { NULL, NULL, {0}, 0, { NULL, 0, 0 } } }; +#define COMMON_ATTRS \ + BGP_ATTR_FLAG_TRANS, \ + BGP_ATTR_ORIGIN, \ + 1, \ + BGP_ORIGIN_EGP +#define COMMON_ATTR_SIZE 4 + /* */ static struct aspath_tests { const char *desc; @@ -474,11 +481,12 @@ static struct aspath_tests { "8466 3 52737 4096", AS2_DATA, 0, 0, - { BGP_ATTR_FLAG_TRANS, - BGP_ATTR_AS_PATH, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS, + BGP_ATTR_AS_PATH, 10, }, - 3, + COMMON_ATTR_SIZE + 3, }, /* 1 */ { @@ -487,11 +495,12 @@ static struct aspath_tests { "8466 3 52737 4096", AS2_DATA, -1, 0, - { BGP_ATTR_FLAG_TRANS, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS, BGP_ATTR_AS_PATH, 8, }, - 3, + COMMON_ATTR_SIZE + 3, }, /* 2 */ { @@ -500,11 +509,12 @@ static struct aspath_tests { "8466 3 52737 4096", AS2_DATA, -1, 0, - { BGP_ATTR_FLAG_TRANS, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS, BGP_ATTR_AS_PATH, 12, }, - 3, + COMMON_ATTR_SIZE + 3, }, /* 3 */ { @@ -513,11 +523,12 @@ static struct aspath_tests { "8466 3 52737 4096", AS2_DATA, -1, 0, - { BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL, BGP_ATTR_AS_PATH, 10, }, - 3, + COMMON_ATTR_SIZE + 3, }, /* 4 */ { @@ -526,11 +537,12 @@ static struct aspath_tests { "8466 3 52737 4096", AS2_DATA, -1, 0, - { BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL, BGP_ATTR_AS4_PATH, 10, }, - 3, + COMMON_ATTR_SIZE + 3, }, /* 5 */ { @@ -539,11 +551,12 @@ static struct aspath_tests { "8466 3 52737 4096", AS4_DATA, -1, PEER_CAP_AS4_RCV, - { BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL, BGP_ATTR_AS4_PATH, 10, }, - 3, + COMMON_ATTR_SIZE + 3, }, /* 6 */ { @@ -552,11 +565,12 @@ static struct aspath_tests { "8466 3 52737 4096", AS4_DATA, 0, PEER_CAP_AS4_RCV|PEER_CAP_AS4_ADV, - { BGP_ATTR_FLAG_TRANS, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS, BGP_ATTR_AS_PATH, 18, }, - 3, + COMMON_ATTR_SIZE + 3, }, /* 7 */ { @@ -565,11 +579,12 @@ static struct aspath_tests { "8466 3 52737 4096", AS4_DATA, -1, PEER_CAP_AS4_RCV|PEER_CAP_AS4_ADV, - { BGP_ATTR_FLAG_TRANS, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS, BGP_ATTR_AS_PATH, 16, }, - 3, + COMMON_ATTR_SIZE + 3, }, /* 8 */ { @@ -578,11 +593,12 @@ static struct aspath_tests { "8466 3 52737 4096", AS4_DATA, -1, PEER_CAP_AS4_RCV|PEER_CAP_AS4_ADV, - { BGP_ATTR_FLAG_TRANS, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS, BGP_ATTR_AS_PATH, 20, }, - 3, + COMMON_ATTR_SIZE + 3, }, /* 9 */ { @@ -591,11 +607,12 @@ static struct aspath_tests { "8466 3 52737 4096", AS4_DATA, -1, PEER_CAP_AS4_RCV|PEER_CAP_AS4_ADV, - { BGP_ATTR_FLAG_TRANS, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS, BGP_ATTR_AS_PATH, 22, }, - 3, + COMMON_ATTR_SIZE + 3, }, /* 10 */ { @@ -604,24 +621,26 @@ static struct aspath_tests { "8466 3 52737 4096", AS4_DATA, -1, PEER_CAP_AS4_RCV|PEER_CAP_AS4_ADV, - { BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL, BGP_ATTR_AS_PATH, 18, }, - 3, + COMMON_ATTR_SIZE + 3, }, /* 11 */ { "4b AS4_PATH w/o AS_PATH", &test_segments[6], NULL, - AS4_DATA, 0, + AS4_DATA, -1, PEER_CAP_AS4_ADV, - { BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL, BGP_ATTR_AS4_PATH, 14, }, - 3, + COMMON_ATTR_SIZE + 3, }, /* 12 */ { @@ -630,11 +649,12 @@ static struct aspath_tests { "8466 3 52737 4096 (123 456 789)", AS4_DATA, 0, PEER_CAP_AS4_ADV, - { BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL, BGP_ATTR_AS4_PATH, 14, }, - 3, + COMMON_ATTR_SIZE + 3, &test_segments[0], }, { NULL, NULL, NULL, 0, 0, 0, { 0 }, 0 }, From 9511633e08ff15c23608983fdc1bc735d427332e Mon Sep 17 00:00:00 2001 From: Balaji Date: Thu, 23 Oct 2014 15:25:25 +0000 Subject: [PATCH 269/482] zebra: MBGP routes should not be installed in the kernel MBGP routes are used only for PIM RPF checks and hence should not be installed in the kernel's FIB. Ignore route node set to Multicast SAFI. Signed-off-by: Balaji.G Acked-by: Everton Marques [pushed down rn->table->info assignment below assert] Signed-off-by: David Lamparter --- zebra/zebra_rib.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index dc7e1ca1..b1d88369 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -1249,9 +1249,12 @@ rib_process (struct route_node *rn) struct nexthop *nexthop = NULL, *tnexthop; int recursing; char buf[INET6_ADDRSTRLEN]; - + rib_table_info_t *info; + assert (rn); - + + info = rn->table->info; + if (IS_ZEBRA_DEBUG_RIB || IS_ZEBRA_DEBUG_RIB_Q) inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); @@ -1286,6 +1289,9 @@ rib_process (struct route_node *rn) if (! nexthop_active_update (rn, rib, 0)) continue; + if (info->safi == SAFI_MULTICAST) + continue; + /* Infinit distance. */ if (rib->distance == DISTANCE_INFINITY) continue; From 88d37b902bc8127379d3293b9671aa6a11479c23 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Mon, 3 Nov 2014 01:20:09 +0000 Subject: [PATCH 270/482] make some structures constant. These pre-initialized arrays are not modified. Signed-off-by: Stephen Hemminger Acked-by: Feng Lu --- isisd/isis_pdu.c | 2 +- ripd/rip_interface.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c index e0208fa4..8d8a5e00 100644 --- a/isisd/isis_pdu.c +++ b/isisd/isis_pdu.c @@ -62,7 +62,7 @@ #endif /* PNBBY */ /* Utility mask array. */ -static u_char maskbit[] = { +static const u_char maskbit[] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; diff --git a/ripd/rip_interface.c b/ripd/rip_interface.c index bdd319e1..35685a75 100644 --- a/ripd/rip_interface.c +++ b/ripd/rip_interface.c @@ -50,7 +50,7 @@ static int rip_enable_if_lookup (const char *ifname); static int rip_enable_network_lookup2 (struct connected *connected); static void rip_enable_apply_all (void); -struct message ri_version_msg[] = +const struct message ri_version_msg[] = { {RI_RIP_VERSION_1, "1"}, {RI_RIP_VERSION_2, "2"}, From 50f38b3500a6af6e1b0d1389d65c62d70c41e8c2 Mon Sep 17 00:00:00 2001 From: Dinesh G Dutt Date: Tue, 30 Sep 2014 12:53:28 -0700 Subject: [PATCH 271/482] Compute and display SPF execution statistics Detailed SPF statistics, all around time spent executing various pieces of SPF such as the SPF algorithm itself, installing routes, pruning unreachable networks etc. Reason codes for firing up SPF are: R - Router LSA, N - Network LSA, S - Summary LSA, ABR - ABR status change, ASBR - ASBR Status Change, AS - ASBR Summary, M - MaxAge Signed-off-by: Dinesh G Dutt Reviewed-by: JR Rivers Reviewed-by: Scott Feldman Reviewed-by: Ayan Banerjee Reviewed-by: Paul Jakma --- lib/thread.c | 2 +- lib/thread.h | 1 + ospfd/ospf_abr.c | 1 + ospfd/ospf_asbr.c | 1 + ospfd/ospf_ase.c | 9 ++++ ospfd/ospf_dump.c | 21 ++++++--- ospfd/ospf_lsa.c | 16 +++++-- ospfd/ospf_spf.c | 115 ++++++++++++++++++++++++++++++++++++++++------ ospfd/ospf_spf.h | 13 ++++++ ospfd/ospf_vty.c | 3 ++ ospfd/ospfd.h | 6 ++- 11 files changed, 161 insertions(+), 27 deletions(-) diff --git a/lib/thread.c b/lib/thread.c index 9c3ee823..cb513323 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -102,7 +102,7 @@ timeval_cmp (struct timeval a, struct timeval b) ? a.tv_usec - b.tv_usec : a.tv_sec - b.tv_sec); } -static unsigned long +unsigned long timeval_elapsed (struct timeval a, struct timeval b) { return (((a.tv_sec - b.tv_sec) * TIMER_SECOND_MICRO) diff --git a/lib/thread.h b/lib/thread.h index 9743a22d..4856dec7 100644 --- a/lib/thread.h +++ b/lib/thread.h @@ -211,6 +211,7 @@ extern struct thread *thread_fetch (struct thread_master *, struct thread *); extern void thread_call (struct thread *); extern unsigned long thread_timer_remain_second (struct thread *); extern int thread_should_yield (struct thread *); +extern unsigned long timeval_elapsed (struct timeval a, struct timeval b); /* Internal libzebra exports */ extern void thread_getrusage (RUSAGE_T *); diff --git a/ospfd/ospf_abr.c b/ospfd/ospf_abr.c index 4bb70b6a..ca1af2c4 100644 --- a/ospfd/ospf_abr.c +++ b/ospfd/ospf_abr.c @@ -556,6 +556,7 @@ ospf_check_abr_status (struct ospf *ospf) if (new_flags != ospf->flags) { + ospf_flag_spf_reason (SPF_FLAG_ABR_STATUS_CHANGE); ospf_spf_calculate_schedule (ospf); if (IS_DEBUG_OSPF_EVENT) zlog_debug ("ospf_check_abr_status(): new router flags: %x",new_flags); diff --git a/ospfd/ospf_asbr.c b/ospfd/ospf_asbr.c index 7e9412c8..dbf7f11f 100644 --- a/ospfd/ospf_asbr.c +++ b/ospfd/ospf_asbr.c @@ -264,6 +264,7 @@ ospf_asbr_status_update (struct ospf *ospf, u_char status) } /* Transition from/to status ASBR, schedule timer. */ + ospf_flag_spf_reason (SPF_FLAG_ASBR_STATUS_CHANGE); ospf_spf_calculate_schedule (ospf); ospf_router_lsa_update (ospf); } diff --git a/ospfd/ospf_ase.c b/ospfd/ospf_ase.c index 6a72e31d..9038b3a5 100644 --- a/ospfd/ospf_ase.c +++ b/ospfd/ospf_ase.c @@ -636,6 +636,7 @@ ospf_ase_calculate_timer (struct thread *t) struct route_node *rn; struct listnode *node; struct ospf_area *area; + struct timeval start_time, stop_time; ospf = THREAD_ARG (t); ospf->t_ase_calc = NULL; @@ -644,6 +645,8 @@ ospf_ase_calculate_timer (struct thread *t) { ospf->ase_calc = 0; + quagga_gettime(QUAGGA_CLK_MONOTONIC, &start_time); + /* Calculate external route for each AS-external-LSA */ LSDB_LOOP (EXTERNAL_LSDB (ospf), rn, lsa) ospf_ase_calculate_route (ospf, lsa); @@ -673,6 +676,12 @@ ospf_ase_calculate_timer (struct thread *t) ospf_route_table_free (ospf->old_external_route); ospf->old_external_route = ospf->new_external_route; ospf->new_external_route = route_table_init (); + + quagga_gettime(QUAGGA_CLK_MONOTONIC, &stop_time); + + zlog_info ("SPF Processing Time(usecs): External Routes: %d\n", + (stop_time.tv_sec - start_time.tv_sec)*1000000L+ + (stop_time.tv_usec - start_time.tv_usec)); } return 0; } diff --git a/ospfd/ospf_dump.c b/ospfd/ospf_dump.c index ef023366..ebcc717f 100644 --- a/ospfd/ospf_dump.c +++ b/ospfd/ospf_dump.c @@ -247,16 +247,21 @@ ospf_timeval_dump (struct timeval *t, char *buf, size_t size) #define HOUR_IN_SECONDS (60*MINUTE_IN_SECONDS) #define DAY_IN_SECONDS (24*HOUR_IN_SECONDS) #define WEEK_IN_SECONDS (7*DAY_IN_SECONDS) - unsigned long w, d, h, m, s, ms; + unsigned long w, d, h, m, s, ms, us; if (!t) return "inactive"; - w = d = h = m = s = ms = 0; + w = d = h = m = s = ms = us = 0; memset (buf, 0, size); - - ms = t->tv_usec / 1000; - + + us = t->tv_usec; + if (us >= 1000) + { + ms = us / 1000; + us %= 1000; + } + if (ms >= 1000) { t->tv_sec += ms / 1000; @@ -297,9 +302,11 @@ ospf_timeval_dump (struct timeval *t, char *buf, size_t size) snprintf (buf, size, "%ldh%02ldm%02lds", h, m, t->tv_sec); else if (m) snprintf (buf, size, "%ldm%02lds", m, t->tv_sec); - else + else if (ms) snprintf (buf, size, "%ld.%03lds", t->tv_sec, ms); - + else + snprintf (buf, size, "%ld usecs", t->tv_usec); + return buf; } diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index fef6b162..31cbaaef 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -2409,8 +2409,10 @@ ospf_router_lsa_install (struct ospf *ospf, struct ospf_lsa *new, ospf_refresher_register_lsa (ospf, new); } if (rt_recalc) - ospf_spf_calculate_schedule (ospf); - + { + ospf_flag_spf_reason (SPF_FLAG_ROUTER_LSA_INSTALL); + ospf_spf_calculate_schedule (ospf); + } return new; } @@ -2444,7 +2446,10 @@ ospf_network_lsa_install (struct ospf *ospf, ospf_refresher_register_lsa (ospf, new); } if (rt_recalc) - ospf_spf_calculate_schedule (ospf); + { + ospf_flag_spf_reason (SPF_FLAG_NETWORK_LSA_INSTALL); + ospf_spf_calculate_schedule (ospf); + } return new; } @@ -2467,11 +2472,10 @@ ospf_summary_lsa_install (struct ospf *ospf, struct ospf_lsa *new, /* This doesn't exist yet... */ ospf_summary_incremental_update(new); */ #else /* #if 0 */ + ospf_flag_spf_reason (SPF_FLAG_SUMMARY_LSA_INSTALL); ospf_spf_calculate_schedule (ospf); #endif /* #if 0 */ - if (IS_DEBUG_OSPF (lsa, LSA_INSTALL)) - zlog_debug ("ospf_summary_lsa_install(): SPF scheduled"); } if (IS_LSA_SELF (new)) @@ -2500,6 +2504,7 @@ ospf_summary_asbr_lsa_install (struct ospf *ospf, struct ospf_lsa *new, - RFC 2328 Section 16.5 implies it should be */ /* ospf_ase_calculate_schedule(); */ #else /* #if 0 */ + ospf_flag_spf_reason (SPF_FLAG_ASBR_SUMMARY_LSA_INSTALL); ospf_spf_calculate_schedule (ospf); #endif /* #if 0 */ } @@ -3022,6 +3027,7 @@ ospf_lsa_maxage_walker_remover (struct ospf *ospf, struct ospf_lsa *lsa) ospf_ase_incremental_update (ospf, lsa); break; default: + ospf_flag_spf_reason (SPF_FLAG_MAXAGE); ospf_spf_calculate_schedule (ospf); break; } diff --git a/ospfd/ospf_spf.c b/ospfd/ospf_spf.c index c40fc33e..a7155bc6 100644 --- a/ospfd/ospf_spf.c +++ b/ospfd/ospf_spf.c @@ -46,6 +46,50 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "ospfd/ospf_abr.h" #include "ospfd/ospf_dump.h" +/* Variables to ensure a SPF scheduled log message is printed only once */ + +static unsigned int spf_reason_flags = 0; + +static void ospf_clear_spf_reason_flags () +{ + spf_reason_flags = 0; +} + +void ospf_flag_spf_reason (unsigned int reason) +{ + if (reason <= SPF_FLAG_MAX_VALUE) + spf_reason_flags |= reason; + else + spf_reason_flags |= SPF_FLAG_MISC; +} + +static void +ospf_get_spf_reason_str (char *buf) +{ + if (buf) + { + buf[0] = '\0'; + if (spf_reason_flags) + { + if (spf_reason_flags & SPF_FLAG_ROUTER_LSA_INSTALL) + strcat (buf, "R, "); + if (spf_reason_flags & SPF_FLAG_NETWORK_LSA_INSTALL) + strcat (buf, "N, "); + if (spf_reason_flags & SPF_FLAG_SUMMARY_LSA_INSTALL) + strcat (buf, "S, "); + if (spf_reason_flags & SPF_FLAG_ASBR_SUMMARY_LSA_INSTALL) + strcat (buf, "AS, "); + if (spf_reason_flags & SPF_FLAG_ABR_STATUS_CHANGE) + strcat (buf, "ABR, "); + if (spf_reason_flags & SPF_FLAG_ASBR_STATUS_CHANGE) + strcat (buf, "ASBR, "); + if (spf_reason_flags & SPF_FLAG_MAXAGE) + strcat (buf, "M, "); + } + buf[strlen(buf)-2] = '\0'; /* skip the last ", " */ + } +} + static void ospf_vertex_free (void *); /* List of allocated vertices, to simplify cleanup of SPF. * Not thread-safe obviously. If it ever needs to be, it'd have to be @@ -1232,27 +1276,28 @@ ospf_spf_calculate (struct ospf_area *area, struct route_table *new_table, /* Free candidate queue. */ pqueue_delete (candidate); - + ospf_vertex_dump (__func__, area->spf, 0, 1); /* Free nexthop information, canonical versions of which are attached * the first level of router vertices attached to the root vertex, see * ospf_nexthop_calculation. */ ospf_canonical_nexthops_free (area->spf); - - /* Free SPF vertices, but not the list. List has ospf_vertex_free - * as deconstructor. - */ - list_delete_all_node (&vertex_list); - + /* Increment SPF Calculation Counter. */ area->spf_calculation++; quagga_gettime (QUAGGA_CLK_MONOTONIC, &area->ospf->ts_spf); + area->ts_spf = area->ospf->ts_spf; if (IS_DEBUG_OSPF_EVENT) zlog_debug ("ospf_spf_calculate: Stop. %ld vertices", mtype_stats_alloc(MTYPE_OSPF_VERTEX)); + + /* Free SPF vertices, but not the list. List has ospf_vertex_free + * as deconstructor. + */ + list_delete_all_node (&vertex_list); } /* Timer for SPF calculation. */ @@ -1263,12 +1308,18 @@ ospf_spf_calculate_timer (struct thread *thread) struct route_table *new_table, *new_rtrs; struct ospf_area *area; struct listnode *node, *nnode; + struct timeval start_time, stop_time, spf_start_time; + int areas_processed = 0; + unsigned long ia_time, prune_time, rt_time; + unsigned long abr_time, total_spf_time, spf_time; + char rbuf[32]; /* reason_buf */ if (IS_DEBUG_OSPF_EVENT) zlog_debug ("SPF: Timer (SPF calculation expire)"); ospf->t_spf_calc = NULL; + quagga_gettime (QUAGGA_CLK_MONOTONIC, &spf_start_time); /* Allocate new table tree. */ new_table = route_table_init (); new_rtrs = route_table_init (); @@ -1283,21 +1334,36 @@ ospf_spf_calculate_timer (struct thread *thread) */ if (ospf->backbone && ospf->backbone == area) continue; - + ospf_spf_calculate (area, new_table, new_rtrs); + areas_processed++; } - + /* SPF for backbone, if required */ if (ospf->backbone) - ospf_spf_calculate (ospf->backbone, new_table, new_rtrs); - + { + ospf_spf_calculate (ospf->backbone, new_table, new_rtrs); + areas_processed++; + } + + quagga_gettime (QUAGGA_CLK_MONOTONIC, &stop_time); + spf_time = timeval_elapsed (stop_time, spf_start_time); + ospf_vl_shut_unapproved (ospf); + start_time = stop_time; /* saving a call */ + ospf_ia_routing (ospf, new_table, new_rtrs); + quagga_gettime (QUAGGA_CLK_MONOTONIC, &stop_time); + ia_time = timeval_elapsed (stop_time, start_time); + + quagga_gettime (QUAGGA_CLK_MONOTONIC, &start_time); ospf_prune_unreachable_networks (new_table); ospf_prune_unreachable_routers (new_rtrs); + quagga_gettime (QUAGGA_CLK_MONOTONIC, &stop_time); + prune_time = timeval_elapsed (stop_time, start_time); /* AS-external-LSA calculation should not be performed here. */ /* If new Router Route is installed, @@ -1307,9 +1373,13 @@ ospf_spf_calculate_timer (struct thread *thread) ospf_ase_calculate_timer_add (ospf); + quagga_gettime (QUAGGA_CLK_MONOTONIC, &start_time); + /* Update routing table. */ ospf_route_install (ospf, new_table); + quagga_gettime (QUAGGA_CLK_MONOTONIC, &stop_time); + rt_time = timeval_elapsed (stop_time, start_time); /* Update ABR/ASBR routing table */ if (ospf->old_rtrs) { @@ -1321,11 +1391,28 @@ ospf_spf_calculate_timer (struct thread *thread) ospf->old_rtrs = ospf->new_rtrs; ospf->new_rtrs = new_rtrs; + quagga_gettime (QUAGGA_CLK_MONOTONIC, &start_time); if (IS_OSPF_ABR (ospf)) ospf_abr_task (ospf); - if (IS_DEBUG_OSPF_EVENT) - zlog_debug ("SPF: calculation complete"); + quagga_gettime (QUAGGA_CLK_MONOTONIC, &stop_time); + abr_time = timeval_elapsed (stop_time, start_time); + + quagga_gettime (QUAGGA_CLK_MONOTONIC, &stop_time); + total_spf_time = timeval_elapsed (stop_time, spf_start_time); + ospf->ts_spf_duration.tv_sec = total_spf_time/1000000; + ospf->ts_spf_duration.tv_usec = total_spf_time % 1000000; + + ospf_get_spf_reason_str (rbuf); + + if (IS_OSPF_ABR (ospf)) + zlog_info ("SPF Processing Time(usecs): # Areas: %d, SPF Time: %ld, InterArea: %ld, Prune: %ld, RouteInstall: %ld, ABR: %ld, Total: %ld, Reason: %s\n", + areas_processed, spf_time, ia_time, prune_time, rt_time, abr_time, total_spf_time, rbuf); + else + zlog_info ("SPF Processing Time(usecs): SPF Time: %ld, InterArea: %ld, Prune: %ld, RouteInstall: %ld, Total: %ld, Reason: %s\n", + spf_time, ia_time, prune_time, rt_time, total_spf_time, rbuf); + + ospf_clear_spf_reason_flags (); return 0; } @@ -1389,6 +1476,8 @@ ospf_spf_calculate_schedule (struct ospf *ospf) if (IS_DEBUG_OSPF_EVENT) zlog_debug ("SPF: calculation timer delay = %ld", delay); + zlog_info ("SPF: Scheduled in %ld msec", delay); + ospf->t_spf_calc = thread_add_timer_msec (master, ospf_spf_calculate_timer, ospf, delay); } diff --git a/ospfd/ospf_spf.h b/ospfd/ospf_spf.h index c1316e4c..c9c539ad 100644 --- a/ospfd/ospf_spf.h +++ b/ospfd/ospf_spf.h @@ -64,4 +64,17 @@ extern void ospf_rtrs_free (struct route_table *); /* void ospf_spf_calculate_timer_add (); */ +/* What triggered the SPF ? Can have at most 32 reasons with this */ +#define SPF_FLAG_ROUTER_LSA_INSTALL 0x1 +#define SPF_FLAG_NETWORK_LSA_INSTALL 0x2 +#define SPF_FLAG_SUMMARY_LSA_INSTALL 0x4 +#define SPF_FLAG_ASBR_SUMMARY_LSA_INSTALL 0x8 +#define SPF_FLAG_MAXAGE 0x10 +#define SPF_FLAG_ABR_STATUS_CHANGE 0x20 +#define SPF_FLAG_ASBR_STATUS_CHANGE 0x40 +#define SPF_FLAG_MAX_VALUE 0x40 /* Update this when adding flags */ +#define SPF_FLAG_MISC 0x1000000 /* Keep this last */ + +extern void ospf_flag_spf_reason (unsigned int reason); + #endif /* _QUAGGA_OSPF_SPF_H */ diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 97fcffd1..5674da0c 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -2749,6 +2749,9 @@ DEFUN (show_ip_ospf, vty_out (vty, "last executed %s ago%s", ospf_timeval_dump (&result, timebuf, sizeof (timebuf)), VTY_NEWLINE); + vty_out (vty, " Last SPF duration %s%s", + ospf_timeval_dump (&ospf->ts_spf_duration, timebuf, sizeof (timebuf)), + VTY_NEWLINE); } else vty_out (vty, "has not been run%s", VTY_NEWLINE); diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h index 4242aa01..bf70d022 100644 --- a/ospfd/ospfd.h +++ b/ospfd/ospfd.h @@ -194,8 +194,9 @@ struct ospf struct route_table *external_lsas; /* Database of external LSAs, prefix is LSA's adv. network*/ - /* Time stamps. */ + /* Time stamps */ struct timeval ts_spf; /* SPF calculation time stamp. */ + struct timeval ts_spf_duration; /* Execution time of last SPF */ struct route_table *maxage_lsa; /* List of MaxAge LSA for deletion. */ int redistribute; /* Num of redistributed protocols. */ @@ -393,6 +394,9 @@ struct ospf_area /* Statistics field. */ u_int32_t spf_calculation; /* SPF Calculation Count. */ + /* Time stamps. */ + struct timeval ts_spf; /* SPF calculation time stamp. */ + /* Router count. */ u_int32_t abr_count; /* ABR router in this area. */ u_int32_t asbr_count; /* ASBR router in this area. */ From b6eef003e1a79471addea0b01853b08aed812cc8 Mon Sep 17 00:00:00 2001 From: Paul Jakma Date: Thu, 9 Oct 2014 14:19:51 +0100 Subject: [PATCH 272/482] ospfd: Some small tweaks to the SPF execution reason patch * ospf_spf.h: use an enum for the reason, and have it as a new argument to ospf_spf_calculate_schedule, no need for additional call, and let compiler do the checking. * ospf_spf.c: format changes - Quagga coding style places function names at the start of a new line, for easy grepping for definition. (ospf_spf_calculate_timer) Change the log format of SPF execution time to avoid ginormous line, and make logging conditional, as is the norm. --- ospfd/ospf_abr.c | 3 +- ospfd/ospf_asbr.c | 3 +- ospfd/ospf_ase.c | 2 +- ospfd/ospf_lsa.c | 19 ++++-------- ospfd/ospf_spf.c | 73 ++++++++++++++++++++++++++--------------------- ospfd/ospf_spf.h | 28 +++++++++--------- ospfd/ospf_vty.c | 6 ++-- 7 files changed, 64 insertions(+), 70 deletions(-) diff --git a/ospfd/ospf_abr.c b/ospfd/ospf_abr.c index ca1af2c4..e172e53c 100644 --- a/ospfd/ospf_abr.c +++ b/ospfd/ospf_abr.c @@ -556,8 +556,7 @@ ospf_check_abr_status (struct ospf *ospf) if (new_flags != ospf->flags) { - ospf_flag_spf_reason (SPF_FLAG_ABR_STATUS_CHANGE); - ospf_spf_calculate_schedule (ospf); + ospf_spf_calculate_schedule (ospf, SPF_FLAG_ABR_STATUS_CHANGE); if (IS_DEBUG_OSPF_EVENT) zlog_debug ("ospf_check_abr_status(): new router flags: %x",new_flags); ospf->flags = new_flags; diff --git a/ospfd/ospf_asbr.c b/ospfd/ospf_asbr.c index dbf7f11f..8bef1754 100644 --- a/ospfd/ospf_asbr.c +++ b/ospfd/ospf_asbr.c @@ -264,8 +264,7 @@ ospf_asbr_status_update (struct ospf *ospf, u_char status) } /* Transition from/to status ASBR, schedule timer. */ - ospf_flag_spf_reason (SPF_FLAG_ASBR_STATUS_CHANGE); - ospf_spf_calculate_schedule (ospf); + ospf_spf_calculate_schedule (ospf, SPF_FLAG_ASBR_STATUS_CHANGE); ospf_router_lsa_update (ospf); } diff --git a/ospfd/ospf_ase.c b/ospfd/ospf_ase.c index 9038b3a5..8aedc808 100644 --- a/ospfd/ospf_ase.c +++ b/ospfd/ospf_ase.c @@ -679,7 +679,7 @@ ospf_ase_calculate_timer (struct thread *t) quagga_gettime(QUAGGA_CLK_MONOTONIC, &stop_time); - zlog_info ("SPF Processing Time(usecs): External Routes: %d\n", + zlog_info ("SPF Processing Time(usecs): External Routes: %ld\n", (stop_time.tv_sec - start_time.tv_sec)*1000000L+ (stop_time.tv_usec - start_time.tv_usec)); } diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index 31cbaaef..94c31c9f 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -2409,10 +2409,7 @@ ospf_router_lsa_install (struct ospf *ospf, struct ospf_lsa *new, ospf_refresher_register_lsa (ospf, new); } if (rt_recalc) - { - ospf_flag_spf_reason (SPF_FLAG_ROUTER_LSA_INSTALL); - ospf_spf_calculate_schedule (ospf); - } + ospf_spf_calculate_schedule (ospf, SPF_FLAG_ROUTER_LSA_INSTALL); return new; } @@ -2446,10 +2443,7 @@ ospf_network_lsa_install (struct ospf *ospf, ospf_refresher_register_lsa (ospf, new); } if (rt_recalc) - { - ospf_flag_spf_reason (SPF_FLAG_NETWORK_LSA_INSTALL); - ospf_spf_calculate_schedule (ospf); - } + ospf_spf_calculate_schedule (ospf, SPF_FLAG_NETWORK_LSA_INSTALL); return new; } @@ -2472,8 +2466,7 @@ ospf_summary_lsa_install (struct ospf *ospf, struct ospf_lsa *new, /* This doesn't exist yet... */ ospf_summary_incremental_update(new); */ #else /* #if 0 */ - ospf_flag_spf_reason (SPF_FLAG_SUMMARY_LSA_INSTALL); - ospf_spf_calculate_schedule (ospf); + ospf_spf_calculate_schedule (ospf, SPF_FLAG_SUMMARY_LSA_INSTALL); #endif /* #if 0 */ } @@ -2504,8 +2497,7 @@ ospf_summary_asbr_lsa_install (struct ospf *ospf, struct ospf_lsa *new, - RFC 2328 Section 16.5 implies it should be */ /* ospf_ase_calculate_schedule(); */ #else /* #if 0 */ - ospf_flag_spf_reason (SPF_FLAG_ASBR_SUMMARY_LSA_INSTALL); - ospf_spf_calculate_schedule (ospf); + ospf_spf_calculate_schedule (ospf, SPF_FLAG_ASBR_SUMMARY_LSA_INSTALL); #endif /* #if 0 */ } @@ -3027,8 +3019,7 @@ ospf_lsa_maxage_walker_remover (struct ospf *ospf, struct ospf_lsa *lsa) ospf_ase_incremental_update (ospf, lsa); break; default: - ospf_flag_spf_reason (SPF_FLAG_MAXAGE); - ospf_spf_calculate_schedule (ospf); + ospf_spf_calculate_schedule (ospf, SPF_FLAG_MAXAGE); break; } ospf_lsa_maxage (ospf, lsa); diff --git a/ospfd/ospf_spf.c b/ospfd/ospf_spf.c index a7155bc6..1fe8ab4b 100644 --- a/ospfd/ospf_spf.c +++ b/ospfd/ospf_spf.c @@ -50,42 +50,41 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA static unsigned int spf_reason_flags = 0; -static void ospf_clear_spf_reason_flags () +static void +ospf_clear_spf_reason_flags () { spf_reason_flags = 0; } -void ospf_flag_spf_reason (unsigned int reason) +static void +ospf_spf_set_reason (ospf_spf_reason_t reason) { - if (reason <= SPF_FLAG_MAX_VALUE) - spf_reason_flags |= reason; - else - spf_reason_flags |= SPF_FLAG_MISC; + spf_reason_flags |= 1 << reason; } static void ospf_get_spf_reason_str (char *buf) { - if (buf) + if (!buf) + return; + + buf[0] = '\0'; + if (spf_reason_flags) { - buf[0] = '\0'; - if (spf_reason_flags) - { - if (spf_reason_flags & SPF_FLAG_ROUTER_LSA_INSTALL) - strcat (buf, "R, "); - if (spf_reason_flags & SPF_FLAG_NETWORK_LSA_INSTALL) - strcat (buf, "N, "); - if (spf_reason_flags & SPF_FLAG_SUMMARY_LSA_INSTALL) - strcat (buf, "S, "); - if (spf_reason_flags & SPF_FLAG_ASBR_SUMMARY_LSA_INSTALL) - strcat (buf, "AS, "); - if (spf_reason_flags & SPF_FLAG_ABR_STATUS_CHANGE) - strcat (buf, "ABR, "); - if (spf_reason_flags & SPF_FLAG_ASBR_STATUS_CHANGE) - strcat (buf, "ASBR, "); - if (spf_reason_flags & SPF_FLAG_MAXAGE) - strcat (buf, "M, "); - } + if (spf_reason_flags & SPF_FLAG_ROUTER_LSA_INSTALL) + strcat (buf, "R, "); + if (spf_reason_flags & SPF_FLAG_NETWORK_LSA_INSTALL) + strcat (buf, "N, "); + if (spf_reason_flags & SPF_FLAG_SUMMARY_LSA_INSTALL) + strcat (buf, "S, "); + if (spf_reason_flags & SPF_FLAG_ASBR_SUMMARY_LSA_INSTALL) + strcat (buf, "AS, "); + if (spf_reason_flags & SPF_FLAG_ABR_STATUS_CHANGE) + strcat (buf, "ABR, "); + if (spf_reason_flags & SPF_FLAG_ASBR_STATUS_CHANGE) + strcat (buf, "ASBR, "); + if (spf_reason_flags & SPF_FLAG_MAXAGE) + strcat (buf, "M, "); buf[strlen(buf)-2] = '\0'; /* skip the last ", " */ } } @@ -1313,7 +1312,7 @@ ospf_spf_calculate_timer (struct thread *thread) unsigned long ia_time, prune_time, rt_time; unsigned long abr_time, total_spf_time, spf_time; char rbuf[32]; /* reason_buf */ - + if (IS_DEBUG_OSPF_EVENT) zlog_debug ("SPF: Timer (SPF calculation expire)"); @@ -1405,12 +1404,18 @@ ospf_spf_calculate_timer (struct thread *thread) ospf_get_spf_reason_str (rbuf); - if (IS_OSPF_ABR (ospf)) - zlog_info ("SPF Processing Time(usecs): # Areas: %d, SPF Time: %ld, InterArea: %ld, Prune: %ld, RouteInstall: %ld, ABR: %ld, Total: %ld, Reason: %s\n", - areas_processed, spf_time, ia_time, prune_time, rt_time, abr_time, total_spf_time, rbuf); - else - zlog_info ("SPF Processing Time(usecs): SPF Time: %ld, InterArea: %ld, Prune: %ld, RouteInstall: %ld, Total: %ld, Reason: %s\n", - spf_time, ia_time, prune_time, rt_time, total_spf_time, rbuf); + if (IS_DEBUG_OSPF_EVENT) + { + zlog_info ("SPF Processing Time(usecs): %ld", total_spf_time); + zlog_info ("\t SPF Time: %ld", spf_time); + zlog_info ("\t InterArea: %ld", ia_time); + zlog_info ("\t Prune: %ld", prune_time); + zlog_info ("\tRouteInstall: %ld", rt_time); + if (IS_OSPF_ABR (ospf)) + zlog_info ("\t ABR: %ld (%d areas)", + abr_time, areas_processed); + zlog_info ("Reason(s) for SPF: %s", rbuf); + } ospf_clear_spf_reason_flags (); @@ -1420,7 +1425,7 @@ ospf_spf_calculate_timer (struct thread *thread) /* Add schedule for SPF calculation. To avoid frequenst SPF calc, we set timer for SPF calc. */ void -ospf_spf_calculate_schedule (struct ospf *ospf) +ospf_spf_calculate_schedule (struct ospf *ospf, ospf_spf_reason_t reason) { unsigned long delay, elapsed, ht; struct timeval result; @@ -1432,6 +1437,8 @@ ospf_spf_calculate_schedule (struct ospf *ospf) if (ospf == NULL) return; + ospf_spf_set_reason (reason); + /* SPF calculation timer is already scheduled. */ if (ospf->t_spf_calc) { diff --git a/ospfd/ospf_spf.h b/ospfd/ospf_spf.h index c9c539ad..e33b3e5f 100644 --- a/ospfd/ospf_spf.h +++ b/ospfd/ospf_spf.h @@ -59,22 +59,20 @@ struct vertex_parent int backlink; /* index back to parent for router-lsa's */ }; -extern void ospf_spf_calculate_schedule (struct ospf *); +/* What triggered the SPF ? */ +typedef enum { + SPF_FLAG_ROUTER_LSA_INSTALL = 1, + SPF_FLAG_NETWORK_LSA_INSTALL, + SPF_FLAG_SUMMARY_LSA_INSTALL, + SPF_FLAG_ASBR_SUMMARY_LSA_INSTALL, + SPF_FLAG_MAXAGE, + SPF_FLAG_ABR_STATUS_CHANGE, + SPF_FLAG_ASBR_STATUS_CHANGE, + SPF_FLAG_CONFIG_CHANGE, +} ospf_spf_reason_t; + +extern void ospf_spf_calculate_schedule (struct ospf *, ospf_spf_reason_t); extern void ospf_rtrs_free (struct route_table *); /* void ospf_spf_calculate_timer_add (); */ - -/* What triggered the SPF ? Can have at most 32 reasons with this */ -#define SPF_FLAG_ROUTER_LSA_INSTALL 0x1 -#define SPF_FLAG_NETWORK_LSA_INSTALL 0x2 -#define SPF_FLAG_SUMMARY_LSA_INSTALL 0x4 -#define SPF_FLAG_ASBR_SUMMARY_LSA_INSTALL 0x8 -#define SPF_FLAG_MAXAGE 0x10 -#define SPF_FLAG_ABR_STATUS_CHANGE 0x20 -#define SPF_FLAG_ASBR_STATUS_CHANGE 0x40 -#define SPF_FLAG_MAX_VALUE 0x40 /* Update this when adding flags */ -#define SPF_FLAG_MISC 0x1000000 /* Keep this last */ - -extern void ospf_flag_spf_reason (unsigned int reason); - #endif /* _QUAGGA_OSPF_SPF_H */ diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 5674da0c..72493a20 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -771,7 +771,7 @@ ospf_find_vl_data (struct ospf *ospf, struct ospf_vl_config_data *vl_config) { vl_data->vl_oi = ospf_vl_new (ospf, vl_data); ospf_vl_add (ospf, vl_data); - ospf_spf_calculate_schedule (ospf); + ospf_spf_calculate_schedule (ospf, SPF_FLAG_CONFIG_CHANGE); } } return vl_data; @@ -2172,7 +2172,7 @@ DEFUN (ospf_compatible_rfc1583, if (!CHECK_FLAG (ospf->config, OSPF_RFC1583_COMPATIBLE)) { SET_FLAG (ospf->config, OSPF_RFC1583_COMPATIBLE); - ospf_spf_calculate_schedule (ospf); + ospf_spf_calculate_schedule (ospf, SPF_FLAG_CONFIG_CHANGE); } return CMD_SUCCESS; } @@ -2189,7 +2189,7 @@ DEFUN (no_ospf_compatible_rfc1583, if (CHECK_FLAG (ospf->config, OSPF_RFC1583_COMPATIBLE)) { UNSET_FLAG (ospf->config, OSPF_RFC1583_COMPATIBLE); - ospf_spf_calculate_schedule (ospf); + ospf_spf_calculate_schedule (ospf, SPF_FLAG_CONFIG_CHANGE); } return CMD_SUCCESS; } From d1b0991ad8404366811a7ccc86e76f70a1ba399e Mon Sep 17 00:00:00 2001 From: Dinesh G Dutt Date: Tue, 30 Sep 2014 12:54:13 -0700 Subject: [PATCH 273/482] Make static IPv6 routes respect non-default routing tables. Signed-off-by: Nolan Leake Reviewed-by: Shrijeet Mukherjee Reviewed-by: Paul Jakma --- zebra/zebra_rib.c | 1 + 1 file changed, 1 insertion(+) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index b1d88369..245011eb 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -2916,6 +2916,7 @@ static_install_ipv6 (struct prefix *p, struct static_ipv6 *si) rib->type = ZEBRA_ROUTE_STATIC; rib->distance = si->distance; rib->metric = 0; + rib->table = zebrad.rtm_table_default; rib->nexthop_num = 0; switch (si->type) From 56a5f7752d9bf7297ea022544112909cf2ccba97 Mon Sep 17 00:00:00 2001 From: Dinesh G Dutt Date: Tue, 30 Sep 2014 12:58:04 -0700 Subject: [PATCH 274/482] Added show command to display only the routes in the RIB that does not count ECMPs Signed-off-by: Ayan Banerjee Reviewed-by: JR Rivers Reviewed-by: Paul Jakma --- zebra/zebra_vty.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 1d12ac5f..9d6c1ddd 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -1074,6 +1074,84 @@ vty_show_ip_route_summary (struct vty *vty, struct route_table *table) fib_cnt[ZEBRA_ROUTE_TOTAL], VTY_NEWLINE); } +/* + * Implementation of the ip route summary prefix command. + * + * This command prints the primary prefixes that have been installed by various + * protocols on the box. + * + */ +static void +vty_show_ip_route_summary_prefix (struct vty *vty, struct route_table *table) +{ + struct route_node *rn; + struct rib *rib; + struct nexthop *nexthop; +#define ZEBRA_ROUTE_IBGP ZEBRA_ROUTE_MAX +#define ZEBRA_ROUTE_TOTAL (ZEBRA_ROUTE_IBGP + 1) + u_int32_t rib_cnt[ZEBRA_ROUTE_TOTAL + 1]; + u_int32_t fib_cnt[ZEBRA_ROUTE_TOTAL + 1]; + u_int32_t i; + int cnt; + + memset (&rib_cnt, 0, sizeof(rib_cnt)); + memset (&fib_cnt, 0, sizeof(fib_cnt)); + for (rn = route_top (table); rn; rn = route_next (rn)) + RNODE_FOREACH_RIB (rn, rib) + { + + /* + * In case of ECMP, count only once. + */ + cnt = 0; + for (nexthop = rib->nexthop; (!cnt && nexthop); nexthop = nexthop->next) + { + cnt++; + rib_cnt[ZEBRA_ROUTE_TOTAL]++; + rib_cnt[rib->type]++; + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) + { + fib_cnt[ZEBRA_ROUTE_TOTAL]++; + fib_cnt[rib->type]++; + } + if (rib->type == ZEBRA_ROUTE_BGP && + CHECK_FLAG (rib->flags, ZEBRA_FLAG_IBGP)) + { + rib_cnt[ZEBRA_ROUTE_IBGP]++; + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) + fib_cnt[ZEBRA_ROUTE_IBGP]++; + } + } + } + + vty_out (vty, "%-20s %-20s %-20s %s", + "Route Source", "Prefix Routes", "FIB", VTY_NEWLINE); + + for (i = 0; i < ZEBRA_ROUTE_MAX; i++) + { + if (rib_cnt[i] > 0) + { + if (i == ZEBRA_ROUTE_BGP) + { + vty_out (vty, "%-20s %-20d %-20d %s", "ebgp", + rib_cnt[ZEBRA_ROUTE_BGP] - rib_cnt[ZEBRA_ROUTE_IBGP], + fib_cnt[ZEBRA_ROUTE_BGP] - fib_cnt[ZEBRA_ROUTE_IBGP], + VTY_NEWLINE); + vty_out (vty, "%-20s %-20d %-20d %s", "ibgp", + rib_cnt[ZEBRA_ROUTE_IBGP], fib_cnt[ZEBRA_ROUTE_IBGP], + VTY_NEWLINE); + } + else + vty_out (vty, "%-20s %-20d %-20d %s", zebra_route_string(i), + rib_cnt[i], fib_cnt[i], VTY_NEWLINE); + } + } + + vty_out (vty, "------%s", VTY_NEWLINE); + vty_out (vty, "%-20s %-20d %-20d %s", "Totals", rib_cnt[ZEBRA_ROUTE_TOTAL], + fib_cnt[ZEBRA_ROUTE_TOTAL], VTY_NEWLINE); +} + /* Show route summary. */ DEFUN (show_ip_route_summary, show_ip_route_summary_cmd, @@ -1094,6 +1172,27 @@ DEFUN (show_ip_route_summary, return CMD_SUCCESS; } +/* Show route summary prefix. */ +DEFUN (show_ip_route_summary_prefix, + show_ip_route_summary_prefix_cmd, + "show ip route summary prefix", + SHOW_STR + IP_STR + "IP routing table\n" + "Summary of all routes\n" + "Prefix routes\n") +{ + struct route_table *table; + + table = vrf_table (AFI_IP, SAFI_UNICAST, 0); + if (! table) + return CMD_SUCCESS; + + vty_show_ip_route_summary_prefix (vty, table); + + return CMD_SUCCESS; +} + /* Write IPv4 static route configuration. */ static int static_config_ipv4 (struct vty *vty) @@ -1931,6 +2030,27 @@ DEFUN (show_ipv6_route_summary, return CMD_SUCCESS; } +/* Show ipv6 route summary prefix. */ +DEFUN (show_ipv6_route_summary_prefix, + show_ipv6_route_summary_prefix_cmd, + "show ipv6 route summary prefix", + SHOW_STR + IP_STR + "IPv6 routing table\n" + "Summary of all IPv6 routes\n" + "Prefix routes\n") +{ + struct route_table *table; + + table = vrf_table (AFI_IP6, SAFI_UNICAST, 0); + if (! table) + return CMD_SUCCESS; + + vty_show_ip_route_summary_prefix (vty, table); + + return CMD_SUCCESS; +} + /* * Show IPv6 mroute command.Used to dump * the Multicast routing table. @@ -2100,6 +2220,7 @@ zebra_vty_init (void) install_element (VIEW_NODE, &show_ip_route_protocol_cmd); install_element (VIEW_NODE, &show_ip_route_supernets_cmd); install_element (VIEW_NODE, &show_ip_route_summary_cmd); + install_element (VIEW_NODE, &show_ip_route_summary_prefix_cmd); install_element (ENABLE_NODE, &show_ip_route_cmd); install_element (ENABLE_NODE, &show_ip_route_addr_cmd); install_element (ENABLE_NODE, &show_ip_route_prefix_cmd); @@ -2107,6 +2228,7 @@ zebra_vty_init (void) install_element (ENABLE_NODE, &show_ip_route_protocol_cmd); install_element (ENABLE_NODE, &show_ip_route_supernets_cmd); install_element (ENABLE_NODE, &show_ip_route_summary_cmd); + install_element (ENABLE_NODE, &show_ip_route_summary_prefix_cmd); install_element (VIEW_NODE, &show_ip_mroute_cmd); install_element (ENABLE_NODE, &show_ip_mroute_cmd); @@ -2131,6 +2253,7 @@ zebra_vty_init (void) install_element (CONFIG_NODE, &no_ipv6_route_ifname_flags_pref_cmd); install_element (VIEW_NODE, &show_ipv6_route_cmd); install_element (VIEW_NODE, &show_ipv6_route_summary_cmd); + install_element (VIEW_NODE, &show_ipv6_route_summary_prefix_cmd); install_element (VIEW_NODE, &show_ipv6_route_protocol_cmd); install_element (VIEW_NODE, &show_ipv6_route_addr_cmd); install_element (VIEW_NODE, &show_ipv6_route_prefix_cmd); @@ -2141,6 +2264,7 @@ zebra_vty_init (void) install_element (ENABLE_NODE, &show_ipv6_route_prefix_cmd); install_element (ENABLE_NODE, &show_ipv6_route_prefix_longer_cmd); install_element (ENABLE_NODE, &show_ipv6_route_summary_cmd); + install_element (ENABLE_NODE, &show_ipv6_route_summary_prefix_cmd); install_element (VIEW_NODE, &show_ipv6_mroute_cmd); install_element (ENABLE_NODE, &show_ipv6_mroute_cmd); From 1c06334f51c00471b0731227384ef42dc463db54 Mon Sep 17 00:00:00 2001 From: Dinesh G Dutt Date: Tue, 30 Sep 2014 13:04:45 -0700 Subject: [PATCH 275/482] Avoid timing out of adjacencies by serving all interfaces in round-robin. Ensure that all interfaces are served in a round robin fashion during write. This prevents adjacencies from timing out when you have a lot of LSAs to be sent out each adjacency. This is essentially a scalability improvement. Signed-off-by: Ayan Banerjee Reviewed-by: Dinesh G Dutt Reviewed-by: Scott Feldman Reviewed-by: Paul Jakma --- ospfd/ospf_packet.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index 17260eb1..d2504c78 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -760,6 +760,11 @@ ospf_write (struct thread *thread) sockopt_iphdrincl_swab_htosys (&iph); ret = sendmsg (ospf->fd, &msg, flags); sockopt_iphdrincl_swab_systoh (&iph); + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_write to %s, " + "id %d, off %d, len %d, interface %s, mtu %u:", + inet_ntoa (iph.ip_dst), iph.ip_id, iph.ip_off, iph.ip_len, + oi->ifp->name, oi->ifp->mtu); if (ret < 0) zlog_warn ("*** sendmsg in ospf_write failed to %s, " @@ -789,10 +794,16 @@ ospf_write (struct thread *thread) /* Now delete packet from queue. */ ospf_packet_delete (oi); + /* Move this interface to the tail of write_q to + serve everyone in a round robin fashion */ + list_delete_node (ospf->oi_write_q, node); if (ospf_fifo_head (oi->obuf) == NULL) { oi->on_write_q = 0; - list_delete_node (ospf->oi_write_q, node); + } + else + { + listnode_add (ospf->oi_write_q, oi); } /* If packets still remain in queue, call write thread. */ @@ -3326,7 +3337,7 @@ ospf_make_ls_upd (struct ospf_interface *oi, struct list *update, struct stream u_int16_t ls_age; if (IS_DEBUG_OSPF_EVENT) - zlog_debug ("ospf_make_ls_upd: List Iteration"); + zlog_debug ("ospf_make_ls_upd: List Iteration %d", count); lsa = listgetdata (node); @@ -3732,7 +3743,8 @@ ospf_ls_upd_queue_send (struct ospf_interface *oi, struct list *update, u_int16_t length = OSPF_HEADER_SIZE; if (IS_DEBUG_OSPF_EVENT) - zlog_debug ("listcount = %d, dst %s", listcount (update), inet_ntoa(addr)); + zlog_debug ("listcount = %d, [%s]dst %s", listcount (update), IF_NAME(oi), + inet_ntoa(addr)); op = ospf_ls_upd_packet_new (update, oi); From 6d831139569dbee69acc631361db917e2c47feeb Mon Sep 17 00:00:00 2001 From: Paul Jakma Date: Thu, 9 Oct 2014 16:05:15 +0100 Subject: [PATCH 276/482] ospfd: Tweak previous iface RR write patch to avoid free/malloc & redundant log * linklist.{c,h}: (listnode_move_to_tail) new unction to move a listnode to tail of list. * ospf_packet.c: (ospf_write) remove debug that seemed to be mostly covered by existing debug. Use listnode_move_to_tail to just move the list node to the end of the tail, rather than freeing the one to hand and allocing a new one. --- lib/linklist.c | 7 +++++++ lib/linklist.h | 1 + ospfd/ospf_packet.c | 12 ++---------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/linklist.c b/lib/linklist.c index 370b2fa6..4b16f07d 100644 --- a/lib/linklist.c +++ b/lib/linklist.c @@ -159,6 +159,13 @@ listnode_add_after (struct list *list, struct listnode *pp, void *val) list->count++; } +/* Move given listnode to tail of the list */ +void +listnode_move_to_tail (struct list *l, struct listnode *n) +{ + LISTNODE_DETACH(l,n); + LISTNODE_ATTACH(l,n); +} /* Delete specific date pointer from the list. */ void diff --git a/lib/linklist.h b/lib/linklist.h index 24a9e206..c8d715e1 100644 --- a/lib/linklist.h +++ b/lib/linklist.h @@ -68,6 +68,7 @@ extern void list_free (struct list *); extern void listnode_add (struct list *, void *); extern void listnode_add_sort (struct list *, void *); extern void listnode_add_after (struct list *, struct listnode *, void *); +extern void listnode_move_to_tail (struct list *, struct listnode *); extern void listnode_delete (struct list *, void *); extern struct listnode *listnode_lookup (struct list *, void *); extern void *listnode_head (struct list *); diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index d2504c78..36aa8958 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -760,11 +760,6 @@ ospf_write (struct thread *thread) sockopt_iphdrincl_swab_htosys (&iph); ret = sendmsg (ospf->fd, &msg, flags); sockopt_iphdrincl_swab_systoh (&iph); - if (IS_DEBUG_OSPF_EVENT) - zlog_debug ("ospf_write to %s, " - "id %d, off %d, len %d, interface %s, mtu %u:", - inet_ntoa (iph.ip_dst), iph.ip_id, iph.ip_off, iph.ip_len, - oi->ifp->name, oi->ifp->mtu); if (ret < 0) zlog_warn ("*** sendmsg in ospf_write failed to %s, " @@ -796,14 +791,11 @@ ospf_write (struct thread *thread) /* Move this interface to the tail of write_q to serve everyone in a round robin fashion */ - list_delete_node (ospf->oi_write_q, node); + listnode_move_to_tail (ospf->oi_write_q, node); if (ospf_fifo_head (oi->obuf) == NULL) { oi->on_write_q = 0; - } - else - { - listnode_add (ospf->oi_write_q, oi); + list_delete_node (ospf->oi_write_q, node); } /* If packets still remain in queue, call write thread. */ From 8306be211f1bcd5a19e74d08cde399e1b518ed25 Mon Sep 17 00:00:00 2001 From: Dinesh G Dutt Date: Tue, 30 Sep 2014 14:11:17 -0700 Subject: [PATCH 277/482] OSPFd: Update timestamps when we MaxAge LSAs. When an LSA is flushed we need to update the timestamps for them. This allows for the node to give the neighbor sufficient time to send back an acknowledgement before retransmission kicks in. Signed-off-by: Dinesh G Dutt Reviewed-by: Scott Feldman Reviewed-by: James Li Reviewed-by: Paul Jakma --- ospfd/ospf_flood.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ospfd/ospf_flood.c b/ospfd/ospf_flood.c index 2c33b00e..d18314a9 100644 --- a/ospfd/ospf_flood.c +++ b/ospfd/ospf_flood.c @@ -980,7 +980,12 @@ ospf_ls_retransmit_delete_nbr_as (struct ospf *ospf, struct ospf_lsa *lsa) void ospf_lsa_flush_area (struct ospf_lsa *lsa, struct ospf_area *area) { + /* Reset the lsa origination time such that it gives + more time for the ACK to be received and avoid + retransmissions */ lsa->data->ls_age = htons (OSPF_LSA_MAXAGE); + lsa->tv_recv = recent_relative_time (); + lsa->tv_orig = lsa->tv_recv; ospf_flood_through_area (area, NULL, lsa); ospf_lsa_maxage (area->ospf, lsa); } @@ -988,7 +993,12 @@ ospf_lsa_flush_area (struct ospf_lsa *lsa, struct ospf_area *area) void ospf_lsa_flush_as (struct ospf *ospf, struct ospf_lsa *lsa) { + /* Reset the lsa origination time such that it gives + more time for the ACK to be received and avoid + retransmissions */ lsa->data->ls_age = htons (OSPF_LSA_MAXAGE); + lsa->tv_recv = recent_relative_time (); + lsa->tv_orig = lsa->tv_recv; ospf_flood_through_as (ospf, NULL, lsa); ospf_lsa_maxage (ospf, lsa); } From ad5233a1bcdd7124992300673ad9c1035336eadd Mon Sep 17 00:00:00 2001 From: Dinesh G Dutt Date: Tue, 30 Sep 2014 14:19:57 -0700 Subject: [PATCH 278/482] Add set ipv6 next-hop peer-address command. IPv4 has the ability to specify the peer address with the keyword peer-address. IPv6 mandates the use of a specific global or local address only in setting the next-hop in routemaps. This makes it cumbersome to configure some large networks with BGP and IPv6. This patch fixes that deficiency. Signed-off-by: Dinesh G Dutt Reviewed-by: Paul Jakma --- bgpd/bgp_routemap.c | 122 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 121 insertions(+), 1 deletion(-) diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 06b08592..a1b7f339 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -2021,6 +2021,100 @@ struct route_map_rule_cmd route_set_ipv6_nexthop_local_cmd = route_set_ipv6_nexthop_local_compile, route_set_ipv6_nexthop_local_free }; + +/* `set ipv6 nexthop peer-address' */ + +/* Set nexthop to object. ojbect must be pointer to struct attr. */ +static route_map_result_t +route_set_ipv6_nexthop_peer (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + int *use_peer_address; + struct in6_addr peer_address; + struct bgp_info *bgp_info; + struct peer *peer; + char peer_addr_buf[INET6_ADDRSTRLEN]; + + if (type == RMAP_BGP) + { + /* Fetch routemap's rule information. */ + use_peer_address = rule; + bgp_info = object; + peer = bgp_info->peer; + + if ((CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_IN) || + CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_IMPORT)) + && peer->su_remote + && sockunion_family (peer->su_remote) == AF_INET6) + { + inet_pton (AF_INET6, sockunion2str (peer->su_remote, + peer_addr_buf, + INET6_ADDRSTRLEN), + &peer_address); + } + else if (CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_OUT) + && peer->su_local + && sockunion_family (peer->su_local) == AF_INET6) + { + inet_pton (AF_INET, sockunion2str (peer->su_local, + peer_addr_buf, + INET6_ADDRSTRLEN), + &peer_address); + } + + if (IN6_IS_ADDR_LINKLOCAL(&peer_address)) + { + /* Set next hop value. */ + (bgp_attr_extra_get (bgp_info->attr))->mp_nexthop_local = peer_address; + + /* Set nexthop length. */ + if (bgp_info->attr->extra->mp_nexthop_len != 32) + bgp_info->attr->extra->mp_nexthop_len = 32; + } + else + { + /* Set next hop value. */ + (bgp_attr_extra_get (bgp_info->attr))->mp_nexthop_global = peer_address; + + /* Set nexthop length. */ + if (bgp_info->attr->extra->mp_nexthop_len == 0) + bgp_info->attr->extra->mp_nexthop_len = 16; + } + } + + return RMAP_OKAY; +} + +/* Route map `ip next-hop' compile function. Given string is converted + to struct in_addr structure. */ +static void * +route_set_ipv6_nexthop_peer_compile (const char *arg) +{ + int ret; + int *rins = NULL; + + rins = XCALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (int)); + *rins = 1; + + return rins; +} + +/* Free route map's compiled `ip next-hop' value. */ +static void +route_set_ipv6_nexthop_peer_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for ip nexthop set. */ +struct route_map_rule_cmd route_set_ipv6_nexthop_peer_cmd = +{ + "ipv6 next-hop peer-address", + route_set_ipv6_nexthop_peer, + route_set_ipv6_nexthop_peer_compile, + route_set_ipv6_nexthop_peer_free +}; + #endif /* HAVE_IPV6 */ /* `set vpnv4 nexthop A.B.C.D' */ @@ -3571,6 +3665,29 @@ DEFUN (no_match_ipv6_address_prefix_list, return bgp_route_match_delete (vty, vty->index, "ipv6 address prefix-list", argv[0]); } +DEFUN (set_ipv6_nexthop_peer, + set_ipv6_nexthop_peer_cmd, + "set ipv6 next-hop peer-address", + SET_STR + IPV6_STR + "Next hop address\n" + "Use peer address (for BGP only)\n") +{ + return bgp_route_set_add (vty, vty->index, "ipv6 next-hop peer-address", NULL); +} + +DEFUN (no_set_ipv6_nexthop_peer, + no_set_ipv6_nexthop_peer_cmd, + "no set ipv6 next-hop peer-address", + NO_STR + SET_STR + IPV6_STR + "IPv6 next-hop address\n" + ) +{ + return bgp_route_set_delete (vty, vty->index, "ipv6 next-hop", argv[0]); +} + DEFUN (set_ipv6_nexthop_global, set_ipv6_nexthop_global_cmd, "set ipv6 next-hop global X:X::X:X", @@ -3909,7 +4026,8 @@ bgp_route_map_init (void) route_map_install_match (&route_match_ipv6_address_prefix_list_cmd); route_map_install_set (&route_set_ipv6_nexthop_global_cmd); route_map_install_set (&route_set_ipv6_nexthop_local_cmd); - + route_map_install_set (&route_set_ipv6_nexthop_peer_cmd); + install_element (RMAP_NODE, &match_ipv6_address_cmd); install_element (RMAP_NODE, &no_match_ipv6_address_cmd); install_element (RMAP_NODE, &match_ipv6_next_hop_cmd); @@ -3922,6 +4040,8 @@ bgp_route_map_init (void) install_element (RMAP_NODE, &set_ipv6_nexthop_local_cmd); install_element (RMAP_NODE, &no_set_ipv6_nexthop_local_cmd); install_element (RMAP_NODE, &no_set_ipv6_nexthop_local_val_cmd); + install_element (RMAP_NODE, &set_ipv6_nexthop_peer_cmd); + install_element (RMAP_NODE, &no_set_ipv6_nexthop_peer_cmd); #endif /* HAVE_IPV6 */ /* AS-Pathlimit: functionality removed, commands kept for From 4bab6806914dbb4b43f376ebf966a034a0ea72cd Mon Sep 17 00:00:00 2001 From: Vipin Kumar Date: Tue, 30 Sep 2014 14:32:22 -0700 Subject: [PATCH 279/482] Fix to take care of ordering between interface and router ospf command. SYMPTOM: Interface mode OSPF area configuration is not retained after restarting quagga. Example - quagga(config)# interface swp49 quagga(config-if)# ip ospf area 0.0.0.0 quagga# sh run interface swp49 ip ospf area 0.0.0.0 ipv6 nd suppress-ra link-detect ! quagga# write memory * Restart quagga at this point* quagga# sh run interface swp49 ipv6 nd suppress-ra link-detect ! ISSUE: The issue is that the interface mode commands can reach the OSPF process even before 'router ospf' command that initializes the default OSPF instance, this is not getting handled properly in OSPF process. FIX: Initialize the default OSPF instance during OSPF process initializations, which is before 'router ospf' command is received in OSPF process. So, when interface mode command is received, it is guaranteed to have ospf instance to work with. Other way could be to call ospf_get() instead of ospf_lookup() while processing the config command callbacks, although OSPF needs to have at least one instance structure anyways, therefore calling it unconditionally in OSPF initializations should be fine too. There could be more elaborate fix(es) possible to handle this, like adding some ordering mechanism for commands as they are read by a process, or storing the received command and applying it after the commands its dependent upon are processed. For the issue at hand, initializing the default instance in main() serves the purpose well. Signed-off-by: Vipin Kumar Reviewed-by: Dinesh Dutt Reviewed-by: Paul Jakma --- ospfd/ospf_main.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ospfd/ospf_main.c b/ospfd/ospf_main.c index 82735b73..96dfd579 100644 --- a/ospfd/ospf_main.c +++ b/ospfd/ospf_main.c @@ -310,6 +310,15 @@ main (int argc, char **argv) ospf_opaque_init (); #endif /* HAVE_OPAQUE_LSA */ + /* Need to initialize the default ospf structure, so the interface mode + commands can be duly processed if they are received before 'router ospf', + when quagga(ospfd) is restarted */ + if (!ospf_get()) + { + zlog_err("OSPF instance init failed: %s", strerror(errno)); + exit (1); + } + /* Get configuration file. */ vty_read_config (config_file, config_default); From 443010383e2c8e5dc1bc722d9e22a97c513b4647 Mon Sep 17 00:00:00 2001 From: Paul Jakma Date: Thu, 9 Oct 2014 18:14:54 +0100 Subject: [PATCH 280/482] bgpd: remove unused variables --- bgpd/bgp_routemap.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index a1b7f339..b74554d6 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -2029,7 +2029,6 @@ static route_map_result_t route_set_ipv6_nexthop_peer (void *rule, struct prefix *prefix, route_map_object_t type, void *object) { - int *use_peer_address; struct in6_addr peer_address; struct bgp_info *bgp_info; struct peer *peer; @@ -2038,7 +2037,6 @@ route_set_ipv6_nexthop_peer (void *rule, struct prefix *prefix, if (type == RMAP_BGP) { /* Fetch routemap's rule information. */ - use_peer_address = rule; bgp_info = object; peer = bgp_info->peer; @@ -2090,7 +2088,6 @@ route_set_ipv6_nexthop_peer (void *rule, struct prefix *prefix, static void * route_set_ipv6_nexthop_peer_compile (const char *arg) { - int ret; int *rins = NULL; rins = XCALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (int)); From dd49eb1f0232cd0600a3565b44b5c066a8d7872d Mon Sep 17 00:00:00 2001 From: Vipin Kumar Date: Tue, 30 Sep 2014 14:36:38 -0700 Subject: [PATCH 281/482] Fix BGP's use of restart bit. bgpd-restart-bit-fix.patch ISSUE: Quagga BGP doesn't send or use the restart-bit via the Graceful-Restart(GR) capability. GR capability implementation isn't complete as per the RFC. PATCH: Patch uses BGP instance creation as the beginning of the startup period, and 'restart_time' is taken as the startup period. As a result, BGP will set the restart bit in the GR capability of the OPEN messages during the startup period. As an indication of quagga implementation's capability of sending End-Of-RIB, helping a restarting neighbor, quagga BGP will now send global GR capability irrespective of the graceful-restart config in BGP and the address-family specific GR capability will be sent only if the GR config is present. Forwarding bit is not set assuming its not preserved. Incorporated feedback from David Lamparter via the quagga-dev mailing list. Signed-off-by: Vipin Kumar Reviewed-by: Pradosh Mohapatra Reviewed-by: Paul Jakma --- bgpd/bgp_open.c | 51 +++++++++++++++++++++++++++++++++++++---------- bgpd/bgp_packet.c | 4 ++++ bgpd/bgpd.c | 15 ++++++++++++++ bgpd/bgpd.h | 4 ++++ 4 files changed, 64 insertions(+), 10 deletions(-) diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c index 7bf35016..fe741aa3 100644 --- a/bgpd/bgp_open.c +++ b/bgpd/bgp_open.c @@ -344,7 +344,10 @@ bgp_capability_restart (struct peer *peer, struct capability_header *caphdr) SET_FLAG (peer->cap, PEER_CAP_RESTART_RCV); restart_flag_time = stream_getw(s); if (CHECK_FLAG (restart_flag_time, RESTART_R_BIT)) - restart_bit = 1; + { + SET_FLAG (peer->cap, PEER_CAP_RESTART_BIT_RCV); + restart_bit = 1; + } UNSET_FLAG (restart_flag_time, 0xF000); peer->v_gr_restart = restart_flag_time; @@ -898,10 +901,11 @@ void bgp_open_capability (struct stream *s, struct peer *peer) { u_char len; - unsigned long cp; + unsigned long cp, capp, rcapp; afi_t afi; safi_t safi; as_t local_as; + u_int32_t restart_time; /* Remember current pointer for Opt Parm Len. */ cp = stream_get_endp (s); @@ -1020,16 +1024,43 @@ bgp_open_capability (struct stream *s, struct peer *peer) stream_putc (s, CAPABILITY_CODE_DYNAMIC_LEN); } - /* Graceful restart capability */ + /* Sending base graceful-restart capability irrespective of the config */ + SET_FLAG (peer->cap, PEER_CAP_RESTART_ADV); + stream_putc (s, BGP_OPEN_OPT_CAP); + capp = stream_get_endp (s); /* Set Capability Len Pointer */ + stream_putc (s, 0); /* Capability Length */ + stream_putc (s, CAPABILITY_CODE_RESTART); + rcapp = stream_get_endp (s); /* Set Restart Capability Len Pointer */ + stream_putc (s, 0); + restart_time = peer->bgp->restart_time; + if (peer->bgp->t_startup) + { + SET_FLAG (restart_time, RESTART_R_BIT); + SET_FLAG (peer->cap, PEER_CAP_RESTART_BIT_ADV); + } + stream_putw (s, restart_time); + + /* Send address-family specific graceful-restart capability only when GR config + is present */ if (bgp_flag_check (peer->bgp, BGP_FLAG_GRACEFUL_RESTART)) { - SET_FLAG (peer->cap, PEER_CAP_RESTART_ADV); - stream_putc (s, BGP_OPEN_OPT_CAP); - stream_putc (s, CAPABILITY_CODE_RESTART_LEN + 2); - stream_putc (s, CAPABILITY_CODE_RESTART); - stream_putc (s, CAPABILITY_CODE_RESTART_LEN); - stream_putw (s, peer->bgp->restart_time); - } + for (afi = AFI_IP ; afi < AFI_MAX ; afi++) + for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++) + if (peer->afc[afi][safi]) + { + stream_putw (s, afi); + stream_putc (s, safi); + stream_putc (s, 0); //Forwarding is not retained as of now. + } + } + + /* Total Graceful restart capability Len. */ + len = stream_get_endp (s) - rcapp - 1; + stream_putc_at (s, rcapp, len); + + /* Total Capability Len. */ + len = stream_get_endp (s) - capp - 1; + stream_putc_at (s, capp, len); /* Total Opt Parm Len. */ len = stream_get_endp (s) - cp - 1; diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index 35a22c1e..14fd6e51 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -613,6 +613,10 @@ bgp_write_packet (struct peer *peer) { if (CHECK_FLAG (adv->binfo->peer->cap, PEER_CAP_RESTART_RCV) && CHECK_FLAG (adv->binfo->peer->cap, PEER_CAP_RESTART_ADV) + && ! (CHECK_FLAG (adv->binfo->peer->cap, + PEER_CAP_RESTART_BIT_RCV) && + CHECK_FLAG (adv->binfo->peer->cap, + PEER_CAP_RESTART_BIT_ADV)) && ! CHECK_FLAG (adv->binfo->flags, BGP_INFO_STALE) && safi != SAFI_MPLS_VPN) { diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 3a9bc480..79bcaaf0 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -1927,6 +1927,18 @@ peer_group_unbind (struct bgp *bgp, struct peer *peer, return 0; } + +static int +bgp_startup_timer_expire (struct thread *thread) +{ + struct bgp *bgp; + + bgp = THREAD_ARG (thread); + bgp->t_startup = NULL; + + return 0; +} + /* BGP instance creation by `router bgp' commands. */ static struct bgp * bgp_create (as_t *as, const char *name) @@ -1972,6 +1984,9 @@ bgp_create (as_t *as, const char *name) if (name) bgp->name = strdup (name); + THREAD_TIMER_ON (master, bgp->t_startup, bgp_startup_timer_expire, + bgp, bgp->restart_time); + return bgp; } diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index eae803de..40c381c2 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -104,6 +104,8 @@ struct bgp as_t *confed_peers; int confed_peers_cnt; + struct thread *t_startup; + /* BGP flags. */ u_int16_t flags; #define BGP_FLAG_ALWAYS_COMPARE_MED (1 << 0) @@ -366,6 +368,8 @@ struct peer #define PEER_CAP_RESTART_RCV (1 << 6) /* restart received */ #define PEER_CAP_AS4_ADV (1 << 7) /* as4 advertised */ #define PEER_CAP_AS4_RCV (1 << 8) /* as4 received */ +#define PEER_CAP_RESTART_BIT_ADV (1 << 9) /* sent restart state */ +#define PEER_CAP_RESTART_BIT_RCV (1 << 10) /* peer restart state */ /* Capability flags (reset in bgp_stop) */ u_int16_t af_cap[AFI_MAX][SAFI_MAX]; From ec98d90767b341877fb7f1547f025b946955899a Mon Sep 17 00:00:00 2001 From: Paul Jakma Date: Tue, 14 Oct 2014 11:14:06 +0100 Subject: [PATCH 282/482] bgpd: trivial, remove unneeded extra variable in bgp_capability_restart --- bgpd/bgp_open.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c index fe741aa3..b9d6e93f 100644 --- a/bgpd/bgp_open.c +++ b/bgpd/bgp_open.c @@ -338,16 +338,13 @@ bgp_capability_restart (struct peer *peer, struct capability_header *caphdr) { struct stream *s = BGP_INPUT (peer); u_int16_t restart_flag_time; - int restart_bit = 0; size_t end = stream_get_getp (s) + caphdr->length; SET_FLAG (peer->cap, PEER_CAP_RESTART_RCV); restart_flag_time = stream_getw(s); if (CHECK_FLAG (restart_flag_time, RESTART_R_BIT)) - { - SET_FLAG (peer->cap, PEER_CAP_RESTART_BIT_RCV); - restart_bit = 1; - } + SET_FLAG (peer->cap, PEER_CAP_RESTART_BIT_RCV); + UNSET_FLAG (restart_flag_time, 0xF000); peer->v_gr_restart = restart_flag_time; @@ -355,7 +352,9 @@ bgp_capability_restart (struct peer *peer, struct capability_header *caphdr) { zlog_debug ("%s OPEN has Graceful Restart capability", peer->host); zlog_debug ("%s Peer has%srestarted. Restart Time : %d", - peer->host, restart_bit ? " " : " not ", + peer->host, + CHECK_FLAG (peer->cap, PEER_CAP_RESTART_BIT_RCV) ? " " + : " not ", peer->v_gr_restart); } From 1a211cb369dc865a4e7e9f58a100c041af457262 Mon Sep 17 00:00:00 2001 From: Paul Jakma Date: Sat, 1 Nov 2014 17:21:47 +0000 Subject: [PATCH 283/482] bgpd: one more fix for tightening of check for missing well-known attributes * bgp_attr.c: (bgp_attr_check) The check for missing NEXT_HOP has the right spirit, but wrong where it counts, on the logic. It wouldn't catch a missing NEXT_HOP on a v4-only UPDATE. It would though have incorrectly flagged next-hop as missing on multi-protocol-only UPDATEs. Caught by Martin Winter with a test-suite. --- bgpd/bgp_attr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index da17e82e..5e213db4 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -1761,8 +1761,8 @@ bgp_attr_check (struct peer *peer, struct attr *attr) /* RFC 2858 makes Next-Hop optional/ignored, if MP_REACH_NLRI is present and * NLRI is empty. We can't easily check NLRI empty here though. */ - if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_MP_REACH_NLRI)) - && !CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP))) + if (!CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP)) + && !CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_MP_REACH_NLRI))) type = BGP_ATTR_NEXT_HOP; if (peer->sort == BGP_PEER_IBGP From 85c854aa720c02a56b1ecbbf12a763a326d11a63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Ter=C3=A4s?= Date: Tue, 30 Sep 2014 11:31:53 +0300 Subject: [PATCH 284/482] bgpd: implement route-map set as-path prepend last-as MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It picks up the AS to add from the aspath, or uses the peers AS number. Useful mostly in iBGP setups. Signed-off-by: Timo Teräs Reviewed-by: Paul Jakma --- bgpd/bgp_aspath.c | 65 ++++++++++++++++++++++++++++----------------- bgpd/bgp_aspath.h | 2 ++ bgpd/bgp_routemap.c | 47 +++++++++++++++++++++++++++++--- doc/bgpd.texi | 1 + 4 files changed, 87 insertions(+), 28 deletions(-) diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c index e8559bea..cfa9bc14 100644 --- a/bgpd/bgp_aspath.c +++ b/bgpd/bgp_aspath.c @@ -476,6 +476,19 @@ aspath_highest (struct aspath *aspath) return highest; } +/* Return the left-most ASN in path */ +as_t +aspath_leftmost (struct aspath *aspath) +{ + struct assegment *seg = aspath->segments; + as_t leftmost = 0; + + if (seg && seg->length && seg->type == AS_SEQUENCE) + leftmost = seg->as[0]; + + return leftmost; +} + /* Return 1 if there are any 4-byte ASes in the path */ unsigned int aspath_has_as4 (struct aspath *aspath) @@ -1362,46 +1375,50 @@ aspath_filter_exclude (struct aspath * source, struct aspath * exclude_list) /* Add specified AS to the leftmost of aspath. */ static struct aspath * -aspath_add_one_as (struct aspath *aspath, as_t asno, u_char type) +aspath_add_asns (struct aspath *aspath, as_t asno, u_char type, unsigned num) { struct assegment *assegment = aspath->segments; + int i; - /* In case of empty aspath. */ - if (assegment == NULL || assegment->length == 0) + if (assegment && assegment->type == type) { - aspath->segments = assegment_new (type, 1); - aspath->segments->as[0] = asno; - - if (assegment) - assegment_free (assegment); - - return aspath; + /* extend existing segment */ + aspath->segments = assegment_prepend_asns (aspath->segments, asno, num); } - - if (assegment->type == type) - aspath->segments = assegment_prepend_asns (aspath->segments, asno, 1); else { - /* create new segment - * push it onto head of aspath's segment chain - */ - struct assegment *newsegment; - - newsegment = assegment_new (type, 1); - newsegment->as[0] = asno; - - newsegment->next = assegment; + /* prepend with new segment */ + struct assegment *newsegment = assegment_new (type, num); + for (i = 0; i < num; i++) + newsegment->as[i] = asno; + + /* insert potentially replacing empty segment */ + if (assegment && assegment->length == 0) + { + newsegment->next = assegment->next; + assegment_free (assegment); + } + else + newsegment->next = assegment; aspath->segments = newsegment; } + aspath_str_update (aspath); return aspath; } +/* Add specified AS to the leftmost of aspath num times. */ +struct aspath * +aspath_add_seq_n (struct aspath *aspath, as_t asno, unsigned num) +{ + return aspath_add_asns (aspath, asno, AS_SEQUENCE, num); +} + /* Add specified AS to the leftmost of aspath. */ struct aspath * aspath_add_seq (struct aspath *aspath, as_t asno) { - return aspath_add_one_as (aspath, asno, AS_SEQUENCE); + return aspath_add_asns (aspath, asno, AS_SEQUENCE, 1); } /* Compare leftmost AS value for MED check. If as1's leftmost AS and @@ -1600,7 +1617,7 @@ aspath_delete_confed_seq (struct aspath *aspath) struct aspath* aspath_add_confed_seq (struct aspath *aspath, as_t asno) { - return aspath_add_one_as (aspath, asno, AS_CONFED_SEQUENCE); + return aspath_add_asns (aspath, asno, AS_CONFED_SEQUENCE, 1); } /* Add new as value to as path structure. */ diff --git a/bgpd/bgp_aspath.h b/bgpd/bgp_aspath.h index e8764cca..1311f8a5 100644 --- a/bgpd/bgp_aspath.h +++ b/bgpd/bgp_aspath.h @@ -71,6 +71,7 @@ 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_n (struct aspath *, as_t, unsigned); 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 (const void *, const void *); @@ -97,6 +98,7 @@ extern unsigned int aspath_count_hops (struct aspath *); extern unsigned int aspath_count_confeds (struct aspath *); extern unsigned int aspath_size (struct aspath *); extern as_t aspath_highest (struct aspath *); +extern as_t aspath_leftmost (struct aspath *); extern size_t aspath_put (struct stream *, struct aspath *, int); extern struct aspath *aspath_reconcile_as4 (struct aspath *, struct aspath *); diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index b74554d6..857781fe 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -1230,7 +1230,6 @@ route_set_aspath_prepend (void *rule, struct prefix *prefix, route_map_object_t if (type == RMAP_BGP) { - aspath = rule; binfo = object; if (binfo->attr->aspath->refcnt) @@ -1238,20 +1237,50 @@ route_set_aspath_prepend (void *rule, struct prefix *prefix, route_map_object_t else new = binfo->attr->aspath; - aspath_prepend (aspath, new); + if ((uintptr_t)rule > 10) + { + aspath = rule; + aspath_prepend (aspath, new); + } + else + { + as_t as = aspath_leftmost(new); + if (!as) as = binfo->peer->as; + new = aspath_add_seq_n (new, as, (uintptr_t) rule); + } + binfo->attr->aspath = new; } return RMAP_OKAY; } +static void * +route_set_aspath_prepend_compile (const char *arg) +{ + unsigned int num; + + if (sscanf(arg, "last-as %u", &num) == 1 && num > 0 && num < 10) + return (void*)(uintptr_t)num; + + return route_aspath_compile(arg); +} + +static void +route_set_aspath_prepend_free (void *rule) +{ + if ((uintptr_t)rule > 10) + route_aspath_free(rule); +} + + /* Set as-path prepend rule structure. */ struct route_map_rule_cmd route_set_aspath_prepend_cmd = { "as-path prepend", route_set_aspath_prepend, - route_aspath_compile, - route_aspath_free, + route_set_aspath_prepend_compile, + route_set_aspath_prepend_free, }; /* `set as-path exclude ASn' */ @@ -3127,6 +3156,15 @@ DEFUN (set_aspath_prepend, return ret; } +ALIAS (set_aspath_prepend, + set_aspath_prepend_lastas_cmd, + "set as-path prepend (last-as) <1-10>", + SET_STR + "Transform BGP AS_PATH attribute\n" + "Prepend to the as-path\n" + "Use the peer's AS-number\n" + "Number of times to insert"); + DEFUN (no_set_aspath_prepend, no_set_aspath_prepend_cmd, "no set as-path prepend", @@ -3983,6 +4021,7 @@ 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_prepend_lastas_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); diff --git a/doc/bgpd.texi b/doc/bgpd.texi index de709707..5004cbfc 100644 --- a/doc/bgpd.texi +++ b/doc/bgpd.texi @@ -496,6 +496,7 @@ This command defines a new AS path access list. @end deffn @deffn {Route Map} {set as-path prepend @var{as-path}} {} +@deffnx {Route Map} {set as-path prepend last-as @var{num}} {} @end deffn @node Private AS Numbers From 5e4ba81dc212b172e715afa7b6ea668cddd8485d Mon Sep 17 00:00:00 2001 From: Paul Jakma Date: Mon, 20 Oct 2014 17:49:44 +0100 Subject: [PATCH 285/482] doc: Document 'set as-path prepend' and 'set as-path prepend last-as' --- doc/bgpd.texi | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/bgpd.texi b/doc/bgpd.texi index 5004cbfc..7d92b5e1 100644 --- a/doc/bgpd.texi +++ b/doc/bgpd.texi @@ -496,7 +496,11 @@ This command defines a new AS path access list. @end deffn @deffn {Route Map} {set as-path prepend @var{as-path}} {} -@deffnx {Route Map} {set as-path prepend last-as @var{num}} {} +Prepend the given string of AS numbers to the AS_PATH. +@end deffn + +@deffn {Route Map} {set as-path prepend last-as @var{num}} {} +Prepend the existing last AS number (the leftmost ASN) to the AS_PATH. @end deffn @node Private AS Numbers From 273b1bd341afff86ba571e0be296d88dba627136 Mon Sep 17 00:00:00 2001 From: Greg Troxel Date: Tue, 2 Dec 2014 14:51:49 -0500 Subject: [PATCH 286/482] zebra/kernel_socket.c: Use platform alignment Use the platform-provided RT_ROUNDUP macro to align sockaddrs on the routing socket, rather than using hard-coded assumptions about alignment. Emit a warning if the OS doesn't define alignment macros. Resolves failure of ripngd on NetBSD 6 i386, which changed alignment to uint64_t from long. --- zebra/kernel_socket.c | 50 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index 3dbeb98b..1518c1ab 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -42,22 +42,52 @@ extern struct zebra_privs_t zserv_privs; extern struct zebra_t zebrad; /* - * Given a sockaddr length, round it up to include pad bytes following - * it. Assumes the kernel pads to sizeof(long). + * Historically, the BSD routing socket has aligned data following a + * struct sockaddr to sizeof(long), which was 4 bytes on some + * platforms, and 8 bytes on others. NetBSD 6 changed the routing + * socket to align to sizeof(uint64_t), which is 8 bytes. OS X + * appears to align to sizeof(int), which is 4 bytes. * - * XXX: why is ROUNDUP(0) sizeof(long)? 0 is an illegal sockaddr - * length anyway (< sizeof (struct sockaddr)), so this shouldn't - * matter. - * On OS X, both 32, 64bit syatems align on 4 byte boundary + * Alignment of zero-sized sockaddrs is nonsensical, but historically + * BSD defines RT_ROUNDUP(0) to be the alignment interval (rather than + * 0). We follow this practice without questioning it, but it is a + * bug if quagga calls ROUNDUP with 0. */ + +/* + * Because of these varying conventions, the only sane approach is for + * the header to define some flavor of ROUNDUP macro. + */ +#if defined(RT_ROUNDUP) +#define ROUNDUP(a) RT_ROUNDUP(a) +#endif /* defined(RT_ROUNDUP) */ + +/* + * If ROUNDUP has not yet been defined in terms of platform-provided + * defines, attempt to cope with heuristics. + */ +#if !defined(ROUNDUP) + +/* + * It's a bug for a platform not to define rounding/alignment for + * sockaddrs on the routing socket. This warning really is + * intentional, to provoke filing bug reports with operating systems + * that don't define RT_ROUNDUP or equivalent. + */ +#warning "net/route.h does not define RT_ROUNDUP; making unwarranted assumptions!" + +/* OS X (Xcode as of 2014-12) is known not to define RT_ROUNDUP */ #ifdef __APPLE__ -#define ROUNDUP(a) \ - ((a) > 0 ? (1 + (((a) - 1) | (sizeof(int) - 1))) : sizeof(int)) +#define ROUNDUP_TYPE long #else -#define ROUNDUP(a) \ - ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) +#define ROUNDUP_TYPE int #endif +#define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(ROUNDUP_TYPE) - 1))) : sizeof(ROUNDUP_TYPE)) + +#endif /* defined(ROUNDUP) */ + /* * Given a pointer (sockaddr or void *), return the number of bytes * taken up by the sockaddr and any padding needed for alignment. From cffe7807c03b7edea005869086720e2f1fb070c6 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sun, 7 Dec 2014 03:27:13 +0100 Subject: [PATCH 287/482] bgpd: stop startup timer on stopping BGP (fixes dd49eb1) bgp_delete() really needs to kill t_startup, otherwise after creating and quickly destroying a BGP instance it may fire on a deallocated struct bgp, overwriting memory. Reported-by: Martin Winter Fixes: dd49eb1 ("Fix BGP's use of restart bit.") Cc: Vipin Kumar Signed-off-by: David Lamparter Acked-by: Vincent JARDIN --- bgpd/bgpd.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 79bcaaf0..d72708e4 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -2103,6 +2103,8 @@ bgp_delete (struct bgp *bgp) afi_t afi; int i; + THREAD_OFF (bgp->t_startup); + /* Delete static route. */ bgp_static_delete (bgp); From daefeb8755e194dd19a5f1910bc78d13c8147efb Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Mon, 8 Dec 2014 17:42:12 +0100 Subject: [PATCH 288/482] bgpd: set BGP_ATTR_MP_[UN]REACH_NLRI (fixes 1a211cb) Unfortunately, the attribute present bits for MP_REACH and MP_UNREACH which 1a211cb ("bgpd: one more fix"...) tests for are never set in their corresponding attribute parsing functions. Reported-by: Martin Winter Fixes: 1a211cb "bgpd: one more fix for tightening of check for missing well-known attributes" Cc: Paul Jakma Signed-off-by: David Lamparter --- bgpd/bgp_attr.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 5e213db4..be316daa 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -1591,6 +1591,8 @@ bgp_mp_reach_parse (struct bgp_attr_parser_args *args, stream_forward_getp (s, nlri_len); + attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_MP_REACH_NLRI); + return BGP_ATTR_PARSE_PROCEED; #undef LEN_LEFT } @@ -1606,6 +1608,7 @@ bgp_mp_unreach_parse (struct bgp_attr_parser_args *args, u_int16_t withdraw_len; int ret; struct peer *const peer = args->peer; + struct attr *const attr = args->attr; const bgp_size_t length = args->length; s = peer->ibuf; @@ -1633,6 +1636,8 @@ bgp_mp_unreach_parse (struct bgp_attr_parser_args *args, stream_forward_getp (s, withdraw_len); + attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_MP_UNREACH_NLRI); + return BGP_ATTR_PARSE_PROCEED; } From 1c6db0d2da34044ddfb42665fda8a3387ecc451d Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Fri, 12 Dec 2014 21:35:28 +0100 Subject: [PATCH 289/482] lib: don't create circular lists (fixes 6d83113) LISTNODE_DETACH doesn't clear out the node, and LISTNODE_ATTACH doesn't set ->next (since it assumes a fresh/zeroed listnode). As a result, the new listnode_move_to_tail() created a nice circular list, in turn crashing ospfd in ospf_write() later. Reported-by: Martin Winter Fixes: 6d83113 ("ospfd: Tweak previous iface RR write patch to avoid free/malloc & redundant log") Cc: Paul Jakma Signed-off-by: David Lamparter Acked-by: Greg Troxel Acked-by: Dinesh Dutt Acked-by: Vincent JARDIN --- lib/linklist.h | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/linklist.h b/lib/linklist.h index c8d715e1..6209c8b9 100644 --- a/lib/linklist.h +++ b/lib/linklist.h @@ -113,6 +113,7 @@ extern void list_add_list (struct list *, struct list *); #define LISTNODE_ATTACH(L,N) \ do { \ (N)->prev = (L)->tail; \ + (N)->next = NULL; \ if ((L)->head == NULL) \ (L)->head = (N); \ else \ From e0b0ac8c97d9b8885785ed0461f87a34e70f368e Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Thu, 24 Apr 2014 20:22:53 +0200 Subject: [PATCH 290/482] zebra: factor out rib debug logs Introduces a logging function that takes a struct route_node * argument, and prefixes log output with that node's prefix. While this removes some duplication, it will also later be useful for srcdest route nodes. Behaviour before and after the patch should be exactly identical. Signed-off-by: David Lamparter --- zebra/zebra_rib.c | 114 +++++++++++++++++++++------------------------- 1 file changed, 51 insertions(+), 63 deletions(-) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 245011eb..4ee1a84c 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -75,6 +75,37 @@ static const struct /* Vector for routing table. */ static vector vrf_vector; +static void +_rnode_zlog(const char *_func, struct route_node *rn, int priority, + const char *msgfmt, ...) +{ + char buf[INET6_ADDRSTRLEN + 4], *bptr; + char msgbuf[512]; + va_list ap; + + va_start(ap, msgfmt); + vsnprintf(msgbuf, sizeof(msgbuf), msgfmt, ap); + va_end(ap); + + if (rn) + { + inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); + bptr = buf + strlen(buf); + snprintf(bptr, buf + sizeof(buf) - bptr, "/%d", rn->p.prefixlen); + } + else + { + snprintf(buf, sizeof(buf), "{(route_node *) NULL}"); + } + + zlog (NULL, priority, "%s: %s: %s", _func, buf, msgbuf); +} + +#define rnode_debug(node, ...) \ + _rnode_zlog(__func__, node, LOG_DEBUG, __VA_ARGS__) +#define rnode_info(node, ...) \ + _rnode_zlog(__func__, node, LOG_INFO, __VA_ARGS__) + /* * vrf_table_create */ @@ -1209,7 +1240,6 @@ int rib_gc_dest (struct route_node *rn) { rib_dest_t *dest; - char buf[INET6_ADDRSTRLEN]; dest = rib_dest_from_rnode (rn); if (!dest) @@ -1219,11 +1249,7 @@ rib_gc_dest (struct route_node *rn) return 0; if (IS_ZEBRA_DEBUG_RIB) - { - inet_ntop (rn->p.family, &rn->p.u.prefix, buf, sizeof (buf)); - zlog_debug ("%s: %s/%d: removing dest from table", __func__, - buf, rn->p.prefixlen); - } + rnode_debug (rn, "removing dest from table"); dest->rnode = NULL; XFREE (MTYPE_RIB_DEST, dest); @@ -1248,16 +1274,12 @@ rib_process (struct route_node *rn) int installed = 0; struct nexthop *nexthop = NULL, *tnexthop; int recursing; - char buf[INET6_ADDRSTRLEN]; rib_table_info_t *info; assert (rn); info = rn->table->info; - if (IS_ZEBRA_DEBUG_RIB || IS_ZEBRA_DEBUG_RIB_Q) - inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); - RNODE_FOREACH_RIB_SAFE (rn, rib, next) { /* Currently installed rib. */ @@ -1275,8 +1297,7 @@ rib_process (struct route_node *rn) if (rib != fib) { if (IS_ZEBRA_DEBUG_RIB) - zlog_debug ("%s: %s/%d: rn %p, removing rib %p", __func__, - buf, rn->p.prefixlen, rn, rib); + rnode_debug (rn, "rn %p, removing rib %p", rn, rib); rib_unlink (rn, rib); } else @@ -1350,8 +1371,8 @@ rib_process (struct route_node *rn) if (select && select == fib) { if (IS_ZEBRA_DEBUG_RIB) - zlog_debug ("%s: %s/%d: Updating existing route, select %p, fib %p", - __func__, buf, rn->p.prefixlen, select, fib); + rnode_debug (rn, "Updating existing route, select %p, fib %p", + select, fib); if (CHECK_FLAG (select->flags, ZEBRA_FLAG_CHANGED)) { zfpm_trigger_update (rn, "updating existing route"); @@ -1395,8 +1416,7 @@ rib_process (struct route_node *rn) if (fib) { if (IS_ZEBRA_DEBUG_RIB) - zlog_debug ("%s: %s/%d: Removing existing route, fib %p", __func__, - buf, rn->p.prefixlen, fib); + rnode_debug (rn, "Removing existing route, fib %p", fib); zfpm_trigger_update (rn, "removing existing route"); @@ -1416,8 +1436,7 @@ rib_process (struct route_node *rn) if (select) { if (IS_ZEBRA_DEBUG_RIB) - zlog_debug ("%s: %s/%d: Adding route, select %p", __func__, buf, - rn->p.prefixlen, select); + rnode_debug (rn, "Adding route, select %p", select); zfpm_trigger_update (rn, "new route selected"); @@ -1434,14 +1453,13 @@ rib_process (struct route_node *rn) if (del) { if (IS_ZEBRA_DEBUG_RIB) - zlog_debug ("%s: %s/%d: Deleting fib %p, rn %p", __func__, buf, - rn->p.prefixlen, del, rn); + rnode_debug (rn, "Deleting fib %p, rn %p", del, rn); rib_unlink (rn, del); } end: if (IS_ZEBRA_DEBUG_RIB_Q) - zlog_debug ("%s: %s/%d: rn %p dequeued", __func__, buf, rn->p.prefixlen, rn); + rnode_debug (rn, "rn %p dequeued", rn); /* * Check if the dest can be deleted now. @@ -1525,10 +1543,6 @@ static void rib_meta_queue_add (struct meta_queue *mq, struct route_node *rn) { 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); RNODE_FOREACH_RIB (rn, rib) { @@ -1539,8 +1553,8 @@ rib_meta_queue_add (struct meta_queue *mq, struct route_node *rn) 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); + rnode_debug (rn, "rn %p is already queued in sub-queue %u", + rn, qindex); continue; } @@ -1550,8 +1564,8 @@ rib_meta_queue_add (struct meta_queue *mq, struct route_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); + rnode_debug (rn, "queued rn %p into sub-queue %u", + rn, qindex); } } @@ -1559,11 +1573,7 @@ rib_meta_queue_add (struct meta_queue *mq, struct route_node *rn) static void rib_queue_add (struct zebra_t *zebra, struct route_node *rn) { - char buf[INET_ADDRSTRLEN]; assert (zebra && rn); - - if (IS_ZEBRA_DEBUG_RIB_Q) - inet_ntop (AF_INET, &rn->p.u.prefix, buf, INET_ADDRSTRLEN); /* Pointless to queue a route_node with no RIB entries to add or remove */ if (!rnode_to_ribs (rn)) @@ -1575,7 +1585,7 @@ rib_queue_add (struct zebra_t *zebra, struct route_node *rn) } if (IS_ZEBRA_DEBUG_RIB_Q) - zlog_info ("%s: %s/%d: work queue added", __func__, buf, rn->p.prefixlen); + rnode_info (rn, "work queue added"); assert (zebra); @@ -1599,7 +1609,7 @@ rib_queue_add (struct zebra_t *zebra, struct route_node *rn) 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); + rnode_debug (rn, "rn %p queued", rn); return; } @@ -1696,25 +1706,17 @@ rib_link (struct route_node *rn, struct rib *rib) { struct rib *head; rib_dest_t *dest; - char buf[INET6_ADDRSTRLEN]; assert (rib && rn); if (IS_ZEBRA_DEBUG_RIB) - { - inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); - zlog_debug ("%s: %s/%d: rn %p, rib %p", __func__, - buf, rn->p.prefixlen, rn, rib); - } + rnode_debug (rn, "rn %p, rib %p", rn, rib); dest = rib_dest_from_rnode (rn); if (!dest) { if (IS_ZEBRA_DEBUG_RIB) - { - zlog_debug ("%s: %s/%d: adding dest to table", __func__, - buf, rn->p.prefixlen); - } + rnode_debug (rn, "adding dest to table"); dest = XCALLOC (MTYPE_RIB_DEST, sizeof (rib_dest_t)); route_lock_node (rn); /* rn route table reference */ @@ -1741,12 +1743,8 @@ rib_addnode (struct route_node *rn, struct rib *rib) if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) { if (IS_ZEBRA_DEBUG_RIB) - { - char buf[INET6_ADDRSTRLEN]; - inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); - zlog_debug ("%s: %s/%d: rn %p, un-removed rib %p", - __func__, buf, rn->p.prefixlen, rn, rib); - } + rnode_debug (rn, "rn %p, un-removed rib %p", rn, rib); + UNSET_FLAG (rib->status, RIB_ENTRY_REMOVED); return; } @@ -1765,17 +1763,12 @@ rib_addnode (struct route_node *rn, struct rib *rib) static void rib_unlink (struct route_node *rn, struct rib *rib) { - char buf[INET6_ADDRSTRLEN]; rib_dest_t *dest; assert (rn && rib); if (IS_ZEBRA_DEBUG_RIB) - { - inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); - zlog_debug ("%s: %s/%d: rn %p, rib %p", - __func__, buf, rn->p.prefixlen, rn, rib); - } + rnode_debug (rn, "rn %p, rib %p", rn, rib); dest = rib_dest_from_rnode (rn); @@ -1799,12 +1792,7 @@ static void rib_delnode (struct route_node *rn, struct rib *rib) { if (IS_ZEBRA_DEBUG_RIB) - { - char buf[INET6_ADDRSTRLEN]; - inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); - zlog_debug ("%s: %s/%d: rn %p, rib %p, removing", __func__, - buf, rn->p.prefixlen, rn, rib); - } + rnode_debug (rn, "rn %p, rib %p, removing", rn, rib); SET_FLAG (rib->status, RIB_ENTRY_REMOVED); rib_queue_add (&zebrad, rn); } From f7b3d1e067ac8088a61136c53d24078b675e0197 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Thu, 22 Jan 2015 19:02:13 +0100 Subject: [PATCH 291/482] zebra: identify MRIB on debug messages since the same code handles both URIB and MRIB, the debug messages can get rather confusing if the RIB isn't identified. Mark the MRIB in debug messages so we can distinguish that. Signed-off-by: David Lamparter --- zebra/zebra_rib.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 4ee1a84c..8354513c 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -89,9 +89,12 @@ _rnode_zlog(const char *_func, struct route_node *rn, int priority, if (rn) { + rib_table_info_t *info = rn->table->info; + inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); bptr = buf + strlen(buf); - snprintf(bptr, buf + sizeof(buf) - bptr, "/%d", rn->p.prefixlen); + snprintf(bptr, buf + sizeof(buf) - bptr, "/%d%s", rn->p.prefixlen, + info->safi == SAFI_MULTICAST ? " (MRIB)" : ""); } else { From 346a8b50334ac837e6a6af0dbe472e9a87dacd1e Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Mon, 22 Sep 2014 19:35:51 -0300 Subject: [PATCH 292/482] zebra: add rib_match_ipv4_safi() This is the same as rib_lookup_ipv4(), without the SAFI hardcoded. Cc: Balaji G Cc: Everton Marques Signed-off-by: David Lamparter --- zebra/rib.h | 1 + zebra/zebra_rib.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/zebra/rib.h b/zebra/rib.h index d3a83c68..13011e26 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -419,6 +419,7 @@ extern int rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p, u_int32_t, safi_t safi); extern struct rib *rib_match_ipv4 (struct in_addr); +extern struct rib *rib_match_ipv4_safi (struct in_addr addr, safi_t safi); extern struct rib *rib_lookup_ipv4 (struct prefix_ipv4 *); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 8354513c..5b9b00ed 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -781,6 +781,63 @@ rib_match_ipv4 (struct in_addr addr) return NULL; } +struct rib * +rib_match_ipv4_safi (struct in_addr addr, safi_t safi) +{ + struct route_table *table; + struct route_node *rn; + struct rib *match; + struct nexthop *newhop, *tnewhop; + int recursing; + + /* Lookup table. */ + table = vrf_table (AFI_IP, safi, 0); + if (! table) + return 0; + + rn = route_node_match_ipv4 (table, &addr); + + while (rn) + { + route_unlock_node (rn); + + /* Pick up selected route. */ + RNODE_FOREACH_RIB (rn, match) + { + if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED)) + continue; + if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED)) + break; + } + + /* If there is no selected route or matched route is EGP, go up + tree. */ + if (! match + || match->type == ZEBRA_ROUTE_BGP) + { + do { + rn = rn->parent; + } while (rn && rn->info == NULL); + if (rn) + route_lock_node (rn); + } + else + { + if (match->type == ZEBRA_ROUTE_CONNECT) + /* Directly point connected route. */ + return match; + else + { + for (ALL_NEXTHOPS_RO(match->nexthop, newhop, tnewhop, recursing)) + if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB)) + return match; + return NULL; + } + } + } + return NULL; +} + struct rib * rib_lookup_ipv4 (struct prefix_ipv4 *p) { From a59b6152bbcd2ff6734872ecbffbc9c43701e1a8 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Fri, 21 Nov 2014 15:57:45 -0800 Subject: [PATCH 293/482] zebra: point rib_match_ipv4() to ._safi() Since rib_match_ipv4() is just rib_match_ipv4_safi() for SAFI_UNICAST, the former can be removed and pointed to the latter instead. Cc: Balaji G Cc: Everton Marques Signed-off-by: David Lamparter --- zebra/zebra_rib.c | 59 +---------------------------------------------- 1 file changed, 1 insertion(+), 58 deletions(-) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 5b9b00ed..469c10b2 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -721,64 +721,7 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, struct rib * rib_match_ipv4 (struct in_addr addr) { - struct prefix_ipv4 p; - struct route_table *table; - struct route_node *rn; - struct rib *match; - struct nexthop *newhop, *tnewhop; - int recursing; - - /* Lookup table. */ - table = vrf_table (AFI_IP, SAFI_UNICAST, 0); - if (! table) - return 0; - - memset (&p, 0, sizeof (struct prefix_ipv4)); - p.family = AF_INET; - p.prefixlen = IPV4_MAX_PREFIXLEN; - p.prefix = addr; - - rn = route_node_match (table, (struct prefix *) &p); - - while (rn) - { - route_unlock_node (rn); - - /* Pick up selected route. */ - RNODE_FOREACH_RIB (rn, match) - { - if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED)) - continue; - if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED)) - break; - } - - /* If there is no selected route or matched route is EGP, go up - tree. */ - if (! match - || match->type == ZEBRA_ROUTE_BGP) - { - do { - rn = rn->parent; - } while (rn && rn->info == NULL); - if (rn) - route_lock_node (rn); - } - else - { - if (match->type == ZEBRA_ROUTE_CONNECT) - /* Directly point connected route. */ - return match; - else - { - for (ALL_NEXTHOPS_RO(match->nexthop, newhop, tnewhop, recursing)) - if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB)) - return match; - return NULL; - } - } - } - return NULL; + return rib_match_ipv4_safi (addr, SAFI_UNICAST); } struct rib * From be4fb4312531cdae986a83b0375dbd1e0606067e Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Tue, 1 Jul 2014 15:15:52 -0300 Subject: [PATCH 294/482] zebra: add ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB This adds a new zapi call "ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB" performing a Multicast RPF lookup for a given source. Details of the lookup behaviour are left to the zebra side of things. Note: this is non-reactive, as in, only delivers a snapshot of the state at a particular point in time. There's no push notification of changes happening to the RIB. This combines the following 3 original patches: - zebra: add zsend_ipv4_nexthop_lookup_mrib() - zserv: Query mrib (SAFI_MULTICAST). - zebra: Cleanups to zebra_rib. Cc: Everton Marques Cc: Balaji G Signed-off-by: David Lamparter --- lib/zebra.h | 3 +- zebra/zserv.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 1 deletion(-) diff --git a/lib/zebra.h b/lib/zebra.h index b289a19e..a4e02148 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -425,7 +425,8 @@ struct in_pktinfo #define ZEBRA_ROUTER_ID_DELETE 21 #define ZEBRA_ROUTER_ID_UPDATE 22 #define ZEBRA_HELLO 23 -#define ZEBRA_MESSAGE_MAX 24 +#define ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB 24 +#define ZEBRA_MESSAGE_MAX 25 /* Marker value used in new Zserv, in the byte location corresponding * the command value in the old zserv header. To allow old and new diff --git a/zebra/zserv.c b/zebra/zserv.c index ca17c2c6..89eb266a 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -599,6 +599,89 @@ zsend_ipv4_nexthop_lookup (struct zserv *client, struct in_addr addr) return zebra_server_send_message(client); } +/* + Modified version of zsend_ipv4_nexthop_lookup(): + Query unicast rib if nexthop is not found on mrib. + Returns both route metric and protocol distance. +*/ +static int +zsend_ipv4_nexthop_lookup_mrib (struct zserv *client, struct in_addr addr) +{ + struct stream *s; + struct rib *rib; + unsigned long nump; + u_char num; + struct nexthop *nexthop; + + /* Lookup nexthop. */ + rib = rib_match_ipv4_safi (addr, SAFI_MULTICAST); + + if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: %s mrib entry found.", __func__, rib ? "Matching" : "No matching"); + + if (!rib) { + /* Retry lookup with unicast rib */ + rib = rib_match_ipv4_safi (addr, SAFI_UNICAST); + if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: %s rib entry found.", __func__, rib ? "Matching" : "No matching"); + } + + /* Get output stream. */ + s = client->obuf; + stream_reset (s); + + /* Fill in result. */ + zserv_create_header (s, ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB); + stream_put_in_addr (s, &addr); + + if (rib) + { + stream_putc (s, rib->distance); + stream_putl (s, rib->metric); + num = 0; + nump = stream_get_endp(s); /* remember position for nexthop_num */ + stream_putc (s, 0); /* reserve room for nexthop_num */ + /* Only non-recursive routes are elegible to resolve the nexthop we + * are looking up. Therefore, we will just iterate over the top + * chain of nexthops. */ + for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) + { + stream_putc (s, nexthop->type); + switch (nexthop->type) + { + case ZEBRA_NEXTHOP_IPV4: + stream_put_in_addr (s, &nexthop->gate.ipv4); + break; + case ZEBRA_NEXTHOP_IPV4_IFINDEX: + stream_put_in_addr (s, &nexthop->gate.ipv4); + stream_putl (s, nexthop->ifindex); + break; + case ZEBRA_NEXTHOP_IFINDEX: + case ZEBRA_NEXTHOP_IFNAME: + stream_putl (s, nexthop->ifindex); + break; + default: + /* do nothing */ + break; + } + num++; + } + + stream_putc_at (s, nump, num); /* store nexthop_num */ + } + else + { + stream_putc (s, 0); /* distance */ + stream_putl (s, 0); /* metric */ + stream_putc (s, 0); /* nexthop_num */ + } + + stream_putw_at (s, 0, stream_get_endp (s)); + + return zebra_server_send_message(client); +} + static int zsend_ipv4_import_lookup (struct zserv *client, struct prefix_ipv4 *p) { @@ -920,6 +1003,16 @@ zread_ipv4_nexthop_lookup (struct zserv *client, u_short length) return zsend_ipv4_nexthop_lookup (client, addr); } +/* MRIB Nexthop lookup for IPv4. */ +static int +zread_ipv4_nexthop_lookup_mrib (struct zserv *client, u_short length) +{ + struct in_addr addr; + + addr.s_addr = stream_get_ipv4 (client->ibuf); + return zsend_ipv4_nexthop_lookup_mrib (client, addr); +} + /* Nexthop lookup for IPv4. */ static int zread_ipv4_import_lookup (struct zserv *client, u_short length) @@ -1352,6 +1445,9 @@ zebra_client_read (struct thread *thread) case ZEBRA_IPV4_NEXTHOP_LOOKUP: zread_ipv4_nexthop_lookup (client, length); break; + case ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB: + zread_ipv4_nexthop_lookup_mrib (client, length); + break; #ifdef HAVE_IPV6 case ZEBRA_IPV6_NEXTHOP_LOOKUP: zread_ipv6_nexthop_lookup (client, length); From 12150f08b76c91225dcd877308fecb13026041ef Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Fri, 19 Sep 2014 16:39:34 -0300 Subject: [PATCH 295/482] zebra: mrib: Include BGP routes in RPF lookups The rib_match_ipv4() function was previously used only for iBGP recursive nexthop lookups, which ignore eBGP routes. This is not desirable for PIM RPF lookups, which may well use an eBGP route. Cc: Everton Marques Cc: Balaji G Signed-off-by: David Lamparter --- zebra/rib.h | 2 +- zebra/zebra_rib.c | 7 +++---- zebra/zserv.c | 5 +++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/zebra/rib.h b/zebra/rib.h index 13011e26..aef71500 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -419,7 +419,7 @@ extern int rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p, u_int32_t, safi_t safi); extern struct rib *rib_match_ipv4 (struct in_addr); -extern struct rib *rib_match_ipv4_safi (struct in_addr addr, safi_t safi); +extern struct rib *rib_match_ipv4_safi (struct in_addr addr, safi_t safi, int skip_bgp); extern struct rib *rib_lookup_ipv4 (struct prefix_ipv4 *); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 469c10b2..f4a91554 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -721,11 +721,11 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, struct rib * rib_match_ipv4 (struct in_addr addr) { - return rib_match_ipv4_safi (addr, SAFI_UNICAST); + return rib_match_ipv4_safi (addr, SAFI_UNICAST, 1); } struct rib * -rib_match_ipv4_safi (struct in_addr addr, safi_t safi) +rib_match_ipv4_safi (struct in_addr addr, safi_t safi, int skip_bgp) { struct route_table *table; struct route_node *rn; @@ -755,8 +755,7 @@ rib_match_ipv4_safi (struct in_addr addr, safi_t safi) /* If there is no selected route or matched route is EGP, go up tree. */ - if (! match - || match->type == ZEBRA_ROUTE_BGP) + if (!match || (skip_bgp && (match->type == ZEBRA_ROUTE_BGP))) { do { rn = rn->parent; diff --git a/zebra/zserv.c b/zebra/zserv.c index 89eb266a..1b693159 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -612,16 +612,17 @@ zsend_ipv4_nexthop_lookup_mrib (struct zserv *client, struct in_addr addr) unsigned long nump; u_char num; struct nexthop *nexthop; + int skip_bgp = 0; /* bool */ /* Lookup nexthop. */ - rib = rib_match_ipv4_safi (addr, SAFI_MULTICAST); + rib = rib_match_ipv4_safi (addr, SAFI_MULTICAST, skip_bgp); if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV) zlog_debug("%s: %s mrib entry found.", __func__, rib ? "Matching" : "No matching"); if (!rib) { /* Retry lookup with unicast rib */ - rib = rib_match_ipv4_safi (addr, SAFI_UNICAST); + rib = rib_match_ipv4_safi (addr, SAFI_UNICAST, skip_bgp); if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV) zlog_debug("%s: %s rib entry found.", __func__, rib ? "Matching" : "No matching"); } From c409791083892f6c95baca1c79c80290dc595be4 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sat, 22 Nov 2014 14:44:20 -0800 Subject: [PATCH 296/482] zebra: kill rib_match_ipv4() Since this function is internal to zebra, there is no reason to keep this one-line indirect wrapper to rib_match_ipv4_safi() around. Cc: Everton Marques Cc: Balaji G Signed-off-by: David Lamparter --- zebra/rib.h | 1 - zebra/zebra_rib.c | 6 ------ zebra/zserv.c | 4 ++-- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/zebra/rib.h b/zebra/rib.h index aef71500..d40b17e8 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -418,7 +418,6 @@ extern int rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p, struct in_addr *gate, unsigned int ifindex, u_int32_t, safi_t safi); -extern struct rib *rib_match_ipv4 (struct in_addr); extern struct rib *rib_match_ipv4_safi (struct in_addr addr, safi_t safi, int skip_bgp); extern struct rib *rib_lookup_ipv4 (struct prefix_ipv4 *); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index f4a91554..08ce9645 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -718,12 +718,6 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, } #endif /* HAVE_IPV6 */ -struct rib * -rib_match_ipv4 (struct in_addr addr) -{ - return rib_match_ipv4_safi (addr, SAFI_UNICAST, 1); -} - struct rib * rib_match_ipv4_safi (struct in_addr addr, safi_t safi, int skip_bgp) { diff --git a/zebra/zserv.c b/zebra/zserv.c index 1b693159..e9236dec 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -539,8 +539,8 @@ zsend_ipv4_nexthop_lookup (struct zserv *client, struct in_addr addr) u_char num; struct nexthop *nexthop; - /* Lookup nexthop. */ - rib = rib_match_ipv4 (addr); + /* Lookup nexthop - eBGP excluded */ + rib = rib_match_ipv4_safi (addr, SAFI_UNICAST, 1); /* Get output stream. */ s = client->obuf; From 96bb266e0fde451f36c5463e789714eacc1ae63c Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Mon, 14 Jul 2014 11:19:00 -0300 Subject: [PATCH 297/482] zebra: mrib: static route support With the MRIB being independent from the Unicast RIB, there's currently now way to add static routes to the MRIB. Address that by adding a separate set of commands for MRIB static routes. Combines these original patches: - zebra: mrib: ip mroute command to add unicast route to MRIB for multicast RPF. - zebra: mrib: no ip mroute: Fix removal of static multicast RPF route. - zebra: mrib: remove unused static_add/delete_ipv4 - zebra: Cleanups to zebra_rib. - pimd: Merge pim-only branch. Cc: Everton Marques Cc: Balaji G Signed-off-by: David Lamparter --- zebra/rib.h | 10 +++--- zebra/zebra_rib.c | 30 ++++++++-------- zebra/zebra_vty.c | 87 +++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 91 insertions(+), 36 deletions(-) diff --git a/zebra/rib.h b/zebra/rib.h index d40b17e8..5eedfde6 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -430,12 +430,12 @@ extern void rib_init (void); extern unsigned long rib_score_proto (u_char proto); extern int -static_add_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, - u_char flags, u_char distance, u_int32_t vrf_id); - +static_add_ipv4_safi (safi_t safi, struct prefix *p, struct in_addr *gate, + const char *ifname, u_char flags, u_char distance, + u_int32_t vrf_id); extern int -static_delete_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, - u_char distance, u_int32_t vrf_id); +static_delete_ipv4_safi (safi_t safi, struct prefix *p, struct in_addr *gate, + const char *ifname, u_char distance, u_int32_t vrf_id); #ifdef HAVE_IPV6 extern int diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 08ce9645..a07036e5 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -2276,14 +2276,14 @@ rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p, /* Install static route into rib. */ static void -static_install_ipv4 (struct prefix *p, struct static_ipv4 *si) +static_install_ipv4 (safi_t safi, struct prefix *p, struct static_ipv4 *si) { struct rib *rib; struct route_node *rn; struct route_table *table; /* Lookup table. */ - table = vrf_table (AFI_IP, SAFI_UNICAST, 0); + table = vrf_table (AFI_IP, safi, 0); if (! table) return; @@ -2368,7 +2368,7 @@ static_ipv4_nexthop_same (struct nexthop *nexthop, struct static_ipv4 *si) /* Uninstall static route from RIB. */ static void -static_uninstall_ipv4 (struct prefix *p, struct static_ipv4 *si) +static_uninstall_ipv4 (safi_t safi, struct prefix *p, struct static_ipv4 *si) { struct route_node *rn; struct rib *rib; @@ -2376,7 +2376,7 @@ static_uninstall_ipv4 (struct prefix *p, struct static_ipv4 *si) struct route_table *table; /* Lookup table. */ - table = vrf_table (AFI_IP, SAFI_UNICAST, 0); + table = vrf_table (AFI_IP, safi, 0); if (! table) return; @@ -2427,10 +2427,10 @@ static_uninstall_ipv4 (struct prefix *p, struct static_ipv4 *si) route_unlock_node (rn); } -/* Add static route into static route configuration. */ int -static_add_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, - u_char flags, u_char distance, u_int32_t vrf_id) +static_add_ipv4_safi (safi_t safi, struct prefix *p, struct in_addr *gate, + const char *ifname, u_char flags, u_char distance, + u_int32_t vrf_id) { u_char type = 0; struct route_node *rn; @@ -2441,7 +2441,7 @@ static_add_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, struct route_table *stable; /* Lookup table. */ - stable = vrf_static_table (AFI_IP, SAFI_UNICAST, vrf_id); + stable = vrf_static_table (AFI_IP, safi, vrf_id); if (! stable) return -1; @@ -2475,7 +2475,7 @@ static_add_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, /* Distance changed. */ if (update) - static_delete_ipv4 (p, gate, ifname, update->distance, vrf_id); + static_delete_ipv4_safi (safi, p, gate, ifname, update->distance, vrf_id); /* Make new static route structure. */ si = XCALLOC (MTYPE_STATIC_IPV4, sizeof (struct static_ipv4)); @@ -2517,15 +2517,14 @@ static_add_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, si->next = cp; /* Install into rib. */ - static_install_ipv4 (p, si); + static_install_ipv4 (safi, p, si); return 1; } -/* Delete static route from static route configuration. */ int -static_delete_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, - u_char distance, u_int32_t vrf_id) +static_delete_ipv4_safi (safi_t safi, struct prefix *p, struct in_addr *gate, + const char *ifname, u_char distance, u_int32_t vrf_id) { u_char type = 0; struct route_node *rn; @@ -2533,7 +2532,7 @@ static_delete_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, struct route_table *stable; /* Lookup table. */ - stable = vrf_static_table (AFI_IP, SAFI_UNICAST, vrf_id); + stable = vrf_static_table (AFI_IP, safi, vrf_id); if (! stable) return -1; @@ -2565,7 +2564,7 @@ static_delete_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, } /* Install into rib. */ - static_uninstall_ipv4 (p, si); + static_uninstall_ipv4 (safi, p, si); /* Unlink static route from linked list. */ if (si->prev) @@ -2586,7 +2585,6 @@ static_delete_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, return 1; } - #ifdef HAVE_IPV6 static int rib_bogus_ipv6 (int type, struct prefix_ipv6 *p, diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 9d6c1ddd..6802eceb 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -30,11 +30,14 @@ #include "zebra/zserv.h" -/* General fucntion for static route. */ +static int do_show_ip_route(struct vty *vty, safi_t safi); + +/* General function for static route. */ static int -zebra_static_ipv4 (struct vty *vty, int add_cmd, const char *dest_str, - const char *mask_str, const char *gate_str, - const char *flag_str, const char *distance_str) +zebra_static_ipv4_safi (struct vty *vty, safi_t safi, int add_cmd, + const char *dest_str, const char *mask_str, + const char *gate_str, const char *flag_str, + const char *distance_str) { int ret; u_char distance; @@ -81,9 +84,9 @@ zebra_static_ipv4 (struct vty *vty, int add_cmd, const char *dest_str, return CMD_WARNING; } if (add_cmd) - static_add_ipv4 (&p, NULL, NULL, ZEBRA_FLAG_BLACKHOLE, distance, 0); + static_add_ipv4_safi (safi, &p, NULL, NULL, ZEBRA_FLAG_BLACKHOLE, distance, 0); else - static_delete_ipv4 (&p, NULL, NULL, distance, 0); + static_delete_ipv4_safi (safi, &p, NULL, NULL, distance, 0); return CMD_SUCCESS; } @@ -107,9 +110,9 @@ zebra_static_ipv4 (struct vty *vty, int add_cmd, const char *dest_str, if (gate_str == NULL) { if (add_cmd) - static_add_ipv4 (&p, NULL, NULL, flag, distance, 0); + static_add_ipv4_safi (safi, &p, NULL, NULL, flag, distance, 0); else - static_delete_ipv4 (&p, NULL, NULL, distance, 0); + static_delete_ipv4_safi (safi, &p, NULL, NULL, distance, 0); return CMD_SUCCESS; } @@ -123,13 +126,58 @@ zebra_static_ipv4 (struct vty *vty, int add_cmd, const char *dest_str, ifname = gate_str; if (add_cmd) - static_add_ipv4 (&p, ifname ? NULL : &gate, ifname, flag, distance, 0); + static_add_ipv4_safi (safi, &p, ifname ? NULL : &gate, ifname, flag, distance, 0); else - static_delete_ipv4 (&p, ifname ? NULL : &gate, ifname, distance, 0); + static_delete_ipv4_safi (safi, &p, ifname ? NULL : &gate, ifname, distance, 0); return CMD_SUCCESS; } +static int +zebra_static_ipv4 (struct vty *vty, int add_cmd, const char *dest_str, + const char *mask_str, const char *gate_str, + const char *flag_str, const char *distance_str) +{ + return zebra_static_ipv4_safi(vty, SAFI_UNICAST, add_cmd, dest_str, mask_str, gate_str, flag_str, distance_str); +} + +/* Static unicast routes for multicast RPF lookup. */ +DEFUN (ip_mroute, + ip_mroute_cmd, + "ip mroute A.B.C.D/M (A.B.C.D|INTERFACE) [<1-255>]", + IP_STR + "Configure static unicast route into MRIB for multicast RPF lookup\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "Nexthop address\n" + "Nexthop interface name\n" + "Distance\n") +{ + return zebra_static_ipv4_safi(vty, SAFI_MULTICAST, 1, argv[0], NULL, argv[1], NULL, argv[2]); +} + +DEFUN (no_ip_mroute, + no_ip_mroute_cmd, + "no ip mroute A.B.C.D/M (A.B.C.D|INTERFACE) [<1-255>]", + IP_STR + "Configure static unicast route into MRIB for multicast RPF lookup\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "Nexthop address\n" + "Nexthop interface name\n" + "Distance\n") +{ + return zebra_static_ipv4_safi(vty, SAFI_MULTICAST, 0, argv[0], NULL, argv[1], NULL, argv[2]); +} + +DEFUN (show_ip_rpf, + show_ip_rpf_cmd, + "show ip rpf", + SHOW_STR + IP_STR + "Display RPF information for multicast source\n") +{ + return do_show_ip_route(vty, SAFI_MULTICAST); +} + /* Static route configuration. */ DEFUN (ip_route, ip_route_cmd, @@ -788,12 +836,16 @@ DEFUN (show_ip_route, IP_STR "IP routing table\n") { + return do_show_ip_route(vty, SAFI_UNICAST); +} + +static int do_show_ip_route(struct vty *vty, safi_t safi) { struct route_table *table; struct route_node *rn; struct rib *rib; int first = 1; - table = vrf_table (AFI_IP, SAFI_UNICAST, 0); + table = vrf_table (AFI_IP, safi, 0); if (! table) return CMD_SUCCESS; @@ -1195,7 +1247,7 @@ DEFUN (show_ip_route_summary_prefix, /* Write IPv4 static route configuration. */ static int -static_config_ipv4 (struct vty *vty) +static_config_ipv4 (struct vty *vty, safi_t safi, const char *cmd) { struct route_node *rn; struct static_ipv4 *si; @@ -1205,14 +1257,14 @@ static_config_ipv4 (struct vty *vty) write = 0; /* Lookup table. */ - stable = vrf_static_table (AFI_IP, SAFI_UNICAST, 0); + stable = vrf_static_table (AFI_IP, safi, 0); if (! stable) return -1; for (rn = route_top (stable); rn; rn = route_next (rn)) for (si = rn->info; si; si = si->next) { - vty_out (vty, "ip route %s/%d", inet_ntoa (rn->p.u.prefix4), + vty_out (vty, "%s %s/%d", cmd, inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen); switch (si->type) @@ -2146,7 +2198,8 @@ zebra_ip_config (struct vty *vty) { int write = 0; - write += static_config_ipv4 (vty); + write += static_config_ipv4 (vty, SAFI_UNICAST, "ip route"); + write += static_config_ipv4 (vty, SAFI_MULTICAST, "ip mroute"); #ifdef HAVE_IPV6 write += static_config_ipv6 (vty); #endif /* HAVE_IPV6 */ @@ -2185,6 +2238,8 @@ zebra_vty_init (void) install_node (&ip_node, zebra_ip_config); install_node (&protocol_node, config_write_protocol); + install_element (CONFIG_NODE, &ip_mroute_cmd); + install_element (CONFIG_NODE, &no_ip_mroute_cmd); install_element (CONFIG_NODE, &ip_protocol_cmd); install_element (CONFIG_NODE, &no_ip_protocol_cmd); install_element (VIEW_NODE, &show_ip_protocol_cmd); @@ -2233,6 +2288,8 @@ zebra_vty_init (void) install_element (VIEW_NODE, &show_ip_mroute_cmd); install_element (ENABLE_NODE, &show_ip_mroute_cmd); + install_element (VIEW_NODE, &show_ip_rpf_cmd); + install_element (ENABLE_NODE, &show_ip_rpf_cmd); #ifdef HAVE_IPV6 install_element (CONFIG_NODE, &ipv6_route_cmd); From 9e6366d73675a5b65220641b71d7710cde8c1143 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Thu, 22 Jan 2015 19:03:53 +0100 Subject: [PATCH 298/482] zebra: fix optional distance on static mrib route Unfortunately, the quagga CLI parser doesn't support [<1-255>]. Fix by working around with an alias. Replaces the following commits: - zebra: mrib: [no] ip mroute - require distance. - zebra: mrib: [no] ip mroute - make distance optional. (Rewritten as alias) Cc: Everton Marques Cc: Balaji G Signed-off-by: David Lamparter --- zebra/zebra_vty.c | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 6802eceb..69245a54 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -142,9 +142,9 @@ zebra_static_ipv4 (struct vty *vty, int add_cmd, const char *dest_str, } /* Static unicast routes for multicast RPF lookup. */ -DEFUN (ip_mroute, - ip_mroute_cmd, - "ip mroute A.B.C.D/M (A.B.C.D|INTERFACE) [<1-255>]", +DEFUN (ip_mroute_dist, + ip_mroute_dist_cmd, + "ip mroute A.B.C.D/M (A.B.C.D|INTERFACE) <1-255>", IP_STR "Configure static unicast route into MRIB for multicast RPF lookup\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" @@ -152,12 +152,22 @@ DEFUN (ip_mroute, "Nexthop interface name\n" "Distance\n") { - return zebra_static_ipv4_safi(vty, SAFI_MULTICAST, 1, argv[0], NULL, argv[1], NULL, argv[2]); + return zebra_static_ipv4_safi(vty, SAFI_MULTICAST, 1, argv[0], NULL, argv[1], + NULL, argc > 2 ? argv[2] : NULL); } -DEFUN (no_ip_mroute, - no_ip_mroute_cmd, - "no ip mroute A.B.C.D/M (A.B.C.D|INTERFACE) [<1-255>]", +ALIAS (ip_mroute_dist, + ip_mroute_cmd, + "ip mroute A.B.C.D/M (A.B.C.D|INTERFACE)", + IP_STR + "Configure static unicast route into MRIB for multicast RPF lookup\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "Nexthop address\n" + "Nexthop interface name\n") + +DEFUN (no_ip_mroute_dist, + no_ip_mroute_dist_cmd, + "no ip mroute A.B.C.D/M (A.B.C.D|INTERFACE) <1-255>", IP_STR "Configure static unicast route into MRIB for multicast RPF lookup\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" @@ -165,9 +175,20 @@ DEFUN (no_ip_mroute, "Nexthop interface name\n" "Distance\n") { - return zebra_static_ipv4_safi(vty, SAFI_MULTICAST, 0, argv[0], NULL, argv[1], NULL, argv[2]); + return zebra_static_ipv4_safi(vty, SAFI_MULTICAST, 0, argv[0], NULL, argv[1], + NULL, argc > 2 ? argv[2] : NULL); } +ALIAS (no_ip_mroute_dist, + no_ip_mroute_cmd, + "no ip mroute A.B.C.D/M (A.B.C.D|INTERFACE)", + NO_STR + IP_STR + "Configure static unicast route into MRIB for multicast RPF lookup\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "Nexthop address\n" + "Nexthop interface name\n") + DEFUN (show_ip_rpf, show_ip_rpf_cmd, "show ip rpf", @@ -2239,7 +2260,9 @@ zebra_vty_init (void) install_node (&protocol_node, config_write_protocol); install_element (CONFIG_NODE, &ip_mroute_cmd); + install_element (CONFIG_NODE, &ip_mroute_dist_cmd); install_element (CONFIG_NODE, &no_ip_mroute_cmd); + install_element (CONFIG_NODE, &no_ip_mroute_dist_cmd); install_element (CONFIG_NODE, &ip_protocol_cmd); install_element (CONFIG_NODE, &no_ip_protocol_cmd); install_element (VIEW_NODE, &show_ip_protocol_cmd); From 149210656045c363d8f59b97ad9251b0c06a15df Mon Sep 17 00:00:00 2001 From: Olivier Dugeon Date: Mon, 3 Nov 2014 14:59:06 +0100 Subject: [PATCH 299/482] ospfd: Fix initial Opaque LSA DB synchronisation ospfd has issues resynchronising its Opaque LSA DB with neighbours after restart or interface events. The problem comes from opaque_lsa.c code that blocks subsequent opaque LSA flooding until the neighbour router acknowledge that, and removes the old opaque LSA from its LSDB. The bug comes from the fact that the lock is never release, thus avoiding subsequent opaque LSA flooding. More detail about the bugs and its solution is describeid in file doc/te-link-params.md Signed-off-by: Olivier Dugeon --- ospfd/ospf_opaque.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/ospfd/ospf_opaque.c b/ospfd/ospf_opaque.c index 744952c9..ecb28ffe 100644 --- a/ospfd/ospf_opaque.c +++ b/ospfd/ospf_opaque.c @@ -2126,10 +2126,12 @@ ospf_opaque_lsa_flush_schedule (struct ospf_lsa *lsa0) *------------------------------------------------------------------------*/ static void ospf_opaque_exclude_lsa_from_lsreq (struct route_table *nbrs, struct ospf_neighbor *inbr, struct ospf_lsa *lsa); +#ifdef BUGGY_UNLOCK static void ospf_opaque_type9_lsa_rxmt_nbr_check (struct ospf_interface *oi); static void ospf_opaque_type10_lsa_rxmt_nbr_check (struct ospf_area *area); static void ospf_opaque_type11_lsa_rxmt_nbr_check (struct ospf *top); static unsigned long ospf_opaque_nrxmt_self (struct route_table *nbrs, int lsa_type); +#endif /* BUGGY_UNLOCK */ void ospf_opaque_adjust_lsreq (struct ospf_neighbor *nbr, struct list *lsas) @@ -2296,17 +2298,20 @@ ospf_opaque_ls_ack_received (struct ospf_neighbor *nbr, struct ospf_lsa *lsa) { case OSPF_OPAQUE_LINK_LSA: if (CHECK_FLAG (top->opaque, OPAQUE_BLOCK_TYPE_09_LSA_BIT)) - ospf_opaque_type9_lsa_rxmt_nbr_check (nbr->oi); + UNSET_FLAG (top->opaque, OPAQUE_BLOCK_TYPE_09_LSA_BIT); + /* BUGGY_UNLOCK: ospf_opaque_type9_lsa_rxmt_nbr_check (nbr->oi); */ /* Callback function... */ break; case OSPF_OPAQUE_AREA_LSA: if (CHECK_FLAG (top->opaque, OPAQUE_BLOCK_TYPE_10_LSA_BIT)) - ospf_opaque_type10_lsa_rxmt_nbr_check (nbr->oi->area); + UNSET_FLAG (top->opaque, OPAQUE_BLOCK_TYPE_10_LSA_BIT); + /* BUGGY_UNLOCK: ospf_opaque_type10_lsa_rxmt_nbr_check (nbr->oi->area); */ /* Callback function... */ break; case OSPF_OPAQUE_AS_LSA: if (CHECK_FLAG (top->opaque, OPAQUE_BLOCK_TYPE_11_LSA_BIT)) - ospf_opaque_type11_lsa_rxmt_nbr_check (top); + UNSET_FLAG (top->opaque, OPAQUE_BLOCK_TYPE_11_LSA_BIT); + /* BUGGY_UNLOCK: ospf_opaque_type11_lsa_rxmt_nbr_check (top); */ /* Callback function... */ break; default: @@ -2315,11 +2320,10 @@ ospf_opaque_ls_ack_received (struct ospf_neighbor *nbr, struct ospf_lsa *lsa) } if (IS_OPAQUE_LSA_ORIGINATION_BLOCKED (top->opaque)) - { - if (IS_DEBUG_OSPF_EVENT) - zlog_debug ("Block Opaque-LSA origination: ON -> OFF"); return; /* Blocking still in progress. */ - } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Block Opaque-LSA origination: ON -> OFF"); if (! CHECK_FLAG (top->config, OSPF_OPAQUE_CAPABLE)) return; /* Opaque capability condition must have changed. */ @@ -2339,6 +2343,7 @@ ospf_opaque_ls_ack_received (struct ospf_neighbor *nbr, struct ospf_lsa *lsa) return; } +#ifdef BUGGY_UNLOCK static void ospf_opaque_type9_lsa_rxmt_nbr_check (struct ospf_interface *oi) { @@ -2439,6 +2444,7 @@ ospf_opaque_nrxmt_self (struct route_table *nbrs, int lsa_type) return n; } +#endif /* BUGGY_UNLOCK */ /*------------------------------------------------------------------------* * Followings are util functions; probably be used by Opaque-LSAs only... From c048dccaf537508019ccda658d75bbb0cfd7ad18 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Mon, 12 Jan 2015 07:05:06 +0100 Subject: [PATCH 300/482] zebra: dummy kernel "install" multicast routes This is a followup to 9511633 ("zebra: MBGP routes should not be installed in the kernel"), which was correct in disabling MRIB routes being installed in the kernel, yet broke the MRIB since now routes were never marked as active. Hence, push down the check into the kernel install functions, so that the routes are still marked active. At the same time, the FPM calls get a check each since otherwise we'd bump the FPM interface on MRIB updates. Fixes: 9511633 ("zebra: MBGP routes should not be installed in the kernel") Cc: Everton Marques Cc: Balaji G Signed-off-by: David Lamparter --- zebra/zebra_rib.c | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index a07036e5..b4ea2424 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -1124,8 +1124,16 @@ rib_install_kernel (struct route_node *rn, struct rib *rib) { int ret = 0; struct nexthop *nexthop, *tnexthop; + rib_table_info_t *info = rn->table->info; int recursing; + if (info->safi != SAFI_UNICAST) + { + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); + return; + } + /* * Make sure we update the FPM any time we send new information to * the kernel. @@ -1157,8 +1165,16 @@ rib_uninstall_kernel (struct route_node *rn, struct rib *rib) { int ret = 0; struct nexthop *nexthop, *tnexthop; + rib_table_info_t *info = rn->table->info; int recursing; + if (info->safi != SAFI_UNICAST) + { + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); + return ret; + } + /* * Make sure we update the FPM any time we send new information to * the kernel. @@ -1187,9 +1203,12 @@ rib_uninstall_kernel (struct route_node *rn, struct rib *rib) static void rib_uninstall (struct route_node *rn, struct rib *rib) { + rib_table_info_t *info = rn->table->info; + if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) { - zfpm_trigger_update (rn, "rib_uninstall"); + if (info->safi == SAFI_UNICAST) + zfpm_trigger_update (rn, "rib_uninstall"); redistribute_delete (&rn->p, rib); if (! RIB_SYSTEM_ROUTE (rib)) @@ -1306,9 +1325,6 @@ rib_process (struct route_node *rn) if (! nexthop_active_update (rn, rib, 0)) continue; - if (info->safi == SAFI_MULTICAST) - continue; - /* Infinit distance. */ if (rib->distance == DISTANCE_INFINITY) continue; @@ -1371,7 +1387,8 @@ rib_process (struct route_node *rn) select, fib); if (CHECK_FLAG (select->flags, ZEBRA_FLAG_CHANGED)) { - zfpm_trigger_update (rn, "updating existing route"); + if (info->safi == SAFI_UNICAST) + zfpm_trigger_update (rn, "updating existing route"); redistribute_delete (&rn->p, select); if (! RIB_SYSTEM_ROUTE (select)) @@ -1414,7 +1431,8 @@ rib_process (struct route_node *rn) if (IS_ZEBRA_DEBUG_RIB) rnode_debug (rn, "Removing existing route, fib %p", fib); - zfpm_trigger_update (rn, "removing existing route"); + if (info->safi == SAFI_UNICAST) + zfpm_trigger_update (rn, "removing existing route"); redistribute_delete (&rn->p, fib); if (! RIB_SYSTEM_ROUTE (fib)) @@ -1434,7 +1452,8 @@ rib_process (struct route_node *rn) if (IS_ZEBRA_DEBUG_RIB) rnode_debug (rn, "Adding route, select %p", select); - zfpm_trigger_update (rn, "new route selected"); + if (info->safi == SAFI_UNICAST) + zfpm_trigger_update (rn, "new route selected"); /* Set real nexthop. */ nexthop_active_update (rn, select, 1); @@ -3267,6 +3286,7 @@ static void rib_close_table (struct route_table *table) { struct route_node *rn; + rib_table_info_t *info = table->info; struct rib *rib; if (table) @@ -3276,7 +3296,8 @@ rib_close_table (struct route_table *table) if (!CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) continue; - zfpm_trigger_update (rn, NULL); + if (info->safi == SAFI_UNICAST) + zfpm_trigger_update (rn, NULL); if (! RIB_SYSTEM_ROUTE (rib)) rib_uninstall_kernel (rn, rib); From 29ce93ebf6427d1d64eae8b385e36873e3ddcb88 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Thu, 22 Jan 2015 19:09:36 +0100 Subject: [PATCH 301/482] zebra: return route_node from rib_match_ipv4_safi The multicast code needs to know the route_node in addition to the rib entry in order to perform distance or prefix-length comparisons. Add it as optional "out" pointer parameter. Cc: Everton Marques Cc: Balaji G Signed-off-by: David Lamparter --- zebra/rib.h | 3 ++- zebra/zebra_rib.c | 21 ++++++++++++++------- zebra/zserv.c | 4 ++-- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/zebra/rib.h b/zebra/rib.h index 5eedfde6..347fadb0 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -418,7 +418,8 @@ extern int rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p, struct in_addr *gate, unsigned int ifindex, u_int32_t, safi_t safi); -extern struct rib *rib_match_ipv4_safi (struct in_addr addr, safi_t safi, int skip_bgp); +extern struct rib *rib_match_ipv4_safi (struct in_addr addr, safi_t safi, + int skip_bgp, struct route_node **rn_out); extern struct rib *rib_lookup_ipv4 (struct prefix_ipv4 *); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index b4ea2424..abef90fb 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -719,7 +719,8 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, #endif /* HAVE_IPV6 */ struct rib * -rib_match_ipv4_safi (struct in_addr addr, safi_t safi, int skip_bgp) +rib_match_ipv4_safi (struct in_addr addr, safi_t safi, int skip_bgp, + struct route_node **rn_out) { struct route_table *table; struct route_node *rn; @@ -759,16 +760,22 @@ rib_match_ipv4_safi (struct in_addr addr, safi_t safi, int skip_bgp) } else { - if (match->type == ZEBRA_ROUTE_CONNECT) - /* Directly point connected route. */ - return match; - else + if (match->type != ZEBRA_ROUTE_CONNECT) { + int found = 0; for (ALL_NEXTHOPS_RO(match->nexthop, newhop, tnewhop, recursing)) if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB)) - return match; - return NULL; + { + found = 1; + break; + } + if (!found) + return NULL; } + + if (rn_out) + *rn_out = rn; + return match; } } return NULL; diff --git a/zebra/zserv.c b/zebra/zserv.c index e9236dec..e678f3a3 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -540,7 +540,7 @@ zsend_ipv4_nexthop_lookup (struct zserv *client, struct in_addr addr) struct nexthop *nexthop; /* Lookup nexthop - eBGP excluded */ - rib = rib_match_ipv4_safi (addr, SAFI_UNICAST, 1); + rib = rib_match_ipv4_safi (addr, SAFI_UNICAST, 1, NULL); /* Get output stream. */ s = client->obuf; @@ -615,7 +615,7 @@ zsend_ipv4_nexthop_lookup_mrib (struct zserv *client, struct in_addr addr) int skip_bgp = 0; /* bool */ /* Lookup nexthop. */ - rib = rib_match_ipv4_safi (addr, SAFI_MULTICAST, skip_bgp); + rib = rib_match_ipv4_safi (addr, SAFI_MULTICAST, skip_bgp, NULL); if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV) zlog_debug("%s: %s mrib entry found.", __func__, rib ? "Matching" : "No matching"); From 240c56f3a41cb7018460ba6e36c9b559c897e3d0 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 6 Jan 2015 19:53:24 +0100 Subject: [PATCH 302/482] zebra: make MRIB lookup behaviour switchable depending on the usage scenario (and availability of multitopology IGP protocols, which is currently zero in Quagga), different approaches of Multicast RPF lookups are useful. Reference behaviours from commercial vendors are urib-only/mrib-only (Juniper, depending on inet.2 availability) and lowest-distance (Cisco). As we are currently without MT IGP support, mrib-first seems the most useful default for Quagga. Cc: Everton Marques Cc: Balaji G Signed-off-by: David Lamparter --- zebra/rib.h | 17 +++++++++++ zebra/zebra_rib.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++ zebra/zebra_vty.c | 76 ++++++++++++++++++++++++++++++++++++++++++++--- zebra/zserv.c | 22 ++++---------- 4 files changed, 169 insertions(+), 21 deletions(-) diff --git a/zebra/rib.h b/zebra/rib.h index 347fadb0..94a74194 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -373,6 +373,21 @@ typedef struct rib_tables_iter_t_ rib_tables_iter_state_t state; } rib_tables_iter_t; +/* RPF lookup behaviour */ +enum multicast_mode +{ + MCAST_NO_CONFIG = 0, /* MIX_MRIB_FIRST, but no show in config write */ + MCAST_MRIB_ONLY, /* MRIB only */ + MCAST_URIB_ONLY, /* URIB only */ + MCAST_MIX_MRIB_FIRST, /* MRIB, if nothing at all then URIB */ + MCAST_MIX_DISTANCE, /* MRIB & URIB, lower distance wins */ + MCAST_MIX_PFXLEN, /* MRIB & URIB, longer prefix wins */ + /* on equal value, MRIB wins for last 2 */ +}; + +extern void multicast_mode_ipv4_set (enum multicast_mode mode); +extern enum multicast_mode multicast_mode_ipv4_get (void); + extern const char *nexthop_type_to_str (enum nexthop_types_t nh_type); extern struct nexthop *nexthop_ifindex_add (struct rib *, unsigned int); extern struct nexthop *nexthop_ifname_add (struct rib *, char *); @@ -420,6 +435,8 @@ extern int rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p, extern struct rib *rib_match_ipv4_safi (struct in_addr addr, safi_t safi, int skip_bgp, struct route_node **rn_out); +extern struct rib *rib_match_ipv4_multicast (struct in_addr addr, + struct route_node **rn_out); extern struct rib *rib_lookup_ipv4 (struct prefix_ipv4 *); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index abef90fb..effe2338 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -75,6 +75,9 @@ static const struct /* Vector for routing table. */ static vector vrf_vector; +/* RPF lookup behaviour */ +static enum multicast_mode ipv4_multicast_mode = MCAST_NO_CONFIG; + static void _rnode_zlog(const char *_func, struct route_node *rn, int priority, const char *msgfmt, ...) @@ -781,6 +784,78 @@ rib_match_ipv4_safi (struct in_addr addr, safi_t safi, int skip_bgp, return NULL; } +struct rib * +rib_match_ipv4_multicast (struct in_addr addr, struct route_node **rn_out) +{ + struct rib *rib = NULL, *mrib = NULL, *urib = NULL; + struct route_node *m_rn = NULL, *u_rn = NULL; + int skip_bgp = 0; /* bool */ + + switch (ipv4_multicast_mode) + { + case MCAST_MRIB_ONLY: + return rib_match_ipv4_safi (addr, SAFI_MULTICAST, skip_bgp, rn_out); + case MCAST_URIB_ONLY: + return rib_match_ipv4_safi (addr, SAFI_UNICAST, skip_bgp, rn_out); + case MCAST_NO_CONFIG: + case MCAST_MIX_MRIB_FIRST: + rib = mrib = rib_match_ipv4_safi (addr, SAFI_MULTICAST, skip_bgp, &m_rn); + if (!mrib) + rib = urib = rib_match_ipv4_safi (addr, SAFI_UNICAST, skip_bgp, &u_rn); + break; + case MCAST_MIX_DISTANCE: + mrib = rib_match_ipv4_safi (addr, SAFI_MULTICAST, skip_bgp, &m_rn); + urib = rib_match_ipv4_safi (addr, SAFI_UNICAST, skip_bgp, &u_rn); + if (mrib && urib) + rib = urib->distance < mrib->distance ? urib : mrib; + else if (mrib) + rib = mrib; + else if (urib) + rib = urib; + break; + case MCAST_MIX_PFXLEN: + mrib = rib_match_ipv4_safi (addr, SAFI_MULTICAST, skip_bgp, &m_rn); + urib = rib_match_ipv4_safi (addr, SAFI_UNICAST, skip_bgp, &u_rn); + if (mrib && urib) + rib = u_rn->p.prefixlen > m_rn->p.prefixlen ? urib : mrib; + else if (mrib) + rib = mrib; + else if (urib) + rib = urib; + break; + } + + if (rn_out) + *rn_out = (rib == mrib) ? m_rn : u_rn; + + if (IS_ZEBRA_DEBUG_RIB) + { + char buf[BUFSIZ]; + inet_ntop (AF_INET, &addr, buf, BUFSIZ); + + zlog_debug("%s: %s: found %s, using %s", + __func__, buf, + mrib ? (urib ? "MRIB+URIB" : "MRIB") : + urib ? "URIB" : "nothing", + rib == urib ? "URIB" : rib == mrib ? "MRIB" : "none"); + } + return rib; +} + +void +multicast_mode_ipv4_set (enum multicast_mode mode) +{ + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug("%s: multicast lookup mode set (%d)", __func__, mode); + ipv4_multicast_mode = mode; +} + +enum multicast_mode +multicast_mode_ipv4_get (void) +{ + return ipv4_multicast_mode; +} + struct rib * rib_lookup_ipv4 (struct prefix_ipv4 *p) { diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 69245a54..f00e35ef 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -189,6 +189,62 @@ ALIAS (no_ip_mroute_dist, "Nexthop address\n" "Nexthop interface name\n") +DEFUN (ip_multicast_mode, + ip_multicast_mode_cmd, + "ip multicast rpf-lookup-mode (urib-only|mrib-only|mrib-then-urib|lower-distance|longer-prefix)", + IP_STR + "Multicast options\n" + "RPF lookup behavior\n" + "Lookup in unicast RIB only\n" + "Lookup in multicast RIB only\n" + "Try multicast RIB first, fall back to unicast RIB\n" + "Lookup both, use entry with lower distance\n" + "Lookup both, use entry with longer prefix\n") +{ + if (!strncmp (argv[0], "u", 1)) + multicast_mode_ipv4_set (MCAST_URIB_ONLY); + else if (!strncmp (argv[0], "mrib-o", 6)) + multicast_mode_ipv4_set (MCAST_MRIB_ONLY); + else if (!strncmp (argv[0], "mrib-t", 6)) + multicast_mode_ipv4_set (MCAST_MIX_MRIB_FIRST); + else if (!strncmp (argv[0], "low", 3)) + multicast_mode_ipv4_set (MCAST_MIX_DISTANCE); + else if (!strncmp (argv[0], "lon", 3)) + multicast_mode_ipv4_set (MCAST_MIX_PFXLEN); + else + { + vty_out (vty, "Invalid mode specified%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN (no_ip_multicast_mode, + no_ip_multicast_mode_cmd, + "no ip multicast rpf-lookup-mode (urib-only|mrib-only|mrib-then-urib|lower-distance|longer-prefix)", + NO_STR + IP_STR + "Multicast options\n" + "RPF lookup behavior\n" + "Lookup in unicast RIB only\n" + "Lookup in multicast RIB only\n" + "Try multicast RIB first, fall back to unicast RIB\n" + "Lookup both, use entry with lower distance\n" + "Lookup both, use entry with longer prefix\n") +{ + multicast_mode_ipv4_set (MCAST_NO_CONFIG); + return CMD_SUCCESS; +} + +ALIAS (no_ip_multicast_mode, + no_ip_multicast_mode_noarg_cmd, + "no ip multicast rpf-lookup-mode", + NO_STR + IP_STR + "Multicast options\n" + "RPF lookup behavior\n") + DEFUN (show_ip_rpf, show_ip_rpf_cmd, "show ip rpf", @@ -2228,10 +2284,19 @@ zebra_ip_config (struct vty *vty) return write; } -/* ip protocol configuration write function */ -static int config_write_protocol(struct vty *vty) -{ +static int config_write_vty(struct vty *vty) +{ int i; + enum multicast_mode ipv4_multicast_mode = multicast_mode_ipv4_get (); + + if (ipv4_multicast_mode != MCAST_NO_CONFIG) + vty_out (vty, "ip multicast rpf-lookup-mode %s%s", + ipv4_multicast_mode == MCAST_URIB_ONLY ? "urib-only" : + ipv4_multicast_mode == MCAST_MRIB_ONLY ? "mrib-only" : + ipv4_multicast_mode == MCAST_MIX_MRIB_FIRST ? "mrib-then-urib" : + ipv4_multicast_mode == MCAST_MIX_DISTANCE ? "lower-distance" : + "longer-prefix", + VTY_NEWLINE); for (i=0;iobuf; @@ -1009,9 +995,11 @@ static int zread_ipv4_nexthop_lookup_mrib (struct zserv *client, u_short length) { struct in_addr addr; + struct rib *rib; addr.s_addr = stream_get_ipv4 (client->ibuf); - return zsend_ipv4_nexthop_lookup_mrib (client, addr); + rib = rib_match_ipv4_multicast (addr, NULL); + return zsend_ipv4_nexthop_lookup_mrib (client, addr, rib); } /* Nexthop lookup for IPv4. */ From ca2b105f3bdd8859117756dc8d8c2406e28af28b Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Thu, 22 Jan 2015 19:12:35 +0100 Subject: [PATCH 303/482] zebra: add "show ip rpf" to get result of RPF lookup Checking what route exactly a RPF lookup for a given source uses is essential for an administrator to debug multicast routing issues. This command provides exactly that, using the multicst RPF lookup function and printing out its result to the CLI. Cc: Everton Marques Cc: Balaji G Signed-off-by: David Lamparter --- zebra/zebra_vty.c | 52 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index f00e35ef..29c01c3c 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -31,6 +31,8 @@ #include "zebra/zserv.h" static int do_show_ip_route(struct vty *vty, safi_t safi); +static void vty_show_ip_route_detail (struct vty *vty, struct route_node *rn, + int mcast); /* General function for static route. */ static int @@ -255,6 +257,36 @@ DEFUN (show_ip_rpf, return do_show_ip_route(vty, SAFI_MULTICAST); } +DEFUN (show_ip_rpf_addr, + show_ip_rpf_addr_cmd, + "show ip rpf A.B.C.D", + SHOW_STR + IP_STR + "Display RPF information for multicast source\n" + "IP multicast source address (e.g. 10.0.0.0)\n") +{ + struct in_addr addr; + struct route_node *rn; + struct rib *rib; + int ret; + + ret = inet_aton (argv[0], &addr); + if (ret == 0) + { + vty_out (vty, "%% Malformed address%s", VTY_NEWLINE); + return CMD_WARNING; + } + + rib = rib_match_ipv4_multicast (addr, &rn); + + if (rib) + vty_show_ip_route_detail (vty, rn, 1); + else + vty_out (vty, "%% No match for RPF lookup%s", VTY_NEWLINE); + + return CMD_SUCCESS; +} + /* Static route configuration. */ DEFUN (ip_route, ip_route_cmd, @@ -655,7 +687,7 @@ DEFUN (no_ip_protocol, /* New RIB. Detailed information for IPv4 route. */ static void -vty_show_ip_route_detail (struct vty *vty, struct route_node *rn) +vty_show_ip_route_detail (struct vty *vty, struct route_node *rn, int mcast) { struct rib *rib; struct nexthop *nexthop, *tnexthop; @@ -663,8 +695,16 @@ vty_show_ip_route_detail (struct vty *vty, struct route_node *rn) RNODE_FOREACH_RIB (rn, rib) { - vty_out (vty, "Routing entry for %s/%d%s", - inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen, + const char *mcast_info; + if (mcast) + { + rib_table_info_t *info = rn->table->info; + mcast_info = (info->safi == SAFI_MULTICAST) + ? " using Multicast RIB" + : " using Unicast RIB"; + } + vty_out (vty, "Routing entry for %s/%d%s%s", + inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen, mcast_info, VTY_NEWLINE); vty_out (vty, " Known via \"%s\"", zebra_route_string (rib->type)); vty_out (vty, ", distance %u, metric %u", rib->distance, rib->metric); @@ -1092,7 +1132,7 @@ DEFUN (show_ip_route_addr, return CMD_WARNING; } - vty_show_ip_route_detail (vty, rn); + vty_show_ip_route_detail (vty, rn, 0); route_unlock_node (rn); @@ -1132,7 +1172,7 @@ DEFUN (show_ip_route_prefix, return CMD_WARNING; } - vty_show_ip_route_detail (vty, rn); + vty_show_ip_route_detail (vty, rn, 0); route_unlock_node (rn); @@ -2381,6 +2421,8 @@ zebra_vty_init (void) install_element (VIEW_NODE, &show_ip_rpf_cmd); install_element (ENABLE_NODE, &show_ip_rpf_cmd); + install_element (VIEW_NODE, &show_ip_rpf_addr_cmd); + install_element (ENABLE_NODE, &show_ip_rpf_addr_cmd); #ifdef HAVE_IPV6 install_element (CONFIG_NODE, &ipv6_route_cmd); From e832c34fd19aa6b2df7c28e78f07617095cf136e Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 27 Jan 2015 20:24:15 +0100 Subject: [PATCH 304/482] zebra: mark multicast commands experimental depending on feedback from actually having these commands in a released version, we may want to adjust them. Thus, mark them as experimental so users are aware of this. Cc: Everton Marques Cc: Balaji G Signed-off-by: David Lamparter --- lib/vty.h | 8 ++++++++ zebra/zebra_vty.c | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/lib/vty.h b/lib/vty.h index 4d6048c9..f31f4b5d 100644 --- a/lib/vty.h +++ b/lib/vty.h @@ -217,6 +217,14 @@ do { } \ } while (0) +#define VTY_WARN_EXPERIMENTAL() \ +do { \ + vty_out (vty, "%% WARNING: this command is experimental. Both its name and" \ + " parameters may%s%% change in a future version of Quagga," \ + " possibly breaking your configuration!%s", \ + VTY_NEWLINE, VTY_NEWLINE); \ +} while (0) + /* Exported variables */ extern char integrate_default[]; diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 29c01c3c..598b40de 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -154,6 +154,7 @@ DEFUN (ip_mroute_dist, "Nexthop interface name\n" "Distance\n") { + VTY_WARN_EXPERIMENTAL(); return zebra_static_ipv4_safi(vty, SAFI_MULTICAST, 1, argv[0], NULL, argv[1], NULL, argc > 2 ? argv[2] : NULL); } @@ -177,6 +178,7 @@ DEFUN (no_ip_mroute_dist, "Nexthop interface name\n" "Distance\n") { + VTY_WARN_EXPERIMENTAL(); return zebra_static_ipv4_safi(vty, SAFI_MULTICAST, 0, argv[0], NULL, argv[1], NULL, argc > 2 ? argv[2] : NULL); } @@ -203,6 +205,8 @@ DEFUN (ip_multicast_mode, "Lookup both, use entry with lower distance\n" "Lookup both, use entry with longer prefix\n") { + VTY_WARN_EXPERIMENTAL(); + if (!strncmp (argv[0], "u", 1)) multicast_mode_ipv4_set (MCAST_URIB_ONLY); else if (!strncmp (argv[0], "mrib-o", 6)) @@ -254,6 +258,7 @@ DEFUN (show_ip_rpf, IP_STR "Display RPF information for multicast source\n") { + VTY_WARN_EXPERIMENTAL(); return do_show_ip_route(vty, SAFI_MULTICAST); } @@ -270,6 +275,8 @@ DEFUN (show_ip_rpf_addr, struct rib *rib; int ret; + VTY_WARN_EXPERIMENTAL(); + ret = inet_aton (argv[0], &addr); if (ret == 0) { From a4e830c7cc0e10a851047aebe008ce7a3f8ef29b Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Fri, 30 Jan 2015 01:44:25 +0100 Subject: [PATCH 305/482] doc: zebra multicast RIB commands Signed-off-by: David Lamparter --- doc/main.texi | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/doc/main.texi b/doc/main.texi index a6bf0d1c..810866aa 100644 --- a/doc/main.texi +++ b/doc/main.texi @@ -10,6 +10,7 @@ different routing protocols. * Invoking zebra:: Running the program * Interface Commands:: Commands for zebra interfaces * Static Route Commands:: Commands for adding static routes +* Multicast RIB Commands:: Commands for controlling MRIB behavior * zebra Route Filtering:: Commands for zebra route filtering * zebra FIB push interface:: Interface to optional FPM component * zebra Terminal Mode Commands:: Commands for zebra's VTY @@ -185,6 +186,81 @@ and later). After setting @var{tableno} with this command, static routes defined after this are added to the specified table. @end deffn +@node Multicast RIB Commands +@section Multicast RIB Commands + +The Multicast RIB provides a separate table of unicast destinations which +is used for Multicast Reverse Path Forwarding decisions. It is used with +a multicast source's IP address, hence contains not multicast group +addresses but unicast addresses. + +This table is fully separate from the default unicast table. However, +RPF lookup can include the unicast table. + +WARNING: RPF lookup results are non-responsive in this version of Quagga, +i.e. multicast routing does not actively react to changes in underlying +unicast topology! + +@deffn Command {ip multicast rpf-lookup-mode @var{mode}} {} +@deffnx Command {no ip multicast rpf-lookup-mode [@var{mode}]} {} + +@var{mode} sets the method used to perform RPF lookups. Supported modes: + +@table @samp +@item urib-only +Performs the lookup on the Unicast RIB. The Multicast RIB is never used. +@item mrib-only +Performs the lookup on the Multicast RIB. The Unicast RIB is never used. +@item mrib-then-urib +Tries to perform the lookup on the Multicast RIB. If any route is found, +that route is used. Otherwise, the Unicast RIB is tried. +@item lower-distance +Performs a lookup on the Multicast RIB and Unicast RIB each. The result +with the lower administrative distance is used; if they're equal, the +Multicast RIB takes precedence. +@item longer-prefix +Performs a lookup on the Multicast RIB and Unicast RIB each. The result +with the longer prefix length is used; if they're equal, the +Multicast RIB takes precedence. +@end table + +WARNING: Unreachable routes do not receive special treatment and do not +cause fallback to a second lookup. +@end deffn + +@deffn Command {show ip rpf @var{addr}} {} + +Performs a Multicast RPF lookup, as configured with +@command{ip multicast rpf-lookup-mode @var{mode}}. @var{addr} specifies +the multicast source address to look up. + +@example +> show ip rpf 192.0.2.1 +Routing entry for 192.0.2.0/24 using Unicast RIB + Known via "kernel", distance 0, metric 0, best + * 198.51.100.1, via eth0 +@end example + +Indicates that a multicast source lookup for 192.0.2.1 would use an +Unicast RIB entry for 192.0.2.0/24 with a gateway of 198.51.100.1. +@end deffn + +@deffn Command {show ip rpf} {} + +Prints the entire Multicast RIB. Note that this is independent of the +configured RPF lookup mode, the Multicast RIB may be printed yet not +used at all. +@end deffn + +@deffn Command {ip mroute @var{prefix} @var{nexthop} [@var{distance}]} {} +@deffnx Command {no ip mroute @var{prefix} @var{nexthop} [@var{distance}]} {} + +Adds a static route entry to the Multicast RIB. This performs exactly as +the @command{ip route} command, except that it inserts the route in the +Multicast RIB instead of the Unicast RIB. +@end deffn + + @node zebra Route Filtering @section zebra Route Filtering Zebra supports @command{prefix-list} and @command{route-map} to match From b162ab753e70328cb6815e58b4bc5b03e9dd4f42 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Mon, 2 Feb 2015 03:00:22 +0100 Subject: [PATCH 306/482] doc: explain rpf lookup default mode Reported-by: Alexis Rosen Signed-off-by: David Lamparter --- doc/main.texi | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/main.texi b/doc/main.texi index 810866aa..4c11d244 100644 --- a/doc/main.texi +++ b/doc/main.texi @@ -224,6 +224,11 @@ with the longer prefix length is used; if they're equal, the Multicast RIB takes precedence. @end table +The @code{mrib-then-urib} setting is the default behavior if nothing is +configured. If this is the desired behavior, it should be explicitly +configured to make the configuration immune against possible changes in +what the default behavior is. + WARNING: Unreachable routes do not receive special treatment and do not cause fallback to a second lookup. @end deffn From 871dbcfede60a8d2d286728bcbd88f27c2035b87 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Tue, 11 Aug 2009 15:43:05 -0300 Subject: [PATCH 307/482] [pim] Initial pim 0.155 --- Makefile.am | 4 +- SERVICES | 1 + configure.ac | 54 +- doc/Makefile.am | 4 +- doc/install.texi | 1 + doc/pimd.8 | 108 + lib/command.h | 2 + lib/log.c | 2 + lib/log.h | 2 + lib/memory.c | 15 +- lib/memtypes.c | 17 + lib/zebra.h | 17 +- pimd/AUTHORS | 9 + pimd/CAVEATS | 137 ++ pimd/COMMANDS | 62 + pimd/COPYING | 340 +++ pimd/DEBUG | 49 + pimd/LINUX_KERNEL_MROUTE_MFC | 22 + pimd/Makefile.am | 70 + pimd/README | 157 ++ pimd/TODO | 356 +++ pimd/pim_assert.c | 803 +++++++ pimd/pim_assert.h | 75 + pimd/pim_cmd.c | 3923 ++++++++++++++++++++++++++++++++++ pimd/pim_cmd.h | 56 + pimd/pim_hello.c | 529 +++++ pimd/pim_hello.h | 46 + pimd/pim_iface.c | 1132 ++++++++++ pimd/pim_iface.h | 160 ++ pimd/pim_ifchannel.c | 893 ++++++++ pimd/pim_ifchannel.h | 145 ++ pimd/pim_igmp.c | 1411 ++++++++++++ pimd/pim_igmp.h | 172 ++ pimd/pim_igmpv3.c | 1717 +++++++++++++++ pimd/pim_igmpv3.h | 100 + pimd/pim_join.c | 428 ++++ pimd/pim_join.h | 43 + pimd/pim_macro.c | 437 ++++ pimd/pim_macro.h | 44 + pimd/pim_main.c | 276 +++ pimd/pim_mroute.c | 432 ++++ pimd/pim_mroute.h | 173 ++ pimd/pim_msg.c | 106 + pimd/pim_msg.h | 52 + pimd/pim_neighbor.c | 696 ++++++ pimd/pim_neighbor.h | 74 + pimd/pim_oil.c | 140 ++ pimd/pim_oil.h | 53 + pimd/pim_pim.c | 716 +++++++ pimd/pim_pim.h | 71 + pimd/pim_rand.c | 60 + pimd/pim_rand.h | 30 + pimd/pim_rpf.c | 254 +++ pimd/pim_rpf.h | 36 + pimd/pim_signals.c | 85 + pimd/pim_signals.h | 28 + pimd/pim_sock.c | 348 +++ pimd/pim_sock.h | 52 + pimd/pim_str.c | 46 + pimd/pim_str.h | 32 + pimd/pim_time.c | 151 ++ pimd/pim_time.h | 39 + pimd/pim_tlv.c | 707 ++++++ pimd/pim_tlv.h | 133 ++ pimd/pim_upstream.c | 686 ++++++ pimd/pim_upstream.h | 122 ++ pimd/pim_util.c | 132 ++ pimd/pim_util.h | 41 + pimd/pim_version.c | 25 + pimd/pim_version.h | 30 + pimd/pim_vty.c | 145 ++ pimd/pim_vty.h | 32 + pimd/pim_zebra.c | 1172 ++++++++++ pimd/pim_zebra.h | 40 + pimd/pim_zlookup.c | 455 ++++ pimd/pim_zlookup.h | 47 + pimd/pimd.c | 125 ++ pimd/pimd.conf.sample | 35 + pimd/pimd.h | 121 ++ pimd/quagga-bootstrap.sh | 21 + pimd/quagga-build.sh | 10 + pimd/quagga-configure.sh | 10 + pimd/quagga-git-add.sh | 12 + pimd/quagga-memtypes.sh | 22 + pimd/savannah-git-clone.sh | 16 + ports/Makefile | 1 + ports/pkg/DESCR | 1 + redhat/quagga.spec.in | 11 +- zebra/zebra_rib.c | 93 +- zebra/zserv.c | 1 + 90 files changed, 21722 insertions(+), 17 deletions(-) create mode 100644 doc/pimd.8 create mode 100644 pimd/AUTHORS create mode 100644 pimd/CAVEATS create mode 100644 pimd/COMMANDS create mode 100644 pimd/COPYING create mode 100644 pimd/DEBUG create mode 100644 pimd/LINUX_KERNEL_MROUTE_MFC create mode 100644 pimd/Makefile.am create mode 100644 pimd/README create mode 100644 pimd/TODO create mode 100644 pimd/pim_assert.c create mode 100644 pimd/pim_assert.h create mode 100644 pimd/pim_cmd.c create mode 100644 pimd/pim_cmd.h create mode 100644 pimd/pim_hello.c create mode 100644 pimd/pim_hello.h create mode 100644 pimd/pim_iface.c create mode 100644 pimd/pim_iface.h create mode 100644 pimd/pim_ifchannel.c create mode 100644 pimd/pim_ifchannel.h create mode 100644 pimd/pim_igmp.c create mode 100644 pimd/pim_igmp.h create mode 100644 pimd/pim_igmpv3.c create mode 100644 pimd/pim_igmpv3.h create mode 100644 pimd/pim_join.c create mode 100644 pimd/pim_join.h create mode 100644 pimd/pim_macro.c create mode 100644 pimd/pim_macro.h create mode 100644 pimd/pim_main.c create mode 100644 pimd/pim_mroute.c create mode 100644 pimd/pim_mroute.h create mode 100644 pimd/pim_msg.c create mode 100644 pimd/pim_msg.h create mode 100644 pimd/pim_neighbor.c create mode 100644 pimd/pim_neighbor.h create mode 100644 pimd/pim_oil.c create mode 100644 pimd/pim_oil.h create mode 100644 pimd/pim_pim.c create mode 100644 pimd/pim_pim.h create mode 100644 pimd/pim_rand.c create mode 100644 pimd/pim_rand.h create mode 100644 pimd/pim_rpf.c create mode 100644 pimd/pim_rpf.h create mode 100644 pimd/pim_signals.c create mode 100644 pimd/pim_signals.h create mode 100644 pimd/pim_sock.c create mode 100644 pimd/pim_sock.h create mode 100644 pimd/pim_str.c create mode 100644 pimd/pim_str.h create mode 100644 pimd/pim_time.c create mode 100644 pimd/pim_time.h create mode 100644 pimd/pim_tlv.c create mode 100644 pimd/pim_tlv.h create mode 100644 pimd/pim_upstream.c create mode 100644 pimd/pim_upstream.h create mode 100644 pimd/pim_util.c create mode 100644 pimd/pim_util.h create mode 100644 pimd/pim_version.c create mode 100644 pimd/pim_version.h create mode 100644 pimd/pim_vty.c create mode 100644 pimd/pim_vty.h create mode 100644 pimd/pim_zebra.c create mode 100644 pimd/pim_zebra.h create mode 100644 pimd/pim_zlookup.c create mode 100644 pimd/pim_zlookup.h create mode 100644 pimd/pimd.c create mode 100644 pimd/pimd.conf.sample create mode 100644 pimd/pimd.h create mode 100755 pimd/quagga-bootstrap.sh create mode 100755 pimd/quagga-build.sh create mode 100755 pimd/quagga-configure.sh create mode 100755 pimd/quagga-git-add.sh create mode 100755 pimd/quagga-memtypes.sh create mode 100755 pimd/savannah-git-clone.sh diff --git a/Makefile.am b/Makefile.am index b6082535..405142f8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,12 +1,12 @@ ## Process this file with automake to produce Makefile.in. SUBDIRS = lib @ZEBRA@ @BGPD@ @RIPD@ @RIPNGD@ @OSPFD@ @OSPF6D@ @BABELD@ \ - @ISISD@ @WATCHQUAGGA@ @VTYSH@ @OSPFCLIENT@ @DOC@ m4 @pkgsrcdir@ \ + @ISISD@ @PIMD@ @WATCHQUAGGA@ @VTYSH@ @OSPFCLIENT@ @DOC@ m4 @pkgsrcdir@ \ redhat @SOLARIS@ tests DIST_SUBDIRS = lib zebra bgpd ripd ripngd ospfd ospf6d babeld \ isisd watchquagga vtysh ospfclient doc m4 pkgsrc redhat tests \ - solaris + solaris pimd EXTRA_DIST = aclocal.m4 SERVICES TODO REPORTING-BUGS INSTALL.quagga.txt \ update-autotools \ diff --git a/SERVICES b/SERVICES index 9c3546bc..c69d0c1a 100644 --- a/SERVICES +++ b/SERVICES @@ -17,3 +17,4 @@ bgpd 2605/tcp ospf6d 2606/tcp ospfapi 2607/tcp isisd 2608/tcp +pimd 2611/tcp diff --git a/configure.ac b/configure.ac index 9d188284..7696bd70 100755 --- a/configure.ac +++ b/configure.ac @@ -220,6 +220,8 @@ AC_ARG_ENABLE(watchquagga, [ --disable-watchquagga do not build watchquagga]) AC_ARG_ENABLE(isisd, [ --enable-isisd build isisd]) +AC_ARG_ENABLE(pimd, +[ --enable-pimd build pimd]) AC_ARG_ENABLE(solaris, [ --enable-solaris build solaris]) AC_ARG_ENABLE(bgp-announce, @@ -1362,6 +1364,12 @@ case "${enable_isisd}" in esac AM_CONDITIONAL(ISISD, test "x$ISISD" = "xisisd") +case "${enable_pimd}" in + "yes") PIMD="pimd";; + "no" ) PIMD="";; + * ) ;; +esac + # XXX Perhaps auto-enable on Solaris, but that's messy for cross builds. case "${enable_solaris}" in "yes") SOLARIS="solaris";; @@ -1385,6 +1393,7 @@ AC_SUBST(OSPF6D) AC_SUBST(BABELD) AC_SUBST(WATCHQUAGGA) AC_SUBST(ISISD) +AC_SUBST(PIMD) AC_SUBST(SOLARIS) AC_SUBST(VTYSH) AC_SUBST(INCLUDES) @@ -1467,7 +1476,8 @@ dnl sockaddr and netinet checks dnl --------------------------- AC_CHECK_TYPES([struct sockaddr, struct sockaddr_in, struct sockaddr_in6, struct sockaddr_un, struct sockaddr_dl, - socklen_t, + socklen_t, struct vifctl, struct mfcctl, struct sioc_sg_req, + vifi_t, struct sioc_vif_req, struct igmpmsg, struct ifaliasreq, struct if6_aliasreq, struct in6_aliasreq, struct nd_opt_adv_interval, struct rt_addrinfo, struct nd_opt_homeagent_info, struct nd_opt_adv_interval], @@ -1496,6 +1506,45 @@ AC_CHECK_TYPES([struct in_pktinfo], AC_MSG_ERROR(['IRDP requires in_pktinfo at the moment!']) fi], [QUAGGA_INCLUDES]) +dnl ----------------------- +dnl checking for IP_PKTINFO +dnl ----------------------- +AC_MSG_CHECKING(for IP_PKTINFO) +AC_TRY_COMPILE([#include ], [ + int opt = IP_PKTINFO; +], [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_IP_PKTINFO, 1, [Have IP_PKTINFO]) +], [ + AC_MSG_RESULT(no) +]) + +dnl --------------------------- +dnl checking for IP_RECVDSTADDR +dnl --------------------------- +AC_MSG_CHECKING(for IP_RECVDSTADDR) +AC_TRY_COMPILE([#include ], [ + int opt = IP_RECVDSTADDR; +], [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_IP_RECVDSTADDR, 1, [Have IP_RECVDSTADDR]) +], [ + AC_MSG_RESULT(no) +]) + +dnl ---------------------- +dnl checking for IP_RECVIF +dnl ---------------------- +AC_MSG_CHECKING(for IP_RECVIF) +AC_TRY_COMPILE([#include ], [ + int opt = IP_RECVIF; +], [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_IP_RECVIF, 1, [Have IP_RECVIF]) +], [ + AC_MSG_RESULT(no) +]) + dnl -------------------------------------- dnl checking for getrusage struct and call dnl -------------------------------------- @@ -1685,6 +1734,7 @@ AC_DEFINE_UNQUOTED(PATH_OSPFD_PID, "$quagga_statedir/ospfd.pid",ospfd PID) AC_DEFINE_UNQUOTED(PATH_OSPF6D_PID, "$quagga_statedir/ospf6d.pid",ospf6d PID) AC_DEFINE_UNQUOTED(PATH_BABELD_PID, "$quagga_statedir/babeld.pid",babeld PID) AC_DEFINE_UNQUOTED(PATH_ISISD_PID, "$quagga_statedir/isisd.pid",isisd PID) +AC_DEFINE_UNQUOTED(PATH_PIMD_PID, "$quagga_statedir/pimd.pid",pimd PID) AC_DEFINE_UNQUOTED(PATH_WATCHQUAGGA_PID, "$quagga_statedir/watchquagga.pid",watchquagga PID) AC_DEFINE_UNQUOTED(ZEBRA_SERV_PATH, "$quagga_statedir/zserv.api",zebra api socket) AC_DEFINE_UNQUOTED(ZEBRA_VTYSH_PATH, "$quagga_statedir/zebra.vty",zebra vty socket) @@ -1695,6 +1745,7 @@ AC_DEFINE_UNQUOTED(OSPF_VTYSH_PATH, "$quagga_statedir/ospfd.vty",ospfd vty socke AC_DEFINE_UNQUOTED(OSPF6_VTYSH_PATH, "$quagga_statedir/ospf6d.vty",ospf6d vty socket) AC_DEFINE_UNQUOTED(BABEL_VTYSH_PATH, "$quagga_statedir/babeld.vty",babeld vty socket) AC_DEFINE_UNQUOTED(ISIS_VTYSH_PATH, "$quagga_statedir/isisd.vty",isisd vty socket) +AC_DEFINE_UNQUOTED(PIM_VTYSH_PATH, "$quagga_statedir/pimd.vty",pimd vty socket) AC_DEFINE_UNQUOTED(DAEMON_VTY_DIR, "$quagga_statedir",daemon vty directory) dnl ------------------------------- @@ -1720,6 +1771,7 @@ AC_CONFIG_FILES([Makefile lib/Makefile zebra/Makefile ripd/Makefile ripngd/Makefile bgpd/Makefile ospfd/Makefile watchquagga/Makefile ospf6d/Makefile isisd/Makefile babeld/Makefile vtysh/Makefile doc/Makefile ospfclient/Makefile tests/Makefile m4/Makefile + pimd/Makefile tests/bgpd.tests/Makefile tests/libzebra.tests/Makefile redhat/Makefile diff --git a/doc/Makefile.am b/doc/Makefile.am index f58657b9..8869c818 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -61,7 +61,9 @@ quagga_TEXINFOS = appendix.texi babeld.texi basic.texi bgpd.texi filter.texi \ .dia.png: $(DIATOPNG) "$@" $< -man_MANS = +if PIMD +man_MANS += pimd.8 +endif if BGPD man_MANS += bgpd.8 diff --git a/doc/install.texi b/doc/install.texi index e958d845..7349e92c 100644 --- a/doc/install.texi +++ b/doc/install.texi @@ -273,6 +273,7 @@ bgpd 2605/tcp # BGPd vty ospf6d 2606/tcp # OSPF6d vty ospfapi 2607/tcp # ospfapi isisd 2608/tcp # ISISd vty +pimd 2611/tcp # PIMd vty @end example If you use a FreeBSD newer than 2.2.8, the above entries are already diff --git a/doc/pimd.8 b/doc/pimd.8 new file mode 100644 index 00000000..b26b1128 --- /dev/null +++ b/doc/pimd.8 @@ -0,0 +1,108 @@ +.TH PIM 8 "10 December 2008" "Quagga PIM daemon" "Version 0.99.11" +.SH NAME +pimd \- a PIM routing for use with Quagga Routing Suite. +.SH SYNOPSIS +.B pimd +[ +.B \-dhvZ +] [ +.B \-f +.I config-file +] [ +.B \-i +.I pid-file +] [ +.B \-P +.I port-number +] [ +.B \-A +.I vty-address +] [ +.B \-u +.I user +] [ +.B \-g +.I group +] +.SH DESCRIPTION +.B pimd +is a protocol-independent multicast component that works with the +.B Quagga +Routing Suite. +.SH OPTIONS +Options available for the +.B pimd +command: +.TP +\fB\-d\fR, \fB\-\-daemon\fR +Runs in daemon mode, forking and exiting from tty. +.TP +\fB\-f\fR, \fB\-\-config-file \fR\fIconfig-file\fR +Specifies the config file to use for startup. If not specified this +option will likely default to \fB\fI/usr/local/etc/pimd.conf\fR. +.TP +\fB\-g\fR, \fB\-\-group \fR\fIgroup\fR +Specify the group to run as. Default is \fIquagga\fR. +.TP +\fB\-h\fR, \fB\-\-help\fR +A brief message. +.TP +\fB\-i\fR, \fB\-\-pid_file \fR\fIpid-file\fR +When pimd starts its process identifier is written to +\fB\fIpid-file\fR. The init system uses the recorded PID to stop or +restart pimd. The likely default is \fB\fI/var/run/pimd.pid\fR. +.TP +\fB\-P\fR, \fB\-\-vty_port \fR\fIport-number\fR +Specify the port that the pimd VTY will listen on. This defaults to +2611, as specified in \fB\fI/etc/services\fR. +.TP +\fB\-A\fR, \fB\-\-vty_addr \fR\fIvty-address\fR +Specify the address that the pimd VTY will listen on. Default is all +interfaces. +.TP +\fB\-u\fR, \fB\-\-user \fR\fIuser\fR +Specify the user to run as. Default is \fIquagga\fR. +.TP +\fB\-v\fR, \fB\-\-version\fR +Print the version and exit. +.TP +\fB\-Z\fR, \fB\-\-debug_zclient\fR +Enable logging information for zclient debugging. +.SH FILES +.TP +.BI /usr/local/sbin/pimd +The default location of the +.B pimd +binary. +.TP +.BI /usr/local/etc/pimd.conf +The default location of the +.B pimd +config file. +.TP +.BI $(PWD)/pimd.log +If the +.B pimd +process is config'd to output logs to a file, then you will find this +file in the directory where you started \fBpimd\fR. +.SH WARNING +This man page is intended to be a quick reference for command line +options. +.SH DIAGNOSTICS +The pimd process may log to standard output, to a VTY, to a log +file, or through syslog to the system logs. +.SH "SEE ALSO" +.BR zebra (8), +.BR vtysh (1) +.SH BUGS +\fBpimd\fR is in early development at the moment and is not ready for +production use. + +.B pimd +eats bugs for breakfast. If you have food for the maintainers try +.BI http://savannah.nongnu.org/projects/qpimd +.SH AUTHORS +See +.BI http://savannah.nongnu.org/projects/qpimd +for an accurate list of authors. + diff --git a/lib/command.h b/lib/command.h index 8dc50d0d..5156dbf1 100644 --- a/lib/command.h +++ b/lib/command.h @@ -1,6 +1,7 @@ /* * Zebra configuration command interface routine * Copyright (C) 1997, 98 Kunihiro Ishiguro + * Portions Copyright (c) 2008 Everton da Silva Marques * * This file is part of GNU Zebra. * @@ -88,6 +89,7 @@ enum node_type OSPF_NODE, /* OSPF protocol mode */ OSPF6_NODE, /* OSPF protocol for IPv6 mode */ ISIS_NODE, /* ISIS protocol mode */ + PIM_NODE, /* PIM protocol mode */ MASC_NODE, /* MASC for multicast. */ IRDP_NODE, /* ICMP Router Discovery Protocol mode. */ IP_NODE, /* Static ip route node. */ diff --git a/lib/log.c b/lib/log.c index 04f8fab6..abfd35bc 100644 --- a/lib/log.c +++ b/lib/log.c @@ -1,6 +1,7 @@ /* * Logging of zebra * Copyright (C) 1997, 1998, 1999 Kunihiro Ishiguro + * Portions Copyright (c) 2008 Everton da Silva Marques * * This file is part of GNU Zebra. * @@ -51,6 +52,7 @@ const char *zlog_proto_names[] = "BABEL", "OSPF6", "ISIS", + "PIM", "MASC", NULL, }; diff --git a/lib/log.h b/lib/log.h index f3b43ad1..d9f1ecaa 100644 --- a/lib/log.h +++ b/lib/log.h @@ -1,6 +1,7 @@ /* * Zebra logging funcions. * Copyright (C) 1997, 1998, 1999 Kunihiro Ishiguro + * Portions Copyright (c) 2008 Everton da Silva Marques * * This file is part of GNU Zebra. * @@ -53,6 +54,7 @@ typedef enum ZLOG_BABEL, ZLOG_OSPF6, ZLOG_ISIS, + ZLOG_PIM, ZLOG_MASC } zlog_proto_t; diff --git a/lib/memory.c b/lib/memory.c index 620bdee5..28bbdc11 100644 --- a/lib/memory.c +++ b/lib/memory.c @@ -1,6 +1,7 @@ /* * Memory management routine * Copyright (C) 1998 Kunihiro Ishiguro + * Portions Copyright (c) 2008 Everton da Silva Marques * * This file is part of GNU Zebra. * @@ -521,6 +522,17 @@ DEFUN (show_memory_isis, return CMD_SUCCESS; } +DEFUN (show_memory_pim, + show_memory_pim_cmd, + "show memory pim", + SHOW_STR + "Memory statistics\n" + "PIM memory\n") +{ + show_memory_vty (vty, memory_list_pim); + return CMD_SUCCESS; +} + void memory_init (void) { @@ -545,6 +557,7 @@ memory_init (void) install_element (VIEW_NODE, &show_memory_ospf_cmd); install_element (VIEW_NODE, &show_memory_ospf6_cmd); install_element (VIEW_NODE, &show_memory_isis_cmd); + install_element (VIEW_NODE, &show_memory_pim_cmd); install_element (ENABLE_NODE, &show_memory_cmd); install_element (ENABLE_NODE, &show_memory_all_cmd); @@ -556,7 +569,7 @@ memory_init (void) install_element (ENABLE_NODE, &show_memory_bgp_cmd); install_element (ENABLE_NODE, &show_memory_ospf_cmd); install_element (ENABLE_NODE, &show_memory_ospf6_cmd); - install_element (ENABLE_NODE, &show_memory_isis_cmd); + install_element (ENABLE_NODE, &show_memory_pim_cmd); } /* Stats querying from users */ diff --git a/lib/memtypes.c b/lib/memtypes.c index 47a34387..26e27e72 100644 --- a/lib/memtypes.c +++ b/lib/memtypes.c @@ -1,4 +1,6 @@ /* + * Portions Copyright (c) 2008 Everton da Silva Marques + * * Memory type definitions. This file is parsed by memtypes.awk to extract * MTYPE_ and memory_list_.. information in order to autogenerate * memtypes.h. @@ -255,6 +257,20 @@ struct memory_list memory_list_isis[] = { -1, NULL }, }; +struct memory_list memory_list_pim[] = +{ + { MTYPE_PIM_CHANNEL_OIL, "PIM SSM (S,G) channel OIL" }, + { MTYPE_PIM_INTERFACE, "PIM interface" }, + { MTYPE_PIM_IGMP_JOIN, "PIM interface IGMP static join" }, + { MTYPE_PIM_IGMP_SOCKET, "PIM interface IGMP socket" }, + { MTYPE_PIM_IGMP_GROUP, "PIM interface IGMP group" }, + { MTYPE_PIM_IGMP_GROUP_SOURCE, "PIM interface IGMP source" }, + { MTYPE_PIM_NEIGHBOR, "PIM interface neighbor" }, + { MTYPE_PIM_IFCHANNEL, "PIM interface (S,G) state" }, + { MTYPE_PIM_UPSTREAM, "PIM upstream (S,G) state" }, + { -1, NULL }, +}; + struct memory_list memory_list_vtysh[] = { { MTYPE_VTYSH_CONFIG, "Vtysh configuration", }, @@ -271,5 +287,6 @@ struct mlist mlists[] __attribute__ ((unused)) = { { memory_list_ospf6, "OSPF6" }, { memory_list_isis, "ISIS" }, { memory_list_bgp, "BGP" }, + { memory_list_pim, "PIM" }, { NULL, NULL}, }; diff --git a/lib/zebra.h b/lib/zebra.h index a4e02148..67d714cf 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -1,5 +1,6 @@ /* Zebra common header. Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Kunihiro Ishiguro + Portions Copyright (c) 2008 Everton da Silva Marques This file is part of GNU Zebra. @@ -434,8 +435,20 @@ struct in_pktinfo */ #define ZEBRA_HEADER_MARKER 255 -/* Zebra route's types are defined in route_types.h */ -#include "route_types.h" +/* Zebra route's types. */ +#define ZEBRA_ROUTE_SYSTEM 0 +#define ZEBRA_ROUTE_KERNEL 1 +#define ZEBRA_ROUTE_CONNECT 2 +#define ZEBRA_ROUTE_STATIC 3 +#define ZEBRA_ROUTE_RIP 4 +#define ZEBRA_ROUTE_RIPNG 5 +#define ZEBRA_ROUTE_OSPF 6 +#define ZEBRA_ROUTE_OSPF6 7 +#define ZEBRA_ROUTE_ISIS 8 +#define ZEBRA_ROUTE_BGP 9 +#define ZEBRA_ROUTE_HSLS 10 +#define ZEBRA_ROUTE_PIM 11 +#define ZEBRA_ROUTE_MAX 12 /* Note: whenever a new route-type or zserv-command is added the * corresponding {command,route}_types[] table in lib/log.c MUST be diff --git a/pimd/AUTHORS b/pimd/AUTHORS new file mode 100644 index 00000000..f6135a41 --- /dev/null +++ b/pimd/AUTHORS @@ -0,0 +1,9 @@ +# $QuaggaId: $Format:%an, %ai, %h$ $ + +# Everton da Silva Marques +$ more ~/.gitconfig +[user] + name = Everton Marques + email = everton.marques@gmail.com + +-x- diff --git a/pimd/CAVEATS b/pimd/CAVEATS new file mode 100644 index 00000000..7dc950fe --- /dev/null +++ b/pimd/CAVEATS @@ -0,0 +1,137 @@ +# $QuaggaId: $Format:%an, %ai, %h$ $ + +C1 IGMPv3 backward compatibility with IGMPv1 and IGMPv2 is not + implemented. See RFC 3376, 7.3. Multicast Router Behavior. That's + because only Source-Specific Multicast is currently targeted. + +C2 IGMPv3 support for forwarding any-source groups is not + implemented. Traffic for groups in mode EXCLUDE {empty} won't be + forwarded. See RFC 3376, 6.3. Source-Specific Forwarding + Rules. That's because only Source-Specific Multicast is currently + targeted. + +C3 Load Splitting of IP Multicast Traffic over ECMP is not supported. + See also: RFC 2991 + Multipath Issues in Unicast and Multicast Next-Hop Selection + http://www.rfc-editor.org/rfc/rfc2991.txt + +C4 IPSec AH authentication is not supported (RFC 4601: + 6.3. Authentication Using IPsec). + +C5 PIM support is limited to SSM mode as defined in section 4.8.2 + (PIM-SSM-Only Routers) of RFC4601. That's because only + Source-Specific Multicast is currently targeted. + +C6 PIM implementation currently does not support IPv6. PIM-SSM + requires IGMPv3 for IPv4 and MLDv2 for IPv6. MLDv2 is currently + missing. See also CAVEAT C9. + +C7 (S,G) Assert state machine (RFC 4601, section 4.6.1) is not + implemented. See also TODO T6. See also CAVEAT C10. + +C8 It is not possible to disable join suppression in order to + explicitly track the join membership of individual downstream + routers. + - IGMPv3 Explicit Membership Tracking is not supported. + When explicit tracking is enabled on a router, the router can + individually track the Internet Group Management Protocol (IGMP) + membership state of all reporting hosts. This feature allows the + router to achieve minimal leave latencies when hosts leave a + multicast group or channel. Example: + conf t + interface eth0 + ip igmp explicit-tracking + +C9 Only IPv4 Address Family (number=1) is supported in the PIM Address + Family field. + See also RFC 4601: 5.1. PIM Address Family + See also CAVEAT C6. + See also http://www.iana.org/assignments/address-family-numbers + +C10 FIXED Assert metric depends on metric_preference and + route_metric. Those parameters should be fetched from RIB + (zebra). See also pim_rpf.c, pim_rpf_update(). + +C11 SSM Mapping is not supported + + SSM Mapping Overview: + + SSM mapping introduces a means for the last hop router to discover + sources sending to groups. When SSM mapping is configured, if a + router receives an IGMPv1 or IGMPv2 membership report for a + particular group G, the router translates this report into one or + more (S, G) channel memberships for the well-known sources + associated with this group. + + When the router receives an IGMPv1 or IGMPv2 membership report for + a group G, the router uses SSM mapping to determine one or more + source IP addresses for the group G. SSM mapping then translates + the membership report as an IGMPv3 report INCLUDE (G, [S1, G], + [S2, G]...[Sn, G] and continues as if it had received an IGMPv3 + report. The router then sends out PIM joins toward (S1, G) to (Sn, + G) and continues to be joined to these groups as long as it + continues to receive the IGMPv1 or IGMPv2 membership reports and + as long as the SSM mapping for the group remains the same. SSM + mapping, thus, enables you to leverage SSM for video delivery to + legacy STBs that do not support IGMPv3 or for applications that do + not take advantage of the IGMPv3 host stack. + + SSM mapping enables the last hop router to determine the source + addresses either by a statically configured table on the router or + by consulting a DNS server. When the statically configured table + is changed, or when the DNS mapping changes, the router will leave + the current sources associated with the joined groups. + +C12 MRIB for incongruent unicast/multicast topologies is not supported. + RPF mechanism currently just looks up the information in the + unicast routing table. + + See also: + RFC5110: 2.2.3. Issue: Overlapping Unicast/Multicast Topology + + Sometimes, multicast RPF mechanisms first look up the multicast + routing table, or M-RIB ("topology database") with a longest + prefix match algorithm, and if they find any entry (including a + default route), that is used; if no match is found, the unicast + routing table is used instead. + +C13 Can't detect change of primary address before the actual change. + Possible approach is to craft old interface address into ip source + address by using raw ip socket. + + See also: + + RFC 4601: 4.3.1. Sending Hello Messages + + Before an interface goes down or changes primary IP address, a + Hello message with a zero HoldTime should be sent immediately + (with the old IP address if the IP address changed). + + See also pim_sock_delete(). + +C14 Detection of interface primary address changes may fail when there + are multiple addresses. + See also TODO T32. + +C15 Changes in interface secondary address list are not immediately + detected. + See also TODO T31. + +C16 AMT Draft (mboned-auto-multicast) is not supported. + AMT = Automatic IP Multicast Without Explicit Tunnels + + See also: + + Draft + http://tools.ietf.org/html/draft-ietf-mboned-auto-multicast + http://tools.ietf.org/html/draft-ietf-mboned-auto-multicast-09 + + AMT gateway implementation for Linux + http://cs.utdallas.edu/amt/ + + AMT for Streaming (IPTV) on Global IP Multicast by Greg Shepherd (Cisco) + http://nznog.miniconf.org/nznog-2008-sysadmin-miniconf-greg-shepherd-iptv.pdf + +C17 SNMP / RFC 5060 (PIM MIB) is not supported. + +-x- diff --git a/pimd/COMMANDS b/pimd/COMMANDS new file mode 100644 index 00000000..5679e95a --- /dev/null +++ b/pimd/COMMANDS @@ -0,0 +1,62 @@ +# $QuaggaId: $Format:%an, %ai, %h$ $ + +global configuration commands: + ip multicast-routing Enable IP multicast forwarding + +interface configuration commands: + ip igmp Enable IGMP operation + ip igmp join IGMP join multicast group + ip igmp query-interval <1-1800> IGMP host query interval + ip igmp query-max-response-time <1-25> IGMP max query response (seconds) + ip igmp query-max-response-time-dsec <10-250> IGMP max query response (deciseconds) + ip pim ssm Enable PIM SSM operation + +verification commands: + show ip igmp interface IGMP interface information + show ip igmp parameters IGMP parameters information + show ip igmp groups IGMP groups information + show ip igmp groups retransmissions IGMP group retransmission + show ip igmp sources IGMP sources information + show ip igmp sources retransmissions IGMP source retransmission + show ip pim address PIM interface address + show ip pim assert PIM interface assert + show ip pim assert-internal PIM interface internal assert state + show ip pim assert-metric PIM interface assert metric + show ip pim assert-winner-metric PIM interface assert winner metric + show ip pim designated-router PIM interface designated router + show ip pim hello PIM interface hello information + show ip pim interface PIM interface information + show ip pim lan-prune-delay PIM neighbors LAN prune delay parameters + show ip pim local-membership PIM interface local-membership + show ip pim jp-override-interval PIM interface J/P override interval + show ip pim join PIM interface join information + show ip pim neighbor PIM neighbor information + show ip pim rpf PIM cached source rpf information + show ip pim secondary PIM neighbor addresses + show ip pim upstream PIM upstream information + show ip pim upstream-join-desired PIM upstream join-desired + show ip pim upstream-rpf PIM upstream source rpf + show ip multicast Multicast global information + show ip mroute IP multicast routing table + show ip mroute count Route and packet count data + show ip route IP routing table + +debug commands: + clear ip interfaces Reset interfaces + clear ip igmp interfaces Reset IGMP interfaces + clear ip pim interfaces Reset PIM interfaces + debug igmp IGMP protocol activity + debug pim PIM protocol activity + debug pim zebra ZEBRA protocol activity + show debugging State of each debugging option + test igmp receive report Test reception of IGMPv3 report + test pim receive assert Test reception of PIM assert + test pim receive hello Test reception of PIM hello + test pim receive join Test reception of PIM join + test pim receive prune Test reception of PIM prune + test pim receive upcall Test reception of kernel upcall + +statistics commands: + show memory pim PIM memory statistics + +-x- diff --git a/pimd/COPYING b/pimd/COPYING new file mode 100644 index 00000000..3912109b --- /dev/null +++ b/pimd/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/pimd/DEBUG b/pimd/DEBUG new file mode 100644 index 00000000..aeed9da1 --- /dev/null +++ b/pimd/DEBUG @@ -0,0 +1,49 @@ +# $QuaggaId: $Format:%an, %ai, %h$ $ + +DEBUG HINTS + + - Check the source is issuing multicast packets with TTL high enough + to reach the recipients. + + - Check the multicast packets are not being dropped due to + fragmentation problems. + + - The following command generates a 100-kbps multicast stream for + channel 1.1.1.1,239.1.1.1 with TTL 10 and 1000-byte payload per UDP + packet (to avoid fragmentation): + + nepim -b 1.1.1.1 -c 239.1.1.1 -T 10 -W 1000 -r 100k -a 1d + + - Remotely you can receive that stream by running: + + nepim -j 1.1.1.1+239.1.1.1@eth0 + (Remember of enabling both "ip pim ssm" and "ip igmp" under eth0.) + + +SAMPLE DEBUG COMMANDS + + conf t + int eth0 + ip pim ssm + + test pim receive hello eth0 192.168.0.2 600 10 111 1000 3000 0 + test pim receive join eth0 600 192.168.0.1 192.168.0.2 239.1.1.1 1.1.1.1 + + show ip pim join + + +INTEROPERABILITY WITH CISCO + + ! Cisco IP Multicast command reference: + ! ftp://ftpeng.cisco.com/ipmulticast/Multicast-Commands + ! + ip pim ssm default ! enable SSM mode for groups 232.0.0.0/8 + ip multicast-routing + ip pim state-refresh disable + no ip pim dm-fallback + ! + interface FastEthernet0 + ip pim sparse-mode + ip igmp version 3 + +-x- diff --git a/pimd/LINUX_KERNEL_MROUTE_MFC b/pimd/LINUX_KERNEL_MROUTE_MFC new file mode 100644 index 00000000..7dc44b0d --- /dev/null +++ b/pimd/LINUX_KERNEL_MROUTE_MFC @@ -0,0 +1,22 @@ +# $QuaggaId: $Format:%an, %ai, %h$ $ + +# +# The Linux Kernel MFC (Multicast Forwarding Cache) +# + +# Check Linux kernel multicast interfaces: +cat /proc/net/dev_mcast + +# Check that interface eth0 is forwarding multicast: +cat /proc/sys/net/ipv4/conf/eth0/mc_forwarding + +# Check Linux kernel multicast VIFs: +cat /proc/net/ip_mr_vif +Interface BytesIn PktsIn BytesOut PktsOut Flags Local Remote + +# Check Linux kernel MFC: +# Oifs format = vifi:TTL +cat /proc/net/ip_mr_cache +Group Origin Iif Pkts Bytes Wrong Oifs + +# -- end-of-file -- diff --git a/pimd/Makefile.am b/pimd/Makefile.am new file mode 100644 index 00000000..80df2e6b --- /dev/null +++ b/pimd/Makefile.am @@ -0,0 +1,70 @@ +## Process this file with automake to produce Makefile.in. +## $QuaggaId: $Format:%an, %ai, %h$ $ + +# qpimd - pimd for quagga +# Copyright (C) 2008 Everton da Silva Marques +# +# qpimd is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2, +# or (at your option) any later version. +# +# qpimd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public +# License along with qpimd; see the file COPYING. If not, write +# to the Free Software Foundation, Inc., 59 Temple Place - Suite +# 330, Boston, MA 02111-1307, USA. + +# PIM_DEBUG_BYDEFAULT: Automatically enables all pimd "debug ..." commands +# PIM_ZCLIENT_DEBUG: Support for internal ZEBRA client debugging +# PIM_MOTD_VERSION: Includes pimd version in default MOTD +# PIM_USE_QUAGGA_INET_CHECKSUM: Prefer Quagga inet checksum +# PIM_CHECK_RECV_IFINDEX_SANITY: Compare socket ifindex with recv ifindex +# PIM_REPORT_RECV_IFINDEX_MISMATCH: Report sock/recv ifindex mismatch +# PIM_ENFORCE_LOOPFREE_MFC: Refuse adding looping MFC entries +# PIM_UNEXPECTED_KERNEL_UPCALL: Report unexpected kernel upcall + +PIM_DEFS = +#PIM_DEFS += -DPIM_DEBUG_BYDEFAULT +PIM_DEFS += -DPIM_CHECK_RECV_IFINDEX_SANITY +#PIM_DEFS += -DPIM_REPORT_RECV_IFINDEX_MISMATCH +PIM_DEFS += -DPIM_ZCLIENT_DEBUG +PIM_DEFS += -DPIM_MOTD_VERSION +PIM_DEFS += -DPIM_USE_QUAGGA_INET_CHECKSUM +PIM_DEFS += -DPIM_ENFORCE_LOOPFREE_MFC +#PIM_DEFS += -DPIM_UNEXPECTED_KERNEL_UPCALL + +INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib +DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" $(PIM_DEFS) +INSTALL_SDATA=@INSTALL@ -m 600 +LIBS = @LIBS@ +noinst_LIBRARIES = libpim.a +sbin_PROGRAMS = pimd + +libpim_a_SOURCES = \ + pimd.c pim_version.c pim_cmd.c pim_signals.c pim_iface.c \ + pim_vty.c pim_igmp.c pim_sock.c pim_zebra.c \ + pim_igmpv3.c pim_str.c pim_mroute.c pim_util.c pim_time.c \ + pim_oil.c pim_zlookup.c pim_pim.c pim_tlv.c pim_neighbor.c \ + pim_hello.c pim_ifchannel.c pim_join.c pim_assert.c \ + pim_msg.c pim_upstream.c pim_rpf.c pim_rand.c pim_macro.c + +noinst_HEADERS = \ + pimd.h pim_version.h pim_cmd.h pim_signals.h pim_iface.h \ + pim_vty.h pim_igmp.h pim_sock.h pim_zebra.h \ + pim_igmpv3.h pim_str.h pim_mroute.h pim_util.h pim_time.h \ + pim_oil.h pim_zlookup.h pim_pim.h pim_tlv.h pim_neighbor.h \ + pim_hello.h pim_ifchannel.h pim_join.h pim_assert.h \ + pim_msg.h pim_upstream.h pim_rpf.h pim_rand.h pim_macro.h + +pimd_SOURCES = \ + pim_main.c $(libpim_a_SOURCES) + +pimd_LDADD = ../lib/libzebra.la @LIBCAP@ + +examplesdir = $(exampledir) +dist_examples_DATA = pimd.conf.sample diff --git a/pimd/README b/pimd/README new file mode 100644 index 00000000..e42ceda2 --- /dev/null +++ b/pimd/README @@ -0,0 +1,157 @@ +# +# $QuaggaId: $Format:%an, %ai, %h$ $ +# + +INTRODUCTION + + qpimd aims to implement a PIM (Protocol Independent Multicast) + daemon for the Quagga Routing Suite. + + Initially qpimd targets only PIM SSM (Source-Specific + Multicast) mode as defined in section 4.8.2 (PIM-SSM-Only + Routers) of RFC 4601. + + In order to deliver end-to-end multicast routing control + plane, qpimd includes the router-side of IGMPv3 (RFC 3376). + +LICENSE + + qpimd - pimd for quagga + Copyright (C) 2008 Everton da Silva Marques + + qpimd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, + or (at your option) any later version. + + qpimd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with qpimd; see the file COPYING. If not, write + to the Free Software Foundation, Inc., 59 Temple Place - Suite + 330, Boston, MA 02111-1307, USA. + +HOME SITE + + qpimd lives at: + + http://savannah.nongnu.org/projects/qpimd + +PLATFORMS + + qpimd has been tested with Debian Lenny under Linux 2.6. + +REQUIREMENTS + + qpimd requires Quagga (0.99.11 or higher from http://www.quagga.net) + + The GNU Build System (Autotools) is required to build from + source code repository. + + gawk is also needed to build with Autotools. Any other awk + usually won't work. + +BUILDING FROM QUAGGA GIT REPOSITORY + + 1) Get the latest quagga source tree + + # git clone git://code.quagga.net/quagga.git quagga + + 2) Apply qpimd patch into quagga source tree + + # patch -p1 -d quagga < pimd-0.153-quagga-git20090623.patch + + 3) Compile and install quagga + + # cd quagga + # ./bootstrap.sh + # ./configure --prefix=/usr/local/quagga --enable-pimd + # make + # make install + +BUILDING FROM QUAGGA TARBALL + + 1) Get the latest quagga tarball + + # wget http://www.quagga.net/download/quagga-0.99.13.tar.gz + + 2) Unpack the quagga tarball + + # tar xzf quagga-0.99.13.tar.gz + + 3) Apply qpimd patch into quagga source tree + + # patch -p1 -d quagga-0.99.13 < pimd-0.153-quagga-0.99.13.patch + + 4) Compile and install quagga + + # cd quagga-0.99.13 + # ./configure --prefix=/usr/local/quagga --enable-pimd + # make + # make install + +USAGE + + 1) Configure and start the zebra daemon + + # cp /usr/local/quagga/etc/zebra.conf.sample /usr/local/quagga/etc/zebra.conf + # vi /usr/local/quagga/etc/zebra.conf + # /usr/local/quagga/sbin/zebra + + 2) Configure and start the pimd daemon + + # cp /usr/local/quagga/etc/pimd.conf.sample /usr/local/quagga/etc/pimd.conf + # vi /usr/local/quagga/etc/pimd.conf + # /usr/local/quagga/sbin/pimd + + 3) Access pimd vty interface at port TCP 2611 + + # telnet localhost 2611 + +CONFIGURATION COMMANDS + + See available commands in the file pimd/COMMANDS. + +KNOWN CAVEATS + + See list of known caveats in the file pimd/CAVEATS. + +SUPPORT + + Please post comments, questions, patches, bug reports at the + support site: + + http://savannah.nongnu.org/projects/qpimd + +RELATED WORK + + igmprt: An IGMPv3-router implementation + - http://www.loria.fr/~lahmadi/igmpv3-router.html + + pimd: PIMv2-SM daemon + - http://netweb.usc.edu/pim/pimd (URL broken in 2008-12-23) + - http://packages.debian.org/source/sid/pimd (from Debian) + + zpimd: zpimd is not dependent of zebra or any other routing daemon + - ftp://robur.slu.se/pub/Routing/Zebra + - http://sunsite2.icm.edu.pl/pub/unix/routing/zpimd + + mrd6: an IPv6 Multicast Router for Linux systems + - http://fivebits.net/proj/mrd6/ + + MBGP: Implementation of RFC 2858 for Quagga + - git://git.coplanar.net/~balajig/quagga + - http://www.gossamer-threads.com/lists/quagga/dev/18000 + +REFERENCES + + IANA Protocol Independent Multicast (PIM) Parameters + http://www.iana.org/assignments/pim-parameters/pim-parameters.txt + + Address Family Numbers + http://www.iana.org/assignments/address-family-numbers + + -- END -- diff --git a/pimd/TODO b/pimd/TODO new file mode 100644 index 00000000..7a2ca0b6 --- /dev/null +++ b/pimd/TODO @@ -0,0 +1,356 @@ +# $QuaggaId: $Format:%an, %ai, %h$ $ + +T1 DONE Implement debug command + test pim receive join + +T2 DONE Implement debug command + test pim receive prune + +T3 DONE Per-interface Downstream (S,G) state machine + (RFC 4601 4.5.3. Receiving (S,G) Join/Prune Messages) + +T4 DONE Upstream (S,G) state machine + (RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages) + +T5 DONE Verify Data Packet Forwarding Rules + RFC 4601 4.2. Data Packet Forwarding Rules + RFC 4601 4.8.2. PIM-SSM-Only Routers + + Additionally, the Packet forwarding rules of Section 4.2 can be + simplified in a PIM-SSM-only router: + + iif is the incoming interface of the packet. + oiflist = NULL + if (iif == RPF_interface(S) AND UpstreamJPState(S,G) == Joined) { + oiflist = inherited_olist(S,G) + } else if (iif is in inherited_olist(S,G)) { + send Assert(S,G) on iif + } + oiflist = oiflist (-) iif + forward packet on all interfaces in oiflist + + Macro: + inherited_olist(S,G) = + joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G) + +T6 DONE Implement (S,G) Assert state machine (RFC 4601, section 4.6.1). + Changes in pim_ifchannel.ifassert_winner should trigger + pim_upstream_update_join_desired(). + Depends on TODO T27. + Depends on TODO T33. + See also CAVEAT C7. + See also: RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages + Transitions from Joined State + RPF'(S,G) changes due to an Assert + + http://www.hep.ucl.ac.uk/~ytl/multi-cast/pim-dm_01.html: + + The PIM Assert mechanism is used to shutoff duplicate flows onto + the same multiaccess network. Routers detect this condiction when + they receive an (S,G) packet via a multi-access interface that is + in the (S,G) OIL. This causes the routers to send Assert + Messages. + + Note that neighbors will not accept Join/Prune or Assert messages + from a router unless they have first heard a Hello message from that + router. Thus, if a router needs to send a Join/Prune or Assert + message on an interface on which it has not yet sent a Hello message + with the currently configured IP address, then it MUST immediately + send the relevant Hello message without waiting for the Hello Timer + to expire, followed by the Join/Prune or Assert message. + +T7 DONE Implement hello option: LAN Prune Delay + +T8 DONE Implement J/P_Override_Interval(I) + Depends on TODO T7. + See pim_ifchannel.c, pim_ifchannel_prune(), jp_override_interval. + +T9 DONE Detect change in IGMPv3 RPF interface/next-hop for S and update. + channel_oil vif index accordingly ? + Beware accidentaly adding looped MFC entries (IIF=OIF). + +T10 DONE React to (S,G) join directed to another upstream address. See + also: + + RFC 4601: 4.5.7. Sending (S,G) Join/Prune Messages + + If a router wishes to propagate a Join(S,G) upstream, it must also + watch for messages on its upstream interface from other routers on + that subnet, and these may modify its behavior. If it sees a + Join(S,G) to the correct upstream neighbor, it should suppress its + own Join(S,G). If it sees a Prune(S,G), Prune(S,G,rpt), or + Prune(*,G) to the correct upstream neighbor towards S, it should + be prepared to override that prune by scheduling a Join(S,G) to be + sent almost immediately. + +T11 DONE Review protocol modifications for SSM + (RFC 4601 4.8.1. Protocol Modifications for SSM Destination + Addresses) + +T12 DONE Review updates of RPF entries. + FIXME pim_upstream.c send_join(): + Currently only one upstream state is affected by detection of RPF change. + RPF change should affect all upstream states sharing the RPF cache. + +T13 DONE Check that RFC macros using S,G,RPF_interface(S) are actually + implemented with this strategy: + rpf_ifch=find_ifch(up->rpf->interface). + See pim_rpf.c pim_rpf_find_rpf_addr() for a correct example. + + $ grep -i macro pimd/*.c + pimd/pim_iface.c: RFC 4601: 4.1.6. State Summarization Macros + pimd/pim_ifchannel.c: RFC 4601: 4.6.5. Assert State Macros + pimd/pim_ifchannel.c: RFC 4601: 4.1.6. State Summarization Macros + pimd/pim_ifchannel.c: RFC 4601: 4.1.6. State Summarization Macros + pimd/pim_ifchannel.c: RFC 4601: 4.6.5. Assert State Macros + pimd/pim_ifchannel.c: Macro: + pimd/pim_rpf.c: RFC 4601: 4.1.6. State Summarization Macros + +T14 DONE Send Assert(S,G) on iif as response to WRONGVIF kernel upcall. + See pim_mroute.c mroute_msg(). + +T15 DONE Interface command to statically join (S,G). + interface eth0 + ip igmp join-group 239.1.1.1 source 1.1.1.1 + +T16 DONE RPF'(S,G) lookup is not working for S reachable with default route. + See "RPF'(S,G) not found" in pim_rpf_update() from pim_rpf.c. + Zebra daemon RIB is not reflecting changes in kernel routes + accurately? + +T17 DONE Prevent CLI from creating bogus interfaces. + Example: + conf t + interface xxx + +T18 Consider reliable pim solution (refresh reduction) + A Reliable Transport Mechanism for PIM + http://tools.ietf.org/wg/pim/draft-ietf-pim-port/ + PORT=PIM-Over-Reliable-Transport + +T19 DONE Fix self as neighbor + See mailing list post: + http://lists.gnu.org/archive/html/qpimd-users/2009-04/msg00000.html + +T20 DONE Fix debug message: "pim_neighbor_update: internal error: + trying to replace same prefix list" + See mailing list post: + http://lists.gnu.org/archive/html/qpimd-users/2009-04/msg00000.html + +T21 DONE Clean-up PIM/IGMP interface mismatch debugging + See option PIM_CHECK_RECV_IFINDEX_SANITY in pimd/Makefile.am + See mailing list post: + http://lists.nongnu.org/archive/html/qpimd-users/2009-04/msg00003.html + +T22 DONE IGMP must be protected against adding looped MFC entries + created by both source and receiver attached to the same + interface. + +T23 DONE libzebra crash after zclient_lookup_nexthop. + See mailing list post: + http://lists.nongnu.org/archive/html/qpimd-users/2009-04/msg00008.html + +T24 DONE zserv may return recursive routes: + - nexthop type is set to ZEBRA_NEXTHOP_IPV4 + - ifindex is not reported + - calls expecting ifindex (fib_lookup_if_vif_index) are disrupted + See also this mailing list post: + [PATCH 21/21] Link detect and recursive routes + http://www.gossamer-threads.com/lists/quagga/dev/17564 + +T25 DONE Zclient nexthop lookup missing OSPF route to 1.1.1.1/32 + See also: + pim_zlookup.c zclient_lookup_nexthop misses OSPF 1.1.1.1/32 + zebra/zebra_vty.c show_ip_route_addr_cmd hits OSPF 1.1.1.1/32 + +T26 DONE Zebra daemon is marking recursive static route as inactive. + + FIXED: zebra daemon was incorrectly marking recursive routes + pointing to kernel routes as inactive: + zebra/zebra_rib.c nexthop_active_ipv4: + -- Original: + else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL)) + -- Fixed: + else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL) || + match->type == ZEBRA_ROUTE_KERNEL) + + Old problem description: + + This prevents rib_match_ipv4 from returning its nexthop: + client: pim_zlookup.c zclient_read_nexthop + server: zebra/zserv.c zsend_ipv4_nexthop_lookup_v2 -> rib_match_ipv4 + + Kernel route is injected into zebra in zebra_rib.c rib_add_ipv4 + Examples: + rt_netlink.c:726: rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, flags, &p, gate, src, index, table, metric, 0); + rt_netlink.c:864: rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, src, index, table, 0, 0); + + This patch didn't fix the issue: + [PATCH 21/21] Link detect and recursive routes + http://www.gossamer-threads.com/lists/quagga/dev/17564 + + See the example below for the route 2.2.2.2. + +bash# route add -host 1.1.1.1 gw 127.0.0.1 +bash# route add -host 2.2.2.2 gw 1.1.1.1 +bash# netstat -nvr +Kernel IP routing table +Destination Gateway Genmask Flags MSS Window irtt Iface +2.2.2.2 1.1.1.1 255.255.255.255 UGH 0 0 0 lo +1.1.1.1 127.0.0.1 255.255.255.255 UGH 0 0 0 lo +192.168.0.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 +0.0.0.0 192.168.0.2 0.0.0.0 UG 0 0 0 eth0 +bash# + +zebra# sh ip route +Codes: K - kernel route, C - connected, S - static, R - RIP, O - OSPF, + I - ISIS, B - BGP, > - selected route, * - FIB route + +K>* 0.0.0.0/0 via 192.168.0.2, eth0 +K>* 1.1.1.1/32 via 127.0.0.1, lo +K * 2.2.2.2/32 via 1.1.1.1, lo inactive +C>* 127.0.0.0/8 is directly connected, lo +C>* 192.168.0.0/24 is directly connected, eth0 + +quagga-pimd-router# sh ip route 1.1.1.1 +Address NextHop Interface Metric Preference +1.1.1.1 127.0.0.1 lo 0 0 +quagga-pimd-router# +quagga-pimd-router# sh ip route 2.2.2.2 +Address NextHop Interface Metric Preference +2.2.2.2 192.168.0.2 eth0 0 0 +quagga-pimd-router# + +T27 DONE Implement debug command + test pim receive assert + See also TODO T6: (S,G) Assert state machine. + +T28 DONE Bad IPv4 address family=02 in Join/Prune dump + Reported by Andrew Lunn + + # 58-byte pim v2 Join/Prune dump + # ------------------------------ + # IPv4 address family=02 is wrong, correct IPv4 address family is 01 + # See http://www.iana.org/assignments/address-family-numbers + # + c8XX YY03 : ip src 200.xx.yy.3 + e000 000d : ip dst 224.0.0.13 + 9404 0000 : ip router alert option 148.4.0.0 + 2300 ab13 : pimv2,type=3 res=00 checksum=ab13 + 0200 : upstream family=02, encoding=00 + c8XX YY08 : upstream 200.xx.yy.8 + 0001 00d2 : res=00 groups=01 holdtime=00d2 + 0200 0020 : group family=02, encoding=00, res=00, mask_len=20 + ef01 0101 : group address 239.1.1.1 + 0001 0000 : joined=0001 pruned=0000 + 0200 0020 : source family=02, encoding=00, res=00, mask_len=20 + 0101 0101 : source address 1.1.1.1 + +T29 DONE Reset interface PIM-hello-sent counter when primary address changes + See pim_ifp->pim_ifstat_hello_sent + + RFC 4601: 4.3.1. Sending Hello Messages + + Thus, if a router needs to send a Join/Prune or Assert message on + an interface on which it has not yet sent a Hello message with the + currently configured IP address, then it MUST immediately send the + relevant Hello message without waiting for the Hello Timer to + expire, followed by the Join/Prune or Assert message. + +T30 DONE Run interface DR election when primary address changes + Reported by Andrew Lunn + See pim_if_dr_election(). + +T31 If an interface changes one of its secondary IP addresses, a Hello + message with an updated Address_List option and a non-zero + HoldTime should be sent immediately. + See also CAVEAT C15. + See also RFC 4601: 4.3.1. Sending Hello Messages + +T32 Detection of interface primary address changes may fail when there + are multiple addresses. + See also CAVEAT C14. + + pim_find_primary_addr() should return interface primary address + from connected list. Currently it returns the first address. + + Zebra daemon "show int" is able to keep the primary address as + first address. + +T33 DONE Implement debug command: test pim receive upcall + See also TODO T6: (S,G) Assert state machine. + +T34 DONE assert_action_a1 + +T35 DONE Review macros depending on interface I. + + See also: grep ,I\) pimd/*.c + + For the case (S,G,I) check if I is either + 1) interface attached to this per-interface S,G state (don't think so) + or + 2) an arbitrary interface (most probably) + + For the arbitrary interface case (2), consider representing + interface ifp as its primary address (struct in_addr ifaddr). The + benefit is in_addr does not need to be dereferenced, so it does + not demand protection against crashes. + +T36 DONE React to zebra daemon link-detect up/down notification. + pim_ifp->primary_address is managed by detect_primary_address_change() + depending on to ifp->connected (managed by zebra_interface_address_read()). + +T37 DONE Review list of variables which may affect pim_upstream.c + pim_upstream_evaluate_join_desired(). + Call pim_upstream_update_join_desired() accordingly. + + See the order of invokation: + pim_if_dr_election(ifp); + pim_if_update_join_desired(pim_ifp); /* depends on DR */ + pim_if_update_could_assert(ifp); /* depends on DR */ + pim_if_update_my_assert_metric(ifp); /* depends on could_assert */ + + join_desired depends on: + pim_ifp->primary_address + pim_ifp->pim_dr_addr + ch->ifassert_winner_metric + ch->ifassert_winner + ch->local_ifmembership + ch->ifjoin_state + ch->upstream->rpf.source_nexthop.mrib_metric_preference + ch->upstream->rpf.source_nexthop.mrib_route_metric + ch->upstream->rpf.source_nexthop.interface + +T38 DONE Detect change in AssertTrackingDesired(S,G,I) + + See the order of invokation: + dr_election: none + update_join_desired: depends on DR + update_tracking_desired: depends on DR, join_desired + + AssertTrackingDesired(S,G,I) depends on: + pim_ifp->primary_address + pim_ifp->pim_dr_addr + ch->local_ifmembership + ch->ifassert_winner + ch->ifjoin_state + ch->upstream->rpf.source_nexthop.interface + PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(ch->upstream->flags) + +T39 DONE AssertTrackingDesired: flags is not matching evaluation + + # show ip pim assert-internal + CA: CouldAssert + ECA: Evaluate CouldAssert + ATD: AssertTrackingDesired + eATD: Evaluate AssertTrackingDesired + + Interface Address Source Group CA eCA ATD eATD + eth0 192.168.1.100 1.1.1.1 239.1.1.1 no no no yes + # + +T40 Lightweight MLDv2 + http://www.ietf.org/internet-drafts/draft-ietf-mboned-lightweight-igmpv3-mldv2-05.txt + http://www.ietf.org/html.charters/mboned-charter.html + +-x- diff --git a/pimd/pim_assert.c b/pimd/pim_assert.c new file mode 100644 index 00000000..0ce0e5d0 --- /dev/null +++ b/pimd/pim_assert.c @@ -0,0 +1,803 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "log.h" +#include "prefix.h" + +#include "pimd.h" +#include "pim_str.h" +#include "pim_tlv.h" +#include "pim_msg.h" +#include "pim_pim.h" +#include "pim_time.h" +#include "pim_iface.h" +#include "pim_hello.h" +#include "pim_macro.h" +#include "pim_assert.h" +#include "pim_ifchannel.h" + +static int assert_action_a3(struct pim_ifchannel *ch); +static void assert_action_a2(struct pim_ifchannel *ch, + struct pim_assert_metric winner_metric); +static void assert_action_a6(struct pim_ifchannel *ch, + struct pim_assert_metric winner_metric); + +void pim_ifassert_winner_set(struct pim_ifchannel *ch, + enum pim_ifassert_state new_state, + struct in_addr winner, + struct pim_assert_metric winner_metric) +{ + int winner_changed = (ch->ifassert_winner.s_addr != winner.s_addr); + int metric_changed = !pim_assert_metric_match(&ch->ifassert_winner_metric, + &winner_metric); + + if (ch->ifassert_state != new_state) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_info("%s: (S,G)=(%s,%s) assert state changed from %s to %s on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, + pim_ifchannel_ifassert_name(ch->ifassert_state), + pim_ifchannel_ifassert_name(new_state), + ch->interface->name); + } + + { + char src_str[100]; + char grp_str[100]; + char winner_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", winner, winner_str, sizeof(winner_str)); + zlog_info("%s: (S,G)=(%s,%s) assert winner now is %s on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, + winner_str, ch->interface->name); + } + + ch->ifassert_state = new_state; + ch->ifassert_winner = winner; + ch->ifassert_winner_metric = winner_metric; + ch->ifassert_creation = pim_time_monotonic_sec(); + + if (winner_changed || metric_changed) { + pim_upstream_update_join_desired(ch->upstream); + pim_ifchannel_update_could_assert(ch); + pim_ifchannel_update_assert_tracking_desired(ch); + } +} + +static void on_trace(const char *label, + struct interface *ifp, struct in_addr src) +{ + if (PIM_DEBUG_PIM_TRACE) { + char src_str[100]; + pim_inet4_dump("", src, src_str, sizeof(src_str)); + zlog_debug("%s: from %s on %s", + label, src_str, ifp->name); + } +} + +static int preferred_assert(const struct pim_ifchannel *ch, + const struct pim_assert_metric *recv_metric) +{ + return pim_assert_metric_better(recv_metric, + &ch->ifassert_winner_metric); +} + +static int acceptable_assert(const struct pim_assert_metric *my_metric, + const struct pim_assert_metric *recv_metric) +{ + return pim_assert_metric_better(recv_metric, + my_metric); +} + +static int inferior_assert(const struct pim_assert_metric *my_metric, + const struct pim_assert_metric *recv_metric) +{ + return pim_assert_metric_better(my_metric, + recv_metric); +} + +static int cancel_assert(const struct pim_assert_metric *recv_metric) +{ + return (recv_metric->metric_preference == PIM_ASSERT_METRIC_PREFERENCE_MAX) + && + (recv_metric->route_metric == PIM_ASSERT_ROUTE_METRIC_MAX); +} + +static void if_could_assert_do_a1(const char *caller, + struct pim_ifchannel *ch) +{ + if (PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) { + if (assert_action_a1(ch)) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_warn("%s: %s: (S,G)=(%s,%s) assert_action_a1 failure on interface %s", + __PRETTY_FUNCTION__, caller, + src_str, grp_str, ch->interface->name); + /* log warning only */ + } + } +} + +static int dispatch_assert(struct interface *ifp, + struct in_addr source_addr, + struct in_addr group_addr, + struct pim_assert_metric recv_metric) +{ + struct pim_ifchannel *ch; + + ch = pim_ifchannel_add(ifp, source_addr, group_addr); + if (!ch) { + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + zlog_warn("%s: (S,G)=(%s,%s) failure creating channel on interface %s", + __PRETTY_FUNCTION__, + source_str, group_str, ifp->name); + return -1; + } + + switch (ch->ifassert_state) { + case PIM_IFASSERT_NOINFO: + if (recv_metric.rpt_bit_flag) { + /* RPT bit set */ + if_could_assert_do_a1(__PRETTY_FUNCTION__, ch); + } + else { + /* RPT bit clear */ + if (inferior_assert(&ch->ifassert_my_metric, &recv_metric)) { + if_could_assert_do_a1(__PRETTY_FUNCTION__, ch); + } + else if (acceptable_assert(&ch->ifassert_my_metric, &recv_metric)) { + if (PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags)) { + assert_action_a6(ch, recv_metric); + } + } + } + break; + case PIM_IFASSERT_I_AM_WINNER: + if (preferred_assert(ch, &recv_metric)) { + assert_action_a2(ch, recv_metric); + } + else { + if (inferior_assert(&ch->ifassert_my_metric, &recv_metric)) { + zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); /* a3 requirement */ + assert_action_a3(ch); + } + } + break; + case PIM_IFASSERT_I_AM_LOSER: + if (recv_metric.ip_address.s_addr == ch->ifassert_winner.s_addr) { + /* Assert from current winner */ + + if (cancel_assert(&recv_metric)) { + assert_action_a5(ch); + } + else { + if (inferior_assert(&ch->ifassert_my_metric, &recv_metric)) { + assert_action_a5(ch); + } + else if (acceptable_assert(&ch->ifassert_my_metric, &recv_metric)) { + if (!recv_metric.rpt_bit_flag) { + assert_action_a2(ch, recv_metric); + } + } + } + } + else if (preferred_assert(ch, &recv_metric)) { + assert_action_a2(ch, recv_metric); + } + break; + default: + { + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + zlog_warn("%s: (S,G)=(%s,%s) invalid assert state %d on interface %s", + __PRETTY_FUNCTION__, + source_str, group_str, ch->ifassert_state, ifp->name); + } + return -2; + } + + return 0; +} + +int pim_assert_recv(struct interface *ifp, + struct pim_neighbor *neigh, + struct in_addr src_addr, + char *buf, int buf_size) +{ + struct prefix msg_group_addr; + struct prefix msg_source_addr; + struct pim_assert_metric msg_metric; + int offset; + char *curr; + int curr_size; + + on_trace(__PRETTY_FUNCTION__, ifp, src_addr); + + curr = buf; + curr_size = buf_size; + + /* + Parse assert group addr + */ + offset = pim_parse_addr_group(ifp->name, src_addr, + &msg_group_addr, + curr, curr_size); + if (offset < 1) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: pim_parse_addr_group() failure: from %s on %s", + __PRETTY_FUNCTION__, + src_str, ifp->name); + return -1; + } + curr += offset; + curr_size -= offset; + + /* + Parse assert source addr + */ + offset = pim_parse_addr_ucast(ifp->name, src_addr, + &msg_source_addr, + curr, curr_size); + if (offset < 1) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s", + __PRETTY_FUNCTION__, + src_str, ifp->name); + return -2; + } + curr += offset; + curr_size -= offset; + + if (curr_size != 8) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: preference/metric size is not 8: size=%d from %s on interface %s", + __PRETTY_FUNCTION__, + curr_size, + src_str, ifp->name); + return -3; + } + + /* + Parse assert metric preference + */ + + msg_metric.metric_preference = ntohl(*(const uint32_t *) curr); + + msg_metric.rpt_bit_flag = msg_metric.metric_preference & 0x80000000; /* save highest bit */ + msg_metric.metric_preference &= ~0x80000000; /* clear highest bit */ + + curr += 4; + + /* + Parse assert route metric + */ + + msg_metric.route_metric = ntohl(*(const uint32_t *) curr); + + if (PIM_DEBUG_PIM_TRACE) { + char neigh_str[100]; + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", src_addr, neigh_str, sizeof(neigh_str)); + pim_inet4_dump("", msg_source_addr.u.prefix4, source_str, sizeof(source_str)); + pim_inet4_dump("", msg_group_addr.u.prefix4, group_str, sizeof(group_str)); + zlog_debug("%s: from %s on %s: (S,G)=(%s,%s) pref=%u metric=%u rpt_bit=%u", + __PRETTY_FUNCTION__, neigh_str, ifp->name, + source_str, group_str, + msg_metric.metric_preference, + msg_metric.route_metric, + PIM_FORCE_BOOLEAN(msg_metric.rpt_bit_flag)); + } + + msg_metric.ip_address = src_addr; + + return dispatch_assert(ifp, + msg_source_addr.u.prefix4, + msg_group_addr.u.prefix4, + msg_metric); +} + +/* + RFC 4601: 4.6.3. Assert Metrics + + Assert metrics are defined as: + + When comparing assert_metrics, the rpt_bit_flag, metric_preference, + and route_metric field are compared in order, where the first lower + value wins. If all fields are equal, the primary IP address of the + router that sourced the Assert message is used as a tie-breaker, + with the highest IP address winning. +*/ +int pim_assert_metric_better(const struct pim_assert_metric *m1, + const struct pim_assert_metric *m2) +{ + if (m1->rpt_bit_flag < m2->rpt_bit_flag) + return 1; + if (m1->rpt_bit_flag > m2->rpt_bit_flag) + return 0; + + if (m1->metric_preference < m2->metric_preference) + return 1; + if (m1->metric_preference > m2->metric_preference) + return 0; + + if (m1->route_metric < m2->route_metric) + return 1; + if (m1->route_metric > m2->route_metric) + return 0; + + return ntohl(m1->ip_address.s_addr) > ntohl(m2->ip_address.s_addr); +} + +int pim_assert_metric_match(const struct pim_assert_metric *m1, + const struct pim_assert_metric *m2) +{ + if (m1->rpt_bit_flag != m2->rpt_bit_flag) + return 0; + if (m1->metric_preference != m2->metric_preference) + return 0; + if (m1->route_metric != m2->route_metric) + return 0; + + return m1->ip_address.s_addr == m2->ip_address.s_addr; +} + +int pim_assert_build_msg(char *pim_msg, int buf_size, + struct interface *ifp, + struct in_addr group_addr, + struct in_addr source_addr, + uint32_t metric_preference, + uint32_t route_metric, + uint32_t rpt_bit_flag) +{ + char *buf_pastend = pim_msg + buf_size; + char *pim_msg_curr; + int pim_msg_size; + int remain; + + pim_msg_curr = pim_msg + PIM_MSG_HEADER_LEN; /* skip room for pim header */ + + /* Encode group */ + remain = buf_pastend - pim_msg_curr; + pim_msg_curr = pim_msg_addr_encode_ipv4_group(pim_msg_curr, + remain, + group_addr); + if (!pim_msg_curr) { + char group_str[100]; + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + zlog_warn("%s: failure encoding group address %s: space left=%d", + __PRETTY_FUNCTION__, group_str, remain); + return -1; + } + + /* Encode source */ + remain = buf_pastend - pim_msg_curr; + pim_msg_curr = pim_msg_addr_encode_ipv4_ucast(pim_msg_curr, + remain, + source_addr); + if (!pim_msg_curr) { + char source_str[100]; + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + zlog_warn("%s: failure encoding source address %s: space left=%d", + __PRETTY_FUNCTION__, source_str, remain); + return -2; + } + + /* Metric preference */ + *((uint32_t *) pim_msg_curr) = htonl(rpt_bit_flag ? + metric_preference | 0x80000000 : + metric_preference); + pim_msg_curr += 4; + + /* Route metric */ + *((uint32_t *) pim_msg_curr) = htonl(route_metric); + pim_msg_curr += 4; + + /* + Add PIM header + */ + pim_msg_size = pim_msg_curr - pim_msg; + pim_msg_build_header(pim_msg, pim_msg_size, + PIM_MSG_TYPE_ASSERT); + + return pim_msg_size; +} + +static int pim_assert_do(struct pim_ifchannel *ch, + struct pim_assert_metric metric) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + char pim_msg[1000]; + int pim_msg_size; + + ifp = ch->interface; + zassert(ifp); + + pim_ifp = ifp->info; + if (!pim_ifp) { + zlog_warn("%s: pim not enabled on interface: %s", + __PRETTY_FUNCTION__, ifp->name); + return -1; + } + + pim_msg_size = pim_assert_build_msg(pim_msg, sizeof(pim_msg), ifp, + ch->group_addr, ch->source_addr, + metric.metric_preference, + metric.route_metric, + metric.rpt_bit_flag); + if (pim_msg_size < 1) { + zlog_warn("%s: failure building PIM assert message: msg_size=%d", + __PRETTY_FUNCTION__, pim_msg_size); + return -2; + } + + /* + RFC 4601: 4.3.1. Sending Hello Messages + + Thus, if a router needs to send a Join/Prune or Assert message on + an interface on which it has not yet sent a Hello message with the + currently configured IP address, then it MUST immediately send the + relevant Hello message without waiting for the Hello Timer to + expire, followed by the Join/Prune or Assert message. + */ + pim_hello_require(ifp); + + if (PIM_DEBUG_PIM_TRACE) { + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", ch->source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", ch->group_addr, group_str, sizeof(group_str)); + zlog_debug("%s: to %s: (S,G)=(%s,%s) pref=%u metric=%u rpt_bit=%u", + __PRETTY_FUNCTION__, + ifp->name, source_str, group_str, + metric.metric_preference, + metric.route_metric, + PIM_FORCE_BOOLEAN(metric.rpt_bit_flag)); + } + + if (pim_msg_send(pim_ifp->pim_sock_fd, + qpim_all_pim_routers_addr, + pim_msg, + pim_msg_size, + ifp->name)) { + zlog_warn("%s: could not send PIM message on interface %s", + __PRETTY_FUNCTION__, ifp->name); + return -3; + } + + return 0; +} + +int pim_assert_send(struct pim_ifchannel *ch) +{ + return pim_assert_do(ch, ch->ifassert_my_metric); +} + +/* + RFC 4601: 4.6.4. AssertCancel Messages + + An AssertCancel(S,G) is an infinite metric assert with the RPT bit + set that names S as the source. + */ +static int pim_assert_cancel(struct pim_ifchannel *ch) +{ + struct pim_assert_metric metric; + + metric.rpt_bit_flag = 0; + metric.metric_preference = PIM_ASSERT_METRIC_PREFERENCE_MAX; + metric.route_metric = PIM_ASSERT_ROUTE_METRIC_MAX; + metric.ip_address = ch->source_addr; + + return pim_assert_do(ch, metric); +} + +static int on_assert_timer(struct thread *t) +{ + struct pim_ifchannel *ch; + struct interface *ifp; + + zassert(t); + ch = THREAD_ARG(t); + zassert(ch); + + ifp = ch->interface; + zassert(ifp); + + if (PIM_DEBUG_PIM_TRACE) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_debug("%s: (S,G)=(%s,%s) timer expired on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, ifp->name); + } + + ch->t_ifassert_timer = 0; + + switch (ch->ifassert_state) { + case PIM_IFASSERT_I_AM_WINNER: + zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); /* a3 requirement */ + assert_action_a3(ch); + break; + case PIM_IFASSERT_I_AM_LOSER: + assert_action_a5(ch); + break; + default: + { + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", ch->source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", ch->group_addr, group_str, sizeof(group_str)); + zlog_warn("%s: (S,G)=(%s,%s) invalid assert state %d on interface %s", + __PRETTY_FUNCTION__, + source_str, group_str, ch->ifassert_state, ifp->name); + } + } + + return 0; +} + +static void assert_timer_off(struct pim_ifchannel *ch) +{ + struct interface *ifp; + + zassert(ch); + ifp = ch->interface; + zassert(ifp); + + if (PIM_DEBUG_PIM_TRACE) { + if (ch->t_ifassert_timer) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_debug("%s: (S,G)=(%s,%s) cancelling timer on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, ifp->name); + } + } + THREAD_OFF(ch->t_ifassert_timer); + zassert(!ch->t_ifassert_timer); +} + +static void pim_assert_timer_set(struct pim_ifchannel *ch, + int interval) +{ + struct interface *ifp; + + zassert(ch); + ifp = ch->interface; + zassert(ifp); + + assert_timer_off(ch); + + if (PIM_DEBUG_PIM_TRACE) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_debug("%s: (S,G)=(%s,%s) starting %u sec timer on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, interval, ifp->name); + } + + THREAD_TIMER_ON(master, ch->t_ifassert_timer, + on_assert_timer, + ch, interval); +} + +static void pim_assert_timer_reset(struct pim_ifchannel *ch) +{ + pim_assert_timer_set(ch, PIM_ASSERT_TIME - PIM_ASSERT_OVERRIDE_INTERVAL); +} + +/* + RFC 4601: 4.6.1. (S,G) Assert Message State Machine + + (S,G) Assert State machine Actions + + A1: Send Assert(S,G). + Set Assert Timer to (Assert_Time - Assert_Override_Interval). + Store self as AssertWinner(S,G,I). + Store spt_assert_metric(S,I) as AssertWinnerMetric(S,G,I). +*/ +int assert_action_a1(struct pim_ifchannel *ch) +{ + struct interface *ifp = ch->interface; + struct pim_interface *pim_ifp; + + zassert(ifp); + + pim_ifp = ifp->info; + if (!pim_ifp) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_warn("%s: (S,G)=(%s,%s) multicast no enabled on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, ifp->name); + return -1; /* must return since pim_ifp is used below */ + } + + /* Switch to I_AM_WINNER before performing action_a3 below */ + pim_ifassert_winner_set(ch, PIM_IFASSERT_I_AM_WINNER, + pim_ifp->primary_address, + pim_macro_spt_assert_metric(&ch->upstream->rpf, + pim_ifp->primary_address)); + + zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); /* a3 requirement */ + if (assert_action_a3(ch)) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_warn("%s: (S,G)=(%s,%s) assert_action_a3 failure on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, ifp->name); + /* warning only */ + } + + zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); + + return 0; +} + +/* + RFC 4601: 4.6.1. (S,G) Assert Message State Machine + + (S,G) Assert State machine Actions + + A2: Store new assert winner as AssertWinner(S,G,I) and assert + winner metric as AssertWinnerMetric(S,G,I). + Set Assert Timer to Assert_Time. +*/ +static void assert_action_a2(struct pim_ifchannel *ch, + struct pim_assert_metric winner_metric) +{ + pim_ifassert_winner_set(ch, PIM_IFASSERT_I_AM_LOSER, + winner_metric.ip_address, + winner_metric); + + pim_assert_timer_set(ch, PIM_ASSERT_TIME); + + zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER); +} + +/* + RFC 4601: 4.6.1. (S,G) Assert Message State Machine + + (S,G) Assert State machine Actions + + A3: Send Assert(S,G). + Set Assert Timer to (Assert_Time - Assert_Override_Interval). +*/ +static int assert_action_a3(struct pim_ifchannel *ch) +{ + zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); + + pim_assert_timer_reset(ch); + + if (pim_assert_send(ch)) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + + zlog_warn("%s: (S,G)=(%s,%s) failure sending assert on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, ch->interface->name); + return -1; + } + + zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); + + return 0; +} + +/* + RFC 4601: 4.6.1. (S,G) Assert Message State Machine + + (S,G) Assert State machine Actions + + A4: Send AssertCancel(S,G). + Delete assert info (AssertWinner(S,G,I) and + AssertWinnerMetric(S,G,I) will then return their default + values). +*/ +void assert_action_a4(struct pim_ifchannel *ch) +{ + if (pim_assert_cancel(ch)) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_warn("%s: failure sending AssertCancel(%s,%s) on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, ch->interface->name); + /* log warning only */ + } + + assert_action_a5(ch); + + zassert(ch->ifassert_state == PIM_IFASSERT_NOINFO); +} + +/* + RFC 4601: 4.6.1. (S,G) Assert Message State Machine + + (S,G) Assert State machine Actions + + A5: Delete assert info (AssertWinner(S,G,I) and + AssertWinnerMetric(S,G,I) will then return their default values). +*/ +void assert_action_a5(struct pim_ifchannel *ch) +{ + reset_ifassert_state(ch); + zassert(ch->ifassert_state == PIM_IFASSERT_NOINFO); +} + +/* + RFC 4601: 4.6.1. (S,G) Assert Message State Machine + + (S,G) Assert State machine Actions + + A6: Store new assert winner as AssertWinner(S,G,I) and assert + winner metric as AssertWinnerMetric(S,G,I). + Set Assert Timer to Assert_Time. + If (I is RPF_interface(S)) AND (UpstreamJPState(S,G) == true) + set SPTbit(S,G) to TRUE. +*/ +static void assert_action_a6(struct pim_ifchannel *ch, + struct pim_assert_metric winner_metric) +{ + assert_action_a2(ch, winner_metric); + + /* + If (I is RPF_interface(S)) AND (UpstreamJPState(S,G) == true) set + SPTbit(S,G) to TRUE. + + Notice: For PIM SSM, SPTbit(S,G) is already always true. + */ + + zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER); +} + diff --git a/pimd/pim_assert.h b/pimd/pim_assert.h new file mode 100644 index 00000000..6a8594ce --- /dev/null +++ b/pimd/pim_assert.h @@ -0,0 +1,75 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_ASSERT_H +#define PIM_ASSERT_H + +#include + +#include "if.h" + +#include "pim_neighbor.h" +#include "pim_ifchannel.h" + +/* + RFC 4601: 4.11. Timer Values + + Note that for historical reasons, the Assert message lacks a + Holdtime field. Thus, changing the Assert Time from the default + value is not recommended. + */ +#define PIM_ASSERT_OVERRIDE_INTERVAL (3) /* seconds */ +#define PIM_ASSERT_TIME (180) /* seconds */ + +#define PIM_ASSERT_METRIC_PREFERENCE_MAX (0xFFFFFFFF) +#define PIM_ASSERT_ROUTE_METRIC_MAX (0xFFFFFFFF) + +void pim_ifassert_winner_set(struct pim_ifchannel *ch, + enum pim_ifassert_state new_state, + struct in_addr winner, + struct pim_assert_metric winner_metric); + +int pim_assert_recv(struct interface *ifp, + struct pim_neighbor *neigh, + struct in_addr src_addr, + char *buf, int buf_size); + +int pim_assert_metric_better(const struct pim_assert_metric *m1, + const struct pim_assert_metric *m2); +int pim_assert_metric_match(const struct pim_assert_metric *m1, + const struct pim_assert_metric *m2); + +int pim_assert_build_msg(char *pim_msg, int buf_size, + struct interface *ifp, + struct in_addr group_addr, + struct in_addr source_addr, + uint32_t metric_preference, + uint32_t route_metric, + uint32_t rpt_bit_flag); + +int pim_assert_send(struct pim_ifchannel *ch); + +int assert_action_a1(struct pim_ifchannel *ch); +void assert_action_a4(struct pim_ifchannel *ch); +void assert_action_a5(struct pim_ifchannel *ch); + +#endif /* PIM_ASSERT_H */ diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c new file mode 100644 index 00000000..b1349ed3 --- /dev/null +++ b/pimd/pim_cmd.c @@ -0,0 +1,3923 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include + +#include "command.h" +#include "if.h" +#include "prefix.h" + +#include "pimd.h" +#include "pim_cmd.h" +#include "pim_iface.h" +#include "pim_vty.h" +#include "pim_mroute.h" +#include "pim_str.h" +#include "pim_igmpv3.h" +#include "pim_sock.h" +#include "pim_time.h" +#include "pim_util.h" +#include "pim_oil.h" +#include "pim_neighbor.h" +#include "pim_pim.h" +#include "pim_ifchannel.h" +#include "pim_hello.h" +#include "pim_msg.h" +#include "pim_upstream.h" +#include "pim_rpf.h" +#include "pim_macro.h" + +static struct cmd_node pim_global_node = { + PIM_NODE, + "", + 1 /* vtysh ? yes */ +}; + +static struct cmd_node pim_interface_node = { + INTERFACE_NODE, + "%s(config-if-pim)# ", + 1 /* vtysh ? yes */ +}; + +static void pim_if_membership_clear(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + if (PIM_IF_TEST_PIM(pim_ifp->options) && + PIM_IF_TEST_IGMP(pim_ifp->options)) { + return; + } + + pim_ifchannel_membership_clear(ifp); +} + +/* + When PIM is disabled on interface, IGMPv3 local membership + information is not injected into PIM interface state. + + The function pim_if_membership_refresh() fetches all IGMPv3 local + membership information into PIM. It is intented to be called + whenever PIM is enabled on the interface in order to collect missed + local membership information. + */ +static void pim_if_membership_refresh(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + struct listnode *sock_node; + struct igmp_sock *igmp; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + if (!PIM_IF_TEST_PIM(pim_ifp->options)) + return; + if (!PIM_IF_TEST_IGMP(pim_ifp->options)) + return; + + /* + First clear off membership from all PIM (S,G) entries on the + interface + */ + + pim_ifchannel_membership_clear(ifp); + + /* + Then restore PIM (S,G) membership from all IGMPv3 (S,G) entries on + the interface + */ + + /* scan igmp sockets */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { + struct listnode *grpnode; + struct igmp_group *grp; + + /* scan igmp groups */ + for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grpnode, grp)) { + struct listnode *srcnode; + struct igmp_source *src; + + /* scan group sources */ + for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, srcnode, src)) { + + if (IGMP_SOURCE_TEST_FORWARDING(src->source_flags)) { + pim_ifchannel_local_membership_add(ifp, + src->source_addr, + grp->group_addr); + } + + } /* scan group sources */ + } /* scan igmp groups */ + } /* scan igmp sockets */ + + /* + Finally delete every PIM (S,G) entry lacking all state info + */ + + pim_ifchannel_delete_on_noinfo(ifp); + +} + +static void pim_show_assert(struct vty *vty) +{ + struct listnode *ifnode; + struct interface *ifp; + time_t now; + + now = pim_time_monotonic_sec(); + + vty_out(vty, + "Interface Address Source Group State Winner Uptime Timer%s", + VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) { + struct pim_interface *pim_ifp; + struct in_addr ifaddr; + struct listnode *ch_node; + struct pim_ifchannel *ch; + + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + ifaddr = pim_ifp->primary_address; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) { + char ch_src_str[100]; + char ch_grp_str[100]; + char winner_str[100]; + char uptime[10]; + char timer[10]; + + pim_inet4_dump("", ch->source_addr, + ch_src_str, sizeof(ch_src_str)); + pim_inet4_dump("", ch->group_addr, + ch_grp_str, sizeof(ch_grp_str)); + pim_inet4_dump("", ch->ifassert_winner, + winner_str, sizeof(winner_str)); + + pim_time_uptime(uptime, sizeof(uptime), now - ch->ifassert_creation); + pim_time_timer_to_mmss(timer, sizeof(timer), + ch->t_ifassert_timer); + + vty_out(vty, "%-9s %-15s %-15s %-15s %-6s %-15s %-8s %-5s%s", + ifp->name, + inet_ntoa(ifaddr), + ch_src_str, + ch_grp_str, + pim_ifchannel_ifassert_name(ch->ifassert_state), + winner_str, + uptime, + timer, + VTY_NEWLINE); + } /* scan interface channels */ + } /* scan interfaces */ +} + +static void pim_show_assert_internal(struct vty *vty) +{ + struct listnode *ifnode; + struct interface *ifp; + + vty_out(vty, + "CA: CouldAssert%s" + "ECA: Evaluate CouldAssert%s" + "ATD: AssertTrackingDesired%s" + "eATD: Evaluate AssertTrackingDesired%s%s", + VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + + vty_out(vty, + "Interface Address Source Group CA eCA ATD eATD%s", + VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) { + struct pim_interface *pim_ifp; + struct in_addr ifaddr; + struct listnode *ch_node; + struct pim_ifchannel *ch; + + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + ifaddr = pim_ifp->primary_address; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) { + char ch_src_str[100]; + char ch_grp_str[100]; + + pim_inet4_dump("", ch->source_addr, + ch_src_str, sizeof(ch_src_str)); + pim_inet4_dump("", ch->group_addr, + ch_grp_str, sizeof(ch_grp_str)); + vty_out(vty, "%-9s %-15s %-15s %-15s %-3s %-3s %-3s %-4s%s", + ifp->name, + inet_ntoa(ifaddr), + ch_src_str, + ch_grp_str, + PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags) ? "yes" : "no", + pim_macro_ch_could_assert_eval(ch) ? "yes" : "no", + PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags) ? "yes" : "no", + pim_macro_assert_tracking_desired_eval(ch) ? "yes" : "no", + VTY_NEWLINE); + } /* scan interface channels */ + } /* scan interfaces */ +} + +static void pim_show_assert_metric(struct vty *vty) +{ + struct listnode *ifnode; + struct interface *ifp; + + vty_out(vty, + "Interface Address Source Group RPT Pref Metric Address %s", + VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) { + struct pim_interface *pim_ifp; + struct in_addr ifaddr; + struct listnode *ch_node; + struct pim_ifchannel *ch; + + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + ifaddr = pim_ifp->primary_address; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) { + char ch_src_str[100]; + char ch_grp_str[100]; + char addr_str[100]; + struct pim_assert_metric am; + + am = pim_macro_spt_assert_metric(&ch->upstream->rpf, pim_ifp->primary_address); + + pim_inet4_dump("", ch->source_addr, + ch_src_str, sizeof(ch_src_str)); + pim_inet4_dump("", ch->group_addr, + ch_grp_str, sizeof(ch_grp_str)); + pim_inet4_dump("", am.ip_address, + addr_str, sizeof(addr_str)); + + vty_out(vty, "%-9s %-15s %-15s %-15s %-3s %4u %6u %-15s%s", + ifp->name, + inet_ntoa(ifaddr), + ch_src_str, + ch_grp_str, + am.rpt_bit_flag ? "yes" : "no", + am.metric_preference, + am.route_metric, + addr_str, + VTY_NEWLINE); + } /* scan interface channels */ + } /* scan interfaces */ +} + +static void pim_show_assert_winner_metric(struct vty *vty) +{ + struct listnode *ifnode; + struct interface *ifp; + + vty_out(vty, + "Interface Address Source Group RPT Pref Metric Address %s", + VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) { + struct pim_interface *pim_ifp; + struct in_addr ifaddr; + struct listnode *ch_node; + struct pim_ifchannel *ch; + + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + ifaddr = pim_ifp->primary_address; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) { + char ch_src_str[100]; + char ch_grp_str[100]; + char addr_str[100]; + struct pim_assert_metric *am; + char pref_str[5]; + char metr_str[7]; + + am = &ch->ifassert_winner_metric; + + pim_inet4_dump("", ch->source_addr, + ch_src_str, sizeof(ch_src_str)); + pim_inet4_dump("", ch->group_addr, + ch_grp_str, sizeof(ch_grp_str)); + pim_inet4_dump("", am->ip_address, + addr_str, sizeof(addr_str)); + + if (am->metric_preference == PIM_ASSERT_METRIC_PREFERENCE_MAX) + snprintf(pref_str, sizeof(pref_str), "INFI"); + else + snprintf(pref_str, sizeof(pref_str), "%4u", am->metric_preference); + + if (am->route_metric == PIM_ASSERT_ROUTE_METRIC_MAX) + snprintf(metr_str, sizeof(metr_str), "INFI"); + else + snprintf(metr_str, sizeof(metr_str), "%6u", am->route_metric); + + vty_out(vty, "%-9s %-15s %-15s %-15s %-3s %-4s %-6s %-15s%s", + ifp->name, + inet_ntoa(ifaddr), + ch_src_str, + ch_grp_str, + am->rpt_bit_flag ? "yes" : "no", + pref_str, + metr_str, + addr_str, + VTY_NEWLINE); + } /* scan interface channels */ + } /* scan interfaces */ +} + +static void pim_show_membership(struct vty *vty) +{ + struct listnode *ifnode; + struct interface *ifp; + + vty_out(vty, + "Interface Address Source Group Membership%s", + VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) { + struct pim_interface *pim_ifp; + struct in_addr ifaddr; + struct listnode *ch_node; + struct pim_ifchannel *ch; + + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + ifaddr = pim_ifp->primary_address; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) { + char ch_src_str[100]; + char ch_grp_str[100]; + + pim_inet4_dump("", ch->source_addr, + ch_src_str, sizeof(ch_src_str)); + pim_inet4_dump("", ch->group_addr, + ch_grp_str, sizeof(ch_grp_str)); + + vty_out(vty, "%-9s %-15s %-15s %-15s %-10s%s", + ifp->name, + inet_ntoa(ifaddr), + ch_src_str, + ch_grp_str, + ch->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO ? + "NOINFO" : "INCLUDE", + VTY_NEWLINE); + } /* scan interface channels */ + } /* scan interfaces */ + +} + +static void igmp_show_interfaces(struct vty *vty) +{ + struct listnode *node; + struct interface *ifp; + time_t now; + + now = pim_time_monotonic_sec(); + + vty_out(vty, + "Interface Address ifIndex Socket Uptime Multi Broad MLoop AllMu Prmsc Del%s", + VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) { + struct pim_interface *pim_ifp; + struct listnode *sock_node; + struct igmp_sock *igmp; + + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { + char uptime[10]; + int mloop; + + pim_time_uptime(uptime, sizeof(uptime), now - igmp->sock_creation); + + mloop = pim_socket_mcastloop_get(igmp->fd); + + vty_out(vty, "%-9s %-15s %7d %6d %8s %5s %5s %5s %5s %5s %3s%s", + ifp->name, + inet_ntoa(igmp->ifaddr), + ifp->ifindex, + igmp->fd, + uptime, + if_is_multicast(ifp) ? "yes" : "no", + if_is_broadcast(ifp) ? "yes" : "no", + (mloop < 0) ? "?" : (mloop ? "yes" : "no"), + (ifp->flags & IFF_ALLMULTI) ? "yes" : "no", + (ifp->flags & IFF_PROMISC) ? "yes" : "no", + PIM_IF_IS_DELETED(ifp) ? "yes" : "no", + VTY_NEWLINE); + } + } +} + +static void show_interface_address(struct vty *vty) +{ + struct listnode *ifpnode; + struct interface *ifp; + + vty_out(vty, + "Interface Primary Secondary %s", + VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(iflist, ifpnode, ifp)) { + struct listnode *ifcnode; + struct connected *ifc; + struct in_addr pri_addr; + char pri_addr_str[100]; + + pri_addr = pim_find_primary_addr(ifp); + + pim_inet4_dump("", pri_addr, pri_addr_str, sizeof(pri_addr_str)); + + for (ALL_LIST_ELEMENTS_RO(ifp->connected, ifcnode, ifc)) { + char sec_addr_str[100]; + struct prefix *p = ifc->address; + + if (p->family != AF_INET) + continue; + + if (p->u.prefix4.s_addr == pri_addr.s_addr) { + sec_addr_str[0] = '\0'; + } + else { + pim_inet4_dump("", p->u.prefix4, sec_addr_str, sizeof(sec_addr_str)); + } + + vty_out(vty, "%-9s %-15s %-15s%s", + ifp->name, + pri_addr_str, + sec_addr_str, + VTY_NEWLINE); + } + } +} + +static void pim_show_dr(struct vty *vty) +{ + struct listnode *node; + struct interface *ifp; + time_t now; + + now = pim_time_monotonic_sec(); + + vty_out(vty, + "NonPri: Number of neighbors missing DR Priority hello option%s%s", + VTY_NEWLINE, VTY_NEWLINE); + + vty_out(vty, "Interface Address DR Uptime Elections NonPri%s", VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) { + struct pim_interface *pim_ifp; + struct in_addr ifaddr; + char dr_str[100]; + char dr_uptime[10]; + + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + if (pim_ifp->pim_sock_fd < 0) + continue; + + ifaddr = pim_ifp->primary_address; + + pim_time_uptime(dr_uptime, sizeof(dr_uptime), + now - pim_ifp->pim_dr_election_last); + + pim_inet4_dump("", pim_ifp->pim_dr_addr, + dr_str, sizeof(dr_str)); + + vty_out(vty, "%-9s %-15s %-15s %8s %9d %6d%s", + ifp->name, + inet_ntoa(ifaddr), + dr_str, + dr_uptime, + pim_ifp->pim_dr_election_count, + pim_ifp->pim_dr_num_nondrpri_neighbors, + VTY_NEWLINE); + } +} + +static void pim_show_hello(struct vty *vty) +{ + struct listnode *node; + struct interface *ifp; + time_t now; + + now = pim_time_monotonic_sec(); + + vty_out(vty, "Interface Address Period Timer StatStart Recv Rfail Send Sfail%s", VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) { + struct pim_interface *pim_ifp; + struct in_addr ifaddr; + char hello_period[10]; + char hello_timer[10]; + char stat_uptime[10]; + + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + if (pim_ifp->pim_sock_fd < 0) + continue; + + ifaddr = pim_ifp->primary_address; + + pim_time_timer_to_mmss(hello_timer, sizeof(hello_timer), pim_ifp->t_pim_hello_timer); + pim_time_mmss(hello_period, sizeof(hello_period), pim_ifp->pim_hello_period); + pim_time_uptime(stat_uptime, sizeof(stat_uptime), now - pim_ifp->pim_ifstat_start); + + vty_out(vty, "%-9s %-15s %6s %5s %9s %4u %5u %4u %5u%s", + ifp->name, + inet_ntoa(ifaddr), + hello_period, + hello_timer, + stat_uptime, + pim_ifp->pim_ifstat_hello_recv, + pim_ifp->pim_ifstat_hello_recvfail, + pim_ifp->pim_ifstat_hello_sent, + pim_ifp->pim_ifstat_hello_sendfail, + VTY_NEWLINE); + } +} + +static void pim_show_interfaces(struct vty *vty) +{ + struct listnode *node; + struct interface *ifp; + time_t now; + + now = pim_time_monotonic_sec(); + + vty_out(vty, "Interface Address ifIndex Socket Uptime Multi Broad MLoop AllMu Prmsc Del%s", VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) { + struct pim_interface *pim_ifp; + struct in_addr ifaddr; + char uptime[10]; + int mloop; + + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + if (pim_ifp->pim_sock_fd < 0) + continue; + + ifaddr = pim_ifp->primary_address; + + pim_time_uptime(uptime, sizeof(uptime), now - pim_ifp->pim_sock_creation); + + mloop = pim_socket_mcastloop_get(pim_ifp->pim_sock_fd); + + vty_out(vty, "%-9s %-15s %7d %6d %8s %5s %5s %5s %5s %5s %3s%s", + ifp->name, + inet_ntoa(ifaddr), + ifp->ifindex, + pim_ifp->pim_sock_fd, + uptime, + if_is_multicast(ifp) ? "yes" : "no", + if_is_broadcast(ifp) ? "yes" : "no", + (mloop < 0) ? "?" : (mloop ? "yes" : "no"), + (ifp->flags & IFF_ALLMULTI) ? "yes" : "no", + (ifp->flags & IFF_PROMISC) ? "yes" : "no", + PIM_IF_IS_DELETED(ifp) ? "yes" : "no", + VTY_NEWLINE); + } +} + +static void pim_show_join(struct vty *vty) +{ + struct listnode *ifnode; + struct interface *ifp; + time_t now; + + now = pim_time_monotonic_sec(); + + vty_out(vty, + "Interface Address Source Group State Uptime Expire Prune%s", + VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) { + struct pim_interface *pim_ifp; + struct in_addr ifaddr; + struct listnode *ch_node; + struct pim_ifchannel *ch; + + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + ifaddr = pim_ifp->primary_address; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) { + char ch_src_str[100]; + char ch_grp_str[100]; + char uptime[10]; + char expire[10]; + char prune[10]; + + pim_inet4_dump("", ch->source_addr, + ch_src_str, sizeof(ch_src_str)); + pim_inet4_dump("", ch->group_addr, + ch_grp_str, sizeof(ch_grp_str)); + + pim_time_uptime(uptime, sizeof(uptime), now - ch->ifjoin_creation); + pim_time_timer_to_mmss(expire, sizeof(expire), + ch->t_ifjoin_expiry_timer); + pim_time_timer_to_mmss(prune, sizeof(prune), + ch->t_ifjoin_prune_pending_timer); + + vty_out(vty, "%-9s %-15s %-15s %-15s %-6s %8s %-6s %5s%s", + ifp->name, + inet_ntoa(ifaddr), + ch_src_str, + ch_grp_str, + pim_ifchannel_ifjoin_name(ch->ifjoin_state), + uptime, + expire, + prune, + VTY_NEWLINE); + } /* scan interface channels */ + } /* scan interfaces */ + +} + +static void pim_show_neighbors(struct vty *vty) +{ + struct listnode *node; + struct interface *ifp; + time_t now; + + now = pim_time_monotonic_sec(); + + vty_out(vty, + "Recv flags: H=holdtime L=lan_prune_delay P=dr_priority G=generation_id A=address_list%s" + " T=can_disable_join_suppression%s%s", + VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + + vty_out(vty, "Interface Address Neighbor Uptime Timer Holdt DrPri GenId Recv %s", VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) { + struct pim_interface *pim_ifp; + struct in_addr ifaddr; + struct listnode *neighnode; + struct pim_neighbor *neigh; + + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + if (pim_ifp->pim_sock_fd < 0) + continue; + + ifaddr = pim_ifp->primary_address; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode, neigh)) { + char uptime[10]; + char holdtime[10]; + char expire[10]; + char neigh_src_str[100]; + char recv[7]; + + pim_inet4_dump("", neigh->source_addr, + neigh_src_str, sizeof(neigh_src_str)); + pim_time_uptime(uptime, sizeof(uptime), now - neigh->creation); + pim_time_mmss(holdtime, sizeof(holdtime), neigh->holdtime); + pim_time_timer_to_mmss(expire, sizeof(expire), neigh->t_expire_timer); + + recv[0] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_HOLDTIME) ? 'H' : ' '; + recv[1] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY) ? 'L' : ' '; + recv[2] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_DR_PRIORITY) ? 'P' : ' '; + recv[3] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_GENERATION_ID) ? 'G' : ' '; + recv[4] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_ADDRESS_LIST) ? 'A' : ' '; + recv[5] = PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION) ? 'T' : ' '; + recv[6] = '\0'; + + vty_out(vty, "%-9s %-15s %-15s %8s %5s %5s %5u %08x %6s%s", + ifp->name, + inet_ntoa(ifaddr), + neigh_src_str, + uptime, + expire, + holdtime, + neigh->dr_priority, + neigh->generation_id, + recv, + VTY_NEWLINE); + } + + + } +} + +static void pim_show_lan_prune_delay(struct vty *vty) +{ + struct listnode *node; + struct interface *ifp; + + vty_out(vty, + "PrDly=propagation_delay (msec) OvInt=override_interval (msec)%s" + "HiDly=highest_propagation_delay (msec) HiInt=highest_override_interval (msec)%s" + "NoDly=number_of_non_lan_delay_neighbors%s" + "T=t_bit LPD=lan_prune_delay_hello_option%s%s", + VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + + vty_out(vty, "Interface Address PrDly OvInt NoDly HiDly HiInt T Neighbor LPD PrDly OvInt T%s", VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) { + struct pim_interface *pim_ifp; + struct in_addr ifaddr; + struct listnode *neighnode; + struct pim_neighbor *neigh; + + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + if (pim_ifp->pim_sock_fd < 0) + continue; + + ifaddr = pim_ifp->primary_address; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode, neigh)) { + char neigh_src_str[100]; + + pim_inet4_dump("", neigh->source_addr, + neigh_src_str, sizeof(neigh_src_str)); + + vty_out(vty, "%-9s %-15s %5u %5u %5u %5u %5u %1u %-15s %s %5u %5u %1u%s", + ifp->name, + inet_ntoa(ifaddr), + pim_ifp->pim_propagation_delay_msec, + pim_ifp->pim_override_interval_msec, + pim_ifp->pim_number_of_nonlandelay_neighbors, + pim_ifp->pim_neighbors_highest_propagation_delay_msec, + pim_ifp->pim_neighbors_highest_override_interval_msec, + PIM_FORCE_BOOLEAN(PIM_IF_TEST_PIM_CAN_DISABLE_JOIN_SUPRESSION(pim_ifp->options)), + neigh_src_str, + PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY) ? "yes" : "no", + neigh->propagation_delay_msec, + neigh->override_interval_msec, + PIM_FORCE_BOOLEAN(PIM_OPTION_IS_SET(neigh->hello_options, + PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION)), + VTY_NEWLINE); + } + + } +} + +static void pim_show_jp_override_interval(struct vty *vty) +{ + struct listnode *node; + struct interface *ifp; + + vty_out(vty, + "EffPDelay=effective_propagation_delay (msec)%s" + "EffOvrInt=override_interval (msec)%s" + "JPOvrInt=jp_override_interval (msec)%s%s", + VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + + vty_out(vty, "Interface Address LAN_Delay EffPDelay EffOvrInt JPOvrInt%s", VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) { + struct pim_interface *pim_ifp; + struct in_addr ifaddr; + + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + if (pim_ifp->pim_sock_fd < 0) + continue; + + ifaddr = pim_ifp->primary_address; + + vty_out(vty, "%-9s %-15s %-9s %9u %9u %8u%s", + ifp->name, + inet_ntoa(ifaddr), + pim_if_lan_delay_enabled(ifp) ? "enabled" : "disabled", + pim_if_effective_propagation_delay_msec(ifp), + pim_if_effective_override_interval_msec(ifp), + pim_if_jp_override_interval_msec(ifp), + VTY_NEWLINE); + } +} + +static void pim_show_neighbors_secondary(struct vty *vty) +{ + struct listnode *node; + struct interface *ifp; + + vty_out(vty, "Interface Address Neighbor Secondary %s", VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) { + struct pim_interface *pim_ifp; + struct in_addr ifaddr; + struct listnode *neighnode; + struct pim_neighbor *neigh; + + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + if (pim_ifp->pim_sock_fd < 0) + continue; + + ifaddr = pim_ifp->primary_address; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode, neigh)) { + char neigh_src_str[100]; + struct listnode *prefix_node; + struct prefix *p; + + if (!neigh->prefix_list) + continue; + + pim_inet4_dump("", neigh->source_addr, + neigh_src_str, sizeof(neigh_src_str)); + + for (ALL_LIST_ELEMENTS_RO(neigh->prefix_list, prefix_node, p)) { + char neigh_sec_str[100]; + + if (p->family != AF_INET) + continue; + + pim_inet4_dump("", p->u.prefix4, + neigh_sec_str, sizeof(neigh_sec_str)); + + vty_out(vty, "%-9s %-15s %-15s %-15s%s", + ifp->name, + inet_ntoa(ifaddr), + neigh_src_str, + neigh_sec_str, + VTY_NEWLINE); + } + } + } +} + +static void pim_show_upstream(struct vty *vty) +{ + struct listnode *upnode; + struct pim_upstream *up; + time_t now; + + now = pim_time_monotonic_sec(); + + vty_out(vty, "Source Group State Uptime JoinTimer RefCnt%s", VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, upnode, up)) { + char src_str[100]; + char grp_str[100]; + char uptime[10]; + char join_timer[10]; + + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + pim_time_uptime(uptime, sizeof(uptime), now - up->state_transition); + pim_time_timer_to_hhmmss(join_timer, sizeof(join_timer), up->t_join_timer); + + vty_out(vty, "%-15s %-15s %-5s %-8s %-9s %6d%s", + src_str, + grp_str, + up->join_state == PIM_UPSTREAM_JOINED ? "Jnd" : "NtJnd", + uptime, + join_timer, + up->ref_count, + VTY_NEWLINE); + } +} + +static void pim_show_join_desired(struct vty *vty) +{ + struct listnode *ifnode; + struct listnode *chnode; + struct interface *ifp; + struct pim_interface *pim_ifp; + struct pim_ifchannel *ch; + struct in_addr me_ifaddr; + char src_str[100]; + char grp_str[100]; + + vty_out(vty, + "Interface Source Group LostAssert Joins PimInclude JoinDesired EvalJD%s", + VTY_NEWLINE); + + /* scan all interfaces */ + for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) { + pim_ifp = ifp->info; + if (!pim_ifp) + continue; + + me_ifaddr = pim_ifp->primary_address; + + /* scan per-interface (S,G) state */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, chnode, ch)) { + struct pim_upstream *up = ch->upstream; + + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + + vty_out(vty, "%-9s %-15s %-15s %-10s %-5s %-10s %-11s %-6s%s", + ifp->name, + src_str, + grp_str, + pim_macro_ch_lost_assert(ch) ? "yes" : "no", + pim_macro_chisin_joins(ch) ? "yes" : "no", + pim_macro_chisin_pim_include(ch) ? "yes" : "no", + PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(up->flags) ? "yes" : "no", + pim_upstream_evaluate_join_desired(up) ? "yes" : "no", + VTY_NEWLINE); + } + } +} + +static void pim_show_upstream_rpf(struct vty *vty) +{ + struct listnode *upnode; + struct pim_upstream *up; + + vty_out(vty, + "Source Group RpfIface RibNextHop RpfAddress %s", + VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, upnode, up)) { + char src_str[100]; + char grp_str[100]; + char rpf_nexthop_str[100]; + char rpf_addr_str[100]; + struct pim_rpf *rpf; + const char *rpf_ifname; + + rpf = &up->rpf; + + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", rpf->source_nexthop.mrib_nexthop_addr, rpf_nexthop_str, sizeof(rpf_nexthop_str)); + pim_inet4_dump("", rpf->rpf_addr, rpf_addr_str, sizeof(rpf_addr_str)); + + rpf_ifname = rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : ""; + + vty_out(vty, "%-15s %-15s %-8s %-15s %-15s%s", + src_str, + grp_str, + rpf_ifname, + rpf_nexthop_str, + rpf_addr_str, + VTY_NEWLINE); + } +} + +static void pim_show_rpf(struct vty *vty) +{ + struct listnode *up_node; + struct pim_upstream *up; + + vty_out(vty, + "Source Group RpfIface RpfAddress RibNextHop Metric Pref%s", + VTY_NEWLINE); + + + for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, up_node, up)) { + char src_str[100]; + char grp_str[100]; + char rpf_addr_str[100]; + char rib_nexthop_str[100]; + const char *rpf_ifname; + struct pim_rpf *rpf = &up->rpf; + + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", rpf->rpf_addr, rpf_addr_str, sizeof(rpf_addr_str)); + pim_inet4_dump("", rpf->source_nexthop.mrib_nexthop_addr, rib_nexthop_str, sizeof(rib_nexthop_str)); + + rpf_ifname = rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : ""; + + vty_out(vty, "%-15s %-15s %-8s %-15s %-15s %6d %4d%s", + src_str, + grp_str, + rpf_ifname, + rpf_addr_str, + rib_nexthop_str, + rpf->source_nexthop.mrib_route_metric, + rpf->source_nexthop.mrib_metric_preference, + VTY_NEWLINE); + } +} + +static void igmp_show_querier(struct vty *vty) +{ + struct listnode *node; + struct interface *ifp; + time_t now; + + now = pim_time_monotonic_sec(); + + vty_out(vty, "Interface Address Querier StartCount Query-Timer Other-Timer%s", VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) { + struct pim_interface *pim_ifp = ifp->info; + struct listnode *sock_node; + struct igmp_sock *igmp; + + if (!pim_ifp) + continue; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { + char query_hhmmss[10]; + char other_hhmmss[10]; + + pim_time_timer_to_hhmmss(query_hhmmss, sizeof(query_hhmmss), igmp->t_igmp_query_timer); + pim_time_timer_to_hhmmss(other_hhmmss, sizeof(other_hhmmss), igmp->t_other_querier_timer); + + vty_out(vty, "%-9s %-15s %7s %10d %11s %11s%s", + ifp->name, + inet_ntoa(igmp->ifaddr), + igmp->t_igmp_query_timer ? "THIS" : "OTHER", + igmp->startup_query_count, + query_hhmmss, + other_hhmmss, + VTY_NEWLINE); + } + } +} + +static void igmp_show_groups(struct vty *vty) +{ + struct listnode *ifnode; + struct interface *ifp; + time_t now; + + now = pim_time_monotonic_sec(); + + vty_out(vty, "Interface Address Group Mode Timer Srcs V Uptime %s", VTY_NEWLINE); + + /* scan interfaces */ + for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) { + struct pim_interface *pim_ifp = ifp->info; + struct listnode *sock_node; + struct igmp_sock *igmp; + + if (!pim_ifp) + continue; + + /* scan igmp sockets */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { + char ifaddr_str[100]; + struct listnode *grpnode; + struct igmp_group *grp; + + pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); + + /* scan igmp groups */ + for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grpnode, grp)) { + char group_str[100]; + char hhmmss[10]; + char uptime[10]; + + pim_inet4_dump("", grp->group_addr, group_str, sizeof(group_str)); + pim_time_timer_to_hhmmss(hhmmss, sizeof(hhmmss), grp->t_group_timer); + pim_time_uptime(uptime, sizeof(uptime), now - grp->group_creation); + + vty_out(vty, "%-9s %-15s %-15s %4s %8s %4d %d %8s%s", + ifp->name, + ifaddr_str, + group_str, + grp->group_filtermode_isexcl ? "EXCL" : "INCL", + hhmmss, + grp->group_source_list ? listcount(grp->group_source_list) : 0, + igmp_group_compat_mode(igmp, grp), + uptime, + VTY_NEWLINE); + + } /* scan igmp groups */ + } /* scan igmp sockets */ + } /* scan interfaces */ +} + +static void igmp_show_group_retransmission(struct vty *vty) +{ + struct listnode *ifnode; + struct interface *ifp; + time_t now; + + now = pim_time_monotonic_sec(); + + vty_out(vty, "Interface Address Group RetTimer Counter RetSrcs%s", VTY_NEWLINE); + + /* scan interfaces */ + for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) { + struct pim_interface *pim_ifp = ifp->info; + struct listnode *sock_node; + struct igmp_sock *igmp; + + if (!pim_ifp) + continue; + + /* scan igmp sockets */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { + char ifaddr_str[100]; + struct listnode *grpnode; + struct igmp_group *grp; + + pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); + + /* scan igmp groups */ + for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grpnode, grp)) { + char group_str[100]; + char grp_retr_mmss[10]; + struct listnode *src_node; + struct igmp_source *src; + int grp_retr_sources = 0; + + pim_inet4_dump("", grp->group_addr, group_str, sizeof(group_str)); + pim_time_timer_to_mmss(grp_retr_mmss, sizeof(grp_retr_mmss), grp->t_group_query_retransmit_timer); + + + /* count group sources with retransmission state */ + for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, src_node, src)) { + if (src->source_query_retransmit_count > 0) { + ++grp_retr_sources; + } + } + + vty_out(vty, "%-9s %-15s %-15s %-8s %7d %7d%s", + ifp->name, + ifaddr_str, + group_str, + grp_retr_mmss, + grp->group_specific_query_retransmit_count, + grp_retr_sources, + VTY_NEWLINE); + + } /* scan igmp groups */ + } /* scan igmp sockets */ + } /* scan interfaces */ +} + +static void igmp_show_parameters(struct vty *vty) +{ + struct listnode *ifnode; + struct interface *ifp; + + vty_out(vty, + "QRV: Robustness Variable SQI: Startup Query Interval%s" + "QQI: Query Interval OQPI: Other Querier Present Interval%s" + "QRI: Query Response Interval LMQT: Last Member Query Time%s" + "GMI: Group Membership Interval OHPI: Older Host Present Interval%s%s", + VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + + vty_out(vty, + "Interface Address QRV QQI QRI GMI SQI OQPI LMQT OHPI %s", + VTY_NEWLINE); + + /* scan interfaces */ + for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) { + struct pim_interface *pim_ifp = ifp->info; + struct listnode *sock_node; + struct igmp_sock *igmp; + + if (!pim_ifp) + continue; + + /* scan igmp sockets */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { + char ifaddr_str[100]; + long gmi_dsec; /* Group Membership Interval */ + long oqpi_dsec; /* Other Querier Present Interval */ + int sqi; + long lmqt_dsec; + long ohpi_dsec; + long qri_dsec; + + pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); + + gmi_dsec = PIM_IGMP_GMI_MSEC(igmp->querier_robustness_variable, + igmp->querier_query_interval, + pim_ifp->igmp_query_max_response_time_dsec) / 100; + + sqi = PIM_IGMP_SQI(pim_ifp->igmp_default_query_interval); + + oqpi_dsec = PIM_IGMP_OQPI_MSEC(igmp->querier_robustness_variable, + igmp->querier_query_interval, + pim_ifp->igmp_query_max_response_time_dsec) / 100; + + lmqt_dsec = PIM_IGMP_LMQT_MSEC(pim_ifp->igmp_query_max_response_time_dsec, + igmp->querier_robustness_variable) / 100; + + ohpi_dsec = PIM_IGMP_OHPI_DSEC(igmp->querier_robustness_variable, + igmp->querier_query_interval, + pim_ifp->igmp_query_max_response_time_dsec); + + qri_dsec = pim_ifp->igmp_query_max_response_time_dsec; + + vty_out(vty, + "%-9s %-15s %3d %3d %3ld.%ld %3ld.%ld %3d %3ld.%ld %3ld.%ld %3ld.%ld%s", + ifp->name, + ifaddr_str, + igmp->querier_robustness_variable, + igmp->querier_query_interval, + qri_dsec / 10, qri_dsec % 10, + gmi_dsec / 10, gmi_dsec % 10, + sqi, + oqpi_dsec / 10, oqpi_dsec % 10, + lmqt_dsec / 10, lmqt_dsec % 10, + ohpi_dsec / 10, ohpi_dsec % 10, + VTY_NEWLINE); + + } /* scan igmp sockets */ + } /* scan interfaces */ +} + +static void igmp_show_sources(struct vty *vty) +{ + struct listnode *ifnode; + struct interface *ifp; + time_t now; + + now = pim_time_monotonic_sec(); + + vty_out(vty, "Interface Address Group Source Timer Fwd Uptime %s", VTY_NEWLINE); + + /* scan interfaces */ + for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) { + struct pim_interface *pim_ifp = ifp->info; + struct listnode *sock_node; + struct igmp_sock *igmp; + + if (!pim_ifp) + continue; + + /* scan igmp sockets */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { + char ifaddr_str[100]; + struct listnode *grpnode; + struct igmp_group *grp; + + pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); + + /* scan igmp groups */ + for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grpnode, grp)) { + char group_str[100]; + struct listnode *srcnode; + struct igmp_source *src; + + pim_inet4_dump("", grp->group_addr, group_str, sizeof(group_str)); + + /* scan group sources */ + for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, srcnode, src)) { + char source_str[100]; + char mmss[10]; + char uptime[10]; + + pim_inet4_dump("", src->source_addr, source_str, sizeof(source_str)); + + pim_time_timer_to_mmss(mmss, sizeof(mmss), src->t_source_timer); + + pim_time_uptime(uptime, sizeof(uptime), now - src->source_creation); + + vty_out(vty, "%-9s %-15s %-15s %-15s %5s %3s %8s%s", + ifp->name, + ifaddr_str, + group_str, + source_str, + mmss, + IGMP_SOURCE_TEST_FORWARDING(src->source_flags) ? "Y" : "N", + uptime, + VTY_NEWLINE); + + } /* scan group sources */ + } /* scan igmp groups */ + } /* scan igmp sockets */ + } /* scan interfaces */ +} + +static void igmp_show_source_retransmission(struct vty *vty) +{ + struct listnode *ifnode; + struct interface *ifp; + time_t now; + + now = pim_time_monotonic_sec(); + + vty_out(vty, "Interface Address Group Source Counter%s", VTY_NEWLINE); + + /* scan interfaces */ + for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) { + struct pim_interface *pim_ifp = ifp->info; + struct listnode *sock_node; + struct igmp_sock *igmp; + + if (!pim_ifp) + continue; + + /* scan igmp sockets */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { + char ifaddr_str[100]; + struct listnode *grpnode; + struct igmp_group *grp; + + pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); + + /* scan igmp groups */ + for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grpnode, grp)) { + char group_str[100]; + struct listnode *srcnode; + struct igmp_source *src; + + pim_inet4_dump("", grp->group_addr, group_str, sizeof(group_str)); + + /* scan group sources */ + for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, srcnode, src)) { + char source_str[100]; + + pim_inet4_dump("", src->source_addr, source_str, sizeof(source_str)); + + vty_out(vty, "%-9s %-15s %-15s %-15s %7d%s", + ifp->name, + ifaddr_str, + group_str, + source_str, + src->source_query_retransmit_count, + VTY_NEWLINE); + + } /* scan group sources */ + } /* scan igmp groups */ + } /* scan igmp sockets */ + } /* scan interfaces */ +} + +static void clear_igmp_interfaces() +{ + struct listnode *ifnode; + struct listnode *ifnextnode; + struct interface *ifp; + + for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) { + pim_if_addr_del_all_igmp(ifp); + } + + for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) { + pim_if_addr_add_all(ifp); + } +} + +static void clear_pim_interfaces() +{ + struct listnode *ifnode; + struct listnode *ifnextnode; + struct interface *ifp; + + for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) { + if (ifp->info) { + pim_neighbor_delete_all(ifp, "interface cleared"); + } + } +} + +static void clear_interfaces() +{ + clear_igmp_interfaces(); + clear_pim_interfaces(); +} + +DEFUN (pim_interface, + pim_interface_cmd, + "interface IFNAME", + "Select an interface to configure\n" + "Interface's name\n") +{ + struct interface *ifp; + const char *ifname = argv[0]; + size_t sl; + + sl = strlen(ifname); + if (sl > INTERFACE_NAMSIZ) { + vty_out(vty, "%% Interface name %s is invalid: length exceeds " + "%d characters%s", + ifname, INTERFACE_NAMSIZ, VTY_NEWLINE); + return CMD_WARNING; + } + + ifp = if_lookup_by_name_len(ifname, sl); + if (!ifp) { + vty_out(vty, "%% Interface %s does not exist%s", ifname, VTY_NEWLINE); + + /* Returning here would prevent pimd from booting when there are + interface commands in pimd.conf, since all interfaces are + unknown at pimd boot time (the zebra daemon has not been + contacted for interface discovery). */ + + ifp = if_get_by_name_len(ifname, sl); + if (!ifp) { + vty_out(vty, "%% Could not create interface %s%s", ifname, VTY_NEWLINE); + return CMD_WARNING; + } + } + + vty->index = ifp; + vty->node = INTERFACE_NODE; + + return CMD_SUCCESS; +} + +DEFUN (clear_ip_interfaces, + clear_ip_interfaces_cmd, + "clear ip interfaces", + CLEAR_STR + IP_STR + "Reset interfaces\n") +{ + clear_interfaces(); + + return CMD_SUCCESS; +} + +DEFUN (clear_ip_igmp_interfaces, + clear_ip_igmp_interfaces_cmd, + "clear ip igmp interfaces", + CLEAR_STR + IP_STR + CLEAR_IP_IGMP_STR + "Reset IGMP interfaces\n") +{ + clear_igmp_interfaces(); + + return CMD_SUCCESS; +} + +DEFUN (clear_ip_pim_interfaces, + clear_ip_pim_interfaces_cmd, + "clear ip pim interfaces", + CLEAR_STR + IP_STR + CLEAR_IP_PIM_STR + "Reset PIM interfaces\n") +{ + clear_pim_interfaces(); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_igmp_interface, + show_ip_igmp_interface_cmd, + "show ip igmp interface", + SHOW_STR + IP_STR + IGMP_STR + "IGMP interface information\n") +{ + igmp_show_interfaces(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_igmp_groups, + show_ip_igmp_groups_cmd, + "show ip igmp groups", + SHOW_STR + IP_STR + IGMP_STR + IGMP_GROUP_STR) +{ + igmp_show_groups(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_igmp_groups_retransmissions, + show_ip_igmp_groups_retransmissions_cmd, + "show ip igmp groups retransmissions", + SHOW_STR + IP_STR + IGMP_STR + IGMP_GROUP_STR + "IGMP group retransmissions\n") +{ + igmp_show_group_retransmission(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_igmp_parameters, + show_ip_igmp_parameters_cmd, + "show ip igmp parameters", + SHOW_STR + IP_STR + IGMP_STR + "IGMP parameters information\n") +{ + igmp_show_parameters(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_igmp_sources, + show_ip_igmp_sources_cmd, + "show ip igmp sources", + SHOW_STR + IP_STR + IGMP_STR + IGMP_SOURCE_STR) +{ + igmp_show_sources(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_igmp_sources_retransmissions, + show_ip_igmp_sources_retransmissions_cmd, + "show ip igmp sources retransmissions", + SHOW_STR + IP_STR + IGMP_STR + IGMP_SOURCE_STR + "IGMP source retransmissions\n") +{ + igmp_show_source_retransmission(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_igmp_querier, + show_ip_igmp_querier_cmd, + "show ip igmp querier", + SHOW_STR + IP_STR + IGMP_STR + "IGMP querier information\n") +{ + igmp_show_querier(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_address, + show_ip_pim_address_cmd, + "show ip pim address", + SHOW_STR + IP_STR + PIM_STR + "PIM interface address\n") +{ + show_interface_address(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_assert, + show_ip_pim_assert_cmd, + "show ip pim assert", + SHOW_STR + IP_STR + PIM_STR + "PIM interface assert\n") +{ + pim_show_assert(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_assert_internal, + show_ip_pim_assert_internal_cmd, + "show ip pim assert-internal", + SHOW_STR + IP_STR + PIM_STR + "PIM interface internal assert state\n") +{ + pim_show_assert_internal(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_assert_metric, + show_ip_pim_assert_metric_cmd, + "show ip pim assert-metric", + SHOW_STR + IP_STR + PIM_STR + "PIM interface assert metric\n") +{ + pim_show_assert_metric(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_assert_winner_metric, + show_ip_pim_assert_winner_metric_cmd, + "show ip pim assert-winner-metric", + SHOW_STR + IP_STR + PIM_STR + "PIM interface assert winner metric\n") +{ + pim_show_assert_winner_metric(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_dr, + show_ip_pim_dr_cmd, + "show ip pim designated-router", + SHOW_STR + IP_STR + PIM_STR + "PIM interface designated router\n") +{ + pim_show_dr(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_hello, + show_ip_pim_hello_cmd, + "show ip pim hello", + SHOW_STR + IP_STR + PIM_STR + "PIM interface hello information\n") +{ + pim_show_hello(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_interface, + show_ip_pim_interface_cmd, + "show ip pim interface", + SHOW_STR + IP_STR + PIM_STR + "PIM interface information\n") +{ + pim_show_interfaces(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_join, + show_ip_pim_join_cmd, + "show ip pim join", + SHOW_STR + IP_STR + PIM_STR + "PIM interface join information\n") +{ + pim_show_join(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_lan_prune_delay, + show_ip_pim_lan_prune_delay_cmd, + "show ip pim lan-prune-delay", + SHOW_STR + IP_STR + PIM_STR + "PIM neighbors LAN prune delay parameters\n") +{ + pim_show_lan_prune_delay(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_local_membership, + show_ip_pim_local_membership_cmd, + "show ip pim local-membership", + SHOW_STR + IP_STR + PIM_STR + "PIM interface local-membership\n") +{ + pim_show_membership(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_jp_override_interval, + show_ip_pim_jp_override_interval_cmd, + "show ip pim jp-override-interval", + SHOW_STR + IP_STR + PIM_STR + "PIM interface J/P override interval\n") +{ + pim_show_jp_override_interval(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_neighbor, + show_ip_pim_neighbor_cmd, + "show ip pim neighbor", + SHOW_STR + IP_STR + PIM_STR + "PIM neighbor information\n") +{ + pim_show_neighbors(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_secondary, + show_ip_pim_secondary_cmd, + "show ip pim secondary", + SHOW_STR + IP_STR + PIM_STR + "PIM neighbor addresses\n") +{ + pim_show_neighbors_secondary(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_upstream, + show_ip_pim_upstream_cmd, + "show ip pim upstream", + SHOW_STR + IP_STR + PIM_STR + "PIM upstream information\n") +{ + pim_show_upstream(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_upstream_join_desired, + show_ip_pim_upstream_join_desired_cmd, + "show ip pim upstream-join-desired", + SHOW_STR + IP_STR + PIM_STR + "PIM upstream join-desired\n") +{ + pim_show_join_desired(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_upstream_rpf, + show_ip_pim_upstream_rpf_cmd, + "show ip pim upstream-rpf", + SHOW_STR + IP_STR + PIM_STR + "PIM upstream source rpf\n") +{ + pim_show_upstream_rpf(vty); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_pim_rpf, + show_ip_pim_rpf_cmd, + "show ip pim rpf", + SHOW_STR + IP_STR + PIM_STR + "PIM cached source rpf information\n") +{ + pim_show_rpf(vty); + + return CMD_SUCCESS; +} + +static void show_multicast_interfaces(struct vty *vty) +{ + struct listnode *node; + struct interface *ifp; + + vty_out(vty, "%s", VTY_NEWLINE); + + vty_out(vty, "Interface Address ifIndex VifIndex PktsIn PktsOut BytesIn BytesOut%s", + VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) { + struct pim_interface *pim_ifp; + struct in_addr ifaddr; + struct sioc_vif_req vreq; + + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + memset(&vreq, 0, sizeof(vreq)); + vreq.vifi = pim_ifp->mroute_vif_index; + + if (ioctl(qpim_mroute_socket_fd, SIOCGETVIFCNT, &vreq)) { + int e = errno; + vty_out(vty, + "ioctl(SIOCGETVIFCNT=%d) failure for interface %s vif_index=%d: errno=%d: %s%s", + SIOCGETVIFCNT, + ifp->name, + pim_ifp->mroute_vif_index, + e, + strerror(e), + VTY_NEWLINE); + continue; + } + + ifaddr = pim_ifp->primary_address; + + vty_out(vty, "%-9s %-15s %7d %8d %6lu %7lu %7lu %8lu%s", + ifp->name, + inet_ntoa(ifaddr), + ifp->ifindex, + pim_ifp->mroute_vif_index, + vreq.icount, + vreq.ocount, + vreq.ibytes, + vreq.obytes, + VTY_NEWLINE); + } +} + +DEFUN (show_ip_multicast, + show_ip_multicast_cmd, + "show ip multicast", + SHOW_STR + IP_STR + "Multicast global information\n") +{ + if (PIM_MROUTE_IS_ENABLED) { + time_t now; + char uptime[10]; + + vty_out(vty, "Mroute socket descriptor: %d%s", + qpim_mroute_socket_fd, + VTY_NEWLINE); + + now = pim_time_monotonic_sec(); + pim_time_uptime(uptime, sizeof(uptime), now - qpim_mroute_socket_creation); + vty_out(vty, "Mroute socket uptime: %s%s", + uptime, + VTY_NEWLINE); + } + else { + vty_out(vty, "Multicast disabled%s", + VTY_NEWLINE); + } + + vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, "Current highest VifIndex: %d%s", + qpim_mroute_oif_highest_vif_index, + VTY_NEWLINE); + vty_out(vty, "Maximum highest VifIndex: %d%s", + MAXVIFS - 1, + VTY_NEWLINE); + + vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, "Upstream Join Timer: %d secs%s", + qpim_t_periodic, + VTY_NEWLINE); + vty_out(vty, "Join/Prune Holdtime: %d secs%s", + PIM_JP_HOLDTIME, + VTY_NEWLINE); + + vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, "RPF Cache Refresh Delay: %ld msecs%s", + qpim_rpf_cache_refresh_delay_msec, + VTY_NEWLINE); + vty_out(vty, "RPF Cache Refresh Timer: %ld msecs%s", + pim_time_timer_remain_msec(qpim_rpf_cache_refresher), + VTY_NEWLINE); + + show_multicast_interfaces(vty); + + return CMD_SUCCESS; +} + +static void show_mroute(struct vty *vty) +{ + struct listnode *node; + struct channel_oil *c_oil; + time_t now; + + vty_out(vty, "Proto: I=IGMP P=PIM%s%s", VTY_NEWLINE, VTY_NEWLINE); + + vty_out(vty, "Source Group Proto Input iVifI Output oVifI TTL Uptime %s", + VTY_NEWLINE); + + now = pim_time_monotonic_sec(); + + for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) { + char group_str[100]; + char source_str[100]; + int oif_vif_index; + + pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + + for (oif_vif_index = 0; oif_vif_index < MAXVIFS; ++oif_vif_index) { + struct interface *ifp_in; + struct interface *ifp_out; + char oif_uptime[10]; + int ttl; + char proto[5]; + + ttl = c_oil->oil.mfcc_ttls[oif_vif_index]; + if (ttl < 1) + continue; + + ifp_in = pim_if_find_by_vif_index(c_oil->oil.mfcc_parent); + ifp_out = pim_if_find_by_vif_index(oif_vif_index); + + pim_time_uptime(oif_uptime, sizeof(oif_uptime), now - c_oil->oif_creation[oif_vif_index]); + + proto[0] = '\0'; + if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_PIM) { + strcat(proto, "P"); + } + if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_PROTO_IGMP) { + strcat(proto, "I"); + } + + vty_out(vty, "%-15s %-15s %-5s %-5s %5d %-6s %5d %3d %8s %s", + source_str, + group_str, + proto, + ifp_in ? ifp_in->name : "", + c_oil->oil.mfcc_parent, + ifp_out ? ifp_out->name : "", + oif_vif_index, + ttl, + oif_uptime, + VTY_NEWLINE); + } + } +} + +DEFUN (show_ip_mroute, + show_ip_mroute_cmd, + "show ip mroute", + SHOW_STR + IP_STR + MROUTE_STR) +{ + show_mroute(vty); + return CMD_SUCCESS; +} + +static void show_mroute_count(struct vty *vty) +{ + struct listnode *node; + struct channel_oil *c_oil; + + vty_out(vty, "%s", VTY_NEWLINE); + + vty_out(vty, "Source Group Packets Bytes WrongIf %s", + VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) { + char group_str[100]; + char source_str[100]; + struct sioc_sg_req sgreq; + + memset(&sgreq, 0, sizeof(sgreq)); + sgreq.src = c_oil->oil.mfcc_origin; + sgreq.grp = c_oil->oil.mfcc_mcastgrp; + + pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + + if (ioctl(qpim_mroute_socket_fd, SIOCGETSGCNT, &sgreq)) { + int e = errno; + vty_out(vty, + "ioctl(SIOCGETSGCNT=%d) failure for (S,G)=(%s,%s): errno=%d: %s%s", + SIOCGETSGCNT, + source_str, + group_str, + e, + strerror(e), + VTY_NEWLINE); + continue; + } + + vty_out(vty, "%-15s %-15s %7ld %10ld %7ld %s", + source_str, + group_str, + sgreq.pktcnt, + sgreq.bytecnt, + sgreq.wrong_if, + VTY_NEWLINE); + + } +} + +DEFUN (show_ip_mroute_count, + show_ip_mroute_count_cmd, + "show ip mroute count", + SHOW_STR + IP_STR + MROUTE_STR + "Route and packet count data\n") +{ + show_mroute_count(vty); + return CMD_SUCCESS; +} + +DEFUN (show_ip_route, + show_ip_route_cmd, + "show ip route A.B.C.D", + SHOW_STR + IP_STR + ROUTE_STR + "Unicast address\n") +{ + struct in_addr addr; + const char *addr_str; + struct pim_nexthop nexthop; + char nexthop_addr_str[100]; + int result; + + addr_str = argv[0]; + result = inet_pton(AF_INET, addr_str, &addr); + if (result <= 0) { + vty_out(vty, "Bad unicast address %s: errno=%d: %s%s", + addr_str, errno, strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + if (pim_nexthop_lookup(&nexthop, addr)) { + vty_out(vty, "Failure querying RIB nexthop for unicast address %s%s", + addr_str, VTY_NEWLINE); + return CMD_WARNING; + } + + vty_out(vty, "Address NextHop Interface Metric Preference%s", + VTY_NEWLINE); + + pim_inet4_dump("", nexthop.mrib_nexthop_addr, + nexthop_addr_str, sizeof(nexthop_addr_str)); + + vty_out(vty, "%-15s %-15s %-9s %6d %10d%s", + addr_str, + nexthop_addr_str, + nexthop.interface ? nexthop.interface->name : "", + nexthop.mrib_route_metric, + nexthop.mrib_metric_preference, + VTY_NEWLINE); + + return CMD_SUCCESS; +} + +static void mroute_add_all() +{ + struct listnode *node; + struct channel_oil *c_oil; + + for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) { + if (pim_mroute_add(&c_oil->oil)) { + /* just log warning */ + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + zlog_warn("%s %s: (S,G)=(%s,%s) failure writing MFC", + __FILE__, __PRETTY_FUNCTION__, + source_str, group_str); + } + } +} + +static void mroute_del_all() +{ + struct listnode *node; + struct channel_oil *c_oil; + + for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) { + if (pim_mroute_del(&c_oil->oil)) { + /* just log warning */ + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + zlog_warn("%s %s: (S,G)=(%s,%s) failure clearing MFC", + __FILE__, __PRETTY_FUNCTION__, + source_str, group_str); + } + } +} + +DEFUN (ip_multicast_routing, + ip_multicast_routing_cmd, + PIM_CMD_IP_MULTICAST_ROUTING, + IP_STR + "Enable IP multicast forwarding\n") +{ + pim_mroute_socket_enable(); + pim_if_add_vif_all(); + mroute_add_all(); + return CMD_SUCCESS; +} + +DEFUN (no_ip_multicast_routing, + no_ip_multicast_routing_cmd, + PIM_CMD_NO " " PIM_CMD_IP_MULTICAST_ROUTING, + NO_STR + IP_STR + "Global IP configuration subcommands\n" + "Enable IP multicast forwarding\n") +{ + mroute_del_all(); + pim_if_del_vif_all(); + pim_mroute_socket_disable(); + return CMD_SUCCESS; +} + +DEFUN (interface_ip_igmp, + interface_ip_igmp_cmd, + "ip igmp", + IP_STR + IFACE_IGMP_STR) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + + ifp = vty->index; + pim_ifp = ifp->info; + + if (!pim_ifp) { + pim_ifp = pim_if_new(ifp, 1 /* igmp=true */, 0 /* pim=false */); + if (!pim_ifp) { + vty_out(vty, "Could not enable IGMP on interface %s%s", + ifp->name, VTY_NEWLINE); + return CMD_WARNING; + } + } + else { + PIM_IF_DO_IGMP(pim_ifp->options); + } + + pim_if_addr_add_all(ifp); + pim_if_membership_refresh(ifp); + + return CMD_SUCCESS; +} + +DEFUN (interface_no_ip_igmp, + interface_no_ip_igmp_cmd, + "no ip igmp", + NO_STR + IP_STR + IFACE_IGMP_STR) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + + ifp = vty->index; + pim_ifp = ifp->info; + if (!pim_ifp) + return CMD_SUCCESS; + + PIM_IF_DONT_IGMP(pim_ifp->options); + + pim_if_membership_clear(ifp); + + pim_if_addr_del_all(ifp); + + if (!PIM_IF_TEST_PIM(pim_ifp->options)) { + pim_if_delete(ifp); + } + + return CMD_SUCCESS; +} + +DEFUN (interface_ip_igmp_join, + interface_ip_igmp_join_cmd, + "ip igmp join A.B.C.D A.B.C.D", + IP_STR + IFACE_IGMP_STR + "IGMP join multicast group\n" + "Multicast group address\n" + "Source address\n") +{ + struct interface *ifp; + const char *group_str; + const char *source_str; + struct in_addr group_addr; + struct in_addr source_addr; + int result; + + ifp = vty->index; + + /* Group address */ + group_str = argv[0]; + result = inet_pton(AF_INET, group_str, &group_addr); + if (result <= 0) { + vty_out(vty, "Bad group address %s: errno=%d: %s%s", + group_str, errno, strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + /* Source address */ + source_str = argv[1]; + result = inet_pton(AF_INET, source_str, &source_addr); + if (result <= 0) { + vty_out(vty, "Bad source address %s: errno=%d: %s%s", + source_str, errno, strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + result = pim_if_igmp_join_add(ifp, group_addr, source_addr); + if (result) { + vty_out(vty, "%% Failure joining IGMP group %s source %s on interface %s: %d%s", + group_str, source_str, ifp->name, result, VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN (interface_no_ip_igmp_join, + interface_no_ip_igmp_join_cmd, + "no ip igmp join A.B.C.D A.B.C.D", + NO_STR + IP_STR + IFACE_IGMP_STR + "IGMP join multicast group\n" + "Multicast group address\n" + "Source address\n") +{ + struct interface *ifp; + const char *group_str; + const char *source_str; + struct in_addr group_addr; + struct in_addr source_addr; + int result; + + ifp = vty->index; + + /* Group address */ + group_str = argv[0]; + result = inet_pton(AF_INET, group_str, &group_addr); + if (result <= 0) { + vty_out(vty, "Bad group address %s: errno=%d: %s%s", + group_str, errno, strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + /* Source address */ + source_str = argv[1]; + result = inet_pton(AF_INET, source_str, &source_addr); + if (result <= 0) { + vty_out(vty, "Bad source address %s: errno=%d: %s%s", + source_str, errno, strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + result = pim_if_igmp_join_del(ifp, group_addr, source_addr); + if (result) { + vty_out(vty, "%% Failure leaving IGMP group %s source %s on interface %s: %d%s", + group_str, source_str, ifp->name, result, VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +/* + CLI reconfiguration affects the interface level (struct pim_interface). + This function propagates the reconfiguration to every active socket + for that interface. + */ +static void igmp_sock_query_interval_reconfig(struct igmp_sock *igmp) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + + zassert(igmp); + + /* other querier present? */ + + if (igmp->t_other_querier_timer) + return; + + /* this is the querier */ + + zassert(igmp->interface); + zassert(igmp->interface->info); + + ifp = igmp->interface; + pim_ifp = ifp->info; + + if (PIM_DEBUG_IGMP_TRACE) { + char ifaddr_str[100]; + pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); + zlog_debug("%s: Querier %s on %s reconfig query_interval=%d", + __PRETTY_FUNCTION__, + ifaddr_str, + ifp->name, + pim_ifp->igmp_default_query_interval); + } + + /* + igmp_startup_mode_on() will reset QQI: + + igmp->querier_query_interval = pim_ifp->igmp_default_query_interval; + */ + igmp_startup_mode_on(igmp); +} + +static void igmp_sock_query_reschedule(struct igmp_sock *igmp) +{ + if (igmp->t_igmp_query_timer) { + /* other querier present */ + zassert(igmp->t_igmp_query_timer); + zassert(!igmp->t_other_querier_timer); + + pim_igmp_general_query_off(igmp); + pim_igmp_general_query_on(igmp); + + zassert(igmp->t_igmp_query_timer); + zassert(!igmp->t_other_querier_timer); + } + else { + /* this is the querier */ + + zassert(!igmp->t_igmp_query_timer); + zassert(igmp->t_other_querier_timer); + + pim_igmp_other_querier_timer_off(igmp); + pim_igmp_other_querier_timer_on(igmp); + + zassert(!igmp->t_igmp_query_timer); + zassert(igmp->t_other_querier_timer); + } +} + +static void change_query_interval(struct pim_interface *pim_ifp, + int query_interval) +{ + struct listnode *sock_node; + struct igmp_sock *igmp; + + pim_ifp->igmp_default_query_interval = query_interval; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { + igmp_sock_query_interval_reconfig(igmp); + igmp_sock_query_reschedule(igmp); + } +} + +static void change_query_max_response_time(struct pim_interface *pim_ifp, + int query_max_response_time_dsec) +{ + struct listnode *sock_node; + struct igmp_sock *igmp; + + pim_ifp->igmp_query_max_response_time_dsec = query_max_response_time_dsec; + + /* + Below we modify socket/group/source timers in order to quickly + reflect the change. Otherwise, those timers would eventually catch + up. + */ + + /* scan all sockets */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) { + struct listnode *grp_node; + struct igmp_group *grp; + + /* reschedule socket general query */ + igmp_sock_query_reschedule(igmp); + + /* scan socket groups */ + for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, grp_node, grp)) { + struct listnode *src_node; + struct igmp_source *src; + + /* reset group timers for groups in EXCLUDE mode */ + if (grp->group_filtermode_isexcl) { + igmp_group_reset_gmi(grp); + } + + /* scan group sources */ + for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, src_node, src)) { + + /* reset source timers for sources with running timers */ + if (src->t_source_timer) { + igmp_source_reset_gmi(igmp, grp, src); + } + } + } + } +} + +#define IGMP_QUERY_INTERVAL_MIN (1) +#define IGMP_QUERY_INTERVAL_MAX (1800) + +DEFUN (interface_ip_igmp_query_interval, + interface_ip_igmp_query_interval_cmd, + PIM_CMD_IP_IGMP_QUERY_INTERVAL " <1-1800>", + IP_STR + IFACE_IGMP_STR + IFACE_IGMP_QUERY_INTERVAL_STR + "Query interval in seconds\n") +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + int query_interval; + int query_interval_dsec; + + ifp = vty->index; + pim_ifp = ifp->info; + + if (!pim_ifp) { + vty_out(vty, + "IGMP not enabled on interface %s. Please enable IGMP first.%s", + ifp->name, + VTY_NEWLINE); + return CMD_WARNING; + } + + query_interval = atoi(argv[0]); + query_interval_dsec = 10 * query_interval; + + /* + It seems we don't need to check bounds since command.c does it + already, but we verify them anyway for extra safety. + */ + if (query_interval < IGMP_QUERY_INTERVAL_MIN) { + vty_out(vty, "General query interval %d lower than minimum %d%s", + query_interval, + IGMP_QUERY_INTERVAL_MIN, + VTY_NEWLINE); + return CMD_WARNING; + } + if (query_interval > IGMP_QUERY_INTERVAL_MAX) { + vty_out(vty, "General query interval %d higher than maximum %d%s", + query_interval, + IGMP_QUERY_INTERVAL_MAX, + VTY_NEWLINE); + return CMD_WARNING; + } + + if (query_interval_dsec <= pim_ifp->igmp_query_max_response_time_dsec) { + vty_out(vty, + "Can't set general query interval %d dsec <= query max response time %d dsec.%s", + query_interval_dsec, pim_ifp->igmp_query_max_response_time_dsec, + VTY_NEWLINE); + return CMD_WARNING; + } + + change_query_interval(pim_ifp, query_interval); + + return CMD_SUCCESS; +} + +DEFUN (interface_no_ip_igmp_query_interval, + interface_no_ip_igmp_query_interval_cmd, + PIM_CMD_NO " " PIM_CMD_IP_IGMP_QUERY_INTERVAL, + NO_STR + IP_STR + IFACE_IGMP_STR + IFACE_IGMP_QUERY_INTERVAL_STR) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + int default_query_interval_dsec; + + ifp = vty->index; + pim_ifp = ifp->info; + + if (!pim_ifp) + return CMD_SUCCESS; + + default_query_interval_dsec = IGMP_GENERAL_QUERY_INTERVAL * 10; + + if (default_query_interval_dsec <= pim_ifp->igmp_query_max_response_time_dsec) { + vty_out(vty, + "Can't set default general query interval %d dsec <= query max response time %d dsec.%s", + default_query_interval_dsec, pim_ifp->igmp_query_max_response_time_dsec, + VTY_NEWLINE); + return CMD_WARNING; + } + + change_query_interval(pim_ifp, IGMP_GENERAL_QUERY_INTERVAL); + + return CMD_SUCCESS; +} + +#define IGMP_QUERY_MAX_RESPONSE_TIME_MIN (1) +#define IGMP_QUERY_MAX_RESPONSE_TIME_MAX (25) + +DEFUN (interface_ip_igmp_query_max_response_time, + interface_ip_igmp_query_max_response_time_cmd, + PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME " <1-25>", + IP_STR + IFACE_IGMP_STR + IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_STR + "Query response value in seconds\n") +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + int query_max_response_time; + + ifp = vty->index; + pim_ifp = ifp->info; + + if (!pim_ifp) { + vty_out(vty, + "IGMP not enabled on interface %s. Please enable IGMP first.%s", + ifp->name, + VTY_NEWLINE); + return CMD_WARNING; + } + + query_max_response_time = atoi(argv[0]); + + /* + It seems we don't need to check bounds since command.c does it + already, but we verify them anyway for extra safety. + */ + if (query_max_response_time < IGMP_QUERY_MAX_RESPONSE_TIME_MIN) { + vty_out(vty, "Query max response time %d sec lower than minimum %d sec%s", + query_max_response_time, + IGMP_QUERY_MAX_RESPONSE_TIME_MIN, + VTY_NEWLINE); + return CMD_WARNING; + } + if (query_max_response_time > IGMP_QUERY_MAX_RESPONSE_TIME_MAX) { + vty_out(vty, "Query max response time %d sec higher than maximum %d sec%s", + query_max_response_time, + IGMP_QUERY_MAX_RESPONSE_TIME_MAX, + VTY_NEWLINE); + return CMD_WARNING; + } + + if (query_max_response_time >= pim_ifp->igmp_default_query_interval) { + vty_out(vty, + "Can't set query max response time %d sec >= general query interval %d sec%s", + query_max_response_time, pim_ifp->igmp_default_query_interval, + VTY_NEWLINE); + return CMD_WARNING; + } + + change_query_max_response_time(pim_ifp, 10 * query_max_response_time); + + return CMD_SUCCESS; +} + +DEFUN (interface_no_ip_igmp_query_max_response_time, + interface_no_ip_igmp_query_max_response_time_cmd, + PIM_CMD_NO " " PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME, + NO_STR + IP_STR + IFACE_IGMP_STR + IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_STR) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + int default_query_interval_dsec; + + ifp = vty->index; + pim_ifp = ifp->info; + + if (!pim_ifp) + return CMD_SUCCESS; + + default_query_interval_dsec = 10 * pim_ifp->igmp_default_query_interval; + + if (IGMP_QUERY_MAX_RESPONSE_TIME_DSEC >= default_query_interval_dsec) { + vty_out(vty, + "Can't set default query max response time %d dsec >= general query interval %d dsec.%s", + IGMP_QUERY_MAX_RESPONSE_TIME_DSEC, default_query_interval_dsec, + VTY_NEWLINE); + return CMD_WARNING; + } + + change_query_max_response_time(pim_ifp, IGMP_QUERY_MAX_RESPONSE_TIME_DSEC); + + return CMD_SUCCESS; +} + +#define IGMP_QUERY_MAX_RESPONSE_TIME_MIN_DSEC (10) +#define IGMP_QUERY_MAX_RESPONSE_TIME_MAX_DSEC (250) + +DEFUN (interface_ip_igmp_query_max_response_time_dsec, + interface_ip_igmp_query_max_response_time_dsec_cmd, + PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC " <10-250>", + IP_STR + IFACE_IGMP_STR + IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC_STR + "Query response value in deciseconds\n") +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + int query_max_response_time_dsec; + int default_query_interval_dsec; + + ifp = vty->index; + pim_ifp = ifp->info; + + if (!pim_ifp) { + vty_out(vty, + "IGMP not enabled on interface %s. Please enable IGMP first.%s", + ifp->name, + VTY_NEWLINE); + return CMD_WARNING; + } + + query_max_response_time_dsec = atoi(argv[0]); + + /* + It seems we don't need to check bounds since command.c does it + already, but we verify them anyway for extra safety. + */ + if (query_max_response_time_dsec < IGMP_QUERY_MAX_RESPONSE_TIME_MIN_DSEC) { + vty_out(vty, "Query max response time %d dsec lower than minimum %d dsec%s", + query_max_response_time_dsec, + IGMP_QUERY_MAX_RESPONSE_TIME_MIN_DSEC, + VTY_NEWLINE); + return CMD_WARNING; + } + if (query_max_response_time_dsec > IGMP_QUERY_MAX_RESPONSE_TIME_MAX_DSEC) { + vty_out(vty, "Query max response time %d dsec higher than maximum %d dsec%s", + query_max_response_time_dsec, + IGMP_QUERY_MAX_RESPONSE_TIME_MAX_DSEC, + VTY_NEWLINE); + return CMD_WARNING; + } + + default_query_interval_dsec = 10 * pim_ifp->igmp_default_query_interval; + + if (query_max_response_time_dsec >= default_query_interval_dsec) { + vty_out(vty, + "Can't set query max response time %d dsec >= general query interval %d dsec%s", + query_max_response_time_dsec, default_query_interval_dsec, + VTY_NEWLINE); + return CMD_WARNING; + } + + change_query_max_response_time(pim_ifp, query_max_response_time_dsec); + + return CMD_SUCCESS; +} + +DEFUN (interface_no_ip_igmp_query_max_response_time_dsec, + interface_no_ip_igmp_query_max_response_time_dsec_cmd, + PIM_CMD_NO " " PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC, + NO_STR + IP_STR + IFACE_IGMP_STR + IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC_STR) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + int default_query_interval_dsec; + + ifp = vty->index; + pim_ifp = ifp->info; + + if (!pim_ifp) + return CMD_SUCCESS; + + default_query_interval_dsec = 10 * pim_ifp->igmp_default_query_interval; + + if (IGMP_QUERY_MAX_RESPONSE_TIME_DSEC >= default_query_interval_dsec) { + vty_out(vty, + "Can't set default query max response time %d dsec >= general query interval %d dsec.%s", + IGMP_QUERY_MAX_RESPONSE_TIME_DSEC, default_query_interval_dsec, + VTY_NEWLINE); + return CMD_WARNING; + } + + change_query_max_response_time(pim_ifp, IGMP_QUERY_MAX_RESPONSE_TIME_DSEC); + + return CMD_SUCCESS; +} + +DEFUN (interface_ip_pim_ssm, + interface_ip_pim_ssm_cmd, + "ip pim ssm", + IP_STR + PIM_STR + IFACE_PIM_STR) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + + ifp = vty->index; + pim_ifp = ifp->info; + + if (!pim_ifp) { + pim_ifp = pim_if_new(ifp, 0 /* igmp=false */, 1 /* pim=true */); + if (!pim_ifp) { + vty_out(vty, "Could not enable PIM on interface%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + else { + PIM_IF_DO_PIM(pim_ifp->options); + } + + pim_if_addr_add_all(ifp); + pim_if_membership_refresh(ifp); + + return CMD_SUCCESS; +} + +DEFUN (interface_no_ip_pim_ssm, + interface_no_ip_pim_ssm_cmd, + "no ip pim ssm", + NO_STR + IP_STR + PIM_STR + IFACE_PIM_STR) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + + ifp = vty->index; + pim_ifp = ifp->info; + if (!pim_ifp) + return CMD_SUCCESS; + + PIM_IF_DONT_PIM(pim_ifp->options); + + pim_if_membership_clear(ifp); + + /* + pim_if_addr_del_all() removes all sockets from + pim_ifp->igmp_socket_list. + */ + pim_if_addr_del_all(ifp); + + /* + pim_sock_delete() removes all neighbors from + pim_ifp->pim_neighbor_list. + */ + pim_sock_delete(ifp, "pim unconfigured on interface"); + + if (!PIM_IF_TEST_IGMP(pim_ifp->options)) { + pim_if_delete(ifp); + } + + return CMD_SUCCESS; +} + +DEFUN (debug_igmp, + debug_igmp_cmd, + "debug igmp", + DEBUG_STR + DEBUG_IGMP_STR) +{ + PIM_DO_DEBUG_IGMP_EVENTS; + PIM_DO_DEBUG_IGMP_PACKETS; + PIM_DO_DEBUG_IGMP_TRACE; + return CMD_SUCCESS; +} + +DEFUN (no_debug_igmp, + no_debug_igmp_cmd, + "no debug igmp", + NO_STR + DEBUG_STR + DEBUG_IGMP_STR) +{ + PIM_DONT_DEBUG_IGMP_EVENTS; + PIM_DONT_DEBUG_IGMP_PACKETS; + PIM_DONT_DEBUG_IGMP_TRACE; + return CMD_SUCCESS; +} + +ALIAS (no_debug_igmp, + undebug_igmp_cmd, + "undebug igmp", + UNDEBUG_STR + DEBUG_IGMP_STR) + +DEFUN (debug_igmp_events, + debug_igmp_events_cmd, + "debug igmp events", + DEBUG_STR + DEBUG_IGMP_STR + DEBUG_IGMP_EVENTS_STR) +{ + PIM_DO_DEBUG_IGMP_EVENTS; + return CMD_SUCCESS; +} + +DEFUN (no_debug_igmp_events, + no_debug_igmp_events_cmd, + "no debug igmp events", + NO_STR + DEBUG_STR + DEBUG_IGMP_STR + DEBUG_IGMP_EVENTS_STR) +{ + PIM_DONT_DEBUG_IGMP_EVENTS; + return CMD_SUCCESS; +} + +ALIAS (no_debug_igmp_events, + undebug_igmp_events_cmd, + "undebug igmp events", + UNDEBUG_STR + DEBUG_IGMP_STR + DEBUG_IGMP_EVENTS_STR) + +DEFUN (debug_igmp_packets, + debug_igmp_packets_cmd, + "debug igmp packets", + DEBUG_STR + DEBUG_IGMP_STR + DEBUG_IGMP_PACKETS_STR) +{ + PIM_DO_DEBUG_IGMP_PACKETS; + return CMD_SUCCESS; +} + +DEFUN (no_debug_igmp_packets, + no_debug_igmp_packets_cmd, + "no debug igmp packets", + NO_STR + DEBUG_STR + DEBUG_IGMP_STR + DEBUG_IGMP_PACKETS_STR) +{ + PIM_DONT_DEBUG_IGMP_PACKETS; + return CMD_SUCCESS; +} + +ALIAS (no_debug_igmp_packets, + undebug_igmp_packets_cmd, + "undebug igmp packets", + UNDEBUG_STR + DEBUG_IGMP_STR + DEBUG_IGMP_PACKETS_STR) + +DEFUN (debug_igmp_trace, + debug_igmp_trace_cmd, + "debug igmp trace", + DEBUG_STR + DEBUG_IGMP_STR + DEBUG_IGMP_TRACE_STR) +{ + PIM_DO_DEBUG_IGMP_TRACE; + return CMD_SUCCESS; +} + +DEFUN (no_debug_igmp_trace, + no_debug_igmp_trace_cmd, + "no debug igmp trace", + NO_STR + DEBUG_STR + DEBUG_IGMP_STR + DEBUG_IGMP_TRACE_STR) +{ + PIM_DONT_DEBUG_IGMP_TRACE; + return CMD_SUCCESS; +} + +ALIAS (no_debug_igmp_trace, + undebug_igmp_trace_cmd, + "undebug igmp trace", + UNDEBUG_STR + DEBUG_IGMP_STR + DEBUG_IGMP_TRACE_STR) + +DEFUN (debug_pim, + debug_pim_cmd, + "debug pim", + DEBUG_STR + DEBUG_PIM_STR) +{ + PIM_DO_DEBUG_PIM_EVENTS; + PIM_DO_DEBUG_PIM_PACKETS; + PIM_DO_DEBUG_PIM_TRACE; + return CMD_SUCCESS; +} + +DEFUN (no_debug_pim, + no_debug_pim_cmd, + "no debug pim", + NO_STR + DEBUG_STR + DEBUG_PIM_STR) +{ + PIM_DONT_DEBUG_PIM_EVENTS; + PIM_DONT_DEBUG_PIM_PACKETS; + PIM_DONT_DEBUG_PIM_TRACE; + return CMD_SUCCESS; +} + +ALIAS (no_debug_pim, + undebug_pim_cmd, + "undebug pim", + UNDEBUG_STR + DEBUG_PIM_STR) + +DEFUN (debug_pim_events, + debug_pim_events_cmd, + "debug pim events", + DEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_EVENTS_STR) +{ + PIM_DO_DEBUG_PIM_EVENTS; + return CMD_SUCCESS; +} + +DEFUN (no_debug_pim_events, + no_debug_pim_events_cmd, + "no debug pim events", + NO_STR + DEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_EVENTS_STR) +{ + PIM_DONT_DEBUG_PIM_EVENTS; + return CMD_SUCCESS; +} + +ALIAS (no_debug_pim_events, + undebug_pim_events_cmd, + "undebug pim events", + UNDEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_EVENTS_STR) + +DEFUN (debug_pim_packets, + debug_pim_packets_cmd, + "debug pim packets", + DEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_PACKETS_STR) +{ + PIM_DO_DEBUG_PIM_PACKETS; + return CMD_SUCCESS; +} + +DEFUN (no_debug_pim_packets, + no_debug_pim_packets_cmd, + "no debug pim packets", + NO_STR + DEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_PACKETS_STR) +{ + PIM_DONT_DEBUG_PIM_PACKETS; + return CMD_SUCCESS; +} + +ALIAS (no_debug_pim_packets, + undebug_pim_packets_cmd, + "undebug pim packets", + UNDEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_PACKETS_STR) + +DEFUN (debug_pim_trace, + debug_pim_trace_cmd, + "debug pim trace", + DEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_TRACE_STR) +{ + PIM_DO_DEBUG_PIM_TRACE; + return CMD_SUCCESS; +} + +DEFUN (no_debug_pim_trace, + no_debug_pim_trace_cmd, + "no debug pim trace", + NO_STR + DEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_TRACE_STR) +{ + PIM_DONT_DEBUG_PIM_TRACE; + return CMD_SUCCESS; +} + +ALIAS (no_debug_pim_trace, + undebug_pim_trace_cmd, + "undebug pim trace", + UNDEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_TRACE_STR) + +DEFUN (debug_pim_zebra, + debug_pim_zebra_cmd, + "debug pim zebra", + DEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_ZEBRA_STR) +{ + PIM_DO_DEBUG_ZEBRA; + return CMD_SUCCESS; +} + +DEFUN (no_debug_pim_zebra, + no_debug_pim_zebra_cmd, + "no debug pim zebra", + NO_STR + DEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_ZEBRA_STR) +{ + PIM_DONT_DEBUG_ZEBRA; + return CMD_SUCCESS; +} + +ALIAS (no_debug_pim_zebra, + undebug_pim_zebra_cmd, + "undebug pim zebra", + UNDEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_ZEBRA_STR) + +DEFUN (show_debugging, + show_debugging_cmd, + "show debugging", + SHOW_STR + "State of each debugging option\n") +{ + pim_debug_config_write(vty); + return CMD_SUCCESS; +} + +static struct igmp_sock *find_igmp_sock_by_fd(int fd) +{ + struct listnode *ifnode; + struct interface *ifp; + + /* scan all interfaces */ + for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) { + struct pim_interface *pim_ifp; + struct igmp_sock *igmp; + + if (!ifp->info) + continue; + + pim_ifp = ifp->info; + + /* lookup igmp socket under current interface */ + igmp = igmp_sock_lookup_by_fd(pim_ifp->igmp_socket_list, fd); + if (igmp) + return igmp; + } + + return 0; +} + +DEFUN (test_igmp_receive_report, + test_igmp_receive_report_cmd, + "test igmp receive report <0-65535> A.B.C.D <1-6> .LINE", + "Test\n" + "Test IGMP protocol\n" + "Test IGMP message\n" + "Test IGMP report\n" + "Socket\n" + "IGMP group address\n" + "Record type\n" + "Sources\n") +{ + char buf[1000]; + char *igmp_msg; + struct ip *ip_hdr; + size_t ip_hlen; /* ip header length in bytes */ + int ip_msg_len; + int igmp_msg_len; + const char *socket; + int socket_fd; + const char *grp_str; + struct in_addr grp_addr; + const char *record_type_str; + int record_type; + const char *src_str; + int result; + struct igmp_sock *igmp; + char *group_record; + int num_sources; + struct in_addr *sources; + struct in_addr *src_addr; + int argi; + + socket = argv[0]; + socket_fd = atoi(socket); + igmp = find_igmp_sock_by_fd(socket_fd); + if (!igmp) { + vty_out(vty, "Could not find IGMP socket %s: fd=%d%s", + socket, socket_fd, VTY_NEWLINE); + return CMD_WARNING; + } + + grp_str = argv[1]; + result = inet_pton(AF_INET, grp_str, &grp_addr); + if (result <= 0) { + vty_out(vty, "Bad group address %s: errno=%d: %s%s", + grp_str, errno, strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + record_type_str = argv[2]; + record_type = atoi(record_type_str); + + /* + Tweak IP header + */ + ip_hdr = (struct ip *) buf; + ip_hdr->ip_p = PIM_IP_PROTO_IGMP; + ip_hlen = PIM_IP_HEADER_MIN_LEN; /* ip header length in bytes */ + ip_hdr->ip_hl = ip_hlen >> 2; /* ip header length in 4-byte words */ + ip_hdr->ip_src = igmp->ifaddr; + ip_hdr->ip_dst = igmp->ifaddr; + + /* + Build IGMP v3 report message + */ + igmp_msg = buf + ip_hlen; + group_record = igmp_msg + IGMP_V3_REPORT_GROUPPRECORD_OFFSET; + *igmp_msg = PIM_IGMP_V3_MEMBERSHIP_REPORT; /* type */ + *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = 0; /* for computing checksum */ + *(uint16_t *) (igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET) = htons(1); /* one group record */ + *(uint8_t *) (group_record + IGMP_V3_GROUP_RECORD_TYPE_OFFSET) = record_type; + *(struct in_addr *)(group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET) = grp_addr; + + /* Scan LINE sources */ + sources = (struct in_addr *) (group_record + IGMP_V3_GROUP_RECORD_SOURCE_OFFSET); + src_addr = sources; + for (argi = 3; argi < argc; ++argi,++src_addr) { + src_str = argv[argi]; + result = inet_pton(AF_INET, src_str, src_addr); + if (result <= 0) { + vty_out(vty, "Bad source address %s: errno=%d: %s%s", + src_str, errno, strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + } + num_sources = src_addr - sources; + + *(uint16_t *)(group_record + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET) = htons(num_sources); + + igmp_msg_len = IGMP_V3_MSG_MIN_SIZE + (num_sources << 4); /* v3 report for one single group record */ + + /* compute checksum */ + *(uint16_t *)(igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = pim_inet_checksum(igmp_msg, igmp_msg_len); + + /* "receive" message */ + + ip_msg_len = ip_hlen + igmp_msg_len; + result = pim_igmp_packet(igmp, buf, ip_msg_len); + if (result) { + vty_out(vty, "pim_igmp_packet(len=%d) returned: %d%s", + ip_msg_len, result, VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN (test_pim_receive_hello, + test_pim_receive_hello_cmd, + "test pim receive hello INTERFACE A.B.C.D <0-65535> <0-65535> <0-65535> <0-32767> <0-65535> <0-1>[LINE]", + "Test\n" + "Test PIM protocol\n" + "Test PIM message reception\n" + "Test PIM hello reception from neighbor\n" + "Interface\n" + "Neighbor address\n" + "Neighbor holdtime\n" + "Neighbor DR priority\n" + "Neighbor generation ID\n" + "Neighbor propagation delay (msec)\n" + "Neighbor override interval (msec)\n" + "Neighbor LAN prune delay T-bit\n" + "Neighbor secondary addresses\n") +{ + char buf[1000]; + char *pim_msg; + struct ip *ip_hdr; + size_t ip_hlen; /* ip header length in bytes */ + int ip_msg_len; + int pim_tlv_size; + int pim_msg_size; + const char *neigh_str; + struct in_addr neigh_addr; + const char *ifname; + struct interface *ifp; + uint16_t neigh_holdtime; + uint16_t neigh_propagation_delay; + uint16_t neigh_override_interval; + int neigh_can_disable_join_suppression; + uint32_t neigh_dr_priority; + uint32_t neigh_generation_id; + int argi; + int result; + + /* Find interface */ + ifname = argv[0]; + ifp = if_lookup_by_name(ifname); + if (!ifp) { + vty_out(vty, "No such interface name %s%s", + ifname, VTY_NEWLINE); + return CMD_WARNING; + } + + /* Neighbor address */ + neigh_str = argv[1]; + result = inet_pton(AF_INET, neigh_str, &neigh_addr); + if (result <= 0) { + vty_out(vty, "Bad neighbor address %s: errno=%d: %s%s", + neigh_str, errno, strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + neigh_holdtime = atoi(argv[2]); + neigh_dr_priority = atoi(argv[3]); + neigh_generation_id = atoi(argv[4]); + neigh_propagation_delay = atoi(argv[5]); + neigh_override_interval = atoi(argv[6]); + neigh_can_disable_join_suppression = atoi(argv[7]); + + /* + Tweak IP header + */ + ip_hdr = (struct ip *) buf; + ip_hdr->ip_p = PIM_IP_PROTO_PIM; + ip_hlen = PIM_IP_HEADER_MIN_LEN; /* ip header length in bytes */ + ip_hdr->ip_hl = ip_hlen >> 2; /* ip header length in 4-byte words */ + ip_hdr->ip_src = neigh_addr; + ip_hdr->ip_dst = qpim_all_pim_routers_addr; + + /* + Build PIM hello message + */ + pim_msg = buf + ip_hlen; + + /* Scan LINE addresses */ + for (argi = 8; argi < argc; ++argi) { + const char *sec_str = argv[argi]; + struct in_addr sec_addr; + result = inet_pton(AF_INET, sec_str, &sec_addr); + if (result <= 0) { + vty_out(vty, "Bad neighbor secondary address %s: errno=%d: %s%s", + sec_str, errno, strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + vty_out(vty, + "FIXME WRITEME consider neighbor secondary address %s%s", + sec_str, VTY_NEWLINE); + } + + pim_tlv_size = pim_hello_build_tlv(ifp->name, + pim_msg + PIM_PIM_MIN_LEN, + sizeof(buf) - ip_hlen - PIM_PIM_MIN_LEN, + neigh_holdtime, + neigh_dr_priority, + neigh_generation_id, + neigh_propagation_delay, + neigh_override_interval, + neigh_can_disable_join_suppression, + 0 /* FIXME secondary address list */); + if (pim_tlv_size < 0) { + vty_out(vty, "pim_hello_build_tlv() returned failure: %d%s", + pim_tlv_size, VTY_NEWLINE); + return CMD_WARNING; + } + + pim_msg_size = pim_tlv_size + PIM_PIM_MIN_LEN; + + pim_msg_build_header(pim_msg, pim_msg_size, + PIM_MSG_TYPE_HELLO); + + /* "receive" message */ + + ip_msg_len = ip_hlen + pim_msg_size; + result = pim_pim_packet(ifp, buf, ip_msg_len); + if (result) { + vty_out(vty, "pim_pim_packet(len=%d) returned failure: %d%s", + ip_msg_len, result, VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN (test_pim_receive_assert, + test_pim_receive_assert_cmd, + "test pim receive assert INTERFACE A.B.C.D A.B.C.D A.B.C.D <0-65535> <0-65535> <0-1>", + "Test\n" + "Test PIM protocol\n" + "Test PIM message reception\n" + "Test reception of PIM assert\n" + "Interface\n" + "Neighbor address\n" + "Assert multicast group address\n" + "Assert unicast source address\n" + "Assert metric preference\n" + "Assert route metric\n" + "Assert RPT bit flag\n") +{ + char buf[1000]; + char *buf_pastend = buf + sizeof(buf); + char *pim_msg; + struct ip *ip_hdr; + size_t ip_hlen; /* ip header length in bytes */ + int ip_msg_len; + int pim_msg_size; + const char *neigh_str; + struct in_addr neigh_addr; + const char *group_str; + struct in_addr group_addr; + const char *source_str; + struct in_addr source_addr; + const char *ifname; + struct interface *ifp; + uint32_t assert_metric_preference; + uint32_t assert_route_metric; + uint32_t assert_rpt_bit_flag; + int remain; + int result; + + /* Find interface */ + ifname = argv[0]; + ifp = if_lookup_by_name(ifname); + if (!ifp) { + vty_out(vty, "No such interface name %s%s", + ifname, VTY_NEWLINE); + return CMD_WARNING; + } + + /* Neighbor address */ + neigh_str = argv[1]; + result = inet_pton(AF_INET, neigh_str, &neigh_addr); + if (result <= 0) { + vty_out(vty, "Bad neighbor address %s: errno=%d: %s%s", + neigh_str, errno, strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + /* Group address */ + group_str = argv[2]; + result = inet_pton(AF_INET, group_str, &group_addr); + if (result <= 0) { + vty_out(vty, "Bad group address %s: errno=%d: %s%s", + group_str, errno, strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + /* Source address */ + source_str = argv[3]; + result = inet_pton(AF_INET, source_str, &source_addr); + if (result <= 0) { + vty_out(vty, "Bad source address %s: errno=%d: %s%s", + source_str, errno, strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + assert_metric_preference = atoi(argv[4]); + assert_route_metric = atoi(argv[5]); + assert_rpt_bit_flag = atoi(argv[6]); + + remain = buf_pastend - buf; + if (remain < (int) sizeof(struct ip)) { + vty_out(vty, "No room for ip header: buf_size=%d < ip_header_size=%d%s", + remain, sizeof(struct ip), VTY_NEWLINE); + return CMD_WARNING; + } + + /* + Tweak IP header + */ + ip_hdr = (struct ip *) buf; + ip_hdr->ip_p = PIM_IP_PROTO_PIM; + ip_hlen = PIM_IP_HEADER_MIN_LEN; /* ip header length in bytes */ + ip_hdr->ip_hl = ip_hlen >> 2; /* ip header length in 4-byte words */ + ip_hdr->ip_src = neigh_addr; + ip_hdr->ip_dst = qpim_all_pim_routers_addr; + + /* + Build PIM assert message + */ + pim_msg = buf + ip_hlen; /* skip ip header */ + + pim_msg_size = pim_assert_build_msg(pim_msg, buf_pastend - pim_msg, ifp, + group_addr, source_addr, + assert_metric_preference, + assert_route_metric, + assert_rpt_bit_flag); + if (pim_msg_size < 0) { + vty_out(vty, "Failure building PIM assert message: size=%d%s", + pim_msg_size, VTY_NEWLINE); + return CMD_WARNING; + } + + /* "receive" message */ + + ip_msg_len = ip_hlen + pim_msg_size; + result = pim_pim_packet(ifp, buf, ip_msg_len); + if (result) { + vty_out(vty, "pim_pim_packet(len=%d) returned failure: %d%s", + ip_msg_len, result, VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +static int recv_joinprune(struct vty *vty, + const char *argv[], + int src_is_join) +{ + char buf[1000]; + const char *buf_pastend = buf + sizeof(buf); + char *pim_msg; + char *pim_msg_curr; + int pim_msg_size; + struct ip *ip_hdr; + size_t ip_hlen; /* ip header length in bytes */ + int ip_msg_len; + uint16_t neigh_holdtime; + const char *neigh_dst_str; + struct in_addr neigh_dst_addr; + const char *neigh_src_str; + struct in_addr neigh_src_addr; + const char *group_str; + struct in_addr group_addr; + const char *source_str; + struct in_addr source_addr; + const char *ifname; + struct interface *ifp; + int result; + int remain; + uint16_t num_joined; + uint16_t num_pruned; + + /* Find interface */ + ifname = argv[0]; + ifp = if_lookup_by_name(ifname); + if (!ifp) { + vty_out(vty, "No such interface name %s%s", + ifname, VTY_NEWLINE); + return CMD_WARNING; + } + + neigh_holdtime = atoi(argv[1]); + + /* Neighbor destination address */ + neigh_dst_str = argv[2]; + result = inet_pton(AF_INET, neigh_dst_str, &neigh_dst_addr); + if (result <= 0) { + vty_out(vty, "Bad neighbor destination address %s: errno=%d: %s%s", + neigh_dst_str, errno, strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + /* Neighbor source address */ + neigh_src_str = argv[3]; + result = inet_pton(AF_INET, neigh_src_str, &neigh_src_addr); + if (result <= 0) { + vty_out(vty, "Bad neighbor source address %s: errno=%d: %s%s", + neigh_src_str, errno, strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + /* Multicast group address */ + group_str = argv[4]; + result = inet_pton(AF_INET, group_str, &group_addr); + if (result <= 0) { + vty_out(vty, "Bad group address %s: errno=%d: %s%s", + group_str, errno, strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + /* Multicast source address */ + source_str = argv[5]; + result = inet_pton(AF_INET, source_str, &source_addr); + if (result <= 0) { + vty_out(vty, "Bad source address %s: errno=%d: %s%s", + source_str, errno, strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + /* + Tweak IP header + */ + ip_hdr = (struct ip *) buf; + ip_hdr->ip_p = PIM_IP_PROTO_PIM; + ip_hlen = PIM_IP_HEADER_MIN_LEN; /* ip header length in bytes */ + ip_hdr->ip_hl = ip_hlen >> 2; /* ip header length in 4-byte words */ + ip_hdr->ip_src = neigh_src_addr; + ip_hdr->ip_dst = qpim_all_pim_routers_addr; + + /* + Build PIM message + */ + pim_msg = buf + ip_hlen; + + /* skip room for pim header */ + pim_msg_curr = pim_msg + PIM_MSG_HEADER_LEN; + + remain = buf_pastend - pim_msg_curr; + pim_msg_curr = pim_msg_addr_encode_ipv4_ucast(pim_msg_curr, + remain, + neigh_dst_addr); + if (!pim_msg_curr) { + vty_out(vty, "Failure encoding destination address %s: space left=%d%s", + neigh_dst_str, remain, VTY_NEWLINE); + return CMD_WARNING; + } + + remain = buf_pastend - pim_msg_curr; + if (remain < 4) { + vty_out(vty, "Group will not fit: space left=%d%s", + remain, VTY_NEWLINE); + return CMD_WARNING; + } + + *pim_msg_curr = 0; /* reserved */ + ++pim_msg_curr; + *pim_msg_curr = 1; /* number of groups */ + ++pim_msg_curr; + *((uint16_t *) pim_msg_curr) = htons(neigh_holdtime); + ++pim_msg_curr; + ++pim_msg_curr; + + remain = buf_pastend - pim_msg_curr; + pim_msg_curr = pim_msg_addr_encode_ipv4_group(pim_msg_curr, + remain, + group_addr); + if (!pim_msg_curr) { + vty_out(vty, "Failure encoding group address %s: space left=%d%s", + group_str, remain, VTY_NEWLINE); + return CMD_WARNING; + } + + remain = buf_pastend - pim_msg_curr; + if (remain < 4) { + vty_out(vty, "Sources will not fit: space left=%d%s", + remain, VTY_NEWLINE); + return CMD_WARNING; + } + + if (src_is_join) { + num_joined = 1; + num_pruned = 0; + } + else { + num_joined = 0; + num_pruned = 1; + } + + /* number of joined sources */ + *((uint16_t *) pim_msg_curr) = htons(num_joined); + ++pim_msg_curr; + ++pim_msg_curr; + + /* number of pruned sources */ + *((uint16_t *) pim_msg_curr) = htons(num_pruned); + ++pim_msg_curr; + ++pim_msg_curr; + + remain = buf_pastend - pim_msg_curr; + pim_msg_curr = pim_msg_addr_encode_ipv4_source(pim_msg_curr, + remain, + source_addr); + if (!pim_msg_curr) { + vty_out(vty, "Failure encoding source address %s: space left=%d%s", + source_str, remain, VTY_NEWLINE); + return CMD_WARNING; + } + + /* Add PIM header */ + + pim_msg_size = pim_msg_curr - pim_msg; + + pim_msg_build_header(pim_msg, pim_msg_size, + PIM_MSG_TYPE_JOIN_PRUNE); + + /* + "Receive" message + */ + + ip_msg_len = ip_hlen + pim_msg_size; + result = pim_pim_packet(ifp, buf, ip_msg_len); + if (result) { + vty_out(vty, "pim_pim_packet(len=%d) returned failure: %d%s", + ip_msg_len, result, VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN (test_pim_receive_join, + test_pim_receive_join_cmd, + "test pim receive join INTERFACE <0-65535> A.B.C.D A.B.C.D A.B.C.D A.B.C.D", + "Test\n" + "Test PIM protocol\n" + "Test PIM message reception\n" + "Test PIM join reception from neighbor\n" + "Interface\n" + "Neighbor holdtime\n" + "Upstream neighbor unicast destination address\n" + "Downstream neighbor unicast source address\n" + "Multicast group address\n" + "Unicast source address\n") +{ + return recv_joinprune(vty, argv, 1 /* src_is_join=true */); +} + +DEFUN (test_pim_receive_prune, + test_pim_receive_prune_cmd, + "test pim receive prune INTERFACE <0-65535> A.B.C.D A.B.C.D A.B.C.D A.B.C.D", + "Test\n" + "Test PIM protocol\n" + "Test PIM message reception\n" + "Test PIM prune reception from neighbor\n" + "Interface\n" + "Neighbor holdtime\n" + "Upstream neighbor unicast destination address\n" + "Downstream neighbor unicast source address\n" + "Multicast group address\n" + "Unicast source address\n") +{ + return recv_joinprune(vty, argv, 0 /* src_is_join=false */); +} + +DEFUN (test_pim_receive_upcall, + test_pim_receive_upcall_cmd, + "test pim receive upcall (nocache|wrongvif|wholepkt) <0-65535> A.B.C.D A.B.C.D", + "Test\n" + "Test PIM protocol\n" + "Test PIM message reception\n" + "Test reception of kernel upcall\n" + "NOCACHE kernel upcall\n" + "WRONGVIF kernel upcall\n" + "WHOLEPKT kernel upcall\n" + "Input interface vif index\n" + "Multicast group address\n" + "Multicast source address\n") +{ + struct igmpmsg msg; + const char *upcall_type; + const char *group_str; + const char *source_str; + int result; + + upcall_type = argv[0]; + + if (upcall_type[0] == 'n') + msg.im_msgtype = IGMPMSG_NOCACHE; + else if (upcall_type[1] == 'r') + msg.im_msgtype = IGMPMSG_WRONGVIF; + else if (upcall_type[1] == 'h') + msg.im_msgtype = IGMPMSG_WHOLEPKT; + else { + vty_out(vty, "Unknown kernel upcall type: %s%s", + upcall_type, VTY_NEWLINE); + return CMD_WARNING; + } + + msg.im_vif = atoi(argv[1]); + + /* Group address */ + group_str = argv[2]; + result = inet_pton(AF_INET, group_str, &msg.im_dst); + if (result <= 0) { + vty_out(vty, "Bad group address %s: errno=%d: %s%s", + group_str, errno, strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + /* Source address */ + source_str = argv[3]; + result = inet_pton(AF_INET, source_str, &msg.im_src); + if (result <= 0) { + vty_out(vty, "Bad source address %s: errno=%d: %s%s", + source_str, errno, strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + msg.im_mbz = 0; /* Must be zero */ + + result = pim_mroute_msg(-1, (char *) &msg, sizeof(msg)); + if (result) { + vty_out(vty, "pim_mroute_msg(len=%d) returned failure: %d%s", + sizeof(msg), result, VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +void pim_cmd_init() +{ + install_node(&pim_global_node, pim_global_config_write); /* PIM_NODE */ + install_node(&pim_interface_node, pim_interface_config_write); /* INTERFACE_NODE */ + + install_element(CONFIG_NODE, &ip_multicast_routing_cmd); + install_element(CONFIG_NODE, &no_ip_multicast_routing_cmd); +#if 0 + install_element(CONFIG_NODE, &interface_cmd); /* from if.h */ +#else + install_element(CONFIG_NODE, &pim_interface_cmd); +#endif + install_element(CONFIG_NODE, &no_interface_cmd); /* from if.h */ + + install_default(INTERFACE_NODE); + install_element(INTERFACE_NODE, &interface_ip_igmp_cmd); + install_element(INTERFACE_NODE, &interface_no_ip_igmp_cmd); + install_element(INTERFACE_NODE, &interface_ip_igmp_join_cmd); + install_element(INTERFACE_NODE, &interface_no_ip_igmp_join_cmd); + install_element(INTERFACE_NODE, &interface_ip_igmp_query_interval_cmd); + install_element(INTERFACE_NODE, &interface_no_ip_igmp_query_interval_cmd); + install_element(INTERFACE_NODE, &interface_ip_igmp_query_max_response_time_cmd); + install_element(INTERFACE_NODE, &interface_no_ip_igmp_query_max_response_time_cmd); + install_element(INTERFACE_NODE, &interface_ip_igmp_query_max_response_time_dsec_cmd); + install_element(INTERFACE_NODE, &interface_no_ip_igmp_query_max_response_time_dsec_cmd); + install_element(INTERFACE_NODE, &interface_ip_pim_ssm_cmd); + install_element(INTERFACE_NODE, &interface_no_ip_pim_ssm_cmd); + + install_element(VIEW_NODE, &show_ip_igmp_interface_cmd); + install_element(VIEW_NODE, &show_ip_igmp_parameters_cmd); + install_element(VIEW_NODE, &show_ip_igmp_groups_cmd); + install_element(VIEW_NODE, &show_ip_igmp_groups_retransmissions_cmd); + install_element(VIEW_NODE, &show_ip_igmp_sources_cmd); + install_element(VIEW_NODE, &show_ip_igmp_sources_retransmissions_cmd); + install_element(VIEW_NODE, &show_ip_igmp_querier_cmd); + install_element(VIEW_NODE, &show_ip_pim_assert_cmd); + install_element(VIEW_NODE, &show_ip_pim_assert_internal_cmd); + install_element(VIEW_NODE, &show_ip_pim_assert_metric_cmd); + install_element(VIEW_NODE, &show_ip_pim_assert_winner_metric_cmd); + install_element(VIEW_NODE, &show_ip_pim_dr_cmd); + install_element(VIEW_NODE, &show_ip_pim_hello_cmd); + install_element(VIEW_NODE, &show_ip_pim_interface_cmd); + install_element(VIEW_NODE, &show_ip_pim_join_cmd); + install_element(VIEW_NODE, &show_ip_pim_jp_override_interval_cmd); + install_element(VIEW_NODE, &show_ip_pim_lan_prune_delay_cmd); + install_element(VIEW_NODE, &show_ip_pim_local_membership_cmd); + install_element(VIEW_NODE, &show_ip_pim_neighbor_cmd); + install_element(VIEW_NODE, &show_ip_pim_rpf_cmd); + install_element(VIEW_NODE, &show_ip_pim_secondary_cmd); + install_element(VIEW_NODE, &show_ip_pim_upstream_cmd); + install_element(VIEW_NODE, &show_ip_pim_upstream_join_desired_cmd); + install_element(VIEW_NODE, &show_ip_pim_upstream_rpf_cmd); + install_element(VIEW_NODE, &show_ip_multicast_cmd); + install_element(VIEW_NODE, &show_ip_mroute_cmd); + install_element(VIEW_NODE, &show_ip_mroute_count_cmd); + install_element(VIEW_NODE, &show_ip_route_cmd); + install_element(VIEW_NODE, &show_debugging_cmd); + + install_element(ENABLE_NODE, &clear_ip_interfaces_cmd); + install_element(ENABLE_NODE, &clear_ip_igmp_interfaces_cmd); + install_element(ENABLE_NODE, &clear_ip_pim_interfaces_cmd); + + install_element(ENABLE_NODE, &show_ip_igmp_interface_cmd); + install_element(ENABLE_NODE, &show_ip_igmp_parameters_cmd); + install_element(ENABLE_NODE, &show_ip_igmp_groups_cmd); + install_element(ENABLE_NODE, &show_ip_igmp_groups_retransmissions_cmd); + install_element(ENABLE_NODE, &show_ip_igmp_sources_cmd); + install_element(ENABLE_NODE, &show_ip_igmp_sources_retransmissions_cmd); + install_element(ENABLE_NODE, &show_ip_igmp_querier_cmd); + install_element(ENABLE_NODE, &show_ip_pim_address_cmd); + install_element(ENABLE_NODE, &show_ip_pim_assert_cmd); + install_element(ENABLE_NODE, &show_ip_pim_assert_internal_cmd); + install_element(ENABLE_NODE, &show_ip_pim_assert_metric_cmd); + install_element(ENABLE_NODE, &show_ip_pim_assert_winner_metric_cmd); + install_element(ENABLE_NODE, &show_ip_pim_dr_cmd); + install_element(ENABLE_NODE, &show_ip_pim_hello_cmd); + install_element(ENABLE_NODE, &show_ip_pim_interface_cmd); + install_element(ENABLE_NODE, &show_ip_pim_join_cmd); + install_element(ENABLE_NODE, &show_ip_pim_jp_override_interval_cmd); + install_element(ENABLE_NODE, &show_ip_pim_lan_prune_delay_cmd); + install_element(ENABLE_NODE, &show_ip_pim_local_membership_cmd); + install_element(ENABLE_NODE, &show_ip_pim_neighbor_cmd); + install_element(ENABLE_NODE, &show_ip_pim_rpf_cmd); + install_element(ENABLE_NODE, &show_ip_pim_secondary_cmd); + install_element(ENABLE_NODE, &show_ip_pim_upstream_cmd); + install_element(ENABLE_NODE, &show_ip_pim_upstream_join_desired_cmd); + install_element(ENABLE_NODE, &show_ip_pim_upstream_rpf_cmd); + install_element(ENABLE_NODE, &show_ip_multicast_cmd); + install_element(ENABLE_NODE, &show_ip_mroute_cmd); + install_element(ENABLE_NODE, &show_ip_mroute_count_cmd); + install_element(ENABLE_NODE, &show_ip_route_cmd); + install_element(ENABLE_NODE, &show_debugging_cmd); + + install_element(ENABLE_NODE, &test_igmp_receive_report_cmd); + install_element(ENABLE_NODE, &test_pim_receive_assert_cmd); + install_element(ENABLE_NODE, &test_pim_receive_hello_cmd); + install_element(ENABLE_NODE, &test_pim_receive_join_cmd); + install_element(ENABLE_NODE, &test_pim_receive_prune_cmd); + install_element(ENABLE_NODE, &test_pim_receive_upcall_cmd); + + install_element(ENABLE_NODE, &debug_igmp_cmd); + install_element(ENABLE_NODE, &no_debug_igmp_cmd); + install_element(ENABLE_NODE, &undebug_igmp_cmd); + install_element(ENABLE_NODE, &debug_igmp_events_cmd); + install_element(ENABLE_NODE, &no_debug_igmp_events_cmd); + install_element(ENABLE_NODE, &undebug_igmp_events_cmd); + install_element(ENABLE_NODE, &debug_igmp_packets_cmd); + install_element(ENABLE_NODE, &no_debug_igmp_packets_cmd); + install_element(ENABLE_NODE, &undebug_igmp_packets_cmd); + install_element(ENABLE_NODE, &debug_igmp_trace_cmd); + install_element(ENABLE_NODE, &no_debug_igmp_trace_cmd); + install_element(ENABLE_NODE, &undebug_igmp_trace_cmd); + install_element(ENABLE_NODE, &debug_pim_cmd); + install_element(ENABLE_NODE, &no_debug_pim_cmd); + install_element(ENABLE_NODE, &undebug_pim_cmd); + install_element(ENABLE_NODE, &debug_pim_events_cmd); + install_element(ENABLE_NODE, &no_debug_pim_events_cmd); + install_element(ENABLE_NODE, &undebug_pim_events_cmd); + install_element(ENABLE_NODE, &debug_pim_packets_cmd); + install_element(ENABLE_NODE, &no_debug_pim_packets_cmd); + install_element(ENABLE_NODE, &undebug_pim_packets_cmd); + install_element(ENABLE_NODE, &debug_pim_trace_cmd); + install_element(ENABLE_NODE, &no_debug_pim_trace_cmd); + install_element(ENABLE_NODE, &undebug_pim_trace_cmd); + install_element(ENABLE_NODE, &debug_pim_zebra_cmd); + install_element(ENABLE_NODE, &no_debug_pim_zebra_cmd); + install_element(ENABLE_NODE, &undebug_pim_zebra_cmd); + + install_element(CONFIG_NODE, &debug_igmp_cmd); + install_element(CONFIG_NODE, &no_debug_igmp_cmd); + install_element(CONFIG_NODE, &undebug_igmp_cmd); + install_element(CONFIG_NODE, &debug_igmp_events_cmd); + install_element(CONFIG_NODE, &no_debug_igmp_events_cmd); + install_element(CONFIG_NODE, &undebug_igmp_events_cmd); + install_element(CONFIG_NODE, &debug_igmp_packets_cmd); + install_element(CONFIG_NODE, &no_debug_igmp_packets_cmd); + install_element(CONFIG_NODE, &undebug_igmp_packets_cmd); + install_element(CONFIG_NODE, &debug_igmp_trace_cmd); + install_element(CONFIG_NODE, &no_debug_igmp_trace_cmd); + install_element(CONFIG_NODE, &undebug_igmp_trace_cmd); + install_element(CONFIG_NODE, &debug_pim_cmd); + install_element(CONFIG_NODE, &no_debug_pim_cmd); + install_element(CONFIG_NODE, &undebug_pim_cmd); + install_element(CONFIG_NODE, &debug_pim_events_cmd); + install_element(CONFIG_NODE, &no_debug_pim_events_cmd); + install_element(CONFIG_NODE, &undebug_pim_events_cmd); + install_element(CONFIG_NODE, &debug_pim_packets_cmd); + install_element(CONFIG_NODE, &no_debug_pim_packets_cmd); + install_element(CONFIG_NODE, &undebug_pim_packets_cmd); + install_element(CONFIG_NODE, &debug_pim_trace_cmd); + install_element(CONFIG_NODE, &no_debug_pim_trace_cmd); + install_element(CONFIG_NODE, &undebug_pim_trace_cmd); + install_element(CONFIG_NODE, &debug_pim_zebra_cmd); + install_element(CONFIG_NODE, &no_debug_pim_zebra_cmd); + install_element(CONFIG_NODE, &undebug_pim_zebra_cmd); +} diff --git a/pimd/pim_cmd.h b/pimd/pim_cmd.h new file mode 100644 index 00000000..c2bb61bb --- /dev/null +++ b/pimd/pim_cmd.h @@ -0,0 +1,56 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_CMD_H +#define PIM_CMD_H + +#define PIM_STR "PIM information\n" +#define IGMP_STR "IGMP information\n" +#define IGMP_GROUP_STR "IGMP groups information\n" +#define IGMP_SOURCE_STR "IGMP sources information\n" +#define IFACE_PIM_STR "Enable PIM SSM operation\n" +#define IFACE_IGMP_STR "Enable IGMP operation\n" +#define IFACE_IGMP_QUERY_INTERVAL_STR "IGMP host query interval\n" +#define IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_STR "IGMP max query response value (seconds)\n" +#define IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC_STR "IGMP max query response value (deciseconds)\n" +#define DEBUG_IGMP_STR "IGMP protocol activity\n" +#define DEBUG_IGMP_EVENTS_STR "IGMP protocol events\n" +#define DEBUG_IGMP_PACKETS_STR "IGMP protocol packets\n" +#define DEBUG_IGMP_TRACE_STR "IGMP internal daemon activity\n" +#define DEBUG_PIM_STR "PIM protocol activity\n" +#define DEBUG_PIM_EVENTS_STR "PIM protocol events\n" +#define DEBUG_PIM_PACKETS_STR "PIM protocol packets\n" +#define DEBUG_PIM_TRACE_STR "PIM internal daemon activity\n" +#define DEBUG_PIM_ZEBRA_STR "ZEBRA protocol activity\n" +#define CLEAR_IP_IGMP_STR "IGMP clear commands\n" +#define CLEAR_IP_PIM_STR "PIM clear commands\n" +#define MROUTE_STR "IP multicast routing table\n" + +#define PIM_CMD_NO "no" +#define PIM_CMD_IP_MULTICAST_ROUTING "ip multicast-routing" +#define PIM_CMD_IP_IGMP_QUERY_INTERVAL "ip igmp query-interval" +#define PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME "ip igmp query-max-response-time" +#define PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC "ip igmp query-max-response-time-dsec" + +void pim_cmd_init(void); + +#endif /* PIM_CMD_H */ diff --git a/pimd/pim_hello.c b/pimd/pim_hello.c new file mode 100644 index 00000000..8b710b8c --- /dev/null +++ b/pimd/pim_hello.c @@ -0,0 +1,529 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "log.h" + +#include "pimd.h" +#include "pim_pim.h" +#include "pim_str.h" +#include "pim_tlv.h" +#include "pim_util.h" +#include "pim_hello.h" +#include "pim_iface.h" +#include "pim_neighbor.h" +#include "pim_upstream.h" + +static void on_trace(const char *label, + struct interface *ifp, struct in_addr src) +{ + if (PIM_DEBUG_PIM_TRACE) { + char src_str[100]; + pim_inet4_dump("", src, src_str, sizeof(src_str)); + zlog_debug("%s: from %s on %s", + label, src_str, ifp->name); + } +} + +static void tlv_trace_bool(const char *label, const char *tlv_name, + const char *ifname, struct in_addr src_addr, + int isset, int value) +{ + if (isset) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_debug("%s: PIM hello option from %s on interface %s: %s=%d", + label, + src_str, ifname, + tlv_name, value); + } +} + +static void tlv_trace_uint16(const char *label, const char *tlv_name, + const char *ifname, struct in_addr src_addr, + int isset, uint16_t value) +{ + if (isset) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_debug("%s: PIM hello option from %s on interface %s: %s=%u", + label, + src_str, ifname, + tlv_name, value); + } +} + +static void tlv_trace_uint32(const char *label, const char *tlv_name, + const char *ifname, struct in_addr src_addr, + int isset, uint32_t value) +{ + if (isset) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_debug("%s: PIM hello option from %s on interface %s: %s=%u", + label, + src_str, ifname, + tlv_name, value); + } +} + +static void tlv_trace_uint32_hex(const char *label, const char *tlv_name, + const char *ifname, struct in_addr src_addr, + int isset, uint32_t value) +{ + if (isset) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_debug("%s: PIM hello option from %s on interface %s: %s=%08x", + label, + src_str, ifname, + tlv_name, value); + } +} + +#if 0 +static void tlv_trace(const char *label, const char *tlv_name, + const char *ifname, struct in_addr src_addr, + int isset) +{ + if (isset) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_debug("%s: PIM hello option from %s on interface %s: %s", + label, + src_str, ifname, + tlv_name); + } +} +#endif + +static void tlv_trace_list(const char *label, const char *tlv_name, + const char *ifname, struct in_addr src_addr, + int isset, struct list *addr_list) +{ + if (isset) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_debug("%s: PIM hello option from %s on interface %s: %s size=%d list=%x", + label, + src_str, ifname, + tlv_name, + addr_list ? ((int) listcount(addr_list)) : -1, + (unsigned) addr_list); + } +} + +#define FREE_ADDR_LIST \ + if (hello_option_addr_list) { \ + list_delete(hello_option_addr_list); \ + } + +#define FREE_ADDR_LIST_THEN_RETURN(code) \ +{ \ + FREE_ADDR_LIST \ + return (code); \ +} + +int pim_hello_recv(struct interface *ifp, + struct in_addr src_addr, + char *tlv_buf, int tlv_buf_size) +{ + struct pim_interface *pim_ifp; + struct pim_neighbor *neigh; + char *tlv_curr; + char *tlv_pastend; + pim_hello_options hello_options = 0; /* bit array recording options found */ + uint16_t hello_option_holdtime = 0; + uint16_t hello_option_propagation_delay = 0; + uint16_t hello_option_override_interval = 0; + uint32_t hello_option_dr_priority = 0; + uint32_t hello_option_generation_id = 0; + struct list *hello_option_addr_list = 0; + + on_trace(__PRETTY_FUNCTION__, ifp, src_addr); + + pim_ifp = ifp->info; + zassert(pim_ifp); + + ++pim_ifp->pim_ifstat_hello_recv; + + /* + Parse PIM hello TLVs + */ + zassert(tlv_buf_size >= 0); + tlv_curr = tlv_buf; + tlv_pastend = tlv_buf + tlv_buf_size; + + while (tlv_curr < tlv_pastend) { + uint16_t option_type; + uint16_t option_len; + int remain = tlv_pastend - tlv_curr; + + if (remain < PIM_TLV_MIN_SIZE) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: short PIM hello TLV size=%d < min=%d from %s on interface %s", + __PRETTY_FUNCTION__, + remain, PIM_TLV_MIN_SIZE, + src_str, ifp->name); + FREE_ADDR_LIST_THEN_RETURN(-1); + } + + option_type = PIM_TLV_GET_TYPE(tlv_curr); + tlv_curr += PIM_TLV_TYPE_SIZE; + option_len = PIM_TLV_GET_LENGTH(tlv_curr); + tlv_curr += PIM_TLV_LENGTH_SIZE; + + if ((tlv_curr + option_len) > tlv_pastend) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: long PIM hello TLV type=%d length=%d > max=%d from %s on interface %s", + __PRETTY_FUNCTION__, + option_type, option_len, tlv_pastend - tlv_curr, + src_str, ifp->name); + FREE_ADDR_LIST_THEN_RETURN(-2); + } + + if (PIM_DEBUG_PIM_TRACE) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_debug("%s: parse left_size=%d: PIM hello TLV type=%d length=%d from %s on %s", + __PRETTY_FUNCTION__, + remain, + option_type, option_len, + src_str, ifp->name); + } + + switch (option_type) { + case PIM_MSG_OPTION_TYPE_HOLDTIME: + if (pim_tlv_parse_holdtime(ifp->name, src_addr, + &hello_options, + &hello_option_holdtime, + option_len, + tlv_curr)) { + FREE_ADDR_LIST_THEN_RETURN(-3); + } + break; + case PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY: + if (pim_tlv_parse_lan_prune_delay(ifp->name, src_addr, + &hello_options, + &hello_option_propagation_delay, + &hello_option_override_interval, + option_len, + tlv_curr)) { + FREE_ADDR_LIST_THEN_RETURN(-4); + } + break; + case PIM_MSG_OPTION_TYPE_DR_PRIORITY: + if (pim_tlv_parse_dr_priority(ifp->name, src_addr, + &hello_options, + &hello_option_dr_priority, + option_len, + tlv_curr)) { + FREE_ADDR_LIST_THEN_RETURN(-5); + } + break; + case PIM_MSG_OPTION_TYPE_GENERATION_ID: + if (pim_tlv_parse_generation_id(ifp->name, src_addr, + &hello_options, + &hello_option_generation_id, + option_len, + tlv_curr)) { + FREE_ADDR_LIST_THEN_RETURN(-6); + } + break; + case PIM_MSG_OPTION_TYPE_ADDRESS_LIST: + if (pim_tlv_parse_addr_list(ifp->name, src_addr, + &hello_options, + &hello_option_addr_list, + option_len, + tlv_curr)) { + return -7; + } + break; + case PIM_MSG_OPTION_TYPE_DM_STATE_REFRESH: + if (PIM_DEBUG_PIM_TRACE) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_debug("%s: ignoring PIM hello dense-mode state refresh TLV option type=%d length=%d from %s on interface %s", + __PRETTY_FUNCTION__, + option_type, option_len, + src_str, ifp->name); + } + break; + default: + { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: ignoring unknown PIM hello TLV type=%d length=%d from %s on interface %s", + __PRETTY_FUNCTION__, + option_type, option_len, + src_str, ifp->name); + } + } + + tlv_curr += option_len; + } + + /* + Check received PIM hello options + */ + + if (PIM_DEBUG_PIM_TRACE) { + tlv_trace_uint16(__PRETTY_FUNCTION__, "holdtime", + ifp->name, src_addr, + PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME), + hello_option_holdtime); + tlv_trace_uint16(__PRETTY_FUNCTION__, "propagation_delay", + ifp->name, src_addr, + PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY), + hello_option_propagation_delay); + tlv_trace_uint16(__PRETTY_FUNCTION__, "override_interval", + ifp->name, src_addr, + PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY), + hello_option_override_interval); + tlv_trace_bool(__PRETTY_FUNCTION__, "can_disable_join_suppression", + ifp->name, src_addr, + PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY), + PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION)); + tlv_trace_uint32(__PRETTY_FUNCTION__, "dr_priority", + ifp->name, src_addr, + PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_DR_PRIORITY), + hello_option_dr_priority); + tlv_trace_uint32_hex(__PRETTY_FUNCTION__, "generation_id", + ifp->name, src_addr, + PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_GENERATION_ID), + hello_option_generation_id); + tlv_trace_list(__PRETTY_FUNCTION__, "address_list", + ifp->name, src_addr, + PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_ADDRESS_LIST), + hello_option_addr_list); + } + + if (!PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME)) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: PIM hello missing holdtime from %s on interface %s", + __PRETTY_FUNCTION__, + src_str, ifp->name); + } + + /* + New neighbor? + */ + + neigh = pim_neighbor_find(ifp, src_addr); + if (!neigh) { + /* Add as new neighbor */ + + neigh = pim_neighbor_add(ifp, src_addr, + hello_options, + hello_option_holdtime, + hello_option_propagation_delay, + hello_option_override_interval, + hello_option_dr_priority, + hello_option_generation_id, + hello_option_addr_list); + if (!neigh) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: failure creating PIM neighbor %s on interface %s", + __PRETTY_FUNCTION__, + src_str, ifp->name); + FREE_ADDR_LIST_THEN_RETURN(-8); + } + + /* actual addr list has been saved under neighbor */ + return 0; + } + + /* + Received generation ID ? + */ + + if (PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_GENERATION_ID)) { + /* GenID mismatch ? */ + if (!PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_GENERATION_ID) || + (hello_option_generation_id != neigh->generation_id)) { + + /* GenID changed */ + + pim_upstream_rpf_genid_changed(neigh->source_addr); + + /* GenID mismatch, then replace neighbor */ + + if (PIM_DEBUG_PIM_TRACE) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_debug("%s: GenId mismatch new=%08x old=%08x: replacing neighbor %s on %s", + __PRETTY_FUNCTION__, + hello_option_generation_id, + neigh->generation_id, + src_str, ifp->name); + } + + pim_upstream_rpf_genid_changed(neigh->source_addr); + + pim_neighbor_delete(ifp, neigh, "GenID mismatch"); + neigh = pim_neighbor_add(ifp, src_addr, + hello_options, + hello_option_holdtime, + hello_option_propagation_delay, + hello_option_override_interval, + hello_option_dr_priority, + hello_option_generation_id, + hello_option_addr_list); + if (!neigh) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: failure re-creating PIM neighbor %s on interface %s", + __PRETTY_FUNCTION__, + src_str, ifp->name); + FREE_ADDR_LIST_THEN_RETURN(-9); + } + /* actual addr list is saved under neighbor */ + return 0; + + } /* GenId mismatch: replace neighbor */ + + } /* GenId received */ + + /* + Update existing neighbor + */ + + pim_neighbor_update(neigh, + hello_options, + hello_option_holdtime, + hello_option_dr_priority, + hello_option_addr_list); + /* actual addr list is saved under neighbor */ + return 0; +} + +int pim_hello_build_tlv(const char *ifname, + char *tlv_buf, int tlv_buf_size, + uint16_t holdtime, + uint32_t dr_priority, + uint32_t generation_id, + uint16_t propagation_delay, + uint16_t override_interval, + int can_disable_join_suppression, + struct list *ifconnected) +{ + char *curr = tlv_buf; + char *pastend = tlv_buf + tlv_buf_size; + char *tmp; + + /* + * Append options + */ + + /* Holdtime */ + curr = pim_tlv_append_uint16(curr, + pastend, + PIM_MSG_OPTION_TYPE_HOLDTIME, + holdtime); + if (!curr) { + zlog_warn("%s: could not set PIM hello Holdtime option for interface %s", + __PRETTY_FUNCTION__, ifname); + return -1; + } + + /* LAN Prune Delay */ + tmp = pim_tlv_append_2uint16(curr, + pastend, + PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY, + propagation_delay, + override_interval); + if (!tmp) { + zlog_warn("%s: could not set PIM LAN Prune Delay option for interface %s", + __PRETTY_FUNCTION__, ifname); + return -1; + } + if (can_disable_join_suppression) { + *((uint8_t*)(curr) + 4) |= 0x80; /* enable T bit */ + } + curr = tmp; + + /* DR Priority */ + curr = pim_tlv_append_uint32(curr, + pastend, + PIM_MSG_OPTION_TYPE_DR_PRIORITY, + dr_priority); + if (!curr) { + zlog_warn("%s: could not set PIM hello DR Priority option for interface %s", + __PRETTY_FUNCTION__, ifname); + return -2; + } + + /* Generation ID */ + curr = pim_tlv_append_uint32(curr, + pastend, + PIM_MSG_OPTION_TYPE_GENERATION_ID, + generation_id); + if (!curr) { + zlog_warn("%s: could not set PIM hello Generation ID option for interface %s", + __PRETTY_FUNCTION__, ifname); + return -3; + } + + /* Secondary Address List */ + if (ifconnected) { + curr = pim_tlv_append_addrlist_ucast(curr, + pastend, + ifconnected); + if (!curr) { + zlog_warn("%s: could not set PIM hello Secondary Address List option for interface %s", + __PRETTY_FUNCTION__, ifname); + return -4; + } + } + + return curr - tlv_buf; +} + +/* + RFC 4601: 4.3.1. Sending Hello Messages + + Thus, if a router needs to send a Join/Prune or Assert message on an + interface on which it has not yet sent a Hello message with the + currently configured IP address, then it MUST immediately send the + relevant Hello message without waiting for the Hello Timer to + expire, followed by the Join/Prune or Assert message. +*/ +void pim_hello_require(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + + zassert(ifp); + + pim_ifp = ifp->info; + + zassert(pim_ifp); + + if (pim_ifp->pim_ifstat_hello_sent) + return; + + pim_hello_restart_now(ifp); /* Send hello and restart timer */ +} diff --git a/pimd/pim_hello.h b/pimd/pim_hello.h new file mode 100644 index 00000000..90a11ba6 --- /dev/null +++ b/pimd/pim_hello.h @@ -0,0 +1,46 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_HELLO_H +#define PIM_HELLO_H + +#include + +#include "if.h" + +int pim_hello_recv(struct interface *ifp, + struct in_addr src_addr, + char *tlv_buf, int tlv_buf_size); + +int pim_hello_build_tlv(const char *ifname, + char *tlv_buf, int tlv_buf_size, + uint16_t holdtime, + uint32_t dr_priority, + uint32_t generation_id, + uint16_t propagation_delay, + uint16_t override_interval, + int can_disable_join_suppression, + struct list *ifconnected); + +void pim_hello_require(struct interface *ifp); + +#endif /* PIM_HELLO_H */ diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c new file mode 100644 index 00000000..b4fbaec5 --- /dev/null +++ b/pimd/pim_iface.c @@ -0,0 +1,1132 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "if.h" +#include "log.h" +#include "vty.h" +#include "memory.h" +#include "prefix.h" + +#include "pimd.h" +#include "pim_iface.h" +#include "pim_igmp.h" +#include "pim_mroute.h" +#include "pim_oil.h" +#include "pim_str.h" +#include "pim_pim.h" +#include "pim_neighbor.h" +#include "pim_ifchannel.h" +#include "pim_rand.h" +#include "pim_sock.h" + +static void pim_if_igmp_join_del_all(struct interface *ifp); + +void pim_if_init() +{ + if_init(); +} + +static void *if_list_clean(struct pim_interface *pim_ifp) +{ + if (pim_ifp->igmp_join_list) { + list_delete(pim_ifp->igmp_join_list); + } + + if (pim_ifp->igmp_socket_list) { + list_delete(pim_ifp->igmp_socket_list); + } + + if (pim_ifp->pim_neighbor_list) { + list_delete(pim_ifp->pim_neighbor_list); + } + + if (pim_ifp->pim_ifchannel_list) { + list_delete(pim_ifp->pim_ifchannel_list); + } + + XFREE(MTYPE_PIM_INTERFACE, pim_ifp); + + return 0; +} + +struct pim_interface *pim_if_new(struct interface *ifp, int igmp, int pim) +{ + struct pim_interface *pim_ifp; + + zassert(ifp); + zassert(!ifp->info); + + pim_ifp = XMALLOC(MTYPE_PIM_INTERFACE, sizeof(*pim_ifp)); + if (!pim_ifp) { + zlog_err("PIM XMALLOC(%d) failure", sizeof(*pim_ifp)); + return 0; + } + + pim_ifp->options = 0; + pim_ifp->mroute_vif_index = -1; + + pim_ifp->igmp_default_robustness_variable = IGMP_DEFAULT_ROBUSTNESS_VARIABLE; + pim_ifp->igmp_default_query_interval = IGMP_GENERAL_QUERY_INTERVAL; + pim_ifp->igmp_query_max_response_time_dsec = IGMP_QUERY_MAX_RESPONSE_TIME_DSEC; + + /* + RFC 3376: 8.3. Query Response Interval + The number of seconds represented by the [Query Response Interval] + must be less than the [Query Interval]. + */ + zassert(pim_ifp->igmp_query_max_response_time_dsec < pim_ifp->igmp_default_query_interval); + + if (pim) + PIM_IF_DO_PIM(pim_ifp->options); + if (igmp) + PIM_IF_DO_IGMP(pim_ifp->options); + +#if 0 + /* FIXME: Should join? */ + PIM_IF_DO_IGMP_LISTEN_ALLROUTERS(pim_ifp->options); +#endif + + pim_ifp->igmp_join_list = 0; + pim_ifp->igmp_socket_list = 0; + pim_ifp->pim_neighbor_list = 0; + pim_ifp->pim_ifchannel_list = 0; + + /* list of struct igmp_sock */ + pim_ifp->igmp_socket_list = list_new(); + if (!pim_ifp->igmp_socket_list) { + zlog_err("%s %s: failure: igmp_socket_list=list_new()", + __FILE__, __PRETTY_FUNCTION__); + return if_list_clean(pim_ifp); + } + pim_ifp->igmp_socket_list->del = (void (*)(void *)) igmp_sock_free; + + /* list of struct pim_neighbor */ + pim_ifp->pim_neighbor_list = list_new(); + if (!pim_ifp->pim_neighbor_list) { + zlog_err("%s %s: failure: pim_neighbor_list=list_new()", + __FILE__, __PRETTY_FUNCTION__); + return if_list_clean(pim_ifp); + } + pim_ifp->pim_neighbor_list->del = (void (*)(void *)) pim_neighbor_free; + + /* list of struct pim_ifchannel */ + pim_ifp->pim_ifchannel_list = list_new(); + if (!pim_ifp->pim_ifchannel_list) { + zlog_err("%s %s: failure: pim_ifchannel_list=list_new()", + __FILE__, __PRETTY_FUNCTION__); + return if_list_clean(pim_ifp); + } + pim_ifp->pim_ifchannel_list->del = (void (*)(void *)) pim_ifchannel_free; + + ifp->info = pim_ifp; + + pim_sock_reset(ifp); + + zassert(PIM_IF_TEST_PIM(pim_ifp->options) || PIM_IF_TEST_IGMP(pim_ifp->options)); + + if (PIM_MROUTE_IS_ENABLED) { + pim_if_add_vif(ifp); + } + + return pim_ifp; +} + +void pim_if_delete(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + + zassert(ifp); + pim_ifp = ifp->info; + zassert(pim_ifp); + + if (pim_ifp->igmp_join_list) { + pim_if_igmp_join_del_all(ifp); + } + zassert(!pim_ifp->igmp_join_list); + + zassert(pim_ifp->igmp_socket_list); + zassert(!listcount(pim_ifp->igmp_socket_list)); + + zassert(pim_ifp->pim_neighbor_list); + zassert(!listcount(pim_ifp->pim_neighbor_list)); + + zassert(pim_ifp->pim_ifchannel_list); + zassert(!listcount(pim_ifp->pim_ifchannel_list)); + + if (PIM_MROUTE_IS_ENABLED) { + pim_if_del_vif(ifp); + } + + list_delete(pim_ifp->igmp_socket_list); + list_delete(pim_ifp->pim_neighbor_list); + list_delete(pim_ifp->pim_ifchannel_list); + + XFREE(MTYPE_PIM_INTERFACE, pim_ifp); + + ifp->info = 0; +} + +void pim_if_update_could_assert(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + struct listnode *node; + struct listnode *next_node; + struct pim_ifchannel *ch; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, node, next_node, ch)) { + pim_ifchannel_update_could_assert(ch); + } +} + +static void pim_if_update_my_assert_metric(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + struct listnode *node; + struct listnode *next_node; + struct pim_ifchannel *ch; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, node, next_node, ch)) { + pim_ifchannel_update_my_assert_metric(ch); + } +} + +static void pim_addr_change(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + pim_if_dr_election(ifp); /* Done TODO T30 */ + pim_if_update_join_desired(pim_ifp); /* depends on DR */ + pim_if_update_could_assert(ifp); /* depends on DR */ + pim_if_update_my_assert_metric(ifp); /* depends on could_assert */ + pim_if_update_assert_tracking_desired(ifp); /* depends on DR, join_desired */ + + /* + RFC 4601: 4.3.1. Sending Hello Messages + + 1) Before an interface goes down or changes primary IP address, a + Hello message with a zero HoldTime should be sent immediately + (with the old IP address if the IP address changed). + -- FIXME See CAVEAT C13 + + 2) After an interface has changed its IP address, it MUST send a + Hello message with its new IP address. + -- DONE below + + 3) If an interface changes one of its secondary IP addresses, a + Hello message with an updated Address_List option and a non-zero + HoldTime should be sent immediately. + -- FIXME See TODO T31 + */ + pim_ifp->pim_ifstat_hello_sent = 0; /* reset hello counter */ + if (pim_ifp->pim_sock_fd < 0) + return; + pim_hello_restart_now(ifp); /* send hello and restart timer */ +} + +static void on_primary_address_change(struct interface *ifp, + const char *caller, + struct in_addr old_addr, + struct in_addr new_addr) +{ + struct pim_interface *pim_ifp; + + { + char old_str[100]; + char new_str[100]; + pim_inet4_dump("", old_addr, old_str, sizeof(old_str)); + pim_inet4_dump("", new_addr, new_str, sizeof(new_str)); + zlog_info("%s: %s: primary address changed from %s to %s on interface %s", + __PRETTY_FUNCTION__, caller, + old_str, new_str, ifp->name); + } + + pim_ifp = ifp->info; + + if (pim_ifp) { + if (PIM_IF_TEST_PIM(pim_ifp->options)) { + pim_addr_change(ifp); + } + } +} + +static void detect_primary_address_change(struct interface *ifp, + const char *caller) +{ + struct pim_interface *pim_ifp; + struct in_addr new_prim_addr; + + pim_ifp = ifp->info; + if (!pim_ifp) + return; + + new_prim_addr = pim_find_primary_addr(ifp); + + if (PIM_DEBUG_ZEBRA) { + char new_prim_str[100]; + char old_prim_str[100]; + pim_inet4_dump("", new_prim_addr, new_prim_str, sizeof(new_prim_str)); + pim_inet4_dump("", pim_ifp->primary_address, old_prim_str, sizeof(old_prim_str)); + zlog_debug("%s: old primary addr %s, new primary addr %s on interface %s", + __PRETTY_FUNCTION__, + old_prim_str, new_prim_str, ifp->name); + } + + if (new_prim_addr.s_addr != pim_ifp->primary_address.s_addr) { + struct in_addr old_addr = pim_ifp->primary_address; + pim_ifp->primary_address = new_prim_addr; + + on_primary_address_change(ifp, caller, old_addr, new_prim_addr); + } +} + +void pim_if_addr_add(struct connected *ifc) +{ + struct pim_interface *pim_ifp; + struct interface *ifp; + struct in_addr ifaddr; + + zassert(ifc); + + ifp = ifc->ifp; + zassert(ifp); + pim_ifp = ifp->info; + if (!pim_ifp) + return; + + if (!if_is_operative(ifp)) + return; + + ifaddr = ifc->address->u.prefix4; + + detect_primary_address_change(ifp, __PRETTY_FUNCTION__); + + if (PIM_IF_TEST_IGMP(pim_ifp->options)) { + struct igmp_sock *igmp; + + /* lookup IGMP socket */ + igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->igmp_socket_list, + ifaddr); + if (!igmp) { + /* if addr new, add IGMP socket */ + pim_igmp_sock_add(pim_ifp->igmp_socket_list, ifaddr, ifp); + } + } /* igmp */ + + if (PIM_IF_TEST_PIM(pim_ifp->options)) { + + /* Interface has a valid primary address ? */ + if (PIM_INADDR_ISNOT_ANY(pim_ifp->primary_address)) { + + /* Interface has a valid socket ? */ + if (pim_ifp->pim_sock_fd < 0) { + if (pim_sock_add(ifp)) { + zlog_warn("Failure creating PIM socket for interface %s", + ifp->name); + } + } + + } + } /* pim */ + + if (PIM_MROUTE_IS_ENABLED) { + /* + PIM or IGMP is enabled on interface, and there is at least one + address assigned, then try to create a vif_index. + */ + if (pim_ifp->mroute_vif_index < 0) { + pim_if_add_vif(ifp); + } + } +} + +static void pim_if_addr_del_igmp(struct connected *ifc) +{ + struct pim_interface *pim_ifp = ifc->ifp->info; + struct igmp_sock *igmp; + struct in_addr ifaddr; + + if (ifc->address->family != AF_INET) { + /* non-IPv4 address */ + return; + } + + if (!pim_ifp) { + /* IGMP not enabled on interface */ + return; + } + + ifaddr = ifc->address->u.prefix4; + + /* lookup IGMP socket */ + igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->igmp_socket_list, + ifaddr); + if (igmp) { + /* if addr found, del IGMP socket */ + igmp_sock_delete(igmp); + } +} + +static void pim_if_addr_del_pim(struct connected *ifc) +{ + struct pim_interface *pim_ifp = ifc->ifp->info; + + if (ifc->address->family != AF_INET) { + /* non-IPv4 address */ + return; + } + + if (!pim_ifp) { + /* PIM not enabled on interface */ + return; + } + + if (PIM_INADDR_ISNOT_ANY(pim_ifp->primary_address)) { + /* Interface keeps a valid primary address */ + return; + } + + if (pim_ifp->pim_sock_fd < 0) { + /* Interface does not hold a valid socket any longer */ + return; + } + + /* + pim_sock_delete() closes the socket, stops read and timer threads, + and kills all neighbors. + */ + pim_sock_delete(ifc->ifp, "last address has been removed from interface"); +} + +void pim_if_addr_del(struct connected *ifc) +{ + struct interface *ifp; + + zassert(ifc); + ifp = ifc->ifp; + zassert(ifp); + + detect_primary_address_change(ifp, __PRETTY_FUNCTION__); + + pim_if_addr_del_igmp(ifc); + pim_if_addr_del_pim(ifc); +} + +void pim_if_addr_add_all(struct interface *ifp) +{ + struct connected *ifc; + struct listnode *node; + struct listnode *nextnode; + + /* PIM/IGMP enabled ? */ + if (!ifp->info) + return; + + for (ALL_LIST_ELEMENTS(ifp->connected, node, nextnode, ifc)) { + struct prefix *p = ifc->address; + + if (p->family != AF_INET) + continue; + + pim_if_addr_add(ifc); + } +} + +void pim_if_addr_del_all(struct interface *ifp) +{ + struct connected *ifc; + struct listnode *node; + struct listnode *nextnode; + + /* PIM/IGMP enabled ? */ + if (!ifp->info) + return; + + for (ALL_LIST_ELEMENTS(ifp->connected, node, nextnode, ifc)) { + struct prefix *p = ifc->address; + + if (p->family != AF_INET) + continue; + + pim_if_addr_del(ifc); + } +} + +void pim_if_addr_del_all_igmp(struct interface *ifp) +{ + struct connected *ifc; + struct listnode *node; + struct listnode *nextnode; + + /* PIM/IGMP enabled ? */ + if (!ifp->info) + return; + + for (ALL_LIST_ELEMENTS(ifp->connected, node, nextnode, ifc)) { + struct prefix *p = ifc->address; + + if (p->family != AF_INET) + continue; + + pim_if_addr_del_igmp(ifc); + } +} + +void pim_if_addr_del_all_pim(struct interface *ifp) +{ + struct connected *ifc; + struct listnode *node; + struct listnode *nextnode; + + /* PIM/IGMP enabled ? */ + if (!ifp->info) + return; + + for (ALL_LIST_ELEMENTS(ifp->connected, node, nextnode, ifc)) { + struct prefix *p = ifc->address; + + if (p->family != AF_INET) + continue; + + pim_if_addr_del_pim(ifc); + } +} + +static struct in_addr find_first_addr(struct interface *ifp) +{ + struct connected *ifc; + struct listnode *node; + struct in_addr addr; + + for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { + struct prefix *p = ifc->address; + + if (p->family != AF_INET) + continue; + + if (PIM_INADDR_IS_ANY(p->u.prefix4)) { + zlog_warn("%s: null IPv4 address connected to interface %s", + __PRETTY_FUNCTION__, ifp->name); + continue; + } + + return p->u.prefix4; + } + + addr.s_addr = PIM_NET_INADDR_ANY; + + return addr; +} + +struct in_addr pim_find_primary_addr(struct interface *ifp) +{ + return find_first_addr(ifp); +} + +/* + pim_if_add_vif() uses ifindex as vif_index + + see also pim_if_find_vifindex_by_ifindex() + */ +int pim_if_add_vif(struct interface *ifp) +{ + struct pim_interface *pim_ifp = ifp->info; + struct in_addr ifaddr; + + zassert(pim_ifp); + + if (pim_ifp->mroute_vif_index > 0) { + zlog_warn("%s: vif_index=%d > 0 on interface %s ifindex=%d", + __PRETTY_FUNCTION__, + pim_ifp->mroute_vif_index, ifp->name, ifp->ifindex); + return -1; + } + + if (ifp->ifindex < 1) { + zlog_warn("%s: ifindex=%d < 1 on interface %s", + __PRETTY_FUNCTION__, + ifp->ifindex, ifp->name); + return -2; + } + + if (ifp->ifindex >= MAXVIFS) { + zlog_warn("%s: ifindex=%d >= MAXVIFS=%d on interface %s", + __PRETTY_FUNCTION__, + ifp->ifindex, MAXVIFS, ifp->name); + return -3; + } + + ifaddr = pim_ifp->primary_address; + if (PIM_INADDR_IS_ANY(ifaddr)) { + zlog_warn("%s: could not get address for interface %s ifindex=%d", + __PRETTY_FUNCTION__, + ifp->name, ifp->ifindex); + return -4; + } + + if (pim_mroute_add_vif(ifp->ifindex, ifaddr)) { + /* pim_mroute_add_vif reported error */ + return -5; + } + + pim_ifp->mroute_vif_index = ifp->ifindex; + + /* + Update highest vif_index + */ + if (pim_ifp->mroute_vif_index > qpim_mroute_oif_highest_vif_index) { + qpim_mroute_oif_highest_vif_index = pim_ifp->mroute_vif_index; + } + + return 0; +} + +static int iflist_find_highest_vif_index() +{ + struct listnode *ifnode; + struct interface *ifp; + struct pim_interface *pim_ifp; + int highest_vif_index = -1; + + for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) { + pim_ifp = ifp->info; + if (!pim_ifp) + continue; + + if (pim_ifp->mroute_vif_index > highest_vif_index) { + highest_vif_index = pim_ifp->mroute_vif_index; + } + } + + return highest_vif_index; +} + +int pim_if_del_vif(struct interface *ifp) +{ + struct pim_interface *pim_ifp = ifp->info; + int old_vif_index; + + if (pim_ifp->mroute_vif_index < 1) { + zlog_warn("%s: vif_index=%d < 1 on interface %s ifindex=%d", + __PRETTY_FUNCTION__, + pim_ifp->mroute_vif_index, ifp->name, ifp->ifindex); + return -1; + } + + if (pim_mroute_del_vif(pim_ifp->mroute_vif_index)) { + /* pim_mroute_del_vif reported error */ + return -2; + } + + /* + Update highest vif_index + */ + + /* save old vif_index in order to compare with highest below */ + old_vif_index = pim_ifp->mroute_vif_index; + + pim_ifp->mroute_vif_index = -1; + + if (old_vif_index == qpim_mroute_oif_highest_vif_index) { + qpim_mroute_oif_highest_vif_index = iflist_find_highest_vif_index(); + } + + return 0; +} + +void pim_if_add_vif_all() +{ + struct listnode *ifnode; + struct listnode *ifnextnode; + struct interface *ifp; + + for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) { + if (!ifp->info) + continue; + + pim_if_add_vif(ifp); + } +} + +void pim_if_del_vif_all() +{ + struct listnode *ifnode; + struct listnode *ifnextnode; + struct interface *ifp; + + for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) { + if (!ifp->info) + continue; + + pim_if_del_vif(ifp); + } +} + +struct interface *pim_if_find_by_vif_index(int vif_index) +{ + struct listnode *ifnode; + struct interface *ifp; + + for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) { + if (ifp->info) { + struct pim_interface *pim_ifp; + pim_ifp = ifp->info; + if (vif_index == pim_ifp->mroute_vif_index) + return ifp; + } + } + + return 0; +} + +/* + pim_if_add_vif() uses ifindex as vif_index + */ +int pim_if_find_vifindex_by_ifindex(int ifindex) +{ + return ifindex; +} + +int pim_if_lan_delay_enabled(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + + pim_ifp = ifp->info; + zassert(pim_ifp); + zassert(pim_ifp->pim_number_of_nonlandelay_neighbors >= 0); + + return pim_ifp->pim_number_of_nonlandelay_neighbors == 0; +} + +uint16_t pim_if_effective_propagation_delay_msec(struct interface *ifp) +{ + if (pim_if_lan_delay_enabled(ifp)) { + struct pim_interface *pim_ifp; + pim_ifp = ifp->info; + return pim_ifp->pim_neighbors_highest_propagation_delay_msec; + } + else { + return PIM_DEFAULT_PROPAGATION_DELAY_MSEC; + } +} + +uint16_t pim_if_effective_override_interval_msec(struct interface *ifp) +{ + if (pim_if_lan_delay_enabled(ifp)) { + struct pim_interface *pim_ifp; + pim_ifp = ifp->info; + return pim_ifp->pim_neighbors_highest_override_interval_msec; + } + else { + return PIM_DEFAULT_OVERRIDE_INTERVAL_MSEC; + } +} + +int pim_if_t_override_msec(struct interface *ifp) +{ + int effective_override_interval_msec; + int t_override_msec; + + effective_override_interval_msec = + pim_if_effective_override_interval_msec(ifp); + + t_override_msec = pim_rand_next(0, effective_override_interval_msec); + + return t_override_msec; +} + +uint16_t pim_if_jp_override_interval_msec(struct interface *ifp) +{ + return pim_if_effective_propagation_delay_msec(ifp) + + pim_if_effective_override_interval_msec(ifp); +} + +/* + RFC 4601: 4.1.6. State Summarization Macros + + The function NBR( I, A ) uses information gathered through PIM Hello + messages to map the IP address A of a directly connected PIM + neighbor router on interface I to the primary IP address of the same + router (Section 4.3.4). The primary IP address of a neighbor is the + address that it uses as the source of its PIM Hello messages. +*/ +struct pim_neighbor *pim_if_find_neighbor(struct interface *ifp, + struct in_addr addr) +{ + struct listnode *neighnode; + struct pim_neighbor *neigh; + struct pim_interface *pim_ifp; + + zassert(ifp); + + pim_ifp = ifp->info; + if (!pim_ifp) { + zlog_warn("%s: multicast not enabled on interface %s", + __PRETTY_FUNCTION__, + ifp->name); + return 0; + } + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode, neigh)) { + + /* primary address ? */ + if (neigh->source_addr.s_addr == addr.s_addr) + return neigh; + + /* secondary address ? */ + if (pim_neighbor_find_secondary(neigh, addr)) + return neigh; + } + + if (PIM_DEBUG_PIM_TRACE) { + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_debug("%s: neighbor not found for address %s on interface %s", + __PRETTY_FUNCTION__, + addr_str, ifp->name); + } + + return 0; +} + +long pim_if_t_suppressed_msec(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + long t_suppressed_msec; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + /* join suppression disabled ? */ + if (PIM_IF_TEST_PIM_CAN_DISABLE_JOIN_SUPRESSION(pim_ifp->options)) + return 0; + + /* t_suppressed = t_periodic * rand(1.1, 1.4) */ + + t_suppressed_msec = qpim_t_periodic * pim_rand_next(1100, 1400); + + return t_suppressed_msec; +} + +static void igmp_join_free(struct igmp_join *ij) +{ + XFREE(MTYPE_PIM_IGMP_JOIN, ij); +} + +static struct igmp_join *igmp_join_find(struct list *join_list, + struct in_addr group_addr, + struct in_addr source_addr) +{ + struct listnode *node; + struct igmp_join *ij; + + zassert(join_list); + + for (ALL_LIST_ELEMENTS_RO(join_list, node, ij)) { + if ((group_addr.s_addr == ij->group_addr.s_addr) && + (source_addr.s_addr == ij->source_addr.s_addr)) + return ij; + } + + return 0; +} + +static int igmp_join_sock(const char *ifname, + int ifindex, + struct in_addr group_addr, + struct in_addr source_addr) +{ + int join_fd; + + join_fd = pim_socket_raw(IPPROTO_IGMP); + if (join_fd < 0) { + return -1; + } + + if (pim_socket_join_source(join_fd, ifindex, group_addr, source_addr, ifname)) { + close(join_fd); + return -2; + } + + return join_fd; +} + +static struct igmp_join *igmp_join_new(struct interface *ifp, + struct in_addr group_addr, + struct in_addr source_addr) +{ + struct pim_interface *pim_ifp; + struct igmp_join *ij; + int join_fd; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + join_fd = igmp_join_sock(ifp->name, ifp->ifindex, group_addr, source_addr); + if (join_fd < 0) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + zlog_warn("%s: igmp_join_sock() failure for IGMP group %s source %s on interface %s", + __PRETTY_FUNCTION__, + group_str, source_str, ifp->name); + return 0; + } + + ij = XMALLOC(MTYPE_PIM_IGMP_JOIN, sizeof(*ij)); + if (!ij) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + zlog_err("%s: XMALLOC(%d) failure for IGMP group %s source %s on interface %s", + __PRETTY_FUNCTION__, + sizeof(*ij), group_str, source_str, ifp->name); + close(join_fd); + return 0; + } + + ij->sock_fd = join_fd; + ij->group_addr = group_addr; + ij->source_addr = source_addr; + + listnode_add(pim_ifp->igmp_join_list, ij); + + return ij; +} + +int pim_if_igmp_join_add(struct interface *ifp, + struct in_addr group_addr, + struct in_addr source_addr) +{ + struct pim_interface *pim_ifp; + struct igmp_join *ij; + + pim_ifp = ifp->info; + if (!pim_ifp) { + zlog_warn("%s: multicast not enabled on interface %s", + __PRETTY_FUNCTION__, + ifp->name); + return -1; + } + + if (!pim_ifp->igmp_join_list) { + pim_ifp->igmp_join_list = list_new(); + if (!pim_ifp->igmp_join_list) { + zlog_err("%s %s: failure: igmp_join_list=list_new()", + __FILE__, __PRETTY_FUNCTION__); + return -2; + } + pim_ifp->igmp_join_list->del = (void (*)(void *)) igmp_join_free; + } + + ij = igmp_join_find(pim_ifp->igmp_join_list, group_addr, source_addr); + if (ij) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + zlog_warn("%s: can't re-join existing IGMP group %s source %s on interface %s", + __PRETTY_FUNCTION__, + group_str, source_str, ifp->name); + return -3; + } + + ij = igmp_join_new(ifp, group_addr, source_addr); + if (!ij) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + zlog_warn("%s: igmp_join_new() failure for IGMP group %s source %s on interface %s", + __PRETTY_FUNCTION__, + group_str, source_str, ifp->name); + return -4; + } + + return 0; +} + + + +int pim_if_igmp_join_del(struct interface *ifp, + struct in_addr group_addr, + struct in_addr source_addr) +{ + struct pim_interface *pim_ifp; + struct igmp_join *ij; + + pim_ifp = ifp->info; + if (!pim_ifp) { + zlog_warn("%s: multicast not enabled on interface %s", + __PRETTY_FUNCTION__, + ifp->name); + return -1; + } + + if (!pim_ifp->igmp_join_list) { + zlog_warn("%s: no IGMP join on interface %s", + __PRETTY_FUNCTION__, + ifp->name); + return -2; + } + + ij = igmp_join_find(pim_ifp->igmp_join_list, group_addr, source_addr); + if (!ij) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + zlog_warn("%s: could not find IGMP group %s source %s on interface %s", + __PRETTY_FUNCTION__, + group_str, source_str, ifp->name); + return -3; + } + + if (close(ij->sock_fd)) { + int e = errno; + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + zlog_warn("%s: failure closing sock_fd=%d for IGMP group %s source %s on interface %s: errno=%d: %s", + __PRETTY_FUNCTION__, + ij->sock_fd, group_str, source_str, ifp->name, e, strerror(e)); + /* warning only */ + } + listnode_delete(pim_ifp->igmp_join_list, ij); + igmp_join_free(ij); + if (listcount(pim_ifp->igmp_join_list) < 1) { + list_delete(pim_ifp->igmp_join_list); + pim_ifp->igmp_join_list = 0; + } + + return 0; +} + +static void pim_if_igmp_join_del_all(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + struct listnode *node; + struct listnode *nextnode; + struct igmp_join *ij; + + pim_ifp = ifp->info; + if (!pim_ifp) { + zlog_warn("%s: multicast not enabled on interface %s", + __PRETTY_FUNCTION__, + ifp->name); + return; + } + + if (!pim_ifp->igmp_join_list) + return; + + for (ALL_LIST_ELEMENTS(pim_ifp->igmp_join_list, node, nextnode, ij)) + pim_if_igmp_join_del(ifp, ij->group_addr, ij->source_addr); +} + +/* + RFC 4601 + + Transitions from "I am Assert Loser" State + + Current Winner's GenID Changes or NLT Expires + + The Neighbor Liveness Timer associated with the current winner + expires or we receive a Hello message from the current winner + reporting a different GenID from the one it previously reported. + This indicates that the current winner's interface or router has + gone down (and may have come back up), and so we must assume it no + longer knows it was the winner. + */ +void pim_if_assert_on_neighbor_down(struct interface *ifp, + struct in_addr neigh_addr) +{ + struct pim_interface *pim_ifp; + struct listnode *node; + struct listnode *next_node; + struct pim_ifchannel *ch; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, node, next_node, ch)) { + /* Is (S,G,I) assert loser ? */ + if (ch->ifassert_state != PIM_IFASSERT_I_AM_LOSER) + continue; + /* Dead neighbor was winner ? */ + if (ch->ifassert_winner.s_addr != neigh_addr.s_addr) + continue; + + assert_action_a5(ch); + } +} + +void pim_if_update_join_desired(struct pim_interface *pim_ifp) +{ + struct listnode *ch_node; + struct pim_ifchannel *ch; + + /* clear off flag from interface's upstreams */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) { + PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED_UPDATED(ch->upstream->flags); + } + + /* scan per-interface (S,G,I) state on this I interface */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) { + struct pim_upstream *up = ch->upstream; + + if (PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED_UPDATED(up->flags)) + continue; + + /* update join_desired for the global (S,G) state */ + pim_upstream_update_join_desired(up); + PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED_UPDATED(up->flags); + } +} + +void pim_if_update_assert_tracking_desired(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + struct listnode *node; + struct listnode *next_node; + struct pim_ifchannel *ch; + + pim_ifp = ifp->info; + if (!pim_ifp) + return; + + for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, node, next_node, ch)) { + pim_ifchannel_update_assert_tracking_desired(ch); + } +} diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h new file mode 100644 index 00000000..6ce866ba --- /dev/null +++ b/pimd/pim_iface.h @@ -0,0 +1,160 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_IFACE_H +#define PIM_IFACE_H + +#include + +#include "if.h" +#include "vty.h" + +#include "pim_igmp.h" + +#define PIM_IF_MASK_PIM (1 << 0) +#define PIM_IF_MASK_IGMP (1 << 1) +#define PIM_IF_MASK_IGMP_LISTEN_ALLROUTERS (1 << 2) +#define PIM_IF_MASK_PIM_CAN_DISABLE_JOIN_SUPRESSION (1 << 3) + +#define PIM_IF_IS_DELETED(ifp) ((ifp)->ifindex == IFINDEX_INTERNAL) + +#define PIM_IF_TEST_PIM(options) (PIM_IF_MASK_PIM & (options)) +#define PIM_IF_TEST_IGMP(options) (PIM_IF_MASK_IGMP & (options)) +#define PIM_IF_TEST_IGMP_LISTEN_ALLROUTERS(options) (PIM_IF_MASK_IGMP_LISTEN_ALLROUTERS & (options)) +#define PIM_IF_TEST_PIM_CAN_DISABLE_JOIN_SUPRESSION(options) (PIM_IF_MASK_PIM_CAN_DISABLE_JOIN_SUPRESSION & (options)) + +#define PIM_IF_DO_PIM(options) ((options) |= PIM_IF_MASK_PIM) +#define PIM_IF_DO_IGMP(options) ((options) |= PIM_IF_MASK_IGMP) +#define PIM_IF_DO_IGMP_LISTEN_ALLROUTERS(options) ((options) |= PIM_IF_MASK_IGMP_LISTEN_ALLROUTERS) +#define PIM_IF_DO_PIM_CAN_DISABLE_JOIN_SUPRESSION(options) ((options) |= PIM_IF_MASK_PIM_CAN_DISABLE_JOIN_SUPRESSION) + +#define PIM_IF_DONT_PIM(options) ((options) &= ~PIM_IF_MASK_PIM) +#define PIM_IF_DONT_IGMP(options) ((options) &= ~PIM_IF_MASK_IGMP) +#define PIM_IF_DONT_IGMP_LISTEN_ALLROUTERS(options) ((options) &= ~PIM_IF_MASK_IGMP_LISTEN_ALLROUTERS) +#define PIM_IF_DONT_PIM_CAN_DISABLE_JOIN_SUPRESSION(options) ((options) &= ~PIM_IF_MASK_PIM_CAN_DISABLE_JOIN_SUPRESSION) + +struct pim_interface { + uint32_t options; /* bit vector */ + int mroute_vif_index; + struct in_addr primary_address; /* remember addr to detect change */ + + int igmp_default_robustness_variable; /* IGMPv3 QRV */ + int igmp_default_query_interval; /* IGMPv3 secs between general queries */ + int igmp_query_max_response_time_dsec; /* IGMPv3 Max Response Time in dsecs */ + struct list *igmp_socket_list; /* list of struct igmp_sock */ + struct list *igmp_join_list; /* list of struct igmp_join */ + + int pim_sock_fd; /* PIM socket file descriptor */ + struct thread *t_pim_sock_read; /* thread for reading PIM socket */ + int64_t pim_sock_creation; /* timestamp of PIM socket creation */ + + struct thread *t_pim_hello_timer; + int pim_hello_period; + int pim_default_holdtime; + int pim_triggered_hello_delay; + uint32_t pim_generation_id; + uint16_t pim_propagation_delay_msec; /* config */ + uint16_t pim_override_interval_msec; /* config */ + struct list *pim_neighbor_list; /* list of struct pim_neighbor */ + struct list *pim_ifchannel_list; /* list of struct pim_ifchannel */ + + /* neighbors without lan_delay */ + int pim_number_of_nonlandelay_neighbors; + uint16_t pim_neighbors_highest_propagation_delay_msec; + uint16_t pim_neighbors_highest_override_interval_msec; + + /* DR Election */ + int64_t pim_dr_election_last; /* timestamp */ + int pim_dr_election_count; + struct in_addr pim_dr_addr; + uint32_t pim_dr_priority; /* config */ + int pim_dr_num_nondrpri_neighbors; /* neighbors without dr_pri */ + + int64_t pim_ifstat_start; /* start timestamp for stats */ + uint32_t pim_ifstat_hello_sent; + uint32_t pim_ifstat_hello_sendfail; + uint32_t pim_ifstat_hello_recv; + uint32_t pim_ifstat_hello_recvfail; +}; + +/* + if default_holdtime is set (>= 0), use it; + otherwise default_holdtime is 3.5 * hello_period + */ +#define PIM_IF_DEFAULT_HOLDTIME(pim_ifp) \ + (((pim_ifp)->pim_default_holdtime < 0) ? \ + ((pim_ifp)->pim_hello_period * 7 / 2) : \ + ((pim_ifp)->pim_default_holdtime)) + +void pim_if_init(void); + +struct pim_interface *pim_if_new(struct interface *ifp, int igmp, int pim); +void pim_if_delete(struct interface *ifp); +int pim_if_igmp_listen(struct vty *vty, + struct interface *ifp); +void pim_if_addr_add(struct connected *ifc); +void pim_if_addr_del(struct connected *ifc); +void pim_if_addr_add_all(struct interface *ifp); +void pim_if_addr_del_all(struct interface *ifp); +void pim_if_addr_del_all_igmp(struct interface *ifp); +void pim_if_addr_del_all_pim(struct interface *ifp); + +int pim_if_add_vif(struct interface *ifp); +int pim_if_del_vif(struct interface *ifp); +void pim_if_add_vif_all(void); +void pim_if_del_vif_all(void); + +struct interface *pim_if_find_by_vif_index(int vif_index); +int pim_if_find_vifindex_by_ifindex(int ifindex); + +int pim_if_lan_delay_enabled(struct interface *ifp); +uint16_t pim_if_effective_propagation_delay_msec(struct interface *ifp); +uint16_t pim_if_effective_override_interval_msec(struct interface *ifp); +uint16_t pim_if_jp_override_interval_msec(struct interface *ifp); +struct pim_neighbor *pim_if_find_neighbor(struct interface *ifp, + struct in_addr addr); + +long pim_if_t_suppressed_msec(struct interface *ifp); +int pim_if_t_override_msec(struct interface *ifp); + +struct in_addr pim_find_primary_addr(struct interface *ifp); + +int pim_if_igmp_join_add(struct interface *ifp, + struct in_addr group_addr, + struct in_addr source_addr); +int pim_if_igmp_join_del(struct interface *ifp, + struct in_addr group_addr, + struct in_addr source_addr); + +void pim_if_update_could_assert(struct interface *ifp); + +void pim_if_assert_on_neighbor_down(struct interface *ifp, + struct in_addr neigh_addr); + +void pim_if_rpf_interface_changed(struct interface *old_rpf_ifp, + struct pim_upstream *up); + +void pim_if_update_join_desired(struct pim_interface *pim_ifp); + +void pim_if_update_assert_tracking_desired(struct interface *ifp); + +#endif /* PIM_IFACE_H */ diff --git a/pimd/pim_ifchannel.c b/pimd/pim_ifchannel.c new file mode 100644 index 00000000..7f946cfd --- /dev/null +++ b/pimd/pim_ifchannel.c @@ -0,0 +1,893 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "linklist.h" +#include "thread.h" +#include "memory.h" + +#include "pimd.h" +#include "pim_str.h" +#include "pim_iface.h" +#include "pim_ifchannel.h" +#include "pim_zebra.h" +#include "pim_time.h" +#include "pim_msg.h" +#include "pim_pim.h" +#include "pim_join.h" +#include "pim_rpf.h" +#include "pim_macro.h" + +void pim_ifchannel_free(struct pim_ifchannel *ch) +{ + zassert(!ch->t_ifjoin_expiry_timer); + zassert(!ch->t_ifjoin_prune_pending_timer); + zassert(!ch->t_ifassert_timer); + + XFREE(MTYPE_PIM_IFCHANNEL, ch); +} + +void pim_ifchannel_delete(struct pim_ifchannel *ch) +{ + struct pim_interface *pim_ifp; + + pim_ifp = ch->interface->info; + zassert(pim_ifp); + + if (ch->ifjoin_state != PIM_IFJOIN_NOINFO) { + pim_upstream_update_join_desired(ch->upstream); + } + + pim_upstream_del(ch->upstream); + + THREAD_OFF(ch->t_ifjoin_expiry_timer); + THREAD_OFF(ch->t_ifjoin_prune_pending_timer); + THREAD_OFF(ch->t_ifassert_timer); + + /* + notice that listnode_delete() can't be moved + into pim_ifchannel_free() because the later is + called by list_delete_all_node() + */ + listnode_delete(pim_ifp->pim_ifchannel_list, ch); + + pim_ifchannel_free(ch); +} + +#define IFCHANNEL_NOINFO(ch) \ + ( \ + ((ch)->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO) \ + && \ + ((ch)->ifjoin_state == PIM_IFJOIN_NOINFO) \ + && \ + ((ch)->ifassert_state == PIM_IFASSERT_NOINFO) \ + ) + +static void delete_on_noinfo(struct pim_ifchannel *ch) +{ + if (IFCHANNEL_NOINFO(ch)) { + + /* In NOINFO state, timers should have been cleared */ + zassert(!ch->t_ifjoin_expiry_timer); + zassert(!ch->t_ifjoin_prune_pending_timer); + zassert(!ch->t_ifassert_timer); + + pim_ifchannel_delete(ch); + } +} + +void pim_ifchannel_ifjoin_switch(const char *caller, + struct pim_ifchannel *ch, + enum pim_ifjoin_state new_state) +{ + enum pim_ifjoin_state old_state = ch->ifjoin_state; + + if (old_state == new_state) { + zlog_debug("%s calledby %s: non-transition on state %d (%s)", + __PRETTY_FUNCTION__, caller, new_state, + pim_ifchannel_ifjoin_name(new_state)); + return; + } + + zassert(old_state != new_state); + + ch->ifjoin_state = new_state; + + /* Transition to/from NOINFO ? */ + if ( + (old_state == PIM_IFJOIN_NOINFO) + || + (new_state == PIM_IFJOIN_NOINFO) + ) { + + if (PIM_DEBUG_PIM_EVENTS) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_debug("PIM_IFCHANNEL_%s: (S,G)=(%s,%s) on interface %s", + ((new_state == PIM_IFJOIN_NOINFO) ? "DOWN" : "UP"), + src_str, grp_str, ch->interface->name); + } + + /* + Record uptime of state transition to/from NOINFO + */ + ch->ifjoin_creation = pim_time_monotonic_sec(); + + pim_upstream_update_join_desired(ch->upstream); + pim_ifchannel_update_could_assert(ch); + pim_ifchannel_update_assert_tracking_desired(ch); + } +} + +const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state) +{ + switch (ifjoin_state) { + case PIM_IFJOIN_NOINFO: return "NOINFO"; + case PIM_IFJOIN_JOIN: return "JOIN"; + case PIM_IFJOIN_PRUNE_PENDING: return "PRUNEP"; + } + + return "ifjoin_bad_state"; +} + +const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state) +{ + switch (ifassert_state) { + case PIM_IFASSERT_NOINFO: return "NOINFO"; + case PIM_IFASSERT_I_AM_WINNER: return "WINNER"; + case PIM_IFASSERT_I_AM_LOSER: return "LOSER"; + } + + return "ifassert_bad_state"; +} + +/* + RFC 4601: 4.6.5. Assert State Macros + + AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I) + defaults to Infinity when in the NoInfo state. +*/ +void reset_ifassert_state(struct pim_ifchannel *ch) +{ + THREAD_OFF(ch->t_ifassert_timer); + + pim_ifassert_winner_set(ch, + PIM_IFASSERT_NOINFO, + qpim_inaddr_any, + qpim_infinite_assert_metric); +} + +static struct pim_ifchannel *pim_ifchannel_new(struct interface *ifp, + struct in_addr source_addr, + struct in_addr group_addr) +{ + struct pim_ifchannel *ch; + struct pim_interface *pim_ifp; + struct pim_upstream *up; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + up = pim_upstream_add(source_addr, group_addr); + if (!up) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", group_addr, grp_str, sizeof(grp_str)); + zlog_err("%s: could not attach upstream (S,G)=(%s,%s) on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, ifp->name); + return 0; + } + + ch = XMALLOC(MTYPE_PIM_IFCHANNEL, sizeof(*ch)); + if (!ch) { + zlog_err("%s: PIM XMALLOC(%d) failure", + __PRETTY_FUNCTION__, sizeof(*ch)); + return 0; + } + + ch->flags = 0; + ch->upstream = up; + ch->interface = ifp; + ch->source_addr = source_addr; + ch->group_addr = group_addr; + ch->local_ifmembership = PIM_IFMEMBERSHIP_NOINFO; + + ch->ifjoin_state = PIM_IFJOIN_NOINFO; + ch->t_ifjoin_expiry_timer = 0; + ch->t_ifjoin_prune_pending_timer = 0; + ch->ifjoin_creation = 0; + + /* Assert state */ + ch->t_ifassert_timer = 0; + reset_ifassert_state(ch); + if (pim_macro_ch_could_assert_eval(ch)) + PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags); + else + PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags); + + if (pim_macro_assert_tracking_desired_eval(ch)) + PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags); + else + PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags); + + ch->ifassert_my_metric = pim_macro_ch_my_assert_metric_eval(ch); + + /* Attach to list */ + listnode_add(pim_ifp->pim_ifchannel_list, ch); + + zassert(IFCHANNEL_NOINFO(ch)); + + return ch; +} + +struct pim_ifchannel *pim_ifchannel_find(struct interface *ifp, + struct in_addr source_addr, + struct in_addr group_addr) +{ + struct pim_interface *pim_ifp; + struct listnode *ch_node; + struct pim_ifchannel *ch; + + zassert(ifp); + + pim_ifp = ifp->info; + + if (!pim_ifp) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", group_addr, grp_str, sizeof(grp_str)); + zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, + ifp->name); + return 0; + } + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) { + if ( + (source_addr.s_addr == ch->source_addr.s_addr) && + (group_addr.s_addr == ch->group_addr.s_addr) + ) { + return ch; + } + } + + return 0; +} + +static void ifmembership_set(struct pim_ifchannel *ch, + enum pim_ifmembership membership) +{ + if (ch->local_ifmembership == membership) + return; + + { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_info("%s: (S,G)=(%s,%s) membership now is %s on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, + membership == PIM_IFMEMBERSHIP_INCLUDE ? "INCLUDE" : "NOINFO", + ch->interface->name); + } + + ch->local_ifmembership = membership; + + pim_upstream_update_join_desired(ch->upstream); + pim_ifchannel_update_could_assert(ch); + pim_ifchannel_update_assert_tracking_desired(ch); +} + + +void pim_ifchannel_membership_clear(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + struct listnode *ch_node; + struct pim_ifchannel *ch; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) { + ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO); + } +} + +void pim_ifchannel_delete_on_noinfo(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + struct listnode *node; + struct listnode *next_node; + struct pim_ifchannel *ch; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, node, next_node, ch)) { + delete_on_noinfo(ch); + } +} + +struct pim_ifchannel *pim_ifchannel_add(struct interface *ifp, + struct in_addr source_addr, + struct in_addr group_addr) +{ + struct pim_ifchannel *ch; + char src_str[100]; + char grp_str[100]; + + ch = pim_ifchannel_find(ifp, source_addr, group_addr); + if (ch) + return ch; + + ch = pim_ifchannel_new(ifp, source_addr, group_addr); + if (ch) + return ch; + + pim_inet4_dump("", source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", group_addr, grp_str, sizeof(grp_str)); + zlog_warn("%s: pim_ifchannel_new() failure for (S,G)=(%s,%s) on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, ifp->name); + + return 0; +} + +static void ifjoin_to_noinfo(struct pim_ifchannel *ch) +{ + pim_forward_stop(ch); + pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_NOINFO); + delete_on_noinfo(ch); +} + +static int on_ifjoin_expiry_timer(struct thread *t) +{ + struct pim_ifchannel *ch; + + zassert(t); + ch = THREAD_ARG(t); + zassert(ch); + + ch->t_ifjoin_expiry_timer = 0; + + zassert(ch->ifjoin_state == PIM_IFJOIN_JOIN); + + ifjoin_to_noinfo(ch); + /* ch may have been deleted */ + + return 0; +} + +static void prune_echo(struct interface *ifp, + struct in_addr source_addr, + struct in_addr group_addr) +{ + struct pim_interface *pim_ifp; + struct in_addr neigh_dst_addr; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + neigh_dst_addr = pim_ifp->primary_address; + + if (PIM_DEBUG_PIM_EVENTS) { + char source_str[100]; + char group_str[100]; + char neigh_dst_str[100]; + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", neigh_dst_addr, neigh_dst_str, sizeof(neigh_dst_str)); + zlog_debug("%s: sending PruneEcho(S,G)=(%s,%s) to upstream=%s on interface %s", + __PRETTY_FUNCTION__, source_str, group_str, neigh_dst_str, ifp->name); + } + + pim_joinprune_send(ifp, neigh_dst_addr, source_addr, group_addr, + 0 /* boolean: send_join=false (prune) */); +} + +static int on_ifjoin_prune_pending_timer(struct thread *t) +{ + struct pim_ifchannel *ch; + int send_prune_echo; /* boolean */ + struct interface *ifp; + struct pim_interface *pim_ifp; + struct in_addr ch_source; + struct in_addr ch_group; + + zassert(t); + ch = THREAD_ARG(t); + zassert(ch); + + ch->t_ifjoin_prune_pending_timer = 0; + + zassert(ch->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING); + + /* Send PruneEcho(S,G) ? */ + ifp = ch->interface; + pim_ifp = ifp->info; + send_prune_echo = (listcount(pim_ifp->pim_neighbor_list) > 1); + + /* Save (S,G) */ + ch_source = ch->source_addr; + ch_group = ch->group_addr; + + ifjoin_to_noinfo(ch); + /* from here ch may have been deleted */ + + if (send_prune_echo) + prune_echo(ifp, ch_source, ch_group); + + return 0; +} + +static void check_recv_upstream(int is_join, + struct interface *recv_ifp, + struct in_addr upstream, + struct in_addr source_addr, + struct in_addr group_addr, + uint8_t source_flags, + int holdtime) +{ + struct pim_upstream *up; + + /* Upstream (S,G) in Joined state ? */ + up = pim_upstream_find(source_addr, group_addr); + if (!up) + return; + if (up->join_state != PIM_UPSTREAM_JOINED) + return; + + /* Upstream (S,G) in Joined state */ + + if (PIM_INADDR_IS_ANY(up->rpf.rpf_addr)) { + /* RPF'(S,G) not found */ + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", group_addr, grp_str, sizeof(grp_str)); + zlog_warn("%s %s: RPF'(%s,%s) not found", + __FILE__, __PRETTY_FUNCTION__, + src_str, grp_str); + return; + } + + /* upstream directed to RPF'(S,G) ? */ + if (upstream.s_addr != up->rpf.rpf_addr.s_addr) { + char src_str[100]; + char grp_str[100]; + char up_str[100]; + char rpf_str[100]; + pim_inet4_dump("", source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", upstream, up_str, sizeof(up_str)); + pim_inet4_dump("", up->rpf.rpf_addr, rpf_str, sizeof(rpf_str)); + zlog_warn("%s %s: (S,G)=(%s,%s) upstream=%s not directed to RPF'(S,G)=%s on interface %s", + __FILE__, __PRETTY_FUNCTION__, + src_str, grp_str, + up_str, rpf_str, recv_ifp->name); + return; + } + /* upstream directed to RPF'(S,G) */ + + if (is_join) { + /* Join(S,G) to RPF'(S,G) */ + pim_upstream_join_suppress(up, up->rpf.rpf_addr, holdtime); + return; + } + + /* Prune to RPF'(S,G) */ + + if (source_flags & PIM_RPT_BIT_MASK) { + if (source_flags & PIM_WILDCARD_BIT_MASK) { + /* Prune(*,G) to RPF'(S,G) */ + pim_upstream_join_timer_decrease_to_t_override("Prune(*,G)", + up, up->rpf.rpf_addr); + return; + } + + /* Prune(S,G,rpt) to RPF'(S,G) */ + pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)", + up, up->rpf.rpf_addr); + return; + } + + /* Prune(S,G) to RPF'(S,G) */ + pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up, + up->rpf.rpf_addr); +} + +static int nonlocal_upstream(int is_join, + struct interface *recv_ifp, + struct in_addr upstream, + struct in_addr source_addr, + struct in_addr group_addr, + uint8_t source_flags, + uint16_t holdtime) +{ + struct pim_interface *recv_pim_ifp; + int is_local; /* boolean */ + + recv_pim_ifp = recv_ifp->info; + zassert(recv_pim_ifp); + + is_local = (upstream.s_addr == recv_pim_ifp->primary_address.s_addr); + + if (PIM_DEBUG_PIM_TRACE) { + char up_str[100]; + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", upstream, up_str, sizeof(up_str)); + pim_inet4_dump("", source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", group_addr, grp_str, sizeof(grp_str)); + zlog_warn("%s: recv %s (S,G)=(%s,%s) to %s upstream=%s on %s", + __PRETTY_FUNCTION__, + is_join ? "join" : "prune", + src_str, grp_str, + is_local ? "local" : "non-local", + up_str, recv_ifp->name); + } + + if (is_local) + return 0; + + /* + Since recv upstream addr was not directed to our primary + address, check if we should react to it in any way. + */ + check_recv_upstream(is_join, recv_ifp, upstream, source_addr, group_addr, + source_flags, holdtime); + + return 1; /* non-local */ +} + +void pim_ifchannel_join_add(struct interface *ifp, + struct in_addr neigh_addr, + struct in_addr upstream, + struct in_addr source_addr, + struct in_addr group_addr, + uint8_t source_flags, + uint16_t holdtime) +{ + struct pim_interface *pim_ifp; + struct pim_ifchannel *ch; + + if (nonlocal_upstream(1 /* join */, ifp, upstream, + source_addr, group_addr, source_flags, holdtime)) { + return; + } + + ch = pim_ifchannel_add(ifp, source_addr, group_addr); + if (!ch) + return; + + /* + RFC 4601: 4.6.1. (S,G) Assert Message State Machine + + Transitions from "I am Assert Loser" State + + Receive Join(S,G) on Interface I + + We receive a Join(S,G) that has the Upstream Neighbor Address + field set to my primary IP address on interface I. The action is + to transition to NoInfo state, delete this (S,G) assert state + (Actions A5 below), and allow the normal PIM Join/Prune mechanisms + to operate. + + Notice: The nonlocal_upstream() test above ensures the upstream + address of the join message is our primary address. + */ + if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) { + char src_str[100]; + char grp_str[100]; + char neigh_str[100]; + pim_inet4_dump("", source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", neigh_addr, neigh_str, sizeof(neigh_str)); + zlog_warn("%s: Assert Loser recv Join(%s,%s) from %s on %s", + __PRETTY_FUNCTION__, + src_str, grp_str, neigh_str, ifp->name); + + assert_action_a5(ch); + } + + pim_ifp = ifp->info; + zassert(pim_ifp); + + switch (ch->ifjoin_state) { + case PIM_IFJOIN_NOINFO: + pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN); + if (pim_macro_chisin_oiflist(ch)) { + pim_forward_start(ch); + } + break; + case PIM_IFJOIN_JOIN: + zassert(!ch->t_ifjoin_prune_pending_timer); + + /* + In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to a + previously received join message with holdtime=0xFFFF. + */ + if (ch->t_ifjoin_expiry_timer) { + unsigned long remain = + thread_timer_remain_second(ch->t_ifjoin_expiry_timer); + if (remain > holdtime) { + /* + RFC 4601: 4.5.3. Receiving (S,G) Join/Prune Messages + + Transitions from Join State + + The (S,G) downstream state machine on interface I remains in + Join state, and the Expiry Timer (ET) is restarted, set to + maximum of its current value and the HoldTime from the + triggering Join/Prune message. + + Conclusion: Do not change the ET if the current value is + higher than the received join holdtime. + */ + return; + } + } + THREAD_OFF(ch->t_ifjoin_expiry_timer); + break; + case PIM_IFJOIN_PRUNE_PENDING: + zassert(!ch->t_ifjoin_expiry_timer); + zassert(ch->t_ifjoin_prune_pending_timer); + THREAD_OFF(ch->t_ifjoin_prune_pending_timer); + pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN); + break; + } + + zassert(!IFCHANNEL_NOINFO(ch)); + + if (holdtime != 0xFFFF) { + THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer, + on_ifjoin_expiry_timer, + ch, holdtime); + } +} + +void pim_ifchannel_prune(struct interface *ifp, + struct in_addr upstream, + struct in_addr source_addr, + struct in_addr group_addr, + uint8_t source_flags, + uint16_t holdtime) +{ + struct pim_ifchannel *ch; + int jp_override_interval_msec; + + if (nonlocal_upstream(0 /* prune */, ifp, upstream, + source_addr, group_addr, source_flags, holdtime)) { + return; + } + + ch = pim_ifchannel_add(ifp, source_addr, group_addr); + if (!ch) + return; + + switch (ch->ifjoin_state) { + case PIM_IFJOIN_NOINFO: + case PIM_IFJOIN_PRUNE_PENDING: + /* nothing to do */ + break; + case PIM_IFJOIN_JOIN: + { + struct pim_interface *pim_ifp; + + pim_ifp = ifp->info; + + zassert(ch->t_ifjoin_expiry_timer); + zassert(!ch->t_ifjoin_prune_pending_timer); + + THREAD_OFF(ch->t_ifjoin_expiry_timer); + + pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_PRUNE_PENDING); + + if (listcount(pim_ifp->pim_neighbor_list) > 1) { + jp_override_interval_msec = pim_if_jp_override_interval_msec(ifp); + } + else { + jp_override_interval_msec = 0; /* schedule to expire immediately */ + /* If we called ifjoin_prune() directly instead, care should + be taken not to use "ch" afterwards since it would be + deleted. */ + } + + THREAD_TIMER_MSEC_ON(master, ch->t_ifjoin_prune_pending_timer, + on_ifjoin_prune_pending_timer, + ch, jp_override_interval_msec); + + zassert(!ch->t_ifjoin_expiry_timer); + zassert(ch->t_ifjoin_prune_pending_timer); + } + break; + } + +} + +void pim_ifchannel_local_membership_add(struct interface *ifp, + struct in_addr source_addr, + struct in_addr group_addr) +{ + struct pim_ifchannel *ch; + struct pim_interface *pim_ifp; + + /* PIM enabled on interface? */ + pim_ifp = ifp->info; + if (!pim_ifp) + return; + if (!PIM_IF_TEST_PIM(pim_ifp->options)) + return; + + ch = pim_ifchannel_add(ifp, source_addr, group_addr); + if (!ch) { + return; + } + + ifmembership_set(ch, PIM_IFMEMBERSHIP_INCLUDE); + + zassert(!IFCHANNEL_NOINFO(ch)); +} + +void pim_ifchannel_local_membership_del(struct interface *ifp, + struct in_addr source_addr, + struct in_addr group_addr) +{ + struct pim_ifchannel *ch; + struct pim_interface *pim_ifp; + + /* PIM enabled on interface? */ + pim_ifp = ifp->info; + if (!pim_ifp) + return; + if (!PIM_IF_TEST_PIM(pim_ifp->options)) + return; + + ch = pim_ifchannel_find(ifp, source_addr, group_addr); + if (!ch) + return; + + ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO); + + delete_on_noinfo(ch); +} + +void pim_ifchannel_update_could_assert(struct pim_ifchannel *ch) +{ + int old_couldassert = PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)); + int new_couldassert = PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch)); + + if (new_couldassert == old_couldassert) + return; + + { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_info("%s: CouldAssert(%s,%s,%s) changed from %d to %d", + __PRETTY_FUNCTION__, + src_str, grp_str, ch->interface->name, + old_couldassert, new_couldassert); + } + + if (new_couldassert) { + /* CouldAssert(S,G,I) switched from FALSE to TRUE */ + PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags); + } + else { + /* CouldAssert(S,G,I) switched from TRUE to FALSE */ + PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags); + + if (ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER) { + assert_action_a4(ch); + } + } + + pim_ifchannel_update_my_assert_metric(ch); +} + +/* + my_assert_metric may be affected by: + + CouldAssert(S,G) + pim_ifp->primary_address + rpf->source_nexthop.mrib_metric_preference; + rpf->source_nexthop.mrib_route_metric; + */ +void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel *ch) +{ + struct pim_assert_metric my_metric_new = pim_macro_ch_my_assert_metric_eval(ch); + + if (pim_assert_metric_match(&my_metric_new, &ch->ifassert_my_metric)) + return; + + { + char src_str[100]; + char grp_str[100]; + char old_addr_str[100]; + char new_addr_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", ch->ifassert_my_metric.ip_address, old_addr_str, sizeof(old_addr_str)); + pim_inet4_dump("", my_metric_new.ip_address, new_addr_str, sizeof(new_addr_str)); + zlog_info("%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s", + __PRETTY_FUNCTION__, + src_str, grp_str, ch->interface->name, + ch->ifassert_my_metric.rpt_bit_flag, + ch->ifassert_my_metric.metric_preference, + ch->ifassert_my_metric.route_metric, + old_addr_str, + my_metric_new.rpt_bit_flag, + my_metric_new.metric_preference, + my_metric_new.route_metric, + new_addr_str); + } + + ch->ifassert_my_metric = my_metric_new; + + if (pim_assert_metric_better(&ch->ifassert_my_metric, + &ch->ifassert_winner_metric)) { + assert_action_a5(ch); + } +} + +void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch) +{ + int old_atd = PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags)); + int new_atd = PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch)); + + if (new_atd == old_atd) + return; + + { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_info("%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d", + __PRETTY_FUNCTION__, + src_str, grp_str, ch->interface->name, + old_atd, new_atd); + } + + if (new_atd) { + /* AssertTrackingDesired(S,G,I) switched from FALSE to TRUE */ + PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags); + } + else { + /* AssertTrackingDesired(S,G,I) switched from TRUE to FALSE */ + PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags); + + if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) { + assert_action_a5(ch); + } + } +} diff --git a/pimd/pim_ifchannel.h b/pimd/pim_ifchannel.h new file mode 100644 index 00000000..e6f1c294 --- /dev/null +++ b/pimd/pim_ifchannel.h @@ -0,0 +1,145 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_IFCHANNEL_H +#define PIM_IFCHANNEL_H + +#include + +#include "if.h" + +#include "pim_upstream.h" + +enum pim_ifmembership { + PIM_IFMEMBERSHIP_NOINFO, + PIM_IFMEMBERSHIP_INCLUDE +}; + +enum pim_ifjoin_state { + PIM_IFJOIN_NOINFO, + PIM_IFJOIN_JOIN, + PIM_IFJOIN_PRUNE_PENDING +}; + +enum pim_ifassert_state { + PIM_IFASSERT_NOINFO, + PIM_IFASSERT_I_AM_WINNER, + PIM_IFASSERT_I_AM_LOSER +}; + +struct pim_assert_metric { + uint32_t rpt_bit_flag; + uint32_t metric_preference; + uint32_t route_metric; + struct in_addr ip_address; /* neighbor router that sourced the Assert message */ +}; + +/* + Flag to detect change in CouldAssert(S,G,I) +*/ +#define PIM_IF_FLAG_MASK_COULD_ASSERT (1 << 0) +#define PIM_IF_FLAG_TEST_COULD_ASSERT(flags) ((flags) & PIM_IF_FLAG_MASK_COULD_ASSERT) +#define PIM_IF_FLAG_SET_COULD_ASSERT(flags) ((flags) |= PIM_IF_FLAG_MASK_COULD_ASSERT) +#define PIM_IF_FLAG_UNSET_COULD_ASSERT(flags) ((flags) &= ~PIM_IF_FLAG_MASK_COULD_ASSERT) +/* + Flag to detect change in AssertTrackingDesired(S,G,I) +*/ +#define PIM_IF_FLAG_MASK_ASSERT_TRACKING_DESIRED (1 << 1) +#define PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(flags) ((flags) & PIM_IF_FLAG_MASK_ASSERT_TRACKING_DESIRED) +#define PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(flags) ((flags) |= PIM_IF_FLAG_MASK_ASSERT_TRACKING_DESIRED) +#define PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(flags) ((flags) &= ~PIM_IF_FLAG_MASK_ASSERT_TRACKING_DESIRED) + +/* + Per-interface (S,G) state +*/ +struct pim_ifchannel { + struct in_addr source_addr; /* (S,G) source key */ + struct in_addr group_addr; /* (S,G) group key */ + struct interface *interface; /* backpointer to interface */ + uint32_t flags; + + /* IGMPv3 determined interface has local members for (S,G) ? */ + enum pim_ifmembership local_ifmembership; + + /* Per-interface (S,G) Join/Prune State (Section 4.1.4 of RFC4601) */ + enum pim_ifjoin_state ifjoin_state; + struct thread *t_ifjoin_expiry_timer; + struct thread *t_ifjoin_prune_pending_timer; + int64_t ifjoin_creation; /* Record uptime of ifjoin state */ + + /* Per-interface (S,G) Assert State (Section 4.6.1 of RFC4601) */ + enum pim_ifassert_state ifassert_state; + struct thread *t_ifassert_timer; + struct in_addr ifassert_winner; + struct pim_assert_metric ifassert_winner_metric; + int64_t ifassert_creation; /* Record uptime of ifassert state */ + struct pim_assert_metric ifassert_my_metric; + + /* Upstream (S,G) state */ + struct pim_upstream *upstream; +}; + +void pim_ifchannel_free(struct pim_ifchannel *ch); +void pim_ifchannel_delete(struct pim_ifchannel *ch); +void pim_ifchannel_membership_clear(struct interface *ifp); +void pim_ifchannel_delete_on_noinfo(struct interface *ifp); +struct pim_ifchannel *pim_ifchannel_find(struct interface *ifp, + struct in_addr source_addr, + struct in_addr group_addr); +struct pim_ifchannel *pim_ifchannel_add(struct interface *ifp, + struct in_addr source_addr, + struct in_addr group_addr); +void pim_ifchannel_join_add(struct interface *ifp, + struct in_addr neigh_addr, + struct in_addr upstream, + struct in_addr source_addr, + struct in_addr group_addr, + uint8_t source_flags, + uint16_t holdtime); +void pim_ifchannel_prune(struct interface *ifp, + struct in_addr upstream, + struct in_addr source_addr, + struct in_addr group_addr, + uint8_t source_flags, + uint16_t holdtime); +void pim_ifchannel_local_membership_add(struct interface *ifp, + struct in_addr source_addr, + struct in_addr group_addr); +void pim_ifchannel_local_membership_del(struct interface *ifp, + struct in_addr source_addr, + struct in_addr group_addr); + +void pim_ifchannel_ifjoin_switch(const char *caller, + struct pim_ifchannel *ch, + enum pim_ifjoin_state new_state); +const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state); +const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state); + +int pim_ifchannel_isin_oiflist(struct pim_ifchannel *ch); + +void reset_ifassert_state(struct pim_ifchannel *ch); + +void pim_ifchannel_update_could_assert(struct pim_ifchannel *ch); +void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel *ch); +void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch); + +#endif /* PIM_IFCHANNEL_H */ diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c new file mode 100644 index 00000000..e38ac96a --- /dev/null +++ b/pimd/pim_igmp.c @@ -0,0 +1,1411 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "memory.h" + +#include "pimd.h" +#include "pim_igmp.h" +#include "pim_igmpv3.h" +#include "pim_iface.h" +#include "pim_sock.h" +#include "pim_mroute.h" +#include "pim_str.h" +#include "pim_util.h" +#include "pim_time.h" +#include "pim_zebra.h" + +#define IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE (1) +#define IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE (2) +#define IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE (3) +#define IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE (4) +#define IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES (5) +#define IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES (6) + +static void group_timer_off(struct igmp_group *group); + +static struct igmp_group *find_group_by_addr(struct igmp_sock *igmp, + struct in_addr group_addr); + +static int igmp_sock_open(struct in_addr ifaddr, int ifindex, uint32_t pim_options) +{ + int fd; + int join = 0; + struct in_addr group; + + fd = pim_socket_mcast(IPPROTO_IGMP, ifaddr, 1 /* loop=true */); + if (fd < 0) + return -1; + + if (PIM_IF_TEST_IGMP_LISTEN_ALLROUTERS(pim_options)) { + if (inet_aton(PIM_ALL_ROUTERS, &group)) { + if (!pim_socket_join(fd, group, ifaddr, ifindex)) + ++join; + } + else { + zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s", + __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr), + PIM_ALL_ROUTERS, errno, strerror(errno)); + } + } + + /* + IGMP routers periodically send IGMP general queries to AllSystems=224.0.0.1 + IGMP routers must receive general queries for querier election. + */ + if (inet_aton(PIM_ALL_SYSTEMS, &group)) { + if (!pim_socket_join(fd, group, ifaddr, ifindex)) + ++join; + } + else { + zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s", + __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr), + PIM_ALL_SYSTEMS, errno, strerror(errno)); + } + + if (inet_aton(PIM_ALL_IGMP_ROUTERS, &group)) { + if (!pim_socket_join(fd, group, ifaddr, ifindex)) { + ++join; + } + } + else { + zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s", + __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr), + PIM_ALL_IGMP_ROUTERS, errno, strerror(errno)); + } + + if (!join) { + zlog_err("IGMP socket fd=%d could not join any group on interface address %s", + fd, inet_ntoa(ifaddr)); + close(fd); + fd = -1; + } + + return fd; +} + +#undef IGMP_SOCK_DUMP + +#ifdef IGMP_SOCK_DUMP +static void igmp_sock_dump(array_t *igmp_sock_array) +{ + int size = array_size(igmp_sock_array); + for (int i = 0; i < size; ++i) { + + struct igmp_sock *igmp = array_get(igmp_sock_array, i); + + zlog_debug("%s %s: [%d/%d] igmp_addr=%s fd=%d", + __FILE__, __PRETTY_FUNCTION__, + i, size, + inet_ntoa(igmp->ifaddr), + igmp->fd); + } +} +#endif + +struct igmp_sock *pim_igmp_sock_lookup_ifaddr(struct list *igmp_sock_list, + struct in_addr ifaddr) +{ + struct listnode *sock_node; + struct igmp_sock *igmp; + +#ifdef IGMP_SOCK_DUMP + igmp_sock_dump(igmp_sock_list); +#endif + + for (ALL_LIST_ELEMENTS_RO(igmp_sock_list, sock_node, igmp)) + if (ifaddr.s_addr == igmp->ifaddr.s_addr) + return igmp; + + return 0; +} + +struct igmp_sock *igmp_sock_lookup_by_fd(struct list *igmp_sock_list, + int fd) +{ + struct listnode *sock_node; + struct igmp_sock *igmp; + + for (ALL_LIST_ELEMENTS_RO(igmp_sock_list, sock_node, igmp)) + if (fd == igmp->fd) + return igmp; + + return 0; +} + +static int pim_igmp_other_querier_expire(struct thread *t) +{ + struct igmp_sock *igmp; + + zassert(t); + igmp = THREAD_ARG(t); + zassert(igmp); + + zassert(igmp->t_other_querier_timer); + zassert(!igmp->t_igmp_query_timer); + + if (PIM_DEBUG_IGMP_TRACE) { + char ifaddr_str[100]; + pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); + zlog_debug("%s: Querier %s resuming", + __PRETTY_FUNCTION__, + ifaddr_str); + } + + igmp->t_other_querier_timer = 0; + + /* + We are the current querier, then + re-start sending general queries. + */ + pim_igmp_general_query_on(igmp); + + return 0; +} + +void pim_igmp_other_querier_timer_on(struct igmp_sock *igmp) +{ + long other_querier_present_interval_msec; + struct pim_interface *pim_ifp; + + zassert(igmp); + zassert(igmp->interface); + zassert(igmp->interface->info); + + pim_ifp = igmp->interface->info; + + if (igmp->t_other_querier_timer) { + /* + There is other querier present already, + then reset the other-querier-present timer. + */ + + if (PIM_DEBUG_IGMP_TRACE) { + char ifaddr_str[100]; + pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); + zlog_debug("Querier %s resetting TIMER event for Other-Querier-Present", + ifaddr_str); + } + + THREAD_OFF(igmp->t_other_querier_timer); + zassert(!igmp->t_other_querier_timer); + } + else { + /* + We are the current querier, then stop sending general queries: + igmp->t_igmp_query_timer = 0; + */ + pim_igmp_general_query_off(igmp); + } + + /* + Since this socket is starting the other-querier-present timer, + there should not be periodic query timer for this socket. + */ + zassert(!igmp->t_igmp_query_timer); + + /* + RFC 3376: 8.5. Other Querier Present Interval + + The Other Querier Present Interval is the length of time that must + pass before a multicast router decides that there is no longer + another multicast router which should be the querier. This value + MUST be ((the Robustness Variable) times (the Query Interval)) plus + (one half of one Query Response Interval). + + other_querier_present_interval_msec = \ + igmp->querier_robustness_variable * \ + 1000 * igmp->querier_query_interval + \ + 100 * (pim_ifp->query_max_response_time_dsec >> 1); + */ + other_querier_present_interval_msec = + PIM_IGMP_OQPI_MSEC(igmp->querier_robustness_variable, + igmp->querier_query_interval, + pim_ifp->igmp_query_max_response_time_dsec); + + if (PIM_DEBUG_IGMP_TRACE) { + char ifaddr_str[100]; + pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); + zlog_debug("Querier %s scheduling %ld.%03ld sec TIMER event for Other-Querier-Present", + ifaddr_str, + other_querier_present_interval_msec / 1000, + other_querier_present_interval_msec % 1000); + } + + THREAD_TIMER_MSEC_ON(master, igmp->t_other_querier_timer, + pim_igmp_other_querier_expire, + igmp, other_querier_present_interval_msec); +} + +void pim_igmp_other_querier_timer_off(struct igmp_sock *igmp) +{ + zassert(igmp); + + if (PIM_DEBUG_IGMP_TRACE) { + if (igmp->t_other_querier_timer) { + char ifaddr_str[100]; + pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); + zlog_debug("IGMP querier %s fd=%d cancelling other-querier-present TIMER event on %s", + ifaddr_str, igmp->fd, igmp->interface->name); + } + } + THREAD_OFF(igmp->t_other_querier_timer); + zassert(!igmp->t_other_querier_timer); +} + +static int recv_igmp_query(struct igmp_sock *igmp, int query_version, + int max_resp_code, + struct in_addr from, const char *from_str, + char *igmp_msg, int igmp_msg_len) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + uint8_t resv_s_qrv; + uint8_t s_flag; + uint8_t qrv; + struct in_addr group_addr; + uint16_t recv_checksum; + uint16_t checksum; + + group_addr = *(struct in_addr *)(igmp_msg + 4); + + ifp = igmp->interface; + pim_ifp = ifp->info; + + recv_checksum = *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET); + + /* for computing checksum */ + *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = 0; + + checksum = pim_inet_checksum(igmp_msg, igmp_msg_len); + if (checksum != recv_checksum) { + zlog_warn("Recv IGMP query v%d from %s on %s: checksum mismatch: received=%x computed=%x", + query_version, from_str, ifp->name, recv_checksum, checksum); + return -1; + } + + if (PIM_DEBUG_IGMP_PACKETS) { + char group_str[100]; + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + zlog_debug("Recv IGMP query v%d from %s on %s: size=%d checksum=%x group=%s", + query_version, from_str, ifp->name, + igmp_msg_len, checksum, group_str); + } + + /* + RFC 3376: 6.6.2. Querier Election + + When a router receives a query with a lower IP address, it sets + the Other-Querier-Present timer to Other Querier Present Interval + and ceases to send queries on the network if it was the previously + elected querier. + */ + if (ntohl(from.s_addr) < ntohl(igmp->ifaddr.s_addr)) { + + if (PIM_DEBUG_IGMP_TRACE) { + char ifaddr_str[100]; + pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); + zlog_debug("%s: local address %s (%u) lost querier election to %s (%u)", + ifp->name, + ifaddr_str, ntohl(igmp->ifaddr.s_addr), + from_str, ntohl(from.s_addr)); + } + + pim_igmp_other_querier_timer_on(igmp); + } + + /* + RFC 3376: 4.1.6. QRV (Querier's Robustness Variable) + + Routers adopt the QRV value from the most recently received Query + as their own [Robustness Variable] value, unless that most + recently received QRV was zero, in which case the receivers use + the default [Robustness Variable] value specified in section 8.1 + or a statically configured value. + */ + resv_s_qrv = igmp_msg[8]; + qrv = 7 & resv_s_qrv; + igmp->querier_robustness_variable = qrv ? qrv : pim_ifp->igmp_default_robustness_variable; + + /* + RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code) + + Multicast routers that are not the current querier adopt the QQI + value from the most recently received Query as their own [Query + Interval] value, unless that most recently received QQI was zero, + in which case the receiving routers use the default. + */ + if (igmp->t_other_querier_timer) { + /* other querier present */ + uint8_t qqic; + uint16_t qqi; + qqic = igmp_msg[9]; + qqi = igmp_msg_decode8to16(qqic); + igmp->querier_query_interval = qqi ? qqi : pim_ifp->igmp_default_query_interval; + + if (PIM_DEBUG_IGMP_TRACE) { + char ifaddr_str[100]; + pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); + zlog_debug("Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)", + ifaddr_str, + qqi ? "recv-non-default" : "default", + igmp->querier_query_interval, + qqic, + from_str); + } + } + + /* + RFC 3376: 6.6.1. Timer Updates + + When a router sends or receives a query with a clear Suppress + Router-Side Processing flag, it must update its timers to reflect + the correct timeout values for the group or sources being queried. + + General queries don't trigger timer update. + */ + s_flag = (1 << 3) & resv_s_qrv; + if (!s_flag) { + /* s_flag is clear */ + + if (PIM_INADDR_IS_ANY(group_addr)) { + /* this is a general query */ + + /* log that general query should have the s_flag set */ + zlog_warn("General IGMP query v%d from %s on %s: Router-Side Processing flag is clear", + query_version, from_str, ifp->name); + } + else { + struct igmp_group *group; + + /* this is a non-general query: perform timer updates */ + + group = find_group_by_addr(igmp, group_addr); + if (group) { + int recv_num_sources = ntohs(*(uint16_t *)(igmp_msg + IGMP_V3_NUMSOURCES_OFFSET)); + + /* + RFC 3376: 6.6.1. Timer Updates + Query Q(G,A): Source Timer for sources in A are lowered to LMQT + Query Q(G): Group Timer is lowered to LMQT + */ + if (recv_num_sources < 1) { + /* Query Q(G): Group Timer is lowered to LMQT */ + + igmp_group_timer_lower_to_lmqt(group); + } + else { + /* Query Q(G,A): Source Timer for sources in A are lowered to LMQT */ + + /* Scan sources in query and lower their timers to LMQT */ + struct in_addr *sources = (struct in_addr *)(igmp_msg + IGMP_V3_SOURCES_OFFSET); + for (int i = 0; i < recv_num_sources; ++i) { + struct in_addr src_addr = sources[i]; + struct igmp_source *src = igmp_find_source_by_addr(group, src_addr); + if (src) { + igmp_source_timer_lower_to_lmqt(src); + } + } + } + + } + else { + char group_str[100]; + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + zlog_warn("IGMP query v%d from %s on %s: could not find group %s for timer update", + query_version, from_str, ifp->name, group_str); + } + } + } /* s_flag is clear: timer updates */ + + return 0; +} + +static int igmp_v3_report(struct igmp_sock *igmp, + struct in_addr from, const char *from_str, + char *igmp_msg, int igmp_msg_len) +{ + uint16_t recv_checksum; + uint16_t checksum; + int num_groups; + uint8_t *group_record; + uint8_t *report_pastend = (uint8_t *) igmp_msg + igmp_msg_len; + struct interface *ifp = igmp->interface; + + if (igmp_msg_len < IGMP_V3_MSG_MIN_SIZE) { + zlog_warn("Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d", + from_str, ifp->name, igmp_msg_len, IGMP_V3_MSG_MIN_SIZE); + return -1; + } + + recv_checksum = *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET); + + /* for computing checksum */ + *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = 0; + + checksum = pim_inet_checksum(igmp_msg, igmp_msg_len); + if (checksum != recv_checksum) { + zlog_warn("Recv IGMP report v3 from %s on %s: checksum mismatch: received=%x computed=%x", + from_str, ifp->name, recv_checksum, checksum); + return -1; + } + + num_groups = ntohs(*(uint16_t *) (igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET)); + if (num_groups < 1) { + zlog_warn("Recv IGMP report v3 from %s on %s: missing group records", + from_str, ifp->name); + return -1; + } + + if (PIM_DEBUG_IGMP_PACKETS) { + zlog_debug("Recv IGMP report v3 from %s on %s: size=%d checksum=%x groups=%d", + from_str, ifp->name, igmp_msg_len, checksum, num_groups); + } + + group_record = (uint8_t *) igmp_msg + IGMP_V3_REPORT_GROUPPRECORD_OFFSET; + + /* Scan groups */ + for (int i = 0; i < num_groups; ++i) { + struct in_addr rec_group; + uint8_t *sources; + uint8_t *src; + int rec_type; + int rec_auxdatalen; + int rec_num_sources; + int j; + + if ((group_record + IGMP_V3_GROUP_RECORD_MIN_SIZE) > report_pastend) { + zlog_warn("Recv IGMP report v3 from %s on %s: group record beyond report end", + from_str, ifp->name); + return -1; + } + + rec_type = group_record[IGMP_V3_GROUP_RECORD_TYPE_OFFSET]; + rec_auxdatalen = group_record[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET]; + rec_num_sources = ntohs(* (uint16_t *) (group_record + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET)); + + rec_group = *(struct in_addr *)(group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET); + + if (PIM_DEBUG_IGMP_PACKETS) { + zlog_debug("Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%s", + from_str, ifp->name, i, rec_type, rec_auxdatalen, rec_num_sources, inet_ntoa(rec_group)); + } + + /* Scan sources */ + + sources = group_record + IGMP_V3_GROUP_RECORD_SOURCE_OFFSET; + + for (j = 0, src = sources; j < rec_num_sources; ++j, src += 4) { + + if ((src + 4) > report_pastend) { + zlog_warn("Recv IGMP report v3 from %s on %s: group source beyond report end", + from_str, ifp->name); + return -1; + } + + if (PIM_DEBUG_IGMP_PACKETS) { + char src_str[200]; + + if (!inet_ntop(AF_INET, src, src_str , sizeof(src_str))) + sprintf(src_str, ""); + + zlog_debug("Recv IGMP report v3 from %s on %s: record=%d group=%s source=%s", + from_str, ifp->name, i, inet_ntoa(rec_group), src_str); + } + } /* for (sources) */ + + switch (rec_type) { + case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE: + igmpv3_report_isin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources); + break; + case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE: + igmpv3_report_isex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources); + break; + case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE: + igmpv3_report_toin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources); + break; + case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE: + igmpv3_report_toex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources); + break; + case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES: + igmpv3_report_allow(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources); + break; + case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES: + igmpv3_report_block(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources); + break; + default: + zlog_warn("Recv IGMP report v3 from %s on %s: unknown record type: type=%d", + from_str, ifp->name, rec_type); + } + + group_record += 8 + (rec_num_sources << 2) + (rec_auxdatalen << 2); + + } /* for (group records) */ + + return 0; +} + +static void on_trace(const char *label, + struct interface *ifp, struct in_addr from) +{ + if (PIM_DEBUG_IGMP_TRACE) { + char from_str[100]; + pim_inet4_dump("", from, from_str, sizeof(from_str)); + zlog_debug("%s: from %s on %s", + label, from_str, ifp->name); + } +} + +static int igmp_v2_report(struct igmp_sock *igmp, + struct in_addr from, const char *from_str, + char *igmp_msg, int igmp_msg_len) +{ + struct interface *ifp = igmp->interface; + struct igmp_group *group; + struct in_addr group_addr; + + on_trace(__PRETTY_FUNCTION__, igmp->interface, from); + + if (igmp_msg_len != IGMP_V12_MSG_SIZE) { + zlog_warn("Recv IGMP report v2 from %s on %s: size=%d other than correct=%d", + from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE); + return -1; + } + + if (PIM_DEBUG_IGMP_TRACE) { + zlog_warn("%s %s: FIXME WRITEME", + __FILE__, __PRETTY_FUNCTION__); + } + + group_addr = *(struct in_addr *)(igmp_msg + 4); + + /* non-existant group is created as INCLUDE {empty} */ + group = igmp_add_group_by_addr(igmp, group_addr, ifp->name); + if (!group) { + return -1; + } + + group->last_igmp_v2_report_dsec = pim_time_monotonic_dsec(); + + return 0; +} + +static int igmp_v2_leave(struct igmp_sock *igmp, + struct in_addr from, const char *from_str, + char *igmp_msg, int igmp_msg_len) +{ + struct interface *ifp = igmp->interface; + + on_trace(__PRETTY_FUNCTION__, igmp->interface, from); + + if (igmp_msg_len != IGMP_V12_MSG_SIZE) { + zlog_warn("Recv IGMP leave v2 from %s on %s: size=%d other than correct=%d", + from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE); + return -1; + } + + if (PIM_DEBUG_IGMP_TRACE) { + zlog_warn("%s %s: FIXME WRITEME", + __FILE__, __PRETTY_FUNCTION__); + } + + return 0; +} + +static int igmp_v1_report(struct igmp_sock *igmp, + struct in_addr from, const char *from_str, + char *igmp_msg, int igmp_msg_len) +{ + struct interface *ifp = igmp->interface; + struct igmp_group *group; + struct in_addr group_addr; + + on_trace(__PRETTY_FUNCTION__, igmp->interface, from); + + if (igmp_msg_len != IGMP_V12_MSG_SIZE) { + zlog_warn("Recv IGMP report v1 from %s on %s: size=%d other than correct=%d", + from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE); + return -1; + } + + if (PIM_DEBUG_IGMP_TRACE) { + zlog_warn("%s %s: FIXME WRITEME", + __FILE__, __PRETTY_FUNCTION__); + } + + group_addr = *(struct in_addr *)(igmp_msg + 4); + + /* non-existant group is created as INCLUDE {empty} */ + group = igmp_add_group_by_addr(igmp, group_addr, ifp->name); + if (!group) { + return -1; + } + + group->last_igmp_v1_report_dsec = pim_time_monotonic_dsec(); + + return 0; +} + +int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len) +{ + struct ip *ip_hdr; + size_t ip_hlen; /* ip header length in bytes */ + char *igmp_msg; + int igmp_msg_len; + int msg_type; + char from_str[100]; + char to_str[100]; + + if (len < sizeof(*ip_hdr)) { + zlog_warn("IGMP packet size=%d shorter than minimum=%d", + len, sizeof(*ip_hdr)); + return -1; + } + + ip_hdr = (struct ip *) buf; + + pim_inet4_dump("", ip_hdr->ip_src, from_str , sizeof(from_str)); + pim_inet4_dump("", ip_hdr->ip_dst, to_str , sizeof(to_str)); + + ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */ + + if (PIM_DEBUG_IGMP_PACKETS) { + zlog_debug("Recv IP packet from %s to %s on %s: size=%d ip_header_size=%d ip_proto=%d", + from_str, to_str, igmp->interface->name, len, ip_hlen, ip_hdr->ip_p); + } + + if (ip_hdr->ip_p != PIM_IP_PROTO_IGMP) { + zlog_warn("IP packet protocol=%d is not IGMP=%d", + ip_hdr->ip_p, PIM_IP_PROTO_IGMP); + return -1; + } + + if (ip_hlen < PIM_IP_HEADER_MIN_LEN) { + zlog_warn("IP packet header size=%d shorter than minimum=%d", + ip_hlen, PIM_IP_HEADER_MIN_LEN); + return -1; + } + if (ip_hlen > PIM_IP_HEADER_MAX_LEN) { + zlog_warn("IP packet header size=%d greater than maximum=%d", + ip_hlen, PIM_IP_HEADER_MAX_LEN); + return -1; + } + + igmp_msg = buf + ip_hlen; + msg_type = *igmp_msg; + igmp_msg_len = len - ip_hlen; + + if (PIM_DEBUG_IGMP_PACKETS) { + zlog_debug("Recv IGMP packet from %s to %s on %s: ttl=%d msg_type=%d msg_size=%d", + from_str, to_str, igmp->interface->name, ip_hdr->ip_ttl, msg_type, + igmp_msg_len); + } + + if (igmp_msg_len < PIM_IGMP_MIN_LEN) { + zlog_warn("IGMP message size=%d shorter than minimum=%d", + igmp_msg_len, PIM_IGMP_MIN_LEN); + return -1; + } + + switch (msg_type) { + case PIM_IGMP_MEMBERSHIP_QUERY: + { + int max_resp_code = igmp_msg[1]; + int query_version; + + /* + RFC 3376: 7.1. Query Version Distinctions + IGMPv1 Query: length = 8 octets AND Max Resp Code field is zero + IGMPv2 Query: length = 8 octets AND Max Resp Code field is non-zero + IGMPv3 Query: length >= 12 octets + */ + + if (igmp_msg_len == 8) { + query_version = max_resp_code ? 2 : 1; + } + else if (igmp_msg_len >= 12) { + query_version = 3; + } + else { + zlog_warn("Unknown IGMP query version"); + return -1; + } + + return recv_igmp_query(igmp, query_version, max_resp_code, + ip_hdr->ip_src, from_str, + igmp_msg, igmp_msg_len); + } + + case PIM_IGMP_V3_MEMBERSHIP_REPORT: + return igmp_v3_report(igmp, ip_hdr->ip_src, from_str, + igmp_msg, igmp_msg_len); + + case PIM_IGMP_V2_MEMBERSHIP_REPORT: + return igmp_v2_report(igmp, ip_hdr->ip_src, from_str, + igmp_msg, igmp_msg_len); + + case PIM_IGMP_V1_MEMBERSHIP_REPORT: + return igmp_v1_report(igmp, ip_hdr->ip_src, from_str, + igmp_msg, igmp_msg_len); + + case PIM_IGMP_V2_LEAVE_GROUP: + return igmp_v2_leave(igmp, ip_hdr->ip_src, from_str, + igmp_msg, igmp_msg_len); + } + + zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type); + + return -1; +} + +static int pim_igmp_general_query(struct thread *t); + +void pim_igmp_general_query_on(struct igmp_sock *igmp) +{ + struct pim_interface *pim_ifp; + int startup_mode; + int query_interval; + + zassert(igmp); + zassert(igmp->interface); + + /* + Since this socket is starting as querier, + there should not exist a timer for other-querier-present. + */ + zassert(!igmp->t_other_querier_timer); + pim_ifp = igmp->interface->info; + zassert(pim_ifp); + + /* + RFC 3376: 8.6. Startup Query Interval + + The Startup Query Interval is the interval between General Queries + sent by a Querier on startup. Default: 1/4 the Query Interval. + */ + startup_mode = igmp->startup_query_count > 0; + if (startup_mode) { + --igmp->startup_query_count; + + /* query_interval = pim_ifp->igmp_default_query_interval >> 2; */ + query_interval = PIM_IGMP_SQI(pim_ifp->igmp_default_query_interval); + } + else { + query_interval = igmp->querier_query_interval; + } + + if (PIM_DEBUG_IGMP_TRACE) { + char ifaddr_str[100]; + pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); + zlog_debug("Querier %s scheduling %d-second (%s) TIMER event for IGMP query on fd=%d", + ifaddr_str, + query_interval, + startup_mode ? "startup" : "non-startup", + igmp->fd); + } + igmp->t_igmp_query_timer = 0; + zassert(!igmp->t_igmp_query_timer); + THREAD_TIMER_ON(master, igmp->t_igmp_query_timer, + pim_igmp_general_query, + igmp, query_interval); +} + +void pim_igmp_general_query_off(struct igmp_sock *igmp) +{ + zassert(igmp); + + if (PIM_DEBUG_IGMP_TRACE) { + if (igmp->t_igmp_query_timer) { + char ifaddr_str[100]; + pim_inet4_dump("", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); + zlog_debug("IGMP querier %s fd=%d cancelling query TIMER event on %s", + ifaddr_str, igmp->fd, igmp->interface->name); + } + } + THREAD_OFF(igmp->t_igmp_query_timer); + zassert(!igmp->t_igmp_query_timer); +} + +/* Issue IGMP general query */ +static int pim_igmp_general_query(struct thread *t) +{ + char query_buf[PIM_IGMP_BUFSIZE_WRITE]; + struct igmp_sock *igmp; + struct in_addr dst_addr; + struct in_addr group_addr; + struct pim_interface *pim_ifp; + + zassert(t); + + igmp = THREAD_ARG(t); + + zassert(igmp); + zassert(igmp->interface); + zassert(igmp->interface->info); + + pim_ifp = igmp->interface->info; + + /* + RFC3376: 4.1.12. IP Destination Addresses for Queries + + In IGMPv3, General Queries are sent with an IP destination address + of 224.0.0.1, the all-systems multicast address. Group-Specific + and Group-and-Source-Specific Queries are sent with an IP + destination address equal to the multicast address of interest. + */ + + dst_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP); + group_addr.s_addr = PIM_NET_INADDR_ANY; + + if (PIM_DEBUG_IGMP_TRACE) { + char querier_str[100]; + char dst_str[100]; + pim_inet4_dump("", igmp->ifaddr, querier_str, + sizeof(querier_str)); + pim_inet4_dump("", dst_addr, dst_str, sizeof(dst_str)); + zlog_debug("Querier %s issuing IGMP general query to %s on %s", + querier_str, dst_str, igmp->interface->name); + } + + pim_igmp_send_membership_query(0 /* igmp_group */, + igmp->fd, + igmp->interface->name, + query_buf, + sizeof(query_buf), + 0 /* num_sources */, + dst_addr, + group_addr, + pim_ifp->igmp_query_max_response_time_dsec, + 1 /* s_flag: always set for general queries */, + igmp->querier_robustness_variable, + igmp->querier_query_interval); + + pim_igmp_general_query_on(igmp); + + return 0; +} + +static int pim_igmp_read(struct thread *t); + +static void igmp_read_on(struct igmp_sock *igmp) +{ + zassert(igmp); + + if (PIM_DEBUG_IGMP_TRACE) { + zlog_debug("Scheduling READ event on IGMP socket fd=%d", + igmp->fd); + } + igmp->t_igmp_read = 0; + zassert(!igmp->t_igmp_read); + THREAD_READ_ON(master, igmp->t_igmp_read, pim_igmp_read, igmp, igmp->fd); +} + +static int pim_igmp_read(struct thread *t) +{ + struct igmp_sock *igmp; + int fd; + struct sockaddr_in from; + struct sockaddr_in to; + socklen_t fromlen = sizeof(from); + socklen_t tolen = sizeof(to); + char buf[PIM_IGMP_BUFSIZE_READ]; + int len; + int ifindex = -1; + int result = -1; /* defaults to bad */ + + zassert(t); + + igmp = THREAD_ARG(t); + + zassert(igmp); + + fd = THREAD_FD(t); + + zassert(fd == igmp->fd); + + len = pim_socket_recvfromto(fd, buf, sizeof(buf), + &from, &fromlen, + &to, &tolen, + &ifindex); + if (len < 0) { + zlog_warn("Failure receiving IP IGMP packet on fd=%d: errno=%d: %s", + fd, errno, strerror(errno)); + goto done; + } + + if (PIM_DEBUG_IGMP_PACKETS) { + char from_str[100]; + char to_str[100]; + + if (!inet_ntop(AF_INET, &from.sin_addr, from_str, sizeof(from_str))) + sprintf(from_str, ""); + if (!inet_ntop(AF_INET, &to.sin_addr, to_str, sizeof(to_str))) + sprintf(to_str, ""); + + zlog_debug("Recv IP IGMP pkt size=%d from %s to %s on fd=%d on ifindex=%d (sock_ifindex=%d)", + len, from_str, to_str, fd, ifindex, igmp->interface->ifindex); + } + +#ifdef PIM_CHECK_RECV_IFINDEX_SANITY + /* ifindex sanity check */ + if (ifindex != (int) igmp->interface->ifindex) { + char from_str[100]; + char to_str[100]; + struct interface *ifp; + + if (!inet_ntop(AF_INET, &from.sin_addr, from_str , sizeof(from_str))) + sprintf(from_str, ""); + if (!inet_ntop(AF_INET, &to.sin_addr, to_str , sizeof(to_str))) + sprintf(to_str, ""); + + ifp = if_lookup_by_index(ifindex); + if (ifp) { + zassert(ifindex == (int) ifp->ifindex); + } + +#ifdef PIM_REPORT_RECV_IFINDEX_MISMATCH + zlog_warn("Interface mismatch: recv IGMP pkt from %s to %s on fd=%d: recv_ifindex=%d (%s) sock_ifindex=%d (%s)", + from_str, to_str, fd, + ifindex, ifp ? ifp->name : "", + igmp->interface->ifindex, igmp->interface->name); +#endif + goto done; + } +#endif + + if (pim_igmp_packet(igmp, buf, len)) { + goto done; + } + + result = 0; /* good */ + + done: + igmp_read_on(igmp); + + return result; +} + +static void sock_close(struct igmp_sock *igmp) +{ + pim_igmp_other_querier_timer_off(igmp); + pim_igmp_general_query_off(igmp); + + if (PIM_DEBUG_IGMP_TRACE) { + if (igmp->t_igmp_read) { + zlog_debug("Cancelling READ event on IGMP socket %s fd=%d on interface %s", + inet_ntoa(igmp->ifaddr), igmp->fd, + igmp->interface->name); + } + } + THREAD_OFF(igmp->t_igmp_read); + zassert(!igmp->t_igmp_read); + + if (close(igmp->fd)) { + zlog_err("Failure closing IGMP socket %s fd=%d on interface %s: errno=%d: %s", + inet_ntoa(igmp->ifaddr), igmp->fd, igmp->interface->name, + errno, strerror(errno)); + } + + if (PIM_DEBUG_IGMP_TRACE) { + zlog_debug("Deleted IGMP socket %s fd=%d on interface %s", + inet_ntoa(igmp->ifaddr), igmp->fd, igmp->interface->name); + } +} + +void igmp_startup_mode_on(struct igmp_sock *igmp) +{ + struct pim_interface *pim_ifp; + + pim_ifp = igmp->interface->info; + + /* + RFC 3376: 8.7. Startup Query Count + + The Startup Query Count is the number of Queries sent out on + startup, separated by the Startup Query Interval. Default: the + Robustness Variable. + */ + igmp->startup_query_count = igmp->querier_robustness_variable; + + /* + Since we're (re)starting, reset QQI to default Query Interval + */ + igmp->querier_query_interval = pim_ifp->igmp_default_query_interval; +} + +static void igmp_group_free(struct igmp_group *group) +{ + zassert(!group->t_group_query_retransmit_timer); + zassert(!group->t_group_timer); + zassert(group->group_source_list); + zassert(!listcount(group->group_source_list)); + + list_free(group->group_source_list); + + XFREE(MTYPE_PIM_IGMP_GROUP, group); +} + +static void igmp_group_delete(struct igmp_group *group) +{ + struct listnode *src_node; + struct listnode *src_nextnode; + struct igmp_source *src; + + if (PIM_DEBUG_IGMP_TRACE) { + char group_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + zlog_debug("Deleting IGMP group %s from socket %d interface %s", + group_str, + group->group_igmp_sock->fd, + group->group_igmp_sock->interface->name); + } + + for (ALL_LIST_ELEMENTS(group->group_source_list, src_node, src_nextnode, src)) { + igmp_source_delete(src); + } + + if (group->t_group_query_retransmit_timer) { + THREAD_OFF(group->t_group_query_retransmit_timer); + zassert(!group->t_group_query_retransmit_timer); + } + + group_timer_off(group); + listnode_delete(group->group_igmp_sock->igmp_group_list, group); + igmp_group_free(group); +} + +void igmp_group_delete_empty_include(struct igmp_group *group) +{ + zassert(!group->group_filtermode_isexcl); + zassert(!listcount(group->group_source_list)); + + igmp_group_delete(group); +} + +void igmp_sock_free(struct igmp_sock *igmp) +{ + zassert(!igmp->t_igmp_read); + zassert(!igmp->t_igmp_query_timer); + zassert(!igmp->t_other_querier_timer); + zassert(igmp->igmp_group_list); + zassert(!listcount(igmp->igmp_group_list)); + + list_free(igmp->igmp_group_list); + + XFREE(MTYPE_PIM_IGMP_SOCKET, igmp); +} + +void igmp_sock_delete(struct igmp_sock *igmp) +{ + struct pim_interface *pim_ifp; + struct listnode *grp_node; + struct listnode *grp_nextnode; + struct igmp_group *grp; + + for (ALL_LIST_ELEMENTS(igmp->igmp_group_list, grp_node, grp_nextnode, grp)) { + igmp_group_delete(grp); + } + + sock_close(igmp); + + pim_ifp = igmp->interface->info; + + listnode_delete(pim_ifp->igmp_socket_list, igmp); + + igmp_sock_free(igmp); +} + +static struct igmp_sock *igmp_sock_new(int fd, + struct in_addr ifaddr, + struct interface *ifp) +{ + struct pim_interface *pim_ifp; + struct igmp_sock *igmp; + + pim_ifp = ifp->info; + + if (PIM_DEBUG_IGMP_TRACE) { + zlog_debug("Creating IGMP socket fd=%d for address %s on interface %s", + fd, inet_ntoa(ifaddr), ifp->name); + } + + igmp = XMALLOC(MTYPE_PIM_IGMP_SOCKET, sizeof(*igmp)); + if (!igmp) { + zlog_warn("%s %s: XMALLOC() failure", + __FILE__, __PRETTY_FUNCTION__); + return 0; + } + + igmp->igmp_group_list = list_new(); + if (!igmp->igmp_group_list) { + zlog_err("%s %s: failure: igmp_group_list = list_new()", + __FILE__, __PRETTY_FUNCTION__); + return 0; + } + igmp->igmp_group_list->del = (void (*)(void *)) igmp_group_free; + + igmp->fd = fd; + igmp->interface = ifp; + igmp->ifaddr = ifaddr; + igmp->t_igmp_read = 0; + igmp->t_igmp_query_timer = 0; + igmp->t_other_querier_timer = 0; /* no other querier present */ + igmp->querier_robustness_variable = pim_ifp->igmp_default_robustness_variable; + igmp->sock_creation = pim_time_monotonic_sec(); + + /* + igmp_startup_mode_on() will reset QQI: + + igmp->querier_query_interval = pim_ifp->igmp_default_query_interval; + */ + igmp_startup_mode_on(igmp); + + igmp_read_on(igmp); + pim_igmp_general_query_on(igmp); + + return igmp; +} + +struct igmp_sock *pim_igmp_sock_add(struct list *igmp_sock_list, + struct in_addr ifaddr, + struct interface *ifp) +{ + struct pim_interface *pim_ifp; + struct igmp_sock *igmp; + int fd; + + pim_ifp = ifp->info; + + fd = igmp_sock_open(ifaddr, ifp->ifindex, pim_ifp->options); + if (fd < 0) { + zlog_warn("Could not open IGMP socket for %s on %s", + inet_ntoa(ifaddr), ifp->name); + return 0; + } + + igmp = igmp_sock_new(fd, ifaddr, ifp); + if (!igmp) { + zlog_err("%s %s: igmp_sock_new() failure", + __FILE__, __PRETTY_FUNCTION__); + close(fd); + return 0; + } + + listnode_add(igmp_sock_list, igmp); + +#ifdef IGMP_SOCK_DUMP + igmp_sock_dump(igmp_sock_array); +#endif + + return igmp; +} + +/* + RFC 3376: 6.5. Switching Router Filter-Modes + + When a router's filter-mode for a group is EXCLUDE and the group + timer expires, the router filter-mode for the group transitions to + INCLUDE. + + A router uses source records with running source timers as its state + for the switch to a filter-mode of INCLUDE. If there are any source + records with source timers greater than zero (i.e., requested to be + forwarded), a router switches to filter-mode of INCLUDE using those + source records. Source records whose timers are zero (from the + previous EXCLUDE mode) are deleted. + */ +static int igmp_group_timer(struct thread *t) +{ + struct igmp_group *group; + + zassert(t); + group = THREAD_ARG(t); + zassert(group); + + if (PIM_DEBUG_IGMP_TRACE) { + char group_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + zlog_debug("%s: Timer for group %s on interface %s", + __PRETTY_FUNCTION__, + group_str, group->group_igmp_sock->interface->name); + } + + zassert(group->group_filtermode_isexcl); + + group->t_group_timer = 0; + group->group_filtermode_isexcl = 0; + + /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */ + igmp_anysource_forward_stop(group); + + igmp_source_delete_expired(group->group_source_list); + + zassert(!group->t_group_timer); + zassert(!group->group_filtermode_isexcl); + + /* + RFC 3376: 6.2.2. Definition of Group Timers + + If there are no more source records for the group, delete group + record. + */ + if (listcount(group->group_source_list) < 1) { + igmp_group_delete_empty_include(group); + } + + return 0; +} + +static void group_timer_off(struct igmp_group *group) +{ + if (!group->t_group_timer) + return; + + if (PIM_DEBUG_IGMP_TRACE) { + char group_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + zlog_debug("Cancelling TIMER event for group %s on %s", + group_str, group->group_igmp_sock->interface->name); + } + + THREAD_OFF(group->t_group_timer); + zassert(!group->t_group_timer); +} + +void igmp_group_timer_on(struct igmp_group *group, + long interval_msec, const char *ifname) +{ + group_timer_off(group); + + if (PIM_DEBUG_IGMP_EVENTS) { + char group_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s on %s", + interval_msec / 1000, + interval_msec % 1000, + group_str, ifname); + } + + /* + RFC 3376: 6.2.2. Definition of Group Timers + + The group timer is only used when a group is in EXCLUDE mode and + it represents the time for the *filter-mode* of the group to + expire and switch to INCLUDE mode. + */ + zassert(group->group_filtermode_isexcl); + + THREAD_TIMER_MSEC_ON(master, group->t_group_timer, + igmp_group_timer, + group, interval_msec); +} + +static struct igmp_group *find_group_by_addr(struct igmp_sock *igmp, + struct in_addr group_addr) +{ + struct igmp_group *group; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, node, group)) + if (group_addr.s_addr == group->group_addr.s_addr) + return group; + + return 0; +} + +struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp, + struct in_addr group_addr, + const char *ifname) +{ + struct igmp_group *group; + + group = find_group_by_addr(igmp, group_addr); + if (group) { + return group; + } + + /* + Non-existant group is created as INCLUDE {empty}: + + RFC 3376 - 5.1. Action on Change of Interface State + + If no interface state existed for that multicast address before + the change (i.e., the change consisted of creating a new + per-interface record), or if no state exists after the change + (i.e., the change consisted of deleting a per-interface record), + then the "non-existent" state is considered to have a filter mode + of INCLUDE and an empty source list. + */ + + group = XMALLOC(MTYPE_PIM_IGMP_GROUP, sizeof(*group)); + if (!group) { + zlog_warn("%s %s: XMALLOC() failure", + __FILE__, __PRETTY_FUNCTION__); + return 0; /* error, not found, could not create */ + } + + group->group_source_list = list_new(); + if (!group->group_source_list) { + zlog_warn("%s %s: list_new() failure", + __FILE__, __PRETTY_FUNCTION__); + XFREE(MTYPE_PIM_IGMP_GROUP, group); /* discard group */ + return 0; /* error, not found, could not initialize */ + } + group->group_source_list->del = (void (*)(void *)) igmp_source_free; + + group->t_group_timer = 0; + group->t_group_query_retransmit_timer = 0; + group->group_specific_query_retransmit_count = 0; + group->group_addr = group_addr; + group->group_igmp_sock = igmp; + group->last_igmp_v1_report_dsec = -1; + group->last_igmp_v2_report_dsec = -1; + group->group_creation = pim_time_monotonic_sec(); + + /* initialize new group as INCLUDE {empty} */ + group->group_filtermode_isexcl = 0; /* 0=INCLUDE, 1=EXCLUDE */ + + listnode_add(igmp->igmp_group_list, group); + + if (PIM_DEBUG_IGMP_TRACE) { + char group_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + zlog_debug("Creating new IGMP group %s on socket %d interface %s", + group_str, group->group_igmp_sock->fd, ifname); + } + + /* + RFC 3376: 6.2.2. Definition of Group Timers + + The group timer is only used when a group is in EXCLUDE mode and + it represents the time for the *filter-mode* of the group to + expire and switch to INCLUDE mode. + */ + zassert(!group->group_filtermode_isexcl); /* INCLUDE mode */ + zassert(!group->t_group_timer); /* group timer == 0 */ + + /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */ + igmp_anysource_forward_stop(group); + + return group; +} diff --git a/pimd/pim_igmp.h b/pimd/pim_igmp.h new file mode 100644 index 00000000..656147f2 --- /dev/null +++ b/pimd/pim_igmp.h @@ -0,0 +1,172 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_IGMP_H +#define PIM_IGMP_H + +#include + +#include +#include "vty.h" +#include "linklist.h" + +/* + The following sizes are likely to support + any message sent within local MTU. +*/ +#define PIM_IGMP_BUFSIZE_READ (20000) +#define PIM_IGMP_BUFSIZE_WRITE (20000) + +#define PIM_IGMP_MEMBERSHIP_QUERY (0x11) +#define PIM_IGMP_V1_MEMBERSHIP_REPORT (0x12) +#define PIM_IGMP_V2_MEMBERSHIP_REPORT (0x16) +#define PIM_IGMP_V2_LEAVE_GROUP (0x17) +#define PIM_IGMP_V3_MEMBERSHIP_REPORT (0x22) + +#define IGMP_V3_REPORT_HEADER_SIZE (8) +#define IGMP_V3_GROUP_RECORD_MIN_SIZE (8) +#define IGMP_V3_MSG_MIN_SIZE (IGMP_V3_REPORT_HEADER_SIZE + \ + IGMP_V3_GROUP_RECORD_MIN_SIZE) +#define IGMP_V12_MSG_SIZE (8) + +#define IGMP_V3_GROUP_RECORD_TYPE_OFFSET (0) +#define IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET (1) +#define IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET (2) +#define IGMP_V3_GROUP_RECORD_GROUP_OFFSET (4) +#define IGMP_V3_GROUP_RECORD_SOURCE_OFFSET (8) + +/* RFC 3376: 8.1. Robustness Variable - Default: 2 */ +#define IGMP_DEFAULT_ROBUSTNESS_VARIABLE (2) + +/* RFC 3376: 8.2. Query Interval - Default: 125 seconds */ +#define IGMP_GENERAL_QUERY_INTERVAL (125) + +/* RFC 3376: 8.3. Query Response Interval - Default: 100 deciseconds */ +#define IGMP_QUERY_MAX_RESPONSE_TIME_DSEC (100) + +struct igmp_join { + struct in_addr group_addr; + struct in_addr source_addr; + int sock_fd; +}; + +struct igmp_sock { + int fd; + struct interface *interface; + struct in_addr ifaddr; + time_t sock_creation; + + struct thread *t_igmp_read; /* read: IGMP sockets */ + struct thread *t_igmp_query_timer; /* timer: issue IGMP general queries */ + struct thread *t_other_querier_timer; /* timer: other querier present */ + + int querier_query_interval; /* QQI */ + int querier_robustness_variable; /* QRV */ + int startup_query_count; + + struct list *igmp_group_list; /* list of struct igmp_group */ +}; + +struct igmp_sock *pim_igmp_sock_lookup_ifaddr(struct list *igmp_sock_list, + struct in_addr ifaddr); +struct igmp_sock *igmp_sock_lookup_by_fd(struct list *igmp_sock_list, + int fd); +struct igmp_sock *pim_igmp_sock_add(struct list *igmp_sock_list, + struct in_addr ifaddr, + struct interface *ifp); +void igmp_sock_delete(struct igmp_sock *igmp); +void igmp_sock_free(struct igmp_sock *igmp); + +int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len); + +void pim_igmp_general_query_on(struct igmp_sock *igmp); +void pim_igmp_general_query_off(struct igmp_sock *igmp); +void pim_igmp_other_querier_timer_on(struct igmp_sock *igmp); +void pim_igmp_other_querier_timer_off(struct igmp_sock *igmp); + +#define IGMP_SOURCE_MASK_FORWARDING (1 << 0) +#define IGMP_SOURCE_MASK_DELETE (1 << 1) +#define IGMP_SOURCE_MASK_SEND (1 << 2) +#define IGMP_SOURCE_TEST_FORWARDING(flags) ((flags) & IGMP_SOURCE_MASK_FORWARDING) +#define IGMP_SOURCE_TEST_DELETE(flags) ((flags) & IGMP_SOURCE_MASK_DELETE) +#define IGMP_SOURCE_TEST_SEND(flags) ((flags) & IGMP_SOURCE_MASK_SEND) +#define IGMP_SOURCE_DO_FORWARDING(flags) ((flags) |= IGMP_SOURCE_MASK_FORWARDING) +#define IGMP_SOURCE_DO_DELETE(flags) ((flags) |= IGMP_SOURCE_MASK_DELETE) +#define IGMP_SOURCE_DO_SEND(flags) ((flags) |= IGMP_SOURCE_MASK_SEND) +#define IGMP_SOURCE_DONT_FORWARDING(flags) ((flags) &= ~IGMP_SOURCE_MASK_FORWARDING) +#define IGMP_SOURCE_DONT_DELETE(flags) ((flags) &= ~IGMP_SOURCE_MASK_DELETE) +#define IGMP_SOURCE_DONT_SEND(flags) ((flags) &= ~IGMP_SOURCE_MASK_SEND) + +struct igmp_source { + struct in_addr source_addr; + struct thread *t_source_timer; + struct igmp_group *source_group; /* back pointer */ + time_t source_creation; + uint32_t source_flags; + struct channel_oil *source_channel_oil; + + /* + RFC 3376: 6.6.3.2. Building and Sending Group and Source Specific Queries + */ + int source_query_retransmit_count; +}; + +struct igmp_group { + /* + RFC 3376: 6.2.2. Definition of Group Timers + + The group timer is only used when a group is in EXCLUDE mode and it + represents the time for the *filter-mode* of the group to expire and + switch to INCLUDE mode. + */ + struct thread *t_group_timer; + + /* Shared between group-specific and + group-and-source-specific retransmissions */ + struct thread *t_group_query_retransmit_timer; + + /* Counter exclusive for group-specific retransmissions + (not used by group-and-source-specific retransmissions, + since sources have their counters) */ + int group_specific_query_retransmit_count; + + struct in_addr group_addr; + int group_filtermode_isexcl; /* 0=INCLUDE, 1=EXCLUDE */ + struct list *group_source_list; /* list of struct igmp_source */ + time_t group_creation; + struct igmp_sock *group_igmp_sock; /* back pointer */ + int64_t last_igmp_v1_report_dsec; + int64_t last_igmp_v2_report_dsec; +}; + +struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp, + struct in_addr group_addr, + const char *ifname); + +void igmp_group_delete_empty_include(struct igmp_group *group); + +void igmp_startup_mode_on(struct igmp_sock *igmp); + +void igmp_group_timer_on(struct igmp_group *group, + long interval_msec, const char *ifname); + +#endif /* PIM_IGMP_H */ diff --git a/pimd/pim_igmpv3.c b/pimd/pim_igmpv3.c new file mode 100644 index 00000000..f9fa123f --- /dev/null +++ b/pimd/pim_igmpv3.c @@ -0,0 +1,1717 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include +#include "log.h" +#include "memory.h" + +#include "pimd.h" +#include "pim_iface.h" +#include "pim_igmp.h" +#include "pim_igmpv3.h" +#include "pim_str.h" +#include "pim_util.h" +#include "pim_time.h" +#include "pim_zebra.h" +#include "pim_oil.h" + +static void group_retransmit_timer_on(struct igmp_group *group); +static long igmp_group_timer_remain_msec(struct igmp_group *group); +static long igmp_source_timer_remain_msec(struct igmp_source *source); +static void group_query_send(struct igmp_group *group); +static void source_query_send_by_flag(struct igmp_group *group, + int num_sources_tosend); + +static void on_trace(const char *label, + struct interface *ifp, struct in_addr from, + struct in_addr group_addr, + int num_sources, struct in_addr *sources) +{ + if (PIM_DEBUG_IGMP_TRACE) { + char from_str[100]; + char group_str[100]; + + pim_inet4_dump("", from, from_str, sizeof(from_str)); + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + + zlog_debug("%s: from %s on %s: group=%s sources=%d", + label, from_str, ifp->name, group_str, num_sources); + } +} + +int igmp_group_compat_mode(const struct igmp_sock *igmp, + const struct igmp_group *group) +{ + struct pim_interface *pim_ifp; + int64_t now_dsec; + long older_host_present_interval_dsec; + + zassert(igmp); + zassert(igmp->interface); + zassert(igmp->interface->info); + + pim_ifp = igmp->interface->info; + + /* + RFC 3376: 8.13. Older Host Present Interval + + This value MUST be ((the Robustness Variable) times (the Query + Interval)) plus (one Query Response Interval). + + older_host_present_interval_dsec = \ + igmp->querier_robustness_variable * \ + 10 * igmp->querier_query_interval + \ + pim_ifp->query_max_response_time_dsec; + */ + older_host_present_interval_dsec = + PIM_IGMP_OHPI_DSEC(igmp->querier_robustness_variable, + igmp->querier_query_interval, + pim_ifp->igmp_query_max_response_time_dsec); + + now_dsec = pim_time_monotonic_dsec(); + if (now_dsec < 1) { + /* broken timer logged by pim_time_monotonic_dsec() */ + return 3; + } + + if ((now_dsec - group->last_igmp_v1_report_dsec) < older_host_present_interval_dsec) + return 1; /* IGMPv1 */ + + if ((now_dsec - group->last_igmp_v2_report_dsec) < older_host_present_interval_dsec) + return 2; /* IGMPv2 */ + + return 3; /* IGMPv3 */ +} + +void igmp_group_reset_gmi(struct igmp_group *group) +{ + long group_membership_interval_msec; + struct pim_interface *pim_ifp; + struct igmp_sock *igmp; + struct interface *ifp; + + igmp = group->group_igmp_sock; + ifp = igmp->interface; + pim_ifp = ifp->info; + + /* + RFC 3376: 8.4. Group Membership Interval + + The Group Membership Interval is the amount of time that must pass + before a multicast router decides there are no more members of a + group or a particular source on a network. + + This value MUST be ((the Robustness Variable) times (the Query + Interval)) plus (one Query Response Interval). + + group_membership_interval_msec = querier_robustness_variable * + (1000 * querier_query_interval) + + 100 * query_response_interval_dsec; + */ + group_membership_interval_msec = + PIM_IGMP_GMI_MSEC(igmp->querier_robustness_variable, + igmp->querier_query_interval, + pim_ifp->igmp_query_max_response_time_dsec); + + if (PIM_DEBUG_IGMP_TRACE) { + char group_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + zlog_debug("Resetting group %s timer to GMI=%ld.%03ld sec on %s", + group_str, + group_membership_interval_msec / 1000, + group_membership_interval_msec % 1000, + ifp->name); + } + + /* + RFC 3376: 6.2.2. Definition of Group Timers + + The group timer is only used when a group is in EXCLUDE mode and + it represents the time for the *filter-mode* of the group to + expire and switch to INCLUDE mode. + */ + zassert(group->group_filtermode_isexcl); + + igmp_group_timer_on(group, group_membership_interval_msec, ifp->name); +} + +static int igmp_source_timer(struct thread *t) +{ + struct igmp_source *source; + struct igmp_group *group; + + zassert(t); + source = THREAD_ARG(t); + zassert(source); + + group = source->source_group; + + if (PIM_DEBUG_IGMP_TRACE) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); + zlog_debug("%s: Source timer expired for group %s source %s on %s", + __PRETTY_FUNCTION__, + group_str, source_str, + group->group_igmp_sock->interface->name); + } + + zassert(source->t_source_timer); + source->t_source_timer = 0; + + /* + RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules + + Group + Filter-Mode Source Timer Value Action + ----------- ------------------ ------ + INCLUDE TIMER == 0 Suggest to stop forwarding + traffic from source and + remove source record. If + there are no more source + records for the group, delete + group record. + + EXCLUDE TIMER == 0 Suggest to not forward + traffic from source + (DO NOT remove record) + + Source timer switched from (T > 0) to (T == 0): disable forwarding. + */ + + zassert(!source->t_source_timer); + + if (group->group_filtermode_isexcl) { + /* EXCLUDE mode */ + + igmp_source_forward_stop(source); + } + else { + /* INCLUDE mode */ + + /* igmp_source_delete() will stop forwarding source */ + igmp_source_delete(source); + + /* + If there are no more source records for the group, delete group + record. + */ + if (!listcount(group->group_source_list)) { + igmp_group_delete_empty_include(group); + } + } + + return 0; +} + +static void source_timer_off(struct igmp_group *group, + struct igmp_source *source) +{ + if (!source->t_source_timer) + return; + + if (PIM_DEBUG_IGMP_TRACE) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); + zlog_debug("Cancelling TIMER event for group %s source %s on %s", + group_str, source_str, + group->group_igmp_sock->interface->name); + } + + THREAD_OFF(source->t_source_timer); + zassert(!source->t_source_timer); +} + +static void igmp_source_timer_on(struct igmp_group *group, + struct igmp_source *source, + long interval_msec) +{ + source_timer_off(group, source); + + if (PIM_DEBUG_IGMP_EVENTS) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); + zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s source %s on %s", + interval_msec / 1000, + interval_msec % 1000, + group_str, source_str, + group->group_igmp_sock->interface->name); + } + + THREAD_TIMER_MSEC_ON(master, source->t_source_timer, + igmp_source_timer, + source, interval_msec); + zassert(source->t_source_timer); + + /* + RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules + + Source timer switched from (T == 0) to (T > 0): enable forwarding. + */ + igmp_source_forward_start(source); +} + +void igmp_source_reset_gmi(struct igmp_sock *igmp, + struct igmp_group *group, + struct igmp_source *source) +{ + long group_membership_interval_msec; + struct pim_interface *pim_ifp; + struct interface *ifp; + + ifp = igmp->interface; + pim_ifp = ifp->info; + + group_membership_interval_msec = + PIM_IGMP_GMI_MSEC(igmp->querier_robustness_variable, + igmp->querier_query_interval, + pim_ifp->igmp_query_max_response_time_dsec); + + if (PIM_DEBUG_IGMP_TRACE) { + char group_str[100]; + char source_str[100]; + + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); + + zlog_debug("Resetting source %s timer to GMI=%ld.%03ld sec for group %s on %s", + source_str, + group_membership_interval_msec / 1000, + group_membership_interval_msec % 1000, + group_str, + ifp->name); + } + + igmp_source_timer_on(group, source, + group_membership_interval_msec); +} + +static void source_mark_delete_flag(struct list *source_list) +{ + struct listnode *src_node; + struct igmp_source *src; + + for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) { + IGMP_SOURCE_DO_DELETE(src->source_flags); + } +} + +static void source_mark_send_flag(struct list *source_list) +{ + struct listnode *src_node; + struct igmp_source *src; + + for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) { + IGMP_SOURCE_DO_SEND(src->source_flags); + } +} + +static int source_mark_send_flag_by_timer(struct list *source_list) +{ + struct listnode *src_node; + struct igmp_source *src; + int num_marked_sources = 0; + + for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) { + /* Is source timer running? */ + if (src->t_source_timer) { + IGMP_SOURCE_DO_SEND(src->source_flags); + ++num_marked_sources; + } + else { + IGMP_SOURCE_DONT_SEND(src->source_flags); + } + } + + return num_marked_sources; +} + +static void source_clear_send_flag(struct list *source_list) +{ + struct listnode *src_node; + struct igmp_source *src; + + for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) { + IGMP_SOURCE_DONT_SEND(src->source_flags); + } +} + +/* + Any source (*,G) is forwarded only if mode is EXCLUDE {empty} +*/ +static void group_exclude_fwd_anysrc_ifempty(struct igmp_group *group) +{ + zassert(group->group_filtermode_isexcl); + + if (listcount(group->group_source_list) < 1) { + igmp_anysource_forward_start(group); + } +} + +void igmp_source_free(struct igmp_source *source) +{ + /* make sure there is no source timer running */ + zassert(!source->t_source_timer); + + XFREE(MTYPE_PIM_IGMP_GROUP_SOURCE, source); +} + +static void source_channel_oil_detach(struct igmp_source *source) +{ + if (source->source_channel_oil) { + pim_channel_oil_del(source->source_channel_oil); + source->source_channel_oil = 0; + } +} + +void igmp_source_delete(struct igmp_source *source) +{ + struct igmp_group *group; + + group = source->source_group; + + if (PIM_DEBUG_IGMP_TRACE) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); + zlog_debug("Deleting IGMP source %s for group %s from socket %d interface %s", + source_str, group_str, + group->group_igmp_sock->fd, + group->group_igmp_sock->interface->name); + } + + source_timer_off(group, source); + igmp_source_forward_stop(source); + + /* make sure forwarding is disabled */ + zassert(!IGMP_SOURCE_TEST_FORWARDING(source->source_flags)); + + source_channel_oil_detach(source); + + /* + notice that listnode_delete() can't be moved + into igmp_source_free() because the later is + called by list_delete_all_node() + */ + listnode_delete(group->group_source_list, source); + + igmp_source_free(source); + + if (group->group_filtermode_isexcl) { + group_exclude_fwd_anysrc_ifempty(group); + } +} + +static void source_delete_by_flag(struct list *source_list) +{ + struct listnode *src_node; + struct listnode *src_nextnode; + struct igmp_source *src; + + for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src)) + if (IGMP_SOURCE_TEST_DELETE(src->source_flags)) + igmp_source_delete(src); +} + +void igmp_source_delete_expired(struct list *source_list) +{ + struct listnode *src_node; + struct listnode *src_nextnode; + struct igmp_source *src; + + for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src)) + if (!src->t_source_timer) + igmp_source_delete(src); +} + +struct igmp_source *igmp_find_source_by_addr(struct igmp_group *group, + struct in_addr src_addr) +{ + struct listnode *src_node; + struct igmp_source *src; + + for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) + if (src_addr.s_addr == src->source_addr.s_addr) + return src; + + return 0; +} + +static struct igmp_source *source_new(struct igmp_group *group, + struct in_addr src_addr, + const char *ifname) +{ + struct igmp_source *src; + + if (PIM_DEBUG_IGMP_TRACE) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", src_addr, source_str, sizeof(source_str)); + zlog_debug("Creating new IGMP source %s for group %s on socket %d interface %s", + source_str, group_str, + group->group_igmp_sock->fd, + ifname); + } + + src = XMALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE, sizeof(*src)); + if (!src) { + zlog_warn("%s %s: XMALLOC() failure", + __FILE__, __PRETTY_FUNCTION__); + return 0; /* error, not found, could not create */ + } + + src->t_source_timer = 0; + src->source_group = group; /* back pointer */ + src->source_addr = src_addr; + src->source_creation = pim_time_monotonic_sec(); + src->source_flags = 0; + src->source_query_retransmit_count = 0; + src->source_channel_oil = 0; + + listnode_add(group->group_source_list, src); + + zassert(!src->t_source_timer); /* source timer == 0 */ + + /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */ + igmp_anysource_forward_stop(group); + + return src; +} + +static struct igmp_source *add_source_by_addr(struct igmp_sock *igmp, + struct igmp_group *group, + struct in_addr src_addr, + const char *ifname) +{ + struct igmp_source *src; + + src = igmp_find_source_by_addr(group, src_addr); + if (src) { + return src; + } + + src = source_new(group, src_addr, ifname); + if (!src) { + return 0; + } + + return src; +} + +static void allow(struct igmp_sock *igmp, struct in_addr from, + struct in_addr group_addr, + int num_sources, struct in_addr *sources) +{ + struct interface *ifp = igmp->interface; + struct pim_interface *pim_ifp; + struct igmp_group *group; + + pim_ifp = ifp->info; + + /* non-existant group is created as INCLUDE {empty} */ + group = igmp_add_group_by_addr(igmp, group_addr, ifp->name); + if (!group) { + return; + } + + /* scan received sources */ + for (int i = 0; i < num_sources; ++i) { + struct igmp_source *source; + struct in_addr *src_addr; + + src_addr = sources + i; + + source = add_source_by_addr(igmp, group, *src_addr, ifp->name); + if (!source) { + continue; + } + + /* + RFC 3376: 6.4.1. Reception of Current-State Records + + When receiving IS_IN reports for groups in EXCLUDE mode is + sources should be moved from set with (timers = 0) to set with + (timers > 0). + + igmp_source_reset_gmi() below, resetting the source timers to + GMI, accomplishes this. + */ + igmp_source_reset_gmi(igmp, group, source); + + } /* scan received sources */ +} + +void igmpv3_report_isin(struct igmp_sock *igmp, struct in_addr from, + struct in_addr group_addr, + int num_sources, struct in_addr *sources) +{ + on_trace(__PRETTY_FUNCTION__, + igmp->interface, from, group_addr, num_sources, sources); + + allow(igmp, from, group_addr, num_sources, sources); +} + +static void isex_excl(struct igmp_group *group, + int num_sources, struct in_addr *sources) +{ + /* EXCLUDE mode */ + zassert(group->group_filtermode_isexcl); + + /* E.1: set deletion flag for known sources (X,Y) */ + source_mark_delete_flag(group->group_source_list); + + /* scan received sources (A) */ + for (int i = 0; i < num_sources; ++i) { + struct igmp_source *source; + struct in_addr *src_addr; + + src_addr = sources + i; + + /* E.2: lookup reported source from (A) in (X,Y) */ + source = igmp_find_source_by_addr(group, *src_addr); + if (source) { + /* E.3: if found, clear deletion flag: (X*A) or (Y*A) */ + IGMP_SOURCE_DONT_DELETE(source->source_flags); + } + else { + /* E.4: if not found, create source with timer=GMI: (A-X-Y) */ + source = source_new(group, *src_addr, + group->group_igmp_sock->interface->name); + if (!source) { + /* ugh, internal malloc failure, skip source */ + continue; + } + zassert(!source->t_source_timer); /* timer == 0 */ + igmp_source_reset_gmi(group->group_igmp_sock, group, source); + zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */ + } + + } /* scan received sources */ + + /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */ + source_delete_by_flag(group->group_source_list); +} + +static void isex_incl(struct igmp_group *group, + int num_sources, struct in_addr *sources) +{ + /* INCLUDE mode */ + zassert(!group->group_filtermode_isexcl); + + /* I.1: set deletion flag for known sources (A) */ + source_mark_delete_flag(group->group_source_list); + + /* scan received sources (B) */ + for (int i = 0; i < num_sources; ++i) { + struct igmp_source *source; + struct in_addr *src_addr; + + src_addr = sources + i; + + /* I.2: lookup reported source (B) */ + source = igmp_find_source_by_addr(group, *src_addr); + if (source) { + /* I.3: if found, clear deletion flag (A*B) */ + IGMP_SOURCE_DONT_DELETE(source->source_flags); + } + else { + /* I.4: if not found, create source with timer=0 (B-A) */ + source = source_new(group, *src_addr, + group->group_igmp_sock->interface->name); + if (!source) { + /* ugh, internal malloc failure, skip source */ + continue; + } + zassert(!source->t_source_timer); /* (B-A) timer=0 */ + } + + } /* scan received sources */ + + /* I.5: delete all sources marked with deletion flag (A-B) */ + source_delete_by_flag(group->group_source_list); + + group->group_filtermode_isexcl = 1; /* boolean=true */ + + zassert(group->group_filtermode_isexcl); + + group_exclude_fwd_anysrc_ifempty(group); +} + +void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from, + struct in_addr group_addr, + int num_sources, struct in_addr *sources) +{ + struct interface *ifp = igmp->interface; + struct pim_interface *pim_ifp; + struct igmp_group *group; + + on_trace(__PRETTY_FUNCTION__, + ifp, from, group_addr, num_sources, sources); + + pim_ifp = ifp->info; + + /* non-existant group is created as INCLUDE {empty} */ + group = igmp_add_group_by_addr(igmp, group_addr, ifp->name); + if (!group) { + return; + } + + if (group->group_filtermode_isexcl) { + /* EXCLUDE mode */ + isex_excl(group, num_sources, sources); + } + else { + /* INCLUDE mode */ + isex_incl(group, num_sources, sources); + zassert(group->group_filtermode_isexcl); + } + + zassert(group->group_filtermode_isexcl); + + igmp_group_reset_gmi(group); +} + +static void toin_incl(struct igmp_group *group, + int num_sources, struct in_addr *sources) +{ + struct igmp_sock *igmp = group->group_igmp_sock; + int num_sources_tosend = listcount(group->group_source_list); + + /* Set SEND flag for all known sources (A) */ + source_mark_send_flag(group->group_source_list); + + /* Scan received sources (B) */ + for (int i = 0; i < num_sources; ++i) { + struct igmp_source *source; + struct in_addr *src_addr; + + src_addr = sources + i; + + /* Lookup reported source (B) */ + source = igmp_find_source_by_addr(group, *src_addr); + if (source) { + /* If found, clear SEND flag (A*B) */ + IGMP_SOURCE_DONT_SEND(source->source_flags); + --num_sources_tosend; + } + else { + /* If not found, create new source */ + source = source_new(group, *src_addr, + group->group_igmp_sock->interface->name); + if (!source) { + /* ugh, internal malloc failure, skip source */ + continue; + } + } + + /* (B)=GMI */ + igmp_source_reset_gmi(igmp, group, source); + } + + /* Send sources marked with SEND flag: Q(G,A-B) */ + if (num_sources_tosend > 0) { + source_query_send_by_flag(group, num_sources_tosend); + } +} + +static void toin_excl(struct igmp_group *group, + int num_sources, struct in_addr *sources) +{ + struct igmp_sock *igmp = group->group_igmp_sock; + int num_sources_tosend; + + /* Set SEND flag for X (sources with timer > 0) */ + num_sources_tosend = source_mark_send_flag_by_timer(group->group_source_list); + + /* Scan received sources (A) */ + for (int i = 0; i < num_sources; ++i) { + struct igmp_source *source; + struct in_addr *src_addr; + + src_addr = sources + i; + + /* Lookup reported source (A) */ + source = igmp_find_source_by_addr(group, *src_addr); + if (source) { + if (source->t_source_timer) { + /* If found and timer running, clear SEND flag (X*A) */ + IGMP_SOURCE_DONT_SEND(source->source_flags); + --num_sources_tosend; + } + } + else { + /* If not found, create new source */ + source = source_new(group, *src_addr, + group->group_igmp_sock->interface->name); + if (!source) { + /* ugh, internal malloc failure, skip source */ + continue; + } + } + + /* (A)=GMI */ + igmp_source_reset_gmi(igmp, group, source); + } + + /* Send sources marked with SEND flag: Q(G,X-A) */ + if (num_sources_tosend > 0) { + source_query_send_by_flag(group, num_sources_tosend); + } + + /* Send Q(G) */ + group_query_send(group); +} + +void igmpv3_report_toin(struct igmp_sock *igmp, struct in_addr from, + struct in_addr group_addr, + int num_sources, struct in_addr *sources) +{ + struct interface *ifp = igmp->interface; + struct pim_interface *pim_ifp; + struct igmp_group *group; + + on_trace(__PRETTY_FUNCTION__, + ifp, from, group_addr, num_sources, sources); + + pim_ifp = ifp->info; + + /* non-existant group is created as INCLUDE {empty} */ + group = igmp_add_group_by_addr(igmp, group_addr, ifp->name); + if (!group) { + return; + } + + if (group->group_filtermode_isexcl) { + /* EXCLUDE mode */ + toin_excl(group, num_sources, sources); + } + else { + /* INCLUDE mode */ + toin_incl(group, num_sources, sources); + } +} + +static void toex_incl(struct igmp_group *group, + int num_sources, struct in_addr *sources) +{ + int num_sources_tosend = 0; + + zassert(!group->group_filtermode_isexcl); + + /* Set DELETE flag for all known sources (A) */ + source_mark_delete_flag(group->group_source_list); + + /* Clear off SEND flag from all known sources (A) */ + source_clear_send_flag(group->group_source_list); + + /* Scan received sources (B) */ + for (int i = 0; i < num_sources; ++i) { + struct igmp_source *source; + struct in_addr *src_addr; + + src_addr = sources + i; + + /* Lookup reported source (B) */ + source = igmp_find_source_by_addr(group, *src_addr); + if (source) { + /* If found, clear deletion flag: (A*B) */ + IGMP_SOURCE_DONT_DELETE(source->source_flags); + /* and set SEND flag (A*B) */ + IGMP_SOURCE_DO_SEND(source->source_flags); + ++num_sources_tosend; + } + else { + /* If source not found, create source with timer=0: (B-A)=0 */ + source = source_new(group, *src_addr, + group->group_igmp_sock->interface->name); + if (!source) { + /* ugh, internal malloc failure, skip source */ + continue; + } + zassert(!source->t_source_timer); /* (B-A) timer=0 */ + } + + } /* Scan received sources (B) */ + + group->group_filtermode_isexcl = 1; /* boolean=true */ + + /* Delete all sources marked with DELETE flag (A-B) */ + source_delete_by_flag(group->group_source_list); + + /* Send sources marked with SEND flag: Q(G,A*B) */ + if (num_sources_tosend > 0) { + source_query_send_by_flag(group, num_sources_tosend); + } + + zassert(group->group_filtermode_isexcl); + + group_exclude_fwd_anysrc_ifempty(group); +} + +static void toex_excl(struct igmp_group *group, + int num_sources, struct in_addr *sources) +{ + int num_sources_tosend = 0; + + /* set DELETE flag for all known sources (X,Y) */ + source_mark_delete_flag(group->group_source_list); + + /* clear off SEND flag from all known sources (X,Y) */ + source_clear_send_flag(group->group_source_list); + + /* scan received sources (A) */ + for (int i = 0; i < num_sources; ++i) { + struct igmp_source *source; + struct in_addr *src_addr; + + src_addr = sources + i; + + /* lookup reported source (A) in known sources (X,Y) */ + source = igmp_find_source_by_addr(group, *src_addr); + if (source) { + /* if found, clear off DELETE flag from reported source (A) */ + IGMP_SOURCE_DONT_DELETE(source->source_flags); + } + else { + /* if not found, create source with Group Timer: (A-X-Y)=Group Timer */ + long group_timer_msec; + source = source_new(group, *src_addr, + group->group_igmp_sock->interface->name); + if (!source) { + /* ugh, internal malloc failure, skip source */ + continue; + } + + zassert(!source->t_source_timer); /* timer == 0 */ + group_timer_msec = igmp_group_timer_remain_msec(group); + igmp_source_timer_on(group, source, group_timer_msec); + zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */ + + /* make sure source is created with DELETE flag unset */ + zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags)); + } + + /* make sure reported source has DELETE flag unset */ + zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags)); + + if (source->t_source_timer) { + /* if source timer>0 mark SEND flag: Q(G,A-Y) */ + IGMP_SOURCE_DO_SEND(source->source_flags); + ++num_sources_tosend; + } + + } /* scan received sources (A) */ + + /* + delete all sources marked with DELETE flag: + Delete (X-A) + Delete (Y-A) + */ + source_delete_by_flag(group->group_source_list); + + /* send sources marked with SEND flag: Q(G,A-Y) */ + if (num_sources_tosend > 0) { + source_query_send_by_flag(group, num_sources_tosend); + } +} + +void igmpv3_report_toex(struct igmp_sock *igmp, struct in_addr from, + struct in_addr group_addr, + int num_sources, struct in_addr *sources) +{ + struct interface *ifp = igmp->interface; + struct pim_interface *pim_ifp; + struct igmp_group *group; + + on_trace(__PRETTY_FUNCTION__, + ifp, from, group_addr, num_sources, sources); + + pim_ifp = ifp->info; + + /* non-existant group is created as INCLUDE {empty} */ + group = igmp_add_group_by_addr(igmp, group_addr, ifp->name); + if (!group) { + return; + } + + if (group->group_filtermode_isexcl) { + /* EXCLUDE mode */ + toex_excl(group, num_sources, sources); + } + else { + /* INCLUDE mode */ + toex_incl(group, num_sources, sources); + zassert(group->group_filtermode_isexcl); + } + zassert(group->group_filtermode_isexcl); + + /* Group Timer=GMI */ + igmp_group_reset_gmi(group); +} + +void igmpv3_report_allow(struct igmp_sock *igmp, struct in_addr from, + struct in_addr group_addr, + int num_sources, struct in_addr *sources) +{ + on_trace(__PRETTY_FUNCTION__, + igmp->interface, from, group_addr, num_sources, sources); + + allow(igmp, from, group_addr, num_sources, sources); +} + +/* + RFC3376: 6.6.3.1. Building and Sending Group Specific Queries + + When transmitting a group specific query, if the group timer is + larger than LMQT, the "Suppress Router-Side Processing" bit is set + in the query message. +*/ +static void group_retransmit_group(struct igmp_group *group) +{ + char query_buf[PIM_IGMP_BUFSIZE_WRITE]; + struct igmp_sock *igmp; + struct pim_interface *pim_ifp; + long lmqc; /* Last Member Query Count */ + long lmqi_msec; /* Last Member Query Interval */ + long lmqt_msec; /* Last Member Query Time */ + int s_flag; + + igmp = group->group_igmp_sock; + pim_ifp = igmp->interface->info; + + lmqc = igmp->querier_robustness_variable; + lmqi_msec = 100 * pim_ifp->igmp_query_max_response_time_dsec; + lmqt_msec = lmqc * lmqi_msec; + + /* + RFC3376: 6.6.3.1. Building and Sending Group Specific Queries + + When transmitting a group specific query, if the group timer is + larger than LMQT, the "Suppress Router-Side Processing" bit is set + in the query message. + */ + s_flag = igmp_group_timer_remain_msec(group) > lmqt_msec; + + if (PIM_DEBUG_IGMP_TRACE) { + char group_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + zlog_debug("retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d", + group_str, igmp->interface->name, s_flag, + group->group_specific_query_retransmit_count); + } + + /* + RFC3376: 4.1.12. IP Destination Addresses for Queries + + Group-Specific and Group-and-Source-Specific Queries are sent with + an IP destination address equal to the multicast address of + interest. + */ + + pim_igmp_send_membership_query(group, + igmp->fd, + igmp->interface->name, + query_buf, + sizeof(query_buf), + 0 /* num_sources_tosend */, + group->group_addr /* dst_addr */, + group->group_addr /* group_addr */, + pim_ifp->igmp_query_max_response_time_dsec, + s_flag, + igmp->querier_robustness_variable, + igmp->querier_query_interval); +} + +/* + RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries + + When building a group and source specific query for a group G, two + separate query messages are sent for the group. The first one has + the "Suppress Router-Side Processing" bit set and contains all the + sources with retransmission state and timers greater than LMQT. The + second has the "Suppress Router-Side Processing" bit clear and + contains all the sources with retransmission state and timers lower + or equal to LMQT. If either of the two calculated messages does not + contain any sources, then its transmission is suppressed. + */ +static int group_retransmit_sources(struct igmp_group *group, + int send_with_sflag_set) +{ + struct igmp_sock *igmp; + struct pim_interface *pim_ifp; + long lmqc; /* Last Member Query Count */ + long lmqi_msec; /* Last Member Query Interval */ + long lmqt_msec; /* Last Member Query Time */ + char query_buf1[PIM_IGMP_BUFSIZE_WRITE]; /* 1 = with s_flag set */ + char query_buf2[PIM_IGMP_BUFSIZE_WRITE]; /* 2 = with s_flag clear */ + int query_buf1_max_sources; + int query_buf2_max_sources; + struct in_addr *source_addr1; + struct in_addr *source_addr2; + int num_sources_tosend1; + int num_sources_tosend2; + struct listnode *src_node; + struct igmp_source *src; + int num_retransmit_sources_left = 0; + + query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2; + query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2; + + source_addr1 = (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET); + source_addr2 = (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET); + + igmp = group->group_igmp_sock; + pim_ifp = igmp->interface->info; + + lmqc = igmp->querier_robustness_variable; + lmqi_msec = 100 * pim_ifp->igmp_query_max_response_time_dsec; + lmqt_msec = lmqc * lmqi_msec; + + /* Scan all group sources */ + for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) { + + /* Source has retransmission state? */ + if (src->source_query_retransmit_count < 1) + continue; + + if (--src->source_query_retransmit_count > 0) { + ++num_retransmit_sources_left; + } + + /* Copy source address into appropriate query buffer */ + if (igmp_source_timer_remain_msec(src) > lmqt_msec) { + *source_addr1 = src->source_addr; + ++source_addr1; + } + else { + *source_addr2 = src->source_addr; + ++source_addr2; + } + + } + + num_sources_tosend1 = source_addr1 - (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET); + num_sources_tosend2 = source_addr2 - (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET); + + if (PIM_DEBUG_IGMP_TRACE) { + char group_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + zlog_debug("retransmit_grp&src_specific_query: group %s on %s: srcs_with_sflag=%d srcs_wo_sflag=%d will_send_sflag=%d retransmit_src_left=%d", + group_str, igmp->interface->name, + num_sources_tosend1, + num_sources_tosend2, + send_with_sflag_set, + num_retransmit_sources_left); + } + + if (num_sources_tosend1 > 0) { + /* + Send group-and-source-specific query with s_flag set and all + sources with timers greater than LMQT. + */ + + if (send_with_sflag_set) { + + query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2; + if (num_sources_tosend1 > query_buf1_max_sources) { + char group_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + zlog_warn("%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%d (max_sources=%d)", + __PRETTY_FUNCTION__, group_str, igmp->interface->name, + num_sources_tosend1, sizeof(query_buf1), query_buf1_max_sources); + } + else { + /* + RFC3376: 4.1.12. IP Destination Addresses for Queries + + Group-Specific and Group-and-Source-Specific Queries are sent with + an IP destination address equal to the multicast address of + interest. + */ + + pim_igmp_send_membership_query(group, + igmp->fd, + igmp->interface->name, + query_buf1, + sizeof(query_buf1), + num_sources_tosend1, + group->group_addr, + group->group_addr, + pim_ifp->igmp_query_max_response_time_dsec, + 1 /* s_flag */, + igmp->querier_robustness_variable, + igmp->querier_query_interval); + + } + + } /* send_with_sflag_set */ + + } + + if (num_sources_tosend2 > 0) { + /* + Send group-and-source-specific query with s_flag clear and all + sources with timers lower or equal to LMQT. + */ + + query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2; + if (num_sources_tosend2 > query_buf2_max_sources) { + char group_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + zlog_warn("%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%d (max_sources=%d)", + __PRETTY_FUNCTION__, group_str, igmp->interface->name, + num_sources_tosend2, sizeof(query_buf2), query_buf2_max_sources); + } + else { + /* + RFC3376: 4.1.12. IP Destination Addresses for Queries + + Group-Specific and Group-and-Source-Specific Queries are sent with + an IP destination address equal to the multicast address of + interest. + */ + + pim_igmp_send_membership_query(group, + igmp->fd, + igmp->interface->name, + query_buf2, + sizeof(query_buf2), + num_sources_tosend2, + group->group_addr, + group->group_addr, + pim_ifp->igmp_query_max_response_time_dsec, + 0 /* s_flag */, + igmp->querier_robustness_variable, + igmp->querier_query_interval); + + } + } + + return num_retransmit_sources_left; +} + +static int igmp_group_retransmit(struct thread *t) +{ + struct igmp_group *group; + int num_retransmit_sources_left; + int send_with_sflag_set; /* boolean */ + + zassert(t); + group = THREAD_ARG(t); + zassert(group); + + if (PIM_DEBUG_IGMP_TRACE) { + char group_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + zlog_debug("group_retransmit_timer: group %s on %s", + group_str, group->group_igmp_sock->interface->name); + } + + /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */ + if (group->group_specific_query_retransmit_count > 0) { + + /* Retransmit group-specific queries (RFC3376: 6.6.3.1) */ + group_retransmit_group(group); + --group->group_specific_query_retransmit_count; + + /* + RFC3376: 6.6.3.2 + If a group specific query is scheduled to be transmitted at the + same time as a group and source specific query for the same group, + then transmission of the group and source specific message with the + "Suppress Router-Side Processing" bit set may be suppressed. + */ + send_with_sflag_set = 0; /* boolean=false */ + } + else { + send_with_sflag_set = 1; /* boolean=true */ + } + + /* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */ + num_retransmit_sources_left = group_retransmit_sources(group, + send_with_sflag_set); + + group->t_group_query_retransmit_timer = 0; + + /* + Keep group retransmit timer running if there is any retransmit + counter pending + */ + if ((num_retransmit_sources_left > 0) || + (group->group_specific_query_retransmit_count > 0)) { + group_retransmit_timer_on(group); + } + + return 0; +} + +/* + group_retransmit_timer_on: + if group retransmit timer isn't running, starts it; + otherwise, do nothing +*/ +static void group_retransmit_timer_on(struct igmp_group *group) +{ + struct igmp_sock *igmp; + struct pim_interface *pim_ifp; + long lmqi_msec; /* Last Member Query Interval */ + + /* if group retransmit timer is running, do nothing */ + if (group->t_group_query_retransmit_timer) { + return; + } + + igmp = group->group_igmp_sock; + pim_ifp = igmp->interface->info; + + lmqi_msec = 100 * pim_ifp->igmp_query_max_response_time_dsec; + + if (PIM_DEBUG_IGMP_TRACE) { + char group_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + zlog_debug("Scheduling %ld.%03ld sec retransmit timer for group %s on %s", + lmqi_msec / 1000, + lmqi_msec % 1000, + group_str, + igmp->interface->name); + } + + THREAD_TIMER_MSEC_ON(master, group->t_group_query_retransmit_timer, + igmp_group_retransmit, + group, lmqi_msec); +} + +static long igmp_group_timer_remain_msec(struct igmp_group *group) +{ + return pim_time_timer_remain_msec(group->t_group_timer); +} + +static long igmp_source_timer_remain_msec(struct igmp_source *source) +{ + return pim_time_timer_remain_msec(source->t_source_timer); +} + +/* + RFC3376: 6.6.3.1. Building and Sending Group Specific Queries +*/ +static void group_query_send(struct igmp_group *group) +{ + long lmqc; /* Last Member Query Count */ + + lmqc = group->group_igmp_sock->querier_robustness_variable; + + /* lower group timer to lmqt */ + igmp_group_timer_lower_to_lmqt(group); + + /* reset retransmission counter */ + group->group_specific_query_retransmit_count = lmqc; + + /* immediately send group specific query (decrease retransmit counter by 1)*/ + group_retransmit_group(group); + + /* make sure group retransmit timer is running */ + group_retransmit_timer_on(group); +} + +/* + RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries +*/ +static void source_query_send_by_flag(struct igmp_group *group, + int num_sources_tosend) +{ + struct igmp_sock *igmp; + struct pim_interface *pim_ifp; + struct listnode *src_node; + struct igmp_source *src; + long lmqc; /* Last Member Query Count */ + long lmqi_msec; /* Last Member Query Interval */ + long lmqt_msec; /* Last Member Query Time */ + + zassert(num_sources_tosend > 0); + + igmp = group->group_igmp_sock; + pim_ifp = igmp->interface->info; + + lmqc = igmp->querier_robustness_variable; + lmqi_msec = 100 * pim_ifp->igmp_query_max_response_time_dsec; + lmqt_msec = lmqc * lmqi_msec; + + /* + RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries + + (...) for each of the sources in X of group G, with source timer larger + than LMQT: + o Set number of retransmissions for each source to [Last Member + Query Count]. + o Lower source timer to LMQT. + */ + for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) { + if (IGMP_SOURCE_TEST_SEND(src->source_flags)) { + /* source "src" in X of group G */ + if (igmp_source_timer_remain_msec(src) > lmqt_msec) { + src->source_query_retransmit_count = lmqc; + igmp_source_timer_lower_to_lmqt(src); + } + } + } + + /* send group-and-source specific queries */ + group_retransmit_sources(group, 1 /* send_with_sflag_set=true */); + + /* make sure group retransmit timer is running */ + group_retransmit_timer_on(group); +} + +static void block_excl(struct igmp_group *group, + int num_sources, struct in_addr *sources) +{ + int num_sources_tosend = 0; + + /* 1. clear off SEND flag from all known sources (X,Y) */ + source_clear_send_flag(group->group_source_list); + + /* 2. scan received sources (A) */ + for (int i = 0; i < num_sources; ++i) { + struct igmp_source *source; + struct in_addr *src_addr; + + src_addr = sources + i; + + /* lookup reported source (A) in known sources (X,Y) */ + source = igmp_find_source_by_addr(group, *src_addr); + if (!source) { + /* 3: if not found, create source with Group Timer: (A-X-Y)=Group Timer */ + long group_timer_msec; + source = source_new(group, *src_addr, + group->group_igmp_sock->interface->name); + if (!source) { + /* ugh, internal malloc failure, skip source */ + continue; + } + + zassert(!source->t_source_timer); /* timer == 0 */ + group_timer_msec = igmp_group_timer_remain_msec(group); + igmp_source_timer_on(group, source, group_timer_msec); + zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */ + } + + if (source->t_source_timer) { + /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */ + IGMP_SOURCE_DO_SEND(source->source_flags); + ++num_sources_tosend; + } + } + + /* 5. send sources marked with SEND flag: Q(G,A-Y) */ + if (num_sources_tosend > 0) { + source_query_send_by_flag(group, num_sources_tosend); + } +} + +static void block_incl(struct igmp_group *group, + int num_sources, struct in_addr *sources) +{ + int num_sources_tosend = 0; + + /* 1. clear off SEND flag from all known sources (B) */ + source_clear_send_flag(group->group_source_list); + + /* 2. scan received sources (A) */ + for (int i = 0; i < num_sources; ++i) { + struct igmp_source *source; + struct in_addr *src_addr; + + src_addr = sources + i; + + /* lookup reported source (A) in known sources (B) */ + source = igmp_find_source_by_addr(group, *src_addr); + if (source) { + /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */ + IGMP_SOURCE_DO_SEND(source->source_flags); + ++num_sources_tosend; + } + } + + /* 4. send sources marked with SEND flag: Q(G,A*B) */ + if (num_sources_tosend > 0) { + source_query_send_by_flag(group, num_sources_tosend); + } +} + +void igmpv3_report_block(struct igmp_sock *igmp, struct in_addr from, + struct in_addr group_addr, + int num_sources, struct in_addr *sources) +{ + struct interface *ifp = igmp->interface; + struct pim_interface *pim_ifp; + struct igmp_group *group; + + on_trace(__PRETTY_FUNCTION__, + ifp, from, group_addr, num_sources, sources); + + pim_ifp = ifp->info; + + /* non-existant group is created as INCLUDE {empty} */ + group = igmp_add_group_by_addr(igmp, group_addr, ifp->name); + if (!group) { + return; + } + + if (group->group_filtermode_isexcl) { + /* EXCLUDE mode */ + block_excl(group, num_sources, sources); + } + else { + /* INCLUDE mode */ + block_incl(group, num_sources, sources); + } +} + +void igmp_group_timer_lower_to_lmqt(struct igmp_group *group) +{ + struct igmp_sock *igmp; + struct interface *ifp; + struct pim_interface *pim_ifp; + char *ifname; + int lmqi_dsec; /* Last Member Query Interval */ + int lmqc; /* Last Member Query Count */ + int lmqt_msec; /* Last Member Query Time */ + + /* + RFC 3376: 6.2.2. Definition of Group Timers + + The group timer is only used when a group is in EXCLUDE mode and + it represents the time for the *filter-mode* of the group to + expire and switch to INCLUDE mode. + */ + if (!group->group_filtermode_isexcl) { + return; + } + + igmp = group->group_igmp_sock; + ifp = igmp->interface; + pim_ifp = ifp->info; + ifname = ifp->name; + + lmqi_dsec = pim_ifp->igmp_query_max_response_time_dsec; + lmqc = igmp->querier_robustness_variable; + lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */ + + if (PIM_DEBUG_IGMP_TRACE) { + char group_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + zlog_debug("%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec", + __PRETTY_FUNCTION__, + group_str, ifname, + lmqc, lmqi_dsec, lmqt_msec); + } + + zassert(group->group_filtermode_isexcl); + + igmp_group_timer_on(group, lmqt_msec, ifname); +} + +void igmp_source_timer_lower_to_lmqt(struct igmp_source *source) +{ + struct igmp_group *group; + struct igmp_sock *igmp; + struct interface *ifp; + struct pim_interface *pim_ifp; + char *ifname; + int lmqi_dsec; /* Last Member Query Interval */ + int lmqc; /* Last Member Query Count */ + int lmqt_msec; /* Last Member Query Time */ + + group = source->source_group; + igmp = group->group_igmp_sock; + ifp = igmp->interface; + pim_ifp = ifp->info; + ifname = ifp->name; + + lmqi_dsec = pim_ifp->igmp_query_max_response_time_dsec; + lmqc = igmp->querier_robustness_variable; + lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */ + + if (PIM_DEBUG_IGMP_TRACE) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); + zlog_debug("%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec", + __PRETTY_FUNCTION__, + group_str, source_str, ifname, + lmqc, lmqi_dsec, lmqt_msec); + } + + igmp_source_timer_on(group, source, lmqt_msec); +} + +/* + Copy sources to message: + + struct in_addr *sources = (struct in_addr *)(query_buf + IGMP_V3_SOURCES_OFFSET); + if (num_sources > 0) { + struct listnode *node; + struct igmp_source *src; + int i = 0; + + for (ALL_LIST_ELEMENTS_RO(source_list, node, src)) { + sources[i++] = src->source_addr; + } + } +*/ +void pim_igmp_send_membership_query(struct igmp_group *group, + int fd, + const char *ifname, + char *query_buf, + int query_buf_size, + int num_sources, + struct in_addr dst_addr, + struct in_addr group_addr, + int query_max_response_time_dsec, + uint8_t s_flag, + uint8_t querier_robustness_variable, + uint16_t querier_query_interval) +{ + ssize_t msg_size; + uint8_t max_resp_code; + uint8_t qqic; + ssize_t sent; + struct sockaddr_in to; + socklen_t tolen; + uint16_t checksum; + + zassert(num_sources >= 0); + + msg_size = IGMP_V3_SOURCES_OFFSET + (num_sources << 2); + if (msg_size > query_buf_size) { + zlog_err("%s %s: unable to send: msg_size=%d larger than query_buf_size=%d", + __FILE__, __PRETTY_FUNCTION__, + msg_size, query_buf_size); + return; + } + + s_flag = PIM_FORCE_BOOLEAN(s_flag); + zassert((s_flag == 0) || (s_flag == 1)); + + max_resp_code = igmp_msg_encode16to8(query_max_response_time_dsec); + qqic = igmp_msg_encode16to8(querier_query_interval); + + /* + RFC 3376: 4.1.6. QRV (Querier's Robustness Variable) + + If non-zero, the QRV field contains the [Robustness Variable] + value used by the querier, i.e., the sender of the Query. If the + querier's [Robustness Variable] exceeds 7, the maximum value of + the QRV field, the QRV is set to zero. + */ + if (querier_robustness_variable > 7) { + querier_robustness_variable = 0; + } + + query_buf[0] = PIM_IGMP_MEMBERSHIP_QUERY; + query_buf[1] = max_resp_code; + *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = 0; /* for computing checksum */ + *(struct in_addr *)(query_buf + 4) = group_addr; + query_buf[8] = (s_flag << 3) | querier_robustness_variable; + query_buf[9] = qqic; + *(uint16_t *)(query_buf + IGMP_V3_NUMSOURCES_OFFSET) = htons(num_sources); + + checksum = pim_inet_checksum(query_buf, msg_size); + *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = checksum; + + if (PIM_DEBUG_IGMP_PACKETS) { + char dst_str[100]; + char group_str[100]; + pim_inet4_dump("", dst_addr, dst_str, sizeof(dst_str)); + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + zlog_debug("%s: to %s on %s: group=%s sources=%d msg_size=%d s_flag=%x QRV=%u QQI=%u QQIC=%02x checksum=%x", + __PRETTY_FUNCTION__, + dst_str, ifname, group_str, num_sources, + msg_size, s_flag, querier_robustness_variable, + querier_query_interval, qqic, checksum); + } + +#if 0 + memset(&to, 0, sizeof(to)); +#endif + to.sin_family = AF_INET; + to.sin_addr = dst_addr; +#if 0 + to.sin_port = htons(0); +#endif + tolen = sizeof(to); + + sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT, &to, tolen); + if (sent != (ssize_t) msg_size) { + int e = errno; + char dst_str[100]; + char group_str[100]; + pim_inet4_dump("", dst_addr, dst_str, sizeof(dst_str)); + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + if (sent < 0) { + zlog_warn("%s: sendto() failure to %s on %s: group=%s msg_size=%d: errno=%d: %s", + __PRETTY_FUNCTION__, + dst_str, ifname, group_str, msg_size, + e, strerror(e)); + } + else { + zlog_warn("%s: sendto() partial to %s on %s: group=%s msg_size=%d: sent=%d", + __PRETTY_FUNCTION__, + dst_str, ifname, group_str, + msg_size, sent); + } + return; + } + + /* + s_flag sanity test: s_flag must be set for general queries + + RFC 3376: 6.6.1. Timer Updates + + When a router sends or receives a query with a clear Suppress + Router-Side Processing flag, it must update its timers to reflect + the correct timeout values for the group or sources being queried. + + General queries don't trigger timer update. + */ + if (!s_flag) { + /* general query? */ + if (PIM_INADDR_IS_ANY(group_addr)) { + char dst_str[100]; + char group_str[100]; + pim_inet4_dump("", dst_addr, dst_str, sizeof(dst_str)); + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + zlog_warn("%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!", + __PRETTY_FUNCTION__, + dst_str, ifname, group_str, num_sources); + } + } + +} diff --git a/pimd/pim_igmpv3.h b/pimd/pim_igmpv3.h new file mode 100644 index 00000000..bb7e9267 --- /dev/null +++ b/pimd/pim_igmpv3.h @@ -0,0 +1,100 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_IGMPV3_H +#define PIM_IGMPV3_H + +#include +#include "if.h" + +#define IGMP_V3_CHECKSUM_OFFSET (2) +#define IGMP_V3_REPORT_NUMGROUPS_OFFSET (6) +#define IGMP_V3_REPORT_GROUPPRECORD_OFFSET (8) +#define IGMP_V3_NUMSOURCES_OFFSET (10) +#define IGMP_V3_SOURCES_OFFSET (12) + +/* GMI: Group Membership Interval */ +#define PIM_IGMP_GMI_MSEC(qrv,qqi,qri_dsec) ((qrv) * (1000 * (qqi)) + 100 * (qri_dsec)) + +/* OQPI: Other Querier Present Interval */ +#define PIM_IGMP_OQPI_MSEC(qrv,qqi,qri_dsec) ((qrv) * (1000 * (qqi)) + 100 * ((qri_dsec) >> 1)) + +/* SQI: Startup Query Interval */ +#define PIM_IGMP_SQI(qi) (((qi) < 4) ? 1 : ((qi) >> 2)) + +/* LMQT: Last Member Query Time */ +#define PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc) ((lmqc) * (100 * (lmqi_dsec))) + +/* OHPI: Older Host Present Interval */ +#define PIM_IGMP_OHPI_DSEC(qrv,qqi,qri_dsec) ((qrv) * (10 * (qqi)) + (qri_dsec)) + +void igmp_group_reset_gmi(struct igmp_group *group); +void igmp_source_reset_gmi(struct igmp_sock *igmp, + struct igmp_group *group, + struct igmp_source *source); + +void igmp_source_free(struct igmp_source *source); +void igmp_source_delete(struct igmp_source *source); +void igmp_source_delete_expired(struct list *source_list); + +int igmp_group_compat_mode(const struct igmp_sock *igmp, + const struct igmp_group *group); + +void igmpv3_report_isin(struct igmp_sock *igmp, struct in_addr from, + struct in_addr group_addr, + int num_sources, struct in_addr *sources); +void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from, + struct in_addr group_addr, + int num_sources, struct in_addr *sources); +void igmpv3_report_toin(struct igmp_sock *igmp, struct in_addr from, + struct in_addr group_addr, + int num_sources, struct in_addr *sources); +void igmpv3_report_toex(struct igmp_sock *igmp, struct in_addr from, + struct in_addr group_addr, + int num_sources, struct in_addr *sources); +void igmpv3_report_allow(struct igmp_sock *igmp, struct in_addr from, + struct in_addr group_addr, + int num_sources, struct in_addr *sources); +void igmpv3_report_block(struct igmp_sock *igmp, struct in_addr from, + struct in_addr group_addr, + int num_sources, struct in_addr *sources); + +void igmp_group_timer_lower_to_lmqt(struct igmp_group *group); +void igmp_source_timer_lower_to_lmqt(struct igmp_source *source); + +struct igmp_source *igmp_find_source_by_addr(struct igmp_group *group, + struct in_addr src_addr); + +void pim_igmp_send_membership_query(struct igmp_group *group, + int fd, + const char *ifname, + char *query_buf, + int query_buf_size, + int num_sources, + struct in_addr dst_addr, + struct in_addr group_addr, + int query_max_response_time_dsec, + uint8_t s_flag, + uint8_t querier_robustness_variable, + uint16_t querier_query_interval); + +#endif /* PIM_IGMPV3_H */ diff --git a/pimd/pim_join.c b/pimd/pim_join.c new file mode 100644 index 00000000..ce4ec4e6 --- /dev/null +++ b/pimd/pim_join.c @@ -0,0 +1,428 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "log.h" +#include "prefix.h" + +#include "pimd.h" +#include "pim_str.h" +#include "pim_tlv.h" +#include "pim_msg.h" +#include "pim_pim.h" +#include "pim_join.h" +#include "pim_iface.h" +#include "pim_hello.h" +#include "pim_ifchannel.h" + +static void on_trace(const char *label, + struct interface *ifp, struct in_addr src) +{ + if (PIM_DEBUG_PIM_TRACE) { + char src_str[100]; + pim_inet4_dump("", src, src_str, sizeof(src_str)); + zlog_debug("%s: from %s on %s", + label, src_str, ifp->name); + } +} + +static void recv_join(struct interface *ifp, + struct pim_neighbor *neigh, + uint16_t holdtime, + struct in_addr upstream, + struct in_addr group, + struct in_addr source, + uint8_t source_flags) +{ + if (PIM_DEBUG_PIM_TRACE) { + char up_str[100]; + char src_str[100]; + char grp_str[100]; + char neigh_str[100]; + pim_inet4_dump("", upstream, up_str, sizeof(up_str)); + pim_inet4_dump("", source, src_str, sizeof(src_str)); + pim_inet4_dump("", group, grp_str, sizeof(grp_str)); + pim_inet4_dump("", neigh->source_addr, neigh_str, sizeof(neigh_str)); + zlog_warn("%s: join (S,G)=(%s,%s) rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s", + __PRETTY_FUNCTION__, + src_str, grp_str, + source_flags & PIM_RPT_BIT_MASK, + source_flags & PIM_WILDCARD_BIT_MASK, + up_str, holdtime, neigh_str, ifp->name); + } + + /* Restart join expiry timer */ + pim_ifchannel_join_add(ifp, neigh->source_addr, upstream, + source, group, source_flags, holdtime); +} + +static void recv_prune(struct interface *ifp, + struct pim_neighbor *neigh, + uint16_t holdtime, + struct in_addr upstream, + struct in_addr group, + struct in_addr source, + uint8_t source_flags) +{ + if (PIM_DEBUG_PIM_TRACE) { + char up_str[100]; + char src_str[100]; + char grp_str[100]; + char neigh_str[100]; + pim_inet4_dump("", upstream, up_str, sizeof(up_str)); + pim_inet4_dump("", source, src_str, sizeof(src_str)); + pim_inet4_dump("", group, grp_str, sizeof(grp_str)); + pim_inet4_dump("", neigh->source_addr, neigh_str, sizeof(neigh_str)); + zlog_warn("%s: prune (S,G)=(%s,%s) rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s", + __PRETTY_FUNCTION__, + src_str, grp_str, + source_flags & PIM_RPT_BIT_MASK, + source_flags & PIM_WILDCARD_BIT_MASK, + up_str, holdtime, neigh_str, ifp->name); + } + + pim_ifchannel_prune(ifp, upstream, source, group, source_flags, holdtime); +} + +int pim_joinprune_recv(struct interface *ifp, + struct pim_neighbor *neigh, + struct in_addr src_addr, + char *tlv_buf, int tlv_buf_size) +{ + struct prefix msg_upstream_addr; + uint8_t msg_num_groups; + uint16_t msg_holdtime; + int addr_offset; + char *buf; + char *pastend; + int remain; + int group; + + on_trace(__PRETTY_FUNCTION__, ifp, src_addr); + + buf = tlv_buf; + pastend = tlv_buf + tlv_buf_size; + + /* + Parse ucast addr + */ + addr_offset = pim_parse_addr_ucast(ifp->name, src_addr, + &msg_upstream_addr, + buf, pastend - buf); + if (addr_offset < 1) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s", + __PRETTY_FUNCTION__, + src_str, ifp->name); + return -1; + } + buf += addr_offset; + + /* + Check upstream address family + */ + if (msg_upstream_addr.family != AF_INET) { + if (PIM_DEBUG_PIM_TRACE) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: ignoring join/prune directed to unexpected addr family=%d from %s on %s", + __PRETTY_FUNCTION__, + msg_upstream_addr.family, src_str, ifp->name); + } + return -2; + } + + remain = pastend - buf; + if (remain < 4) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: short join/prune message buffer for group list: size=%d minimum=%d from %s on %s", + __PRETTY_FUNCTION__, + remain, 4, src_str, ifp->name); + return -4; + } + + ++buf; /* skip reserved byte */ + msg_num_groups = *(const uint8_t *) buf; + ++buf; + msg_holdtime = ntohs(*(const uint16_t *) buf); + ++buf; + ++buf; + + if (PIM_DEBUG_PIM_TRACE) { + char src_str[100]; + char upstream_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", msg_upstream_addr.u.prefix4, + upstream_str, sizeof(upstream_str)); + zlog_warn("%s: join/prune upstream=%s groups=%d holdtime=%d from %s on %s", + __PRETTY_FUNCTION__, + upstream_str, msg_num_groups, msg_holdtime, + src_str, ifp->name); + } + + /* Scan groups */ + for (group = 0; group < msg_num_groups; ++group) { + struct prefix msg_group_addr; + struct prefix msg_source_addr; + uint8_t msg_source_flags; + uint16_t msg_num_joined_sources; + uint16_t msg_num_pruned_sources; + int source; + + addr_offset = pim_parse_addr_group(ifp->name, src_addr, + &msg_group_addr, + buf, pastend - buf); + if (addr_offset < 1) { + return -5; + } + buf += addr_offset; + + remain = pastend - buf; + if (remain < 4) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: short join/prune buffer for source list: size=%d minimum=%d from %s on %s", + __PRETTY_FUNCTION__, + remain, 4, src_str, ifp->name); + return -6; + } + + msg_num_joined_sources = ntohs(*(const uint16_t *) buf); + buf += 2; + msg_num_pruned_sources = ntohs(*(const uint16_t *) buf); + buf += 2; + + if (PIM_DEBUG_PIM_TRACE) { + char src_str[100]; + char upstream_str[100]; + char group_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", msg_upstream_addr.u.prefix4, + upstream_str, sizeof(upstream_str)); + pim_inet4_dump("", msg_group_addr.u.prefix4, + group_str, sizeof(group_str)); + zlog_warn("%s: join/prune upstream=%s group=%s/%d join_src=%d prune_src=%d from %s on %s", + __PRETTY_FUNCTION__, + upstream_str, group_str, msg_group_addr.prefixlen, + msg_num_joined_sources, msg_num_pruned_sources, + src_str, ifp->name); + } + + /* Scan joined sources */ + for (source = 0; source < msg_num_joined_sources; ++source) { + addr_offset = pim_parse_addr_source(ifp->name, src_addr, + &msg_source_addr, + &msg_source_flags, + buf, pastend - buf); + if (addr_offset < 1) { + return -7; + } + buf += addr_offset; + + recv_join(ifp, neigh, msg_holdtime, + msg_upstream_addr.u.prefix4, + msg_group_addr.u.prefix4, + msg_source_addr.u.prefix4, + msg_source_flags); + } + + /* Scan pruned sources */ + for (source = 0; source < msg_num_pruned_sources; ++source) { + addr_offset = pim_parse_addr_source(ifp->name, src_addr, + &msg_source_addr, + &msg_source_flags, + buf, pastend - buf); + if (addr_offset < 1) { + return -8; + } + buf += addr_offset; + + recv_prune(ifp, neigh, msg_holdtime, + msg_upstream_addr.u.prefix4, + msg_group_addr.u.prefix4, + msg_source_addr.u.prefix4, + msg_source_flags); + } + + } /* scan groups */ + + return 0; +} + +int pim_joinprune_send(struct interface *ifp, + struct in_addr upstream_addr, + struct in_addr source_addr, + struct in_addr group_addr, + int send_join) +{ + struct pim_interface *pim_ifp; + char pim_msg[1000]; + const char *pastend = pim_msg + sizeof(pim_msg); + char *pim_msg_curr = pim_msg + PIM_MSG_HEADER_LEN; /* room for pim header */ + int pim_msg_size; + int remain; + + zassert(ifp); + + pim_ifp = ifp->info; + + if (!pim_ifp) { + zlog_warn("%s: multicast not enabled on interface %s", + __PRETTY_FUNCTION__, + ifp->name); + return -1; + } + + if (PIM_DEBUG_PIM_TRACE) { + char source_str[100]; + char group_str[100]; + char dst_str[100]; + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", upstream_addr, dst_str, sizeof(dst_str)); + zlog_debug("%s: sending %s(S,G)=(%s,%s) to upstream=%s on interface %s", + __PRETTY_FUNCTION__, + send_join ? "Join" : "Prune", + source_str, group_str, dst_str, ifp->name); + } + + if (PIM_INADDR_IS_ANY(upstream_addr)) { + if (PIM_DEBUG_PIM_TRACE) { + char source_str[100]; + char group_str[100]; + char dst_str[100]; + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", upstream_addr, dst_str, sizeof(dst_str)); + zlog_debug("%s: %s(S,G)=(%s,%s): upstream=%s is myself on interface %s", + __PRETTY_FUNCTION__, + send_join ? "Join" : "Prune", + source_str, group_str, dst_str, ifp->name); + } + return 0; + } + + /* + RFC 4601: 4.3.1. Sending Hello Messages + + Thus, if a router needs to send a Join/Prune or Assert message on + an interface on which it has not yet sent a Hello message with the + currently configured IP address, then it MUST immediately send the + relevant Hello message without waiting for the Hello Timer to + expire, followed by the Join/Prune or Assert message. + */ + pim_hello_require(ifp); + + /* + Build PIM message + */ + + remain = pastend - pim_msg_curr; + pim_msg_curr = pim_msg_addr_encode_ipv4_ucast(pim_msg_curr, + remain, + upstream_addr); + if (!pim_msg_curr) { + char dst_str[100]; + pim_inet4_dump("", upstream_addr, dst_str, sizeof(dst_str)); + zlog_warn("%s: failure encoding destination address %s: space left=%d", + __PRETTY_FUNCTION__, dst_str, remain); + return -3; + } + + remain = pastend - pim_msg_curr; + if (remain < 4) { + zlog_warn("%s: group will not fit: space left=%d", + __PRETTY_FUNCTION__, remain); + return -4; + } + + *pim_msg_curr = 0; /* reserved */ + ++pim_msg_curr; + *pim_msg_curr = 1; /* number of groups */ + ++pim_msg_curr; + *((uint16_t *) pim_msg_curr) = htons(PIM_JP_HOLDTIME); + ++pim_msg_curr; + ++pim_msg_curr; + + remain = pastend - pim_msg_curr; + pim_msg_curr = pim_msg_addr_encode_ipv4_group(pim_msg_curr, + remain, + group_addr); + if (!pim_msg_curr) { + char group_str[100]; + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + zlog_warn("%s: failure encoding group address %s: space left=%d", + __PRETTY_FUNCTION__, group_str, remain); + return -5; + } + + remain = pastend - pim_msg_curr; + if (remain < 4) { + zlog_warn("%s: sources will not fit: space left=%d", + __PRETTY_FUNCTION__, remain); + return -6; + } + + /* number of joined sources */ + *((uint16_t *) pim_msg_curr) = htons(send_join ? 1 : 0); + ++pim_msg_curr; + ++pim_msg_curr; + + /* number of pruned sources */ + *((uint16_t *) pim_msg_curr) = htons(send_join ? 0 : 1); + ++pim_msg_curr; + ++pim_msg_curr; + + remain = pastend - pim_msg_curr; + pim_msg_curr = pim_msg_addr_encode_ipv4_source(pim_msg_curr, + remain, + source_addr); + if (!pim_msg_curr) { + char source_str[100]; + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + zlog_warn("%s: failure encoding source address %s: space left=%d", + __PRETTY_FUNCTION__, source_str, remain); + return -7; + } + + /* Add PIM header */ + + pim_msg_size = pim_msg_curr - pim_msg; + + pim_msg_build_header(pim_msg, pim_msg_size, + PIM_MSG_TYPE_JOIN_PRUNE); + + if (pim_msg_send(pim_ifp->pim_sock_fd, + qpim_all_pim_routers_addr, + pim_msg, + pim_msg_size, + ifp->name)) { + zlog_warn("%s: could not send PIM message on interface %s", + __PRETTY_FUNCTION__, ifp->name); + return -8; + } + + return 0; +} diff --git a/pimd/pim_join.h b/pimd/pim_join.h new file mode 100644 index 00000000..4d9407be --- /dev/null +++ b/pimd/pim_join.h @@ -0,0 +1,43 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_JOIN_H +#define PIM_JOIN_H + +#include + +#include "if.h" + +#include "pim_neighbor.h" + +int pim_joinprune_recv(struct interface *ifp, + struct pim_neighbor *neigh, + struct in_addr src_addr, + char *tlv_buf, int tlv_buf_size); + +int pim_joinprune_send(struct interface *ifp, + struct in_addr upstream_addr, + struct in_addr source_addr, + struct in_addr group_addr, + int send_join); + +#endif /* PIM_JOIN_H */ diff --git a/pimd/pim_macro.c b/pimd/pim_macro.c new file mode 100644 index 00000000..3f565325 --- /dev/null +++ b/pimd/pim_macro.c @@ -0,0 +1,437 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "log.h" + +#include "pim_macro.h" +#include "pimd.h" +#include "pim_str.h" +#include "pim_iface.h" +#include "pim_ifchannel.h" + +#define PIM_IFP_I_am_DR(pim_ifp) ((pim_ifp)->pim_dr_addr.s_addr == (pim_ifp)->primary_address.s_addr) + +/* + DownstreamJPState(S,G,I) is the per-interface state machine for + receiving (S,G) Join/Prune messages. + + DownstreamJPState(S,G,I) is either Join or Prune-Pending ? +*/ +static int downstream_jpstate_isjoined(const struct pim_ifchannel *ch) +{ + return ch->ifjoin_state != PIM_IFJOIN_NOINFO; +} + +/* + The clause "local_receiver_include(S,G,I)" is true if the IGMP/MLD + module or other local membership mechanism has determined that local + members on interface I desire to receive traffic sent specifically + by S to G. +*/ +static int local_receiver_include(const struct pim_ifchannel *ch) +{ + /* local_receiver_include(S,G,I) ? */ + return ch->local_ifmembership == PIM_IFMEMBERSHIP_INCLUDE; +} + +/* + RFC 4601: 4.1.6. State Summarization Macros + + The set "joins(S,G)" is the set of all interfaces on which the + router has received (S,G) Joins: + + joins(S,G) = + { all interfaces I such that + DownstreamJPState(S,G,I) is either Join or Prune-Pending } + + DownstreamJPState(S,G,I) is either Join or Prune-Pending ? +*/ +int pim_macro_chisin_joins(const struct pim_ifchannel *ch) +{ + return downstream_jpstate_isjoined(ch); +} + +/* + RFC 4601: 4.6.5. Assert State Macros + + The set "lost_assert(S,G)" is the set of all interfaces on which the + router has received (S,G) joins but has lost an (S,G) assert. + + lost_assert(S,G) = + { all interfaces I such that + lost_assert(S,G,I) == TRUE } + + bool lost_assert(S,G,I) { + if ( RPF_interface(S) == I ) { + return FALSE + } else { + return ( AssertWinner(S,G,I) != NULL AND + AssertWinner(S,G,I) != me AND + (AssertWinnerMetric(S,G,I) is better + than spt_assert_metric(S,I) ) + } + } + + AssertWinner(S,G,I) is the IP source address of the Assert(S,G) + packet that won an Assert. +*/ +int pim_macro_ch_lost_assert(const struct pim_ifchannel *ch) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + struct pim_assert_metric spt_assert_metric; + + ifp = ch->interface; + if (!ifp) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_warn("%s: (S,G)=(%s,%s): null interface", + __PRETTY_FUNCTION__, + src_str, grp_str); + return 0; /* false */ + } + + /* RPF_interface(S) == I ? */ + if (ch->upstream->rpf.source_nexthop.interface == ifp) + return 0; /* false */ + + pim_ifp = ifp->info; + if (!pim_ifp) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, ifp->name); + return 0; /* false */ + } + + if (PIM_INADDR_IS_ANY(ch->ifassert_winner)) + return 0; /* false */ + + /* AssertWinner(S,G,I) == me ? */ + if (ch->ifassert_winner.s_addr == pim_ifp->primary_address.s_addr) + return 0; /* false */ + + spt_assert_metric = pim_macro_spt_assert_metric(&ch->upstream->rpf, + pim_ifp->primary_address); + + return pim_assert_metric_better(&ch->ifassert_winner_metric, + &spt_assert_metric); +} + +/* + RFC 4601: 4.1.6. State Summarization Macros + + pim_include(S,G) = + { all interfaces I such that: + ( (I_am_DR( I ) AND lost_assert(S,G,I) == FALSE ) + OR AssertWinner(S,G,I) == me ) + AND local_receiver_include(S,G,I) } + + AssertWinner(S,G,I) is the IP source address of the Assert(S,G) + packet that won an Assert. +*/ +int pim_macro_chisin_pim_include(const struct pim_ifchannel *ch) +{ + struct pim_interface *pim_ifp = ch->interface->info; + + if (!pim_ifp) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, ch->interface->name); + return 0; /* false */ + } + + /* local_receiver_include(S,G,I) ? */ + if (!local_receiver_include(ch)) + return 0; /* false */ + + /* OR AssertWinner(S,G,I) == me ? */ + if (ch->ifassert_winner.s_addr == pim_ifp->primary_address.s_addr) + return 1; /* true */ + + return ( + /* I_am_DR( I ) ? */ + PIM_IFP_I_am_DR(pim_ifp) + && + /* lost_assert(S,G,I) == FALSE ? */ + (!pim_macro_ch_lost_assert(ch)) + ); +} + +int pim_macro_chisin_joins_or_include(const struct pim_ifchannel *ch) +{ + if (pim_macro_chisin_joins(ch)) + return 1; /* true */ + + return pim_macro_chisin_pim_include(ch); +} + +/* + RFC 4601: 4.6.1. (S,G) Assert Message State Machine + + CouldAssert(S,G,I) = + SPTbit(S,G)==TRUE + AND (RPF_interface(S) != I) + AND (I in ( ( joins(*,*,RP(G)) (+) joins(*,G) (-) prunes(S,G,rpt) ) + (+) ( pim_include(*,G) (-) pim_exclude(S,G) ) + (-) lost_assert(*,G) + (+) joins(S,G) (+) pim_include(S,G) ) ) + + CouldAssert(S,G,I) is true for downstream interfaces that would be in + the inherited_olist(S,G) if (S,G) assert information was not taken + into account. + + CouldAssert(S,G,I) may be affected by changes in the following: + + pim_ifp->primary_address + pim_ifp->pim_dr_addr + ch->ifassert_winner_metric + ch->ifassert_winner + ch->local_ifmembership + ch->ifjoin_state + ch->upstream->rpf.source_nexthop.mrib_metric_preference + ch->upstream->rpf.source_nexthop.mrib_route_metric + ch->upstream->rpf.source_nexthop.interface +*/ +int pim_macro_ch_could_assert_eval(const struct pim_ifchannel *ch) +{ + struct interface *ifp; + + /* SPTbit(S,G) is always true for PIM-SSM-Only Routers */ + + ifp = ch->interface; + if (!ifp) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_warn("%s: (S,G)=(%s,%s): null interface", + __PRETTY_FUNCTION__, + src_str, grp_str); + return 0; /* false */ + } + + /* RPF_interface(S) != I ? */ + if (ch->upstream->rpf.source_nexthop.interface == ifp) + return 0; /* false */ + + /* I in joins(S,G) (+) pim_include(S,G) ? */ + return pim_macro_chisin_joins_or_include(ch); +} + +/* + RFC 4601: 4.6.3. Assert Metrics + + spt_assert_metric(S,I) gives the assert metric we use if we're + sending an assert based on active (S,G) forwarding state: + + assert_metric + spt_assert_metric(S,I) { + return {0,MRIB.pref(S),MRIB.metric(S),my_ip_address(I)} + } +*/ +struct pim_assert_metric pim_macro_spt_assert_metric(const struct pim_rpf *rpf, + struct in_addr ifaddr) +{ + struct pim_assert_metric metric; + + metric.rpt_bit_flag = 0; + metric.metric_preference = rpf->source_nexthop.mrib_metric_preference; + metric.route_metric = rpf->source_nexthop.mrib_route_metric; + metric.ip_address = ifaddr; + + return metric; +} + +/* + RFC 4601: 4.6.3. Assert Metrics + + An assert metric for (S,G) to include in (or compare against) an + Assert message sent on interface I should be computed using the + following pseudocode: + + assert_metric my_assert_metric(S,G,I) { + if( CouldAssert(S,G,I) == TRUE ) { + return spt_assert_metric(S,I) + } else if( CouldAssert(*,G,I) == TRUE ) { + return rpt_assert_metric(G,I) + } else { + return infinite_assert_metric() + } + } +*/ +struct pim_assert_metric pim_macro_ch_my_assert_metric_eval(const struct pim_ifchannel *ch) +{ + struct pim_interface *pim_ifp; + + pim_ifp = ch->interface->info; + + if (pim_ifp) { + if (PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) { + return pim_macro_spt_assert_metric(&ch->upstream->rpf, pim_ifp->primary_address); + } + } + + return qpim_infinite_assert_metric; +} + +/* + RFC 4601 4.2. Data Packet Forwarding Rules + RFC 4601 4.8.2. PIM-SSM-Only Routers + + Macro: + inherited_olist(S,G) = + joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G) +*/ +static int pim_macro_chisin_inherited_olist(const struct pim_ifchannel *ch) +{ + if (pim_macro_ch_lost_assert(ch)) + return 0; /* false */ + + return pim_macro_chisin_joins_or_include(ch); +} + +/* + RFC 4601 4.2. Data Packet Forwarding Rules + RFC 4601 4.8.2. PIM-SSM-Only Routers + + Additionally, the Packet forwarding rules of Section 4.2 can be + simplified in a PIM-SSM-only router: + + iif is the incoming interface of the packet. + oiflist = NULL + if (iif == RPF_interface(S) AND UpstreamJPState(S,G) == Joined) { + oiflist = inherited_olist(S,G) + } else if (iif is in inherited_olist(S,G)) { + send Assert(S,G) on iif + } + oiflist = oiflist (-) iif + forward packet on all interfaces in oiflist + + Macro: + inherited_olist(S,G) = + joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G) + + Note: + - The following test is performed as response to WRONGVIF kernel + upcall: + if (iif is in inherited_olist(S,G)) { + send Assert(S,G) on iif + } + See pim_mroute.c mroute_msg(). +*/ +int pim_macro_chisin_oiflist(const struct pim_ifchannel *ch) +{ + if (ch->upstream->join_state != PIM_UPSTREAM_JOINED) { + /* oiflist is NULL */ + return 0; /* false */ + } + + /* oiflist = oiflist (-) iif */ + if (ch->interface == ch->upstream->rpf.source_nexthop.interface) + return 0; /* false */ + + return pim_macro_chisin_inherited_olist(ch); +} + +/* + RFC 4601: 4.6.1. (S,G) Assert Message State Machine + + AssertTrackingDesired(S,G,I) = + (I in ( ( joins(*,*,RP(G)) (+) joins(*,G) (-) prunes(S,G,rpt) ) + (+) ( pim_include(*,G) (-) pim_exclude(S,G) ) + (-) lost_assert(*,G) + (+) joins(S,G) ) ) + OR (local_receiver_include(S,G,I) == TRUE + AND (I_am_DR(I) OR (AssertWinner(S,G,I) == me))) + OR ((RPF_interface(S) == I) AND (JoinDesired(S,G) == TRUE)) + OR ((RPF_interface(RP(G)) == I) AND (JoinDesired(*,G) == TRUE) + AND (SPTbit(S,G) == FALSE)) + + AssertTrackingDesired(S,G,I) is true on any interface in which an + (S,G) assert might affect our behavior. +*/ +int pim_macro_assert_tracking_desired_eval(const struct pim_ifchannel *ch) +{ + struct pim_interface *pim_ifp; + struct interface *ifp; + + ifp = ch->interface; + if (!ifp) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_warn("%s: (S,G)=(%s,%s): null interface", + __PRETTY_FUNCTION__, + src_str, grp_str); + return 0; /* false */ + } + + pim_ifp = ifp->info; + if (!pim_ifp) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, ch->interface->name); + return 0; /* false */ + } + + /* I in joins(S,G) ? */ + if (pim_macro_chisin_joins(ch)) + return 1; /* true */ + + /* local_receiver_include(S,G,I) ? */ + if (local_receiver_include(ch)) { + /* I_am_DR(I) ? */ + if (PIM_IFP_I_am_DR(pim_ifp)) + return 1; /* true */ + + /* AssertWinner(S,G,I) == me ? */ + if (ch->ifassert_winner.s_addr == pim_ifp->primary_address.s_addr) + return 1; /* true */ + } + + /* RPF_interface(S) == I ? */ + if (ch->upstream->rpf.source_nexthop.interface == ifp) { + /* JoinDesired(S,G) ? */ + if (PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(ch->upstream->flags)) + return 1; /* true */ + } + + return 0; /* false */ +} + diff --git a/pimd/pim_macro.h b/pimd/pim_macro.h new file mode 100644 index 00000000..472fa9b4 --- /dev/null +++ b/pimd/pim_macro.h @@ -0,0 +1,44 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_MACRO_H +#define PIM_MACRO_H + +#include + +#include "if.h" + +#include "pim_upstream.h" +#include "pim_ifchannel.h" + +int pim_macro_ch_lost_assert(const struct pim_ifchannel *ch); +int pim_macro_chisin_joins(const struct pim_ifchannel *ch); +int pim_macro_chisin_pim_include(const struct pim_ifchannel *ch); +int pim_macro_chisin_joins_or_include(const struct pim_ifchannel *ch); +int pim_macro_ch_could_assert_eval(const struct pim_ifchannel *ch); +struct pim_assert_metric pim_macro_spt_assert_metric(const struct pim_rpf *rpf, + struct in_addr ifaddr); +struct pim_assert_metric pim_macro_ch_my_assert_metric_eval(const struct pim_ifchannel *ch); +int pim_macro_chisin_oiflist(const struct pim_ifchannel *ch); +int pim_macro_assert_tracking_desired_eval(const struct pim_ifchannel *ch); + +#endif /* PIM_MACRO_H */ diff --git a/pimd/pim_main.c b/pimd/pim_main.c new file mode 100644 index 00000000..1206b551 --- /dev/null +++ b/pimd/pim_main.c @@ -0,0 +1,276 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "log.h" +#include "version.h" +#include +#include "command.h" +#include "thread.h" +#include + +#include "memory.h" +#include "filter.h" +#include "vty.h" +#include "sigevent.h" +#include "version.h" + +#include "pimd.h" +#include "pim_version.h" +#include "pim_signals.h" +#include "pim_zebra.h" + +#ifdef PIM_ZCLIENT_DEBUG +extern int zclient_debug; +#endif + +extern struct host host; +extern const char *default_motd; + +char config_default[] = SYSCONFDIR PIMD_DEFAULT_CONFIG; + +struct option longopts[] = { + { "daemon", no_argument, NULL, 'd'}, + { "config_file", required_argument, NULL, 'f'}, + { "pid_file", required_argument, NULL, 'i'}, + { "vty_addr", required_argument, NULL, 'A'}, + { "vty_port", required_argument, NULL, 'P'}, + { "version", no_argument, NULL, 'v'}, + { "debug_zclient", no_argument, NULL, 'Z'}, + { "help", no_argument, NULL, 'h'}, + { 0 } +}; + +char* progname; +const char *pid_file = PATH_PIMD_PID; + +static void usage(int status) +{ + if (status != 0) + fprintf (stderr, "Try `%s --help' for more information.\n", progname); + else { + printf ("Usage : %s [OPTION...]\n\ +Daemon which manages PIM.\n\n\ +-d, --daemon Run in daemon mode\n\ +-f, --config_file Set configuration file name\n\ +-i, --pid_file Set process identifier file name\n\ +-A, --vty_addr Set vty's bind address\n\ +-P, --vty_port Set vty's port number\n\ +-v, --version Print program version\n\ +" + +#ifdef PIM_ZCLIENT_DEBUG +"\ +-Z, --debug_zclient Enable zclient debugging\n\ +" +#endif + +"\ +-h, --help Display this help and exit\n\ +\n\ +Report bugs to %s\n", progname, PIMD_BUG_ADDRESS); + } + + exit (status); +} + + +int main(int argc, char** argv, char** envp) { + char *p; + char *vty_addr = NULL; + int vty_port = -1; + int daemon_mode = 0; + char *config_file = NULL; + struct thread thread; + + umask(0027); + + progname = ((p = strrchr(argv[0], '/')) ? ++p : argv[0]); + + zlog_default = openzlog(progname, ZLOG_PIM, + LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON); + + /* this while just reads the options */ + while (1) { + int opt; + + opt = getopt_long (argc, argv, "df:i:A:P:vZh", longopts, 0); + + if (opt == EOF) + break; + + switch (opt) { + case 0: + break; + case 'd': + daemon_mode = 1; + break; + case 'f': + config_file = optarg; + break; + case 'i': + pid_file = optarg; + break; + case 'A': + vty_addr = optarg; + break; + case 'P': + vty_port = atoi (optarg); + break; + case 'v': + printf(PIMD_PROGNAME " version %s\n", PIMD_VERSION); + print_version(QUAGGA_PROGNAME); + exit (0); + break; +#ifdef PIM_ZCLIENT_DEBUG + case 'Z': + zclient_debug = 1; + break; +#endif + case 'h': + usage (0); + break; + default: + usage (1); + break; + } + } + + master = thread_master_create(); + + /* + * Temporarily send zlog to stdout + */ + zlog_default->maxlvl[ZLOG_DEST_STDOUT] = zlog_default->default_lvl; + zlog_notice("Boot logging temporarily directed to stdout - begin"); + + zlog_notice("Quagga %s " PIMD_PROGNAME " %s starting", + QUAGGA_VERSION, PIMD_VERSION); + + /* + * Initializations + */ + pim_signals_init(); + cmd_init(1); + vty_init(master); + memory_init(); + access_list_init(); + pim_init(); + sort_node(); + + /* + * reset zlog default, then will obey configuration file + */ + zlog_notice("Boot logging temporarily directed to stdout - end"); +#if 0 + /* this would disable logging to stdout, but config has not been + loaded yet to reconfig the logging output */ + zlog_default->maxlvl[ZLOG_DEST_STDOUT] = ZLOG_DISABLED; +#endif + + zlog_notice("Loading configuration - begin"); + + /* Get configuration file. */ + vty_read_config(config_file, config_default); + + /* + Starting from here zlog_* functions will log according configuration + */ + + zlog_notice("Loading configuration - end"); + + /* Change to the daemon program. */ + if (daemon_mode) { + if (daemon(0, 0)) { + zlog_warn("failed to daemonize"); + } + } + + /* Process ID file creation. */ + pid_output(pid_file); + + /* Create pimd VTY socket */ + if (vty_port < 0) + vty_port = PIMD_VTY_PORT; + vty_serv_sock(vty_addr, vty_port, PIM_VTYSH_PATH); + + zlog_notice("Quagga %s " PIMD_PROGNAME " %s starting, VTY interface at port TCP %d", + QUAGGA_VERSION, PIMD_VERSION, vty_port); + +#ifdef PIM_MOTD_VERSION + /* Tweak default MOTD to include pimd version */ + zlog_notice("PIM_MOTD_VERSION: adding pimd version to default MOTD"); + if (host.motd == default_motd) { + host.motd = + "\r\n\ +Hello, this is " QUAGGA_PROGNAME " " QUAGGA_VERSION " " PIMD_PROGNAME " " PIMD_VERSION_STR "\r\n\ +" QUAGGA_COPYRIGHT "\r\n\ +\r\n"; + } +#endif + +#ifdef PIM_DEBUG_BYDEFAULT + zlog_notice("PIM_DEBUG_BYDEFAULT: Enabling all debug commands"); + PIM_DO_DEBUG_PIM_EVENTS; + PIM_DO_DEBUG_PIM_PACKETS; + PIM_DO_DEBUG_PIM_TRACE; + PIM_DO_DEBUG_IGMP_EVENTS; + PIM_DO_DEBUG_IGMP_PACKETS; + PIM_DO_DEBUG_IGMP_TRACE; + PIM_DO_DEBUG_ZEBRA; +#endif + +#ifdef PIM_ZCLIENT_DEBUG + zlog_notice("PIM_ZCLIENT_DEBUG: zclient debugging is supported, mode is %s (see option -Z)", + zclient_debug ? "ON" : "OFF"); +#endif + +#ifdef PIM_CHECK_RECV_IFINDEX_SANITY + zlog_notice("PIM_CHECK_RECV_IFINDEX_SANITY: will match sock/recv ifindex"); +#ifdef PIM_REPORT_RECV_IFINDEX_MISMATCH + zlog_notice("PIM_REPORT_RECV_IFINDEX_MISMATCH: will report sock/recv ifindex mismatch"); +#endif +#endif + +#ifdef PIM_USE_QUAGGA_INET_CHECKSUM + zlog_notice("PIM_USE_QUAGGA_INET_CHECKSUM: using Quagga's builtin checksum"); +#endif + +#ifdef PIM_UNEXPECTED_KERNEL_UPCALL + zlog_notice("PIM_UNEXPECTED_KERNEL_UPCALL: report unexpected kernel upcall"); +#endif + + /* + Initialize zclient "update" and "lookup" sockets + */ + pim_zebra_init(); + + while (thread_fetch(master, &thread)) + thread_call(&thread); + + zlog_err("%s %s: thread_fetch() returned NULL, exiting", + __FILE__, __PRETTY_FUNCTION__); + + /* never reached */ + return 0; +} diff --git a/pimd/pim_mroute.c b/pimd/pim_mroute.c new file mode 100644 index 00000000..f021abaa --- /dev/null +++ b/pimd/pim_mroute.c @@ -0,0 +1,432 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include +#include "log.h" + +#include "pimd.h" +#include "pim_mroute.h" +#include "pim_str.h" +#include "pim_time.h" +#include "pim_iface.h" +#include "pim_macro.h" + +static void mroute_read_on(void); + +static int pim_mroute_set(int fd, int enable) +{ + int err; + int opt = enable ? MRT_INIT : MRT_DONE; + socklen_t opt_len = sizeof(opt); + + err = setsockopt(fd, IPPROTO_IP, opt, &opt, opt_len); + if (err) { + int e = errno; + zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,%s=%d): errno=%d: %s", + __FILE__, __PRETTY_FUNCTION__, + fd, enable ? "MRT_INIT" : "MRT_DONE", opt, e, strerror(e)); + errno = e; + return -1; + } + +#if 0 + zlog_info("%s %s: setsockopt(fd=%d,IPPROTO_IP,MRT_INIT,opt=%d): ok", + __FILE__, __PRETTY_FUNCTION__, + fd, opt); +#endif + + return 0; +} + +int pim_mroute_msg(int fd, const char *buf, int buf_size) +{ + struct interface *ifp; + const struct ip *ip_hdr; + const struct igmpmsg *msg; + const char *upcall; + char src_str[100]; + char grp_str[100]; + + ip_hdr = (const struct ip *) buf; + + /* kernel upcall must have protocol=0 */ + if (ip_hdr->ip_p) { + /* this is not a kernel upcall */ +#ifdef PIM_UNEXPECTED_KERNEL_UPCALL + zlog_warn("%s: not a kernel upcall proto=%d msg_size=%d", + __PRETTY_FUNCTION__, ip_hdr->ip_p, buf_size); +#endif + return 0; + } + + msg = (const struct igmpmsg *) buf; + + switch (msg->im_msgtype) { + case IGMPMSG_NOCACHE: upcall = "NOCACHE"; break; + case IGMPMSG_WRONGVIF: upcall = "WRONGVIF"; break; + case IGMPMSG_WHOLEPKT: upcall = "WHOLEPKT"; break; + default: upcall = ""; + } + ifp = pim_if_find_by_vif_index(msg->im_vif); + pim_inet4_dump("", msg->im_src, src_str, sizeof(src_str)); + pim_inet4_dump("", msg->im_dst, grp_str, sizeof(grp_str)); + + if (msg->im_msgtype == IGMPMSG_WRONGVIF) { + struct pim_ifchannel *ch; + struct pim_interface *pim_ifp; + + /* + Send Assert(S,G) on iif as response to WRONGVIF kernel upcall. + + RFC 4601 4.8.2. PIM-SSM-Only Routers + + iif is the incoming interface of the packet. + if (iif is in inherited_olist(S,G)) { + send Assert(S,G) on iif + } + */ + + if (PIM_DEBUG_PIM_TRACE) { + zlog_debug("%s: WRONGVIF from fd=%d for (S,G)=(%s,%s) on %s vifi=%d", + __PRETTY_FUNCTION__, + fd, + src_str, + grp_str, + ifp ? ifp->name : "", + msg->im_vif); + } + + if (!ifp) { + zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) could not find input interface for input_vif_index=%d", + __PRETTY_FUNCTION__, + src_str, grp_str, msg->im_vif); + return -1; + } + + pim_ifp = ifp->info; + if (!pim_ifp) { + zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) multicast not enabled on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, ifp->name); + return -2; + } + + ch = pim_ifchannel_find(ifp, msg->im_src, msg->im_dst); + if (!ch) { + zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) could not find channel on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, ifp->name); + return -3; + } + + /* + RFC 4601: 4.6.1. (S,G) Assert Message State Machine + + Transitions from NoInfo State + + An (S,G) data packet arrives on interface I, AND + CouldAssert(S,G,I)==TRUE An (S,G) data packet arrived on an + downstream interface that is in our (S,G) outgoing interface + list. We optimistically assume that we will be the assert + winner for this (S,G), and so we transition to the "I am Assert + Winner" state and perform Actions A1 (below), which will + initiate the assert negotiation for (S,G). + */ + + if (ch->ifassert_state != PIM_IFASSERT_NOINFO) { + zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) channel is not on Assert NoInfo state for interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, ifp->name); + return -4; + } + + if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) { + zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) interface %s is not downstream for channel", + __PRETTY_FUNCTION__, + src_str, grp_str, ifp->name); + return -5; + } + + if (assert_action_a1(ch)) { + zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) assert_action_a1 failure on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, ifp->name); + return -6; + } + + return 0; + } /* IGMPMSG_WRONGVIF */ + + zlog_warn("%s: kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d", + __PRETTY_FUNCTION__, + upcall, + msg->im_msgtype, + ip_hdr->ip_p, + fd, + src_str, + grp_str, + ifp ? ifp->name : "", + msg->im_vif); + + return 0; +} + +static int mroute_read_msg(int fd) +{ + const int msg_min_size = MAX(sizeof(struct ip), sizeof(struct igmpmsg)); + char buf[1000]; + int rd; + + if (((int) sizeof(buf)) < msg_min_size) { + zlog_err("%s: fd=%d: buf size=%d lower than msg_min=%d", + __PRETTY_FUNCTION__, fd, sizeof(buf), msg_min_size); + return -1; + } + + rd = read(fd, buf, sizeof(buf)); + if (rd < 0) { + zlog_warn("%s: failure reading fd=%d: errno=%d: %s", + __PRETTY_FUNCTION__, fd, errno, strerror(errno)); + return -2; + } + + if (rd < msg_min_size) { + zlog_warn("%s: short message reading fd=%d: read=%d msg_min=%d", + __PRETTY_FUNCTION__, fd, rd, msg_min_size); + return -3; + } + + return pim_mroute_msg(fd, buf, rd); +} + +static int mroute_read(struct thread *t) +{ + int fd; + int result; + + zassert(t); + zassert(!THREAD_ARG(t)); + + fd = THREAD_FD(t); + zassert(fd == qpim_mroute_socket_fd); + + result = mroute_read_msg(fd); + + /* Keep reading */ + qpim_mroute_socket_reader = 0; + mroute_read_on(); + + return result; +} + +static void mroute_read_on() +{ + zassert(!qpim_mroute_socket_reader); + zassert(PIM_MROUTE_IS_ENABLED); + + THREAD_READ_ON(master, qpim_mroute_socket_reader, + mroute_read, 0, qpim_mroute_socket_fd); +} + +static void mroute_read_off() +{ + THREAD_OFF(qpim_mroute_socket_reader); +} + +int pim_mroute_socket_enable() +{ + int fd; + + if (PIM_MROUTE_IS_ENABLED) + return -1; + + fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP); + if (fd < 0) { + zlog_warn("Could not create mroute socket: errno=%d: %s", + errno, strerror(errno)); + return -2; + } + + if (pim_mroute_set(fd, 1)) { + zlog_warn("Could not enable mroute on socket fd=%d: errno=%d: %s", + fd, errno, strerror(errno)); + close(fd); + return -3; + } + + qpim_mroute_socket_fd = fd; + qpim_mroute_socket_creation = pim_time_monotonic_sec(); + mroute_read_on(); + + zassert(PIM_MROUTE_IS_ENABLED); + + return 0; +} + +int pim_mroute_socket_disable() +{ + if (PIM_MROUTE_IS_DISABLED) + return -1; + + if (pim_mroute_set(qpim_mroute_socket_fd, 0)) { + zlog_warn("Could not disable mroute on socket fd=%d: errno=%d: %s", + qpim_mroute_socket_fd, errno, strerror(errno)); + return -2; + } + + if (close(qpim_mroute_socket_fd)) { + zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s", + qpim_mroute_socket_fd, errno, strerror(errno)); + return -3; + } + + mroute_read_off(); + qpim_mroute_socket_fd = -1; + + zassert(PIM_MROUTE_IS_DISABLED); + + return 0; +} + +/* + For each network interface (e.g., physical or a virtual tunnel) that + would be used for multicast forwarding, a corresponding multicast + interface must be added to the kernel. + */ +int pim_mroute_add_vif(int vif_index, struct in_addr ifaddr) +{ + struct vifctl vc; + int err; + + if (PIM_MROUTE_IS_DISABLED) { + zlog_warn("%s: global multicast is disabled", + __PRETTY_FUNCTION__); + return -1; + } + + memset(&vc, 0, sizeof(vc)); + vc.vifc_vifi = vif_index; + vc.vifc_flags = 0; + vc.vifc_threshold = PIM_MROUTE_MIN_TTL; + vc.vifc_rate_limit = 0; + memcpy(&vc.vifc_lcl_addr, &ifaddr, sizeof(vc.vifc_lcl_addr)); + +#ifdef PIM_DVMRP_TUNNEL + if (vc.vifc_flags & VIFF_TUNNEL) { + memcpy(&vc.vifc_rmt_addr, &vif_remote_addr, sizeof(vc.vifc_rmt_addr)); + } +#endif + + err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_VIF, (void*) &vc, sizeof(vc)); + if (err) { + char ifaddr_str[100]; + int e = errno; + + pim_inet4_dump("", ifaddr, ifaddr_str, sizeof(ifaddr_str)); + + zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s): errno=%d: %s", + __FILE__, __PRETTY_FUNCTION__, + qpim_mroute_socket_fd, vif_index, ifaddr_str, + e, strerror(e)); + errno = e; + return -2; + } + + return 0; +} + +int pim_mroute_del_vif(int vif_index) +{ + struct vifctl vc; + int err; + + if (PIM_MROUTE_IS_DISABLED) { + zlog_warn("%s: global multicast is disabled", + __PRETTY_FUNCTION__); + return -1; + } + + memset(&vc, 0, sizeof(vc)); + vc.vifc_vifi = vif_index; + + err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_VIF, (void*) &vc, sizeof(vc)); + if (err) { + int e = errno; + zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s", + __FILE__, __PRETTY_FUNCTION__, + qpim_mroute_socket_fd, vif_index, + e, strerror(e)); + errno = e; + return -2; + } + + return 0; +} + +int pim_mroute_add(struct mfcctl *mc) +{ + int err; + + if (PIM_MROUTE_IS_DISABLED) { + zlog_warn("%s: global multicast is disabled", + __PRETTY_FUNCTION__); + return -1; + } + + err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_MFC, + mc, sizeof(*mc)); + if (err) { + int e = errno; + zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s", + __FILE__, __PRETTY_FUNCTION__, + qpim_mroute_socket_fd, + e, strerror(e)); + errno = e; + return -2; + } + + return 0; +} + +int pim_mroute_del(struct mfcctl *mc) +{ + int err; + + if (PIM_MROUTE_IS_DISABLED) { + zlog_warn("%s: global multicast is disabled", + __PRETTY_FUNCTION__); + return -1; + } + + err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_MFC, mc, sizeof(*mc)); + if (err) { + int e = errno; + zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s", + __FILE__, __PRETTY_FUNCTION__, + qpim_mroute_socket_fd, + e, strerror(e)); + errno = e; + return -2; + } + + return 0; +} diff --git a/pimd/pim_mroute.h b/pimd/pim_mroute.h new file mode 100644 index 00000000..350b1e37 --- /dev/null +++ b/pimd/pim_mroute.h @@ -0,0 +1,173 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_MROUTE_H +#define PIM_MROUTE_H + +/* + For msghdr.msg_control in Solaris 10 +*/ +#ifndef _XPG4_2 +#define _XPG4_2 +#endif +#ifndef __EXTENSIONS__ +#define __EXTENSIONS__ +#endif + +#include +#ifdef HAVE_NETINET_IP_MROUTE_H +#include +#endif + +#define PIM_MROUTE_MIN_TTL (1) + +/* + Below: from +*/ + +#ifndef MAXVIFS +#define MAXVIFS (32) +#endif + +#ifndef SIOCGETVIFCNT +#define SIOCGETVIFCNT SIOCPROTOPRIVATE /* IP protocol privates */ +#define SIOCGETSGCNT (SIOCPROTOPRIVATE+1) +#define SIOCGETRPF (SIOCPROTOPRIVATE+2) +#endif + +#ifndef MRT_INIT +#define MRT_BASE 200 +#define MRT_INIT (MRT_BASE) /* Activate the kernel mroute code */ +#define MRT_DONE (MRT_BASE+1) /* Shutdown the kernel mroute */ +#define MRT_ADD_VIF (MRT_BASE+2) /* Add a virtual interface */ +#define MRT_DEL_VIF (MRT_BASE+3) /* Delete a virtual interface */ +#define MRT_ADD_MFC (MRT_BASE+4) /* Add a multicast forwarding entry */ +#define MRT_DEL_MFC (MRT_BASE+5) /* Delete a multicast forwarding entry */ +#define MRT_VERSION (MRT_BASE+6) /* Get the kernel multicast version */ +#define MRT_ASSERT (MRT_BASE+7) /* Activate PIM assert mode */ +#define MRT_PIM (MRT_BASE+8) /* enable PIM code */ +#endif + +#ifndef HAVE_VIFI_T +typedef unsigned short vifi_t; +#endif + +#ifndef HAVE_STRUCT_VIFCTL +struct vifctl { + vifi_t vifc_vifi; /* Index of VIF */ + unsigned char vifc_flags; /* VIFF_ flags */ + unsigned char vifc_threshold; /* ttl limit */ + unsigned int vifc_rate_limit; /* Rate limiter values (NI) */ + struct in_addr vifc_lcl_addr; /* Our address */ + struct in_addr vifc_rmt_addr; /* IPIP tunnel addr */ +}; +#endif + +#ifndef HAVE_STRUCT_MFCCTL +struct mfcctl { + struct in_addr mfcc_origin; /* Origin of mcast */ + struct in_addr mfcc_mcastgrp; /* Group in question */ + vifi_t mfcc_parent; /* Where it arrived */ + unsigned char mfcc_ttls[MAXVIFS]; /* Where it is going */ + unsigned int mfcc_pkt_cnt; /* pkt count for src-grp */ + unsigned int mfcc_byte_cnt; + unsigned int mfcc_wrong_if; + int mfcc_expire; +}; +#endif + +/* + * Group count retrieval for mrouted + */ +/* + struct sioc_sg_req sgreq; + memset(&sgreq, 0, sizeof(sgreq)); + memcpy(&sgreq.src, &source_addr, sizeof(sgreq.src)); + memcpy(&sgreq.grp, &group_addr, sizeof(sgreq.grp)); + ioctl(mrouter_s4, SIOCGETSGCNT, &sgreq); + */ +#ifndef HAVE_STRUCT_SIOC_SG_REQ +struct sioc_sg_req { + struct in_addr src; + struct in_addr grp; + unsigned long pktcnt; + unsigned long bytecnt; + unsigned long wrong_if; +}; +#endif + +/* + * To get vif packet counts + */ +/* + struct sioc_vif_req vreq; + memset(&vreq, 0, sizeof(vreq)); + vreq.vifi = vif_index; + ioctl(mrouter_s4, SIOCGETVIFCNT, &vreq); + */ +#ifndef HAVE_STRUCT_SIOC_VIF_REQ +struct sioc_vif_req { + vifi_t vifi; /* Which iface */ + unsigned long icount; /* In packets */ + unsigned long ocount; /* Out packets */ + unsigned long ibytes; /* In bytes */ + unsigned long obytes; /* Out bytes */ +}; +#endif + +/* + * Pseudo messages used by mrouted + */ +#ifndef IGMPMSG_NOCACHE +#define IGMPMSG_NOCACHE 1 /* Kern cache fill request to mrouted */ +#define IGMPMSG_WRONGVIF 2 /* For PIM assert processing (unused) */ +#define IGMPMSG_WHOLEPKT 3 /* For PIM Register processing */ +#endif + +#ifndef HAVE_STRUCT_IGMPMSG +struct igmpmsg +{ + uint32_t unused1,unused2; + unsigned char im_msgtype; /* What is this */ + unsigned char im_mbz; /* Must be zero */ + unsigned char im_vif; /* Interface (this ought to be a vifi_t!) */ + unsigned char unused3; + struct in_addr im_src,im_dst; +}; +#endif + +/* + Above: from +*/ + +int pim_mroute_socket_enable(void); +int pim_mroute_socket_disable(void); + +int pim_mroute_add_vif(int vif_index, struct in_addr ifaddr); +int pim_mroute_del_vif(int vif_index); + +int pim_mroute_add(struct mfcctl *mc); +int pim_mroute_del(struct mfcctl *mc); + +int pim_mroute_msg(int fd, const char *buf, int buf_size); + +#endif /* PIM_MROUTE_H */ diff --git a/pimd/pim_msg.c b/pimd/pim_msg.c new file mode 100644 index 00000000..76f78f8a --- /dev/null +++ b/pimd/pim_msg.c @@ -0,0 +1,106 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "pimd.h" +#include "pim_pim.h" +#include "pim_msg.h" +#include "pim_util.h" + +void pim_msg_build_header(char *pim_msg, int pim_msg_size, + uint8_t pim_msg_type) +{ + uint16_t checksum; + + zassert(pim_msg_size >= PIM_PIM_MIN_LEN); + + /* + * Write header + */ + + *(uint8_t *) PIM_MSG_HDR_OFFSET_VERSION(pim_msg) = (PIM_PROTO_VERSION << 4) | pim_msg_type; + *(uint8_t *) PIM_MSG_HDR_OFFSET_RESERVED(pim_msg) = 0; + + /* + * Compute checksum + */ + + *(uint16_t *) PIM_MSG_HDR_OFFSET_CHECKSUM(pim_msg) = 0; + checksum = pim_inet_checksum(pim_msg, pim_msg_size); + *(uint16_t *) PIM_MSG_HDR_OFFSET_CHECKSUM(pim_msg) = checksum; +} + +char *pim_msg_addr_encode_ipv4_ucast(char *buf, + int buf_size, + struct in_addr addr) +{ + const int ENCODED_IPV4_UCAST_SIZE = 6; + + if (buf_size < ENCODED_IPV4_UCAST_SIZE) { + return 0; + } + + buf[0] = PIM_MSG_ADDRESS_FAMILY_IPV4; /* addr family */ + buf[1] = '\0'; /* native encoding */ + *(struct in_addr *)(buf + 2) = addr; + + return buf + ENCODED_IPV4_UCAST_SIZE; +} + +char *pim_msg_addr_encode_ipv4_group(char *buf, + int buf_size, + struct in_addr addr) +{ + const int ENCODED_IPV4_GROUP_SIZE = 8; + + if (buf_size < ENCODED_IPV4_GROUP_SIZE) { + return 0; + } + + buf[0] = PIM_MSG_ADDRESS_FAMILY_IPV4; /* addr family */ + buf[1] = '\0'; /* native encoding */ + buf[2] = '\0'; /* reserved */ + buf[3] = 32; /* mask len */ + *(struct in_addr *)(buf + 4) = addr; + + return buf + ENCODED_IPV4_GROUP_SIZE; +} + +char *pim_msg_addr_encode_ipv4_source(char *buf, + int buf_size, + struct in_addr addr) +{ + const int ENCODED_IPV4_SOURCE_SIZE = 8; + + if (buf_size < ENCODED_IPV4_SOURCE_SIZE) { + return 0; + } + + buf[0] = PIM_MSG_ADDRESS_FAMILY_IPV4; /* addr family */ + buf[1] = '\0'; /* native encoding */ + buf[2] = '\0'; /* reserved */ + buf[3] = 32; /* mask len */ + *(struct in_addr *)(buf + 4) = addr; + + return buf + ENCODED_IPV4_SOURCE_SIZE; +} diff --git a/pimd/pim_msg.h b/pimd/pim_msg.h new file mode 100644 index 00000000..228d6a85 --- /dev/null +++ b/pimd/pim_msg.h @@ -0,0 +1,52 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_MSG_H +#define PIM_MSG_H + +#include + +/* + Number Description + ---------- ------------------ + 0 Reserved + 1 IP (IP version 4) + 2 IP6 (IP version 6) + + From: + http://www.iana.org/assignments/address-family-numbers +*/ +#define PIM_MSG_ADDRESS_FAMILY_IPV4 (1) + +void pim_msg_build_header(char *pim_msg, int pim_msg_size, + uint8_t pim_msg_type); +char *pim_msg_addr_encode_ipv4_ucast(char *buf, + int buf_size, + struct in_addr addr); +char *pim_msg_addr_encode_ipv4_group(char *buf, + int buf_size, + struct in_addr addr); +char *pim_msg_addr_encode_ipv4_source(char *buf, + int buf_size, + struct in_addr addr); + +#endif /* PIM_MSG_H */ diff --git a/pimd/pim_neighbor.c b/pimd/pim_neighbor.c new file mode 100644 index 00000000..67aa9d08 --- /dev/null +++ b/pimd/pim_neighbor.c @@ -0,0 +1,696 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "log.h" +#include "prefix.h" +#include "memory.h" + +#include "pimd.h" +#include "pim_neighbor.h" +#include "pim_time.h" +#include "pim_str.h" +#include "pim_iface.h" +#include "pim_pim.h" +#include "pim_upstream.h" +#include "pim_ifchannel.h" + +static void dr_election_by_addr(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + struct listnode *node; + struct pim_neighbor *neigh; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + pim_ifp->pim_dr_addr = pim_ifp->primary_address; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, node, neigh)) { + if (ntohl(neigh->source_addr.s_addr) > ntohl(pim_ifp->pim_dr_addr.s_addr)) { + pim_ifp->pim_dr_addr = neigh->source_addr; + } + } +} + +static void dr_election_by_pri(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + struct listnode *node; + struct pim_neighbor *neigh; + uint32_t dr_pri; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + pim_ifp->pim_dr_addr = pim_ifp->primary_address; + dr_pri = pim_ifp->pim_dr_priority; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, node, neigh)) { + if ( + (neigh->dr_priority > dr_pri) || + ( + (neigh->dr_priority == dr_pri) && + (ntohl(neigh->source_addr.s_addr) > ntohl(pim_ifp->pim_dr_addr.s_addr)) + ) + ) { + pim_ifp->pim_dr_addr = neigh->source_addr; + dr_pri = neigh->dr_priority; + } + } +} + +/* + RFC 4601: 4.3.2. DR Election + + A router's idea of the current DR on an interface can change when a + PIM Hello message is received, when a neighbor times out, or when a + router's own DR Priority changes. + */ +void pim_if_dr_election(struct interface *ifp) +{ + struct pim_interface *pim_ifp = ifp->info; + struct in_addr old_dr_addr; + + pim_ifp->pim_dr_election_last = pim_time_monotonic_sec(); /* timestamp */ + ++pim_ifp->pim_dr_election_count; + + old_dr_addr = pim_ifp->pim_dr_addr; + + if (pim_ifp->pim_dr_num_nondrpri_neighbors) { + dr_election_by_addr(ifp); + } + else { + dr_election_by_pri(ifp); + } + + /* DR changed ? */ + if (old_dr_addr.s_addr != pim_ifp->pim_dr_addr.s_addr) { + char dr_old_str[100]; + char dr_new_str[100]; + pim_inet4_dump("", old_dr_addr, dr_old_str, sizeof(dr_old_str)); + pim_inet4_dump("", pim_ifp->pim_dr_addr, dr_new_str, sizeof(dr_new_str)); + zlog_info("%s: DR was %s now is %s on interface %s", + __PRETTY_FUNCTION__, + dr_old_str, dr_new_str, ifp->name); + + pim_if_update_join_desired(pim_ifp); + pim_if_update_could_assert(ifp); + pim_if_update_assert_tracking_desired(ifp); + } +} + +static void update_dr_priority(struct pim_neighbor *neigh, + pim_hello_options hello_options, + uint32_t dr_priority) +{ + pim_hello_options will_set_pri; /* boolean */ + pim_hello_options bit_flip; /* boolean */ + pim_hello_options pri_change; /* boolean */ + + will_set_pri = PIM_OPTION_IS_SET(hello_options, + PIM_OPTION_MASK_DR_PRIORITY); + + bit_flip = + ( + will_set_pri != + PIM_OPTION_IS_SET(neigh->hello_options, PIM_OPTION_MASK_DR_PRIORITY) + ); + + if (bit_flip) { + struct pim_interface *pim_ifp = neigh->interface->info; + + /* update num. of neighbors without dr_pri */ + + if (will_set_pri) { + --pim_ifp->pim_dr_num_nondrpri_neighbors; + } + else { + ++pim_ifp->pim_dr_num_nondrpri_neighbors; + } + } + + pri_change = + ( + bit_flip + || + (neigh->dr_priority != dr_priority) + ); + + if (will_set_pri) { + neigh->dr_priority = dr_priority; + } + else { + neigh->dr_priority = 0; /* cosmetic unset */ + } + + if (pri_change) { + /* + RFC 4601: 4.3.2. DR Election + + A router's idea of the current DR on an interface can change when a + PIM Hello message is received, when a neighbor times out, or when a + router's own DR Priority changes. + */ + pim_if_dr_election(neigh->interface); + } +} + +static int on_neighbor_timer(struct thread *t) +{ + struct pim_neighbor *neigh; + struct interface *ifp; + char msg[100]; + + zassert(t); + neigh = THREAD_ARG(t); + zassert(neigh); + + ifp = neigh->interface; + + if (PIM_DEBUG_PIM_TRACE) { + char src_str[100]; + pim_inet4_dump("", neigh->source_addr, src_str, sizeof(src_str)); + zlog_debug("Expired %d sec holdtime for neighbor %s on interface %s", + neigh->holdtime, src_str, ifp->name); + } + + neigh->t_expire_timer = 0; + + snprintf(msg, sizeof(msg), "%d-sec holdtime expired", neigh->holdtime); + pim_neighbor_delete(ifp, neigh, msg); + + /* + RFC 4601: 4.3.2. DR Election + + A router's idea of the current DR on an interface can change when a + PIM Hello message is received, when a neighbor times out, or when a + router's own DR Priority changes. + */ + pim_if_dr_election(ifp); + + return 0; +} + +static void neighbor_timer_off(struct pim_neighbor *neigh) +{ + if (PIM_DEBUG_PIM_TRACE) { + if (neigh->t_expire_timer) { + char src_str[100]; + pim_inet4_dump("", neigh->source_addr, src_str, sizeof(src_str)); + zlog_debug("%s: cancelling timer for neighbor %s on %s", + __PRETTY_FUNCTION__, + src_str, neigh->interface->name); + } + } + THREAD_OFF(neigh->t_expire_timer); + zassert(!neigh->t_expire_timer); +} + +void pim_neighbor_timer_reset(struct pim_neighbor *neigh, uint16_t holdtime) +{ + neigh->holdtime = holdtime; + + neighbor_timer_off(neigh); + + /* + 0xFFFF is request for no holdtime + */ + if (neigh->holdtime == 0xFFFF) { + return; + } + + if (PIM_DEBUG_PIM_TRACE) { + char src_str[100]; + pim_inet4_dump("", neigh->source_addr, src_str, sizeof(src_str)); + zlog_debug("%s: starting %u sec timer for neighbor %s on %s", + __PRETTY_FUNCTION__, + neigh->holdtime, src_str, neigh->interface->name); + } + + THREAD_TIMER_ON(master, neigh->t_expire_timer, + on_neighbor_timer, + neigh, neigh->holdtime); +} + +static struct pim_neighbor *pim_neighbor_new(struct interface *ifp, + struct in_addr source_addr, + pim_hello_options hello_options, + uint16_t holdtime, + uint16_t propagation_delay, + uint16_t override_interval, + uint32_t dr_priority, + uint32_t generation_id, + struct list *addr_list) +{ + struct pim_interface *pim_ifp; + struct pim_neighbor *neigh; + char src_str[100]; + + zassert(ifp); + pim_ifp = ifp->info; + zassert(pim_ifp); + + neigh = XMALLOC(MTYPE_PIM_NEIGHBOR, sizeof(*neigh)); + if (!neigh) { + zlog_err("%s: PIM XMALLOC(%d) failure", + __PRETTY_FUNCTION__, sizeof(*neigh)); + return 0; + } + + neigh->creation = pim_time_monotonic_sec(); + neigh->source_addr = source_addr; + neigh->hello_options = hello_options; + neigh->propagation_delay_msec = propagation_delay; + neigh->override_interval_msec = override_interval; + neigh->dr_priority = dr_priority; + neigh->generation_id = generation_id; + neigh->prefix_list = addr_list; + neigh->t_expire_timer = 0; + neigh->interface = ifp; + + pim_neighbor_timer_reset(neigh, holdtime); + + pim_inet4_dump("", source_addr, src_str, sizeof(src_str)); + + if (PIM_DEBUG_PIM_EVENTS) { + zlog_debug("%s: creating PIM neighbor %s on interface %s", + __PRETTY_FUNCTION__, + src_str, ifp->name); + } + + zlog_info("PIM NEIGHBOR UP: neighbor %s on interface %s", + src_str, ifp->name); + + if (neigh->propagation_delay_msec > pim_ifp->pim_neighbors_highest_propagation_delay_msec) { + pim_ifp->pim_neighbors_highest_propagation_delay_msec = neigh->propagation_delay_msec; + } + if (neigh->override_interval_msec > pim_ifp->pim_neighbors_highest_override_interval_msec) { + pim_ifp->pim_neighbors_highest_override_interval_msec = neigh->override_interval_msec; + } + + if (!PIM_OPTION_IS_SET(neigh->hello_options, + PIM_OPTION_MASK_LAN_PRUNE_DELAY)) { + /* update num. of neighbors without hello option lan_delay */ + ++pim_ifp->pim_number_of_nonlandelay_neighbors; + } + + if (!PIM_OPTION_IS_SET(neigh->hello_options, + PIM_OPTION_MASK_DR_PRIORITY)) { + /* update num. of neighbors without hello option dr_pri */ + ++pim_ifp->pim_dr_num_nondrpri_neighbors; + } + + /* + RFC 4601: 4.3.2. DR Election + + A router's idea of the current DR on an interface can change when a + PIM Hello message is received, when a neighbor times out, or when a + router's own DR Priority changes. + */ + pim_if_dr_election(neigh->interface); + + /* + RFC 4601: 4.3.1. Sending Hello Messages + + To allow new or rebooting routers to learn of PIM neighbors quickly, + when a Hello message is received from a new neighbor, or a Hello + message with a new GenID is received from an existing neighbor, a + new Hello message should be sent on this interface after a + randomized delay between 0 and Triggered_Hello_Delay. + */ + pim_hello_restart_triggered(neigh->interface); + + return neigh; +} + +static void delete_prefix_list(struct pim_neighbor *neigh) +{ + if (neigh->prefix_list) { + +#ifdef DUMP_PREFIX_LIST + struct listnode *p_node; + struct prefix *p; + char addr_str[10]; + int list_size = neigh->prefix_list ? (int) listcount(neigh->prefix_list) : -1; + int i = 0; + for (ALL_LIST_ELEMENTS_RO(neigh->prefix_list, p_node, p)) { + pim_inet4_dump("", p->u.prefix4, addr_str, sizeof(addr_str)); + zlog_debug("%s: DUMP_PREFIX_LIST neigh=%x prefix_list=%x prefix=%x addr=%s [%d/%d]", + __PRETTY_FUNCTION__, + (unsigned) neigh, (unsigned) neigh->prefix_list, (unsigned) p, + addr_str, i, list_size); + ++i; + } +#endif + + list_delete(neigh->prefix_list); + neigh->prefix_list = 0; + } +} + +void pim_neighbor_free(struct pim_neighbor *neigh) +{ + zassert(!neigh->t_expire_timer); + + delete_prefix_list(neigh); + + XFREE(MTYPE_PIM_NEIGHBOR, neigh); +} + +struct pim_neighbor *pim_neighbor_find(struct interface *ifp, + struct in_addr source_addr) +{ + struct pim_interface *pim_ifp; + struct listnode *node; + struct pim_neighbor *neigh; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, node, neigh)) { + if (source_addr.s_addr == neigh->source_addr.s_addr) { + return neigh; + } + } + + return 0; +} + +struct pim_neighbor *pim_neighbor_add(struct interface *ifp, + struct in_addr source_addr, + pim_hello_options hello_options, + uint16_t holdtime, + uint16_t propagation_delay, + uint16_t override_interval, + uint32_t dr_priority, + uint32_t generation_id, + struct list *addr_list) +{ + struct pim_interface *pim_ifp; + struct pim_neighbor *neigh; + + neigh = pim_neighbor_new(ifp, source_addr, + hello_options, + holdtime, + propagation_delay, + override_interval, + dr_priority, + generation_id, + addr_list); + if (!neigh) { + return 0; + } + + pim_ifp = ifp->info; + zassert(pim_ifp); + + listnode_add(pim_ifp->pim_neighbor_list, neigh); + + return neigh; +} + +static uint16_t +find_neighbors_next_highest_propagation_delay_msec(struct interface *ifp, + struct pim_neighbor *highest_neigh) +{ + struct pim_interface *pim_ifp; + struct listnode *neigh_node; + struct pim_neighbor *neigh; + uint16_t next_highest_delay_msec; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + next_highest_delay_msec = pim_ifp->pim_propagation_delay_msec; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neigh_node, neigh)) { + if (neigh == highest_neigh) + continue; + if (neigh->propagation_delay_msec > next_highest_delay_msec) + next_highest_delay_msec = neigh->propagation_delay_msec; + } + + return next_highest_delay_msec; +} + +static uint16_t +find_neighbors_next_highest_override_interval_msec(struct interface *ifp, + struct pim_neighbor *highest_neigh) +{ + struct pim_interface *pim_ifp; + struct listnode *neigh_node; + struct pim_neighbor *neigh; + uint16_t next_highest_interval_msec; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + next_highest_interval_msec = pim_ifp->pim_override_interval_msec; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neigh_node, neigh)) { + if (neigh == highest_neigh) + continue; + if (neigh->override_interval_msec > next_highest_interval_msec) + next_highest_interval_msec = neigh->override_interval_msec; + } + + return next_highest_interval_msec; +} + +void pim_neighbor_delete(struct interface *ifp, + struct pim_neighbor *neigh, + const char *delete_message) +{ + struct pim_interface *pim_ifp; + char src_str[100]; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + pim_inet4_dump("", neigh->source_addr, src_str, sizeof(src_str)); + zlog_info("PIM NEIGHBOR DOWN: neighbor %s on interface %s: %s", + src_str, ifp->name, delete_message); + + neighbor_timer_off(neigh); + + pim_if_assert_on_neighbor_down(ifp, neigh->source_addr); + + if (!PIM_OPTION_IS_SET(neigh->hello_options, + PIM_OPTION_MASK_LAN_PRUNE_DELAY)) { + /* update num. of neighbors without hello option lan_delay */ + + --pim_ifp->pim_number_of_nonlandelay_neighbors; + } + + if (!PIM_OPTION_IS_SET(neigh->hello_options, + PIM_OPTION_MASK_DR_PRIORITY)) { + /* update num. of neighbors without dr_pri */ + + --pim_ifp->pim_dr_num_nondrpri_neighbors; + } + + zassert(neigh->propagation_delay_msec <= pim_ifp->pim_neighbors_highest_propagation_delay_msec); + zassert(neigh->override_interval_msec <= pim_ifp->pim_neighbors_highest_override_interval_msec); + + if (pim_if_lan_delay_enabled(ifp)) { + + /* will delete a neighbor with highest propagation delay? */ + if (neigh->propagation_delay_msec == pim_ifp->pim_neighbors_highest_propagation_delay_msec) { + /* then find the next highest propagation delay */ + pim_ifp->pim_neighbors_highest_propagation_delay_msec = + find_neighbors_next_highest_propagation_delay_msec(ifp, neigh); + } + + /* will delete a neighbor with highest override interval? */ + if (neigh->override_interval_msec == pim_ifp->pim_neighbors_highest_override_interval_msec) { + /* then find the next highest propagation delay */ + pim_ifp->pim_neighbors_highest_override_interval_msec = + find_neighbors_next_highest_override_interval_msec(ifp, neigh); + } + } + + if (PIM_DEBUG_PIM_TRACE) { + zlog_debug("%s: deleting PIM neighbor %s on interface %s", + __PRETTY_FUNCTION__, + src_str, ifp->name); + } + + listnode_delete(pim_ifp->pim_neighbor_list, neigh); + + pim_neighbor_free(neigh); +} + +void pim_neighbor_delete_all(struct interface *ifp, + const char *delete_message) +{ + struct pim_interface *pim_ifp; + struct listnode *neigh_node; + struct listnode *neigh_nextnode; + struct pim_neighbor *neigh; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + for (ALL_LIST_ELEMENTS(pim_ifp->pim_neighbor_list, neigh_node, + neigh_nextnode, neigh)) { + pim_neighbor_delete(ifp, neigh, delete_message); + } +} + +struct prefix *pim_neighbor_find_secondary(struct pim_neighbor *neigh, + struct in_addr addr) +{ + struct listnode *node; + struct prefix *p; + + if (!neigh->prefix_list) + return 0; + + for (ALL_LIST_ELEMENTS_RO(neigh->prefix_list, node, p)) { + if (p->family == AF_INET) { + if (addr.s_addr == p->u.prefix4.s_addr) { + return p; + } + } + } + + return 0; +} + +/* + RFC 4601: 4.3.4. Maintaining Secondary Address Lists + + All the advertised secondary addresses in received Hello messages + must be checked against those previously advertised by all other + PIM neighbors on that interface. If there is a conflict and the + same secondary address was previously advertised by another + neighbor, then only the most recently received mapping MUST be + maintained, and an error message SHOULD be logged to the + administrator in a rate-limited manner. +*/ +static void delete_from_neigh_addr(struct interface *ifp, + struct list *addr_list, + struct in_addr neigh_addr) +{ + struct listnode *addr_node; + struct prefix *addr; + struct pim_interface *pim_ifp; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + zassert(addr_list); + + /* + Scan secondary address list + */ + for (ALL_LIST_ELEMENTS_RO(addr_list, addr_node, + addr)) { + struct listnode *neigh_node; + struct pim_neighbor *neigh; + + if (addr->family != AF_INET) + continue; + + /* + Scan neighbors + */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neigh_node, + neigh)) { + { + struct prefix *p = pim_neighbor_find_secondary(neigh, addr->u.prefix4); + if (p) { + char addr_str[100]; + char this_neigh_str[100]; + char other_neigh_str[100]; + + pim_inet4_dump("", addr->u.prefix4, addr_str, sizeof(addr_str)); + pim_inet4_dump("", neigh_addr, this_neigh_str, sizeof(this_neigh_str)); + pim_inet4_dump("", neigh->source_addr, other_neigh_str, sizeof(other_neigh_str)); + + zlog_info("secondary addr %s recvd from neigh %s deleted from neigh %s on %s", + addr_str, this_neigh_str, other_neigh_str, ifp->name); + + listnode_delete(neigh->prefix_list, p); + prefix_free(p); + } + } + + } /* scan neighbors */ + + } /* scan addr list */ + +} + +void pim_neighbor_update(struct pim_neighbor *neigh, + pim_hello_options hello_options, + uint16_t holdtime, + uint32_t dr_priority, + struct list *addr_list) +{ + struct pim_interface *pim_ifp = neigh->interface->info; + + /* Received holdtime ? */ + if (PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME)) { + pim_neighbor_timer_reset(neigh, holdtime); + } + else { + pim_neighbor_timer_reset(neigh, PIM_IF_DEFAULT_HOLDTIME(pim_ifp)); + } + +#ifdef DUMP_PREFIX_LIST + zlog_debug("%s: DUMP_PREFIX_LIST old_prefix_list=%x old_size=%d new_prefix_list=%x new_size=%d", + __PRETTY_FUNCTION__, + (unsigned) neigh->prefix_list, + neigh->prefix_list ? (int) listcount(neigh->prefix_list) : -1, + (unsigned) addr_list, + addr_list ? (int) listcount(addr_list) : -1); +#endif + + if (neigh->prefix_list == addr_list) { + if (addr_list) { + zlog_err("%s: internal error: trying to replace same prefix list=%u", + __PRETTY_FUNCTION__, (unsigned) addr_list); + } + } + else { + /* Delete existing secondary address list */ + delete_prefix_list(neigh); + } + + if (addr_list) { + delete_from_neigh_addr(neigh->interface, addr_list, neigh->source_addr); + } + + /* Replace secondary address list */ + neigh->prefix_list = addr_list; + + update_dr_priority(neigh, + hello_options, + dr_priority); + /* + Copy flags + */ + neigh->hello_options = hello_options; +} diff --git a/pimd/pim_neighbor.h b/pimd/pim_neighbor.h new file mode 100644 index 00000000..8f19c750 --- /dev/null +++ b/pimd/pim_neighbor.h @@ -0,0 +1,74 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_NEIGHBOR_H +#define PIM_NEIGHBOR_H + +#include + +#include "if.h" +#include "linklist.h" + +#include "pim_tlv.h" + +struct pim_neighbor { + int64_t creation; /* timestamp of creation */ + struct in_addr source_addr; + pim_hello_options hello_options; + uint16_t holdtime; + uint16_t propagation_delay_msec; + uint16_t override_interval_msec; + uint32_t dr_priority; + uint32_t generation_id; + struct list *prefix_list; /* list of struct prefix */ + struct thread *t_expire_timer; + struct interface *interface; +}; + +void pim_neighbor_timer_reset(struct pim_neighbor *neigh, uint16_t holdtime); +void pim_neighbor_free(struct pim_neighbor *neigh); +struct pim_neighbor *pim_neighbor_find(struct interface *ifp, + struct in_addr source_addr); +struct pim_neighbor *pim_neighbor_add(struct interface *ifp, + struct in_addr source_addr, + pim_hello_options hello_options, + uint16_t holdtime, + uint16_t propagation_delay, + uint16_t override_interval, + uint32_t dr_priority, + uint32_t generation_id, + struct list *addr_list); +void pim_neighbor_delete(struct interface *ifp, + struct pim_neighbor *neigh, + const char *delete_message); +void pim_neighbor_delete_all(struct interface *ifp, + const char *delete_message); +void pim_neighbor_update(struct pim_neighbor *neigh, + pim_hello_options hello_options, + uint16_t holdtime, + uint32_t dr_priority, + struct list *addr_list); +struct prefix *pim_neighbor_find_secondary(struct pim_neighbor *neigh, + struct in_addr addr); +void pim_if_dr_election(struct interface *ifp); + +#endif /* PIM_NEIGHBOR_H */ diff --git a/pimd/pim_oil.c b/pimd/pim_oil.c new file mode 100644 index 00000000..2c8b056e --- /dev/null +++ b/pimd/pim_oil.c @@ -0,0 +1,140 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "log.h" +#include "memory.h" +#include "linklist.h" + +#include "pimd.h" +#include "pim_oil.h" +#include "pim_str.h" +#include "pim_iface.h" + +void pim_channel_oil_free(struct channel_oil *c_oil) +{ + XFREE(MTYPE_PIM_CHANNEL_OIL, c_oil); +} + +static void pim_channel_oil_delete(struct channel_oil *c_oil) +{ + /* + notice that listnode_delete() can't be moved + into pim_channel_oil_free() because the later is + called by list_delete_all_node() + */ + listnode_delete(qpim_channel_oil_list, c_oil); + + pim_channel_oil_free(c_oil); +} + +static struct channel_oil *channel_oil_new(struct in_addr group_addr, + struct in_addr source_addr, + int input_vif_index) +{ + struct channel_oil *c_oil; + struct interface *ifp_in; + + ifp_in = pim_if_find_by_vif_index(input_vif_index); + if (!ifp_in) { + /* warning only */ + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + zlog_warn("%s: (S,G)=(%s,%s) could not find input interface for input_vif_index=%d", + __PRETTY_FUNCTION__, + source_str, group_str, input_vif_index); + } + + c_oil = XCALLOC(MTYPE_PIM_CHANNEL_OIL, sizeof(*c_oil)); + if (!c_oil) { + zlog_err("PIM XCALLOC(%d) failure", sizeof(*c_oil)); + return 0; + } + + c_oil->oil.mfcc_mcastgrp = group_addr; + c_oil->oil.mfcc_origin = source_addr; + c_oil->oil.mfcc_parent = input_vif_index; + c_oil->oil_ref_count = 1; + + zassert(c_oil->oil_size == 0); + + return c_oil; +} + +static struct channel_oil *pim_add_channel_oil(struct in_addr group_addr, + struct in_addr source_addr, + int input_vif_index) +{ + struct channel_oil *c_oil; + + c_oil = channel_oil_new(group_addr, source_addr, input_vif_index); + if (!c_oil) { + zlog_warn("PIM XCALLOC(%d) failure", sizeof(*c_oil)); + return 0; + } + + listnode_add(qpim_channel_oil_list, c_oil); + + return c_oil; +} + +static struct channel_oil *pim_find_channel_oil(struct in_addr group_addr, + struct in_addr source_addr) +{ + struct listnode *node; + struct channel_oil *c_oil; + + for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) { + if ((group_addr.s_addr == c_oil->oil.mfcc_mcastgrp.s_addr) && + (source_addr.s_addr == c_oil->oil.mfcc_origin.s_addr)) + return c_oil; + } + + return 0; +} + +struct channel_oil *pim_channel_oil_add(struct in_addr group_addr, + struct in_addr source_addr, + int input_vif_index) +{ + struct channel_oil *c_oil; + + c_oil = pim_find_channel_oil(group_addr, source_addr); + if (c_oil) { + ++c_oil->oil_ref_count; + return c_oil; + } + + return pim_add_channel_oil(group_addr, source_addr, input_vif_index); +} + +void pim_channel_oil_del(struct channel_oil *c_oil) +{ + --c_oil->oil_ref_count; + + if (c_oil->oil_ref_count < 1) { + pim_channel_oil_delete(c_oil); + } +} diff --git a/pimd/pim_oil.h b/pimd/pim_oil.h new file mode 100644 index 00000000..1753545a --- /dev/null +++ b/pimd/pim_oil.h @@ -0,0 +1,53 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_OIL_H +#define PIM_OIL_H + +#include "pim_mroute.h" + +#define PIM_OIF_FLAG_PROTO_IGMP (1 << 0) /* bitmask 1 */ +#define PIM_OIF_FLAG_PROTO_PIM (1 << 1) /* bitmask 2 */ +#define PIM_OIF_FLAG_PROTO_ANY (3) /* bitmask (1 | 2) */ + +/* + qpim_channel_oil_list holds a list of struct channel_oil. + + Each channel_oil.oil is used to control an (S,G) entry in the Kernel + Multicast Forwarding Cache. +*/ + +struct channel_oil { + struct mfcctl oil; + int oil_size; + int oil_ref_count; + time_t oif_creation[MAXVIFS]; + uint32_t oif_flags[MAXVIFS]; +}; + +void pim_channel_oil_free(struct channel_oil *c_oil); +struct channel_oil *pim_channel_oil_add(struct in_addr group_addr, + struct in_addr source_addr, + int input_vif_index); +void pim_channel_oil_del(struct channel_oil *c_oil); + +#endif /* PIM_OIL_H */ diff --git a/pimd/pim_pim.c b/pimd/pim_pim.c new file mode 100644 index 00000000..bbf67763 --- /dev/null +++ b/pimd/pim_pim.c @@ -0,0 +1,716 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "log.h" +#include "thread.h" +#include "memory.h" + +#include "pimd.h" +#include "pim_pim.h" +#include "pim_time.h" +#include "pim_iface.h" +#include "pim_sock.h" +#include "pim_str.h" +#include "pim_util.h" +#include "pim_tlv.h" +#include "pim_neighbor.h" +#include "pim_hello.h" +#include "pim_join.h" +#include "pim_assert.h" +#include "pim_msg.h" +#include "pim_rand.h" + +static int on_pim_hello_send(struct thread *t); +static int pim_hello_send(struct interface *ifp, + uint16_t holdtime); + +static void sock_close(struct interface *ifp) +{ + struct pim_interface *pim_ifp = ifp->info; + + if (PIM_DEBUG_PIM_TRACE) { + if (pim_ifp->t_pim_sock_read) { + zlog_debug("Cancelling READ event for PIM socket fd=%d on interface %s", + pim_ifp->pim_sock_fd, + ifp->name); + } + } + THREAD_OFF(pim_ifp->t_pim_sock_read); + + if (PIM_DEBUG_PIM_TRACE) { + if (pim_ifp->t_pim_hello_timer) { + zlog_debug("Cancelling PIM hello timer for interface %s", + ifp->name); + } + } + THREAD_OFF(pim_ifp->t_pim_hello_timer); + + if (PIM_DEBUG_PIM_TRACE) { + zlog_debug("Deleting PIM socket fd=%d on interface %s", + pim_ifp->pim_sock_fd, ifp->name); + } + + if (close(pim_ifp->pim_sock_fd)) { + zlog_warn("Failure closing PIM socket fd=%d on interface %s: errno=%d: %s", + pim_ifp->pim_sock_fd, ifp->name, + errno, strerror(errno)); + } + + pim_ifp->pim_sock_fd = -1; + pim_ifp->pim_sock_creation = 0; + + zassert(pim_ifp->pim_sock_fd < 0); + zassert(!pim_ifp->t_pim_sock_read); + zassert(!pim_ifp->t_pim_hello_timer); + zassert(!pim_ifp->pim_sock_creation); +} + +void pim_sock_delete(struct interface *ifp, const char *delete_message) +{ + zlog_info("PIM INTERFACE DOWN: on interface %s: %s", + ifp->name, delete_message); + + /* + RFC 4601: 4.3.1. Sending Hello Messages + + Before an interface goes down or changes primary IP address, a Hello + message with a zero HoldTime should be sent immediately (with the + old IP address if the IP address changed). + */ + pim_hello_send(ifp, 0 /* zero-sec holdtime */); + + pim_neighbor_delete_all(ifp, delete_message); + + sock_close(ifp); +} + +int pim_pim_packet(struct interface *ifp, char *buf, size_t len) +{ + struct ip *ip_hdr; + size_t ip_hlen; /* ip header length in bytes */ + char src_str[100]; + char dst_str[100]; + char *pim_msg; + int pim_msg_len; + uint8_t pim_version; + uint8_t pim_type; + uint16_t pim_checksum; /* received checksum */ + uint16_t checksum; /* computed checksum */ + struct pim_neighbor *neigh; + + if (!ifp->info) { + zlog_warn("%s: PIM not enabled on interface %s", + __PRETTY_FUNCTION__, ifp->name); + return -1; + } + + if (len < sizeof(*ip_hdr)) { + zlog_warn("PIM packet size=%d shorter than minimum=%d", + len, sizeof(*ip_hdr)); + return -1; + } + + ip_hdr = (struct ip *) buf; + + pim_inet4_dump("", ip_hdr->ip_src, src_str, sizeof(src_str)); + pim_inet4_dump("", ip_hdr->ip_dst, dst_str, sizeof(dst_str)); + + ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */ + + if (PIM_DEBUG_PIM_PACKETS) { + zlog_debug("Recv IP packet from %s to %s on %s: size=%d ip_header_size=%d ip_proto=%d", + src_str, dst_str, ifp->name, len, ip_hlen, ip_hdr->ip_p); + } + + if (ip_hdr->ip_p != PIM_IP_PROTO_PIM) { + zlog_warn("IP packet protocol=%d is not PIM=%d", + ip_hdr->ip_p, PIM_IP_PROTO_PIM); + return -1; + } + + if (ip_hlen < PIM_IP_HEADER_MIN_LEN) { + zlog_warn("IP packet header size=%d shorter than minimum=%d", + ip_hlen, PIM_IP_HEADER_MIN_LEN); + return -1; + } + if (ip_hlen > PIM_IP_HEADER_MAX_LEN) { + zlog_warn("IP packet header size=%d greater than maximum=%d", + ip_hlen, PIM_IP_HEADER_MAX_LEN); + return -1; + } + + pim_msg = buf + ip_hlen; + pim_msg_len = len - ip_hlen; + + if (pim_msg_len < PIM_PIM_MIN_LEN) { + zlog_warn("PIM message size=%d shorter than minimum=%d", + pim_msg_len, PIM_PIM_MIN_LEN); + return -1; + } + + pim_version = PIM_MSG_HDR_GET_VERSION(pim_msg); + pim_type = PIM_MSG_HDR_GET_TYPE(pim_msg); + + if (pim_version != PIM_PROTO_VERSION) { + zlog_warn("Ignoring PIM pkt from %s with unsupported version: %d", + ifp->name, pim_version); + return -1; + } + + /* save received checksum */ + pim_checksum = PIM_MSG_HDR_GET_CHECKSUM(pim_msg); + + /* for computing checksum */ + *(uint16_t *) PIM_MSG_HDR_OFFSET_CHECKSUM(pim_msg) = 0; + + checksum = pim_inet_checksum(pim_msg, pim_msg_len); + if (checksum != pim_checksum) { + zlog_warn("Ignoring PIM pkt from %s with invalid checksum: received=%x calculated=%x", + ifp->name, pim_checksum, checksum); + return -1; + } + + if (PIM_DEBUG_PIM_PACKETS) { + zlog_debug("Recv PIM packet from %s to %s on %s: ttl=%d pim_version=%d pim_type=%d pim_msg_size=%d checksum=%x", + src_str, dst_str, ifp->name, ip_hdr->ip_ttl, + pim_version, pim_type, pim_msg_len, checksum); + } + + if (pim_type == PIM_MSG_TYPE_HELLO) { + return pim_hello_recv(ifp, + ip_hdr->ip_src, + pim_msg + PIM_MSG_HEADER_LEN, + pim_msg_len - PIM_MSG_HEADER_LEN); + } + + neigh = pim_neighbor_find(ifp, ip_hdr->ip_src); + if (!neigh) { + zlog_warn("%s %s: non-hello PIM message type=%d from non-neighbor %s on %s", + __FILE__, __PRETTY_FUNCTION__, + pim_type, src_str, ifp->name); + return -1; + } + + switch (pim_type) { + case PIM_MSG_TYPE_JOIN_PRUNE: + return pim_joinprune_recv(ifp, neigh, + ip_hdr->ip_src, + pim_msg + PIM_MSG_HEADER_LEN, + pim_msg_len - PIM_MSG_HEADER_LEN); + case PIM_MSG_TYPE_ASSERT: + return pim_assert_recv(ifp, neigh, + ip_hdr->ip_src, + pim_msg + PIM_MSG_HEADER_LEN, + pim_msg_len - PIM_MSG_HEADER_LEN); + default: + zlog_warn("%s %s: unsupported PIM message type=%d from %s on %s", + __FILE__, __PRETTY_FUNCTION__, + pim_type, src_str, ifp->name); + } + + return -1; +} + +static void pim_sock_read_on(struct interface *ifp); + +static int pim_sock_read(struct thread *t) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + int fd; + struct sockaddr_in from; + struct sockaddr_in to; + socklen_t fromlen = sizeof(from); + socklen_t tolen = sizeof(to); + char buf[PIM_PIM_BUFSIZE_READ]; + int len; + int ifindex = -1; + int result = -1; /* defaults to bad */ + + zassert(t); + + ifp = THREAD_ARG(t); + zassert(ifp); + + fd = THREAD_FD(t); + + pim_ifp = ifp->info; + zassert(pim_ifp); + + zassert(fd == pim_ifp->pim_sock_fd); + + len = pim_socket_recvfromto(fd, buf, sizeof(buf), + &from, &fromlen, + &to, &tolen, + &ifindex); + if (len < 0) { + zlog_warn("Failure receiving IP PIM packet on fd=%d: errno=%d: %s", + fd, errno, strerror(errno)); + goto done; + } + + if (PIM_DEBUG_PIM_PACKETS) { + char from_str[100]; + char to_str[100]; + + if (!inet_ntop(AF_INET, &from.sin_addr, from_str, sizeof(from_str))) + sprintf(from_str, ""); + if (!inet_ntop(AF_INET, &to.sin_addr, to_str, sizeof(to_str))) + sprintf(to_str, ""); + + zlog_debug("Recv IP PIM pkt size=%d from %s to %s on fd=%d on ifindex=%d (sock_ifindex=%d)", + len, from_str, to_str, fd, ifindex, ifp->ifindex); + } + +#ifdef PIM_CHECK_RECV_IFINDEX_SANITY + /* ifindex sanity check */ + if (ifindex != (int) ifp->ifindex) { + char from_str[100]; + char to_str[100]; + struct interface *recv_ifp; + + if (!inet_ntop(AF_INET, &from.sin_addr, from_str , sizeof(from_str))) + sprintf(from_str, ""); + if (!inet_ntop(AF_INET, &to.sin_addr, to_str , sizeof(to_str))) + sprintf(to_str, ""); + + recv_ifp = if_lookup_by_index(ifindex); + if (recv_ifp) { + zassert(ifindex == (int) recv_ifp->ifindex); + } + +#ifdef PIM_REPORT_RECV_IFINDEX_MISMATCH + zlog_warn("Interface mismatch: recv PIM pkt from %s to %s on fd=%d: recv_ifindex=%d (%s) sock_ifindex=%d (%s)", + from_str, to_str, fd, + ifindex, recv_ifp ? recv_ifp->name : "", + ifp->ifindex, ifp->name); +#endif + goto done; + } +#endif + + if (pim_pim_packet(ifp, buf, len)) { + goto done; + } + + result = 0; /* good */ + + done: + pim_sock_read_on(ifp); + + if (result) { + ++pim_ifp->pim_ifstat_hello_recvfail; + } + + return result; +} + +static void pim_sock_read_on(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + + zassert(ifp); + zassert(ifp->info); + + pim_ifp = ifp->info; + + if (PIM_DEBUG_PIM_TRACE) { + zlog_debug("Scheduling READ event on PIM socket fd=%d", + pim_ifp->pim_sock_fd); + } + pim_ifp->t_pim_sock_read = 0; + zassert(!pim_ifp->t_pim_sock_read); + THREAD_READ_ON(master, pim_ifp->t_pim_sock_read, pim_sock_read, ifp, + pim_ifp->pim_sock_fd); +} + +static int pim_sock_open(struct in_addr ifaddr, int ifindex) +{ + int fd; + + fd = pim_socket_mcast(IPPROTO_PIM, ifaddr, 0 /* loop=false */); + if (fd < 0) + return -1; + + if (pim_socket_join(fd, qpim_all_pim_routers_addr, ifaddr, ifindex)) { + return -2; + } + + return fd; +} + +void pim_ifstat_reset(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + + zassert(ifp); + + pim_ifp = ifp->info; + if (!pim_ifp) { + return; + } + + pim_ifp->pim_ifstat_start = pim_time_monotonic_sec(); + pim_ifp->pim_ifstat_hello_sent = 0; + pim_ifp->pim_ifstat_hello_sendfail = 0; + pim_ifp->pim_ifstat_hello_recv = 0; + pim_ifp->pim_ifstat_hello_recvfail = 0; +} + +void pim_sock_reset(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + + zassert(ifp); + zassert(ifp->info); + + pim_ifp = ifp->info; + + pim_ifp->primary_address = pim_find_primary_addr(ifp); + + pim_ifp->pim_sock_fd = -1; + pim_ifp->pim_sock_creation = 0; + pim_ifp->t_pim_sock_read = 0; + + pim_ifp->t_pim_hello_timer = 0; + pim_ifp->pim_hello_period = PIM_DEFAULT_HELLO_PERIOD; + pim_ifp->pim_default_holdtime = -1; /* unset: means 3.5 * pim_hello_period */ + pim_ifp->pim_triggered_hello_delay = PIM_DEFAULT_TRIGGERED_HELLO_DELAY; + pim_ifp->pim_dr_priority = PIM_DEFAULT_DR_PRIORITY; + pim_ifp->pim_propagation_delay_msec = PIM_DEFAULT_PROPAGATION_DELAY_MSEC; + pim_ifp->pim_override_interval_msec = PIM_DEFAULT_OVERRIDE_INTERVAL_MSEC; + if (PIM_DEFAULT_CAN_DISABLE_JOIN_SUPPRESSION) { + PIM_IF_DO_PIM_CAN_DISABLE_JOIN_SUPRESSION(pim_ifp->options); + } + else { + PIM_IF_DONT_PIM_CAN_DISABLE_JOIN_SUPRESSION(pim_ifp->options); + } + + /* neighbors without lan_delay */ + pim_ifp->pim_number_of_nonlandelay_neighbors = 0; + pim_ifp->pim_neighbors_highest_propagation_delay_msec = 0; + pim_ifp->pim_neighbors_highest_override_interval_msec = 0; + + /* DR Election */ + pim_ifp->pim_dr_election_last = 0; /* timestamp */ + pim_ifp->pim_dr_election_count = 0; + pim_ifp->pim_dr_num_nondrpri_neighbors = 0; /* neighbors without dr_pri */ + pim_ifp->pim_dr_addr = pim_ifp->primary_address; + + pim_ifstat_reset(ifp); +} + +int pim_msg_send(int fd, + struct in_addr dst, + char *pim_msg, + int pim_msg_size, + const char *ifname) +{ + ssize_t sent; + struct sockaddr_in to; + socklen_t tolen; + + if (PIM_DEBUG_PIM_PACKETS) { + char dst_str[100]; + pim_inet4_dump("", dst, dst_str, sizeof(dst_str)); + zlog_debug("%s: to %s on %s: msg_size=%d checksum=%x", + __PRETTY_FUNCTION__, + dst_str, ifname, pim_msg_size, + *(uint16_t *) PIM_MSG_HDR_OFFSET_CHECKSUM(pim_msg)); + } + +#if 0 + memset(&to, 0, sizeof(to)); +#endif + to.sin_family = AF_INET; + to.sin_addr = dst; +#if 0 + to.sin_port = htons(0); +#endif + tolen = sizeof(to); + + sent = sendto(fd, pim_msg, pim_msg_size, MSG_DONTWAIT, &to, tolen); + if (sent != (ssize_t) pim_msg_size) { + int e = errno; + char dst_str[100]; + pim_inet4_dump("", dst, dst_str, sizeof(dst_str)); + if (sent < 0) { + zlog_warn("%s: sendto() failure to %s on %s: fd=%d msg_size=%d: errno=%d: %s", + __PRETTY_FUNCTION__, + dst_str, ifname, fd, pim_msg_size, + e, strerror(e)); + } + else { + zlog_warn("%s: sendto() partial to %s on %s: fd=%d msg_size=%d: sent=%d", + __PRETTY_FUNCTION__, + dst_str, ifname, fd, + pim_msg_size, sent); + } + return -1; + } + + return 0; +} + +static int hello_send(struct interface *ifp, + uint16_t holdtime) +{ + char pim_msg[PIM_PIM_BUFSIZE_WRITE]; + struct pim_interface *pim_ifp; + int pim_tlv_size; + int pim_msg_size; + + pim_ifp = ifp->info; + + if (PIM_DEBUG_PIM_PACKETS) { + char dst_str[100]; + pim_inet4_dump("", qpim_all_pim_routers_addr, dst_str, sizeof(dst_str)); + zlog_debug("%s: to %s on %s: holdt=%u prop_d=%u overr_i=%u dis_join_supp=%d dr_prio=%u gen_id=%08x addrs=%d", + __PRETTY_FUNCTION__, + dst_str, ifp->name, + holdtime, + pim_ifp->pim_propagation_delay_msec, pim_ifp->pim_override_interval_msec, + PIM_IF_TEST_PIM_CAN_DISABLE_JOIN_SUPRESSION(pim_ifp->options), + pim_ifp->pim_dr_priority, pim_ifp->pim_generation_id, + listcount(ifp->connected)); + } + + pim_tlv_size = pim_hello_build_tlv(ifp->name, + pim_msg + PIM_PIM_MIN_LEN, + sizeof(pim_msg) - PIM_PIM_MIN_LEN, + holdtime, + pim_ifp->pim_dr_priority, + pim_ifp->pim_generation_id, + pim_ifp->pim_propagation_delay_msec, + pim_ifp->pim_override_interval_msec, + PIM_IF_TEST_PIM_CAN_DISABLE_JOIN_SUPRESSION(pim_ifp->options), + ifp->connected); + if (pim_tlv_size < 0) { + return -1; + } + + pim_msg_size = pim_tlv_size + PIM_PIM_MIN_LEN; + + zassert(pim_msg_size >= PIM_PIM_MIN_LEN); + zassert(pim_msg_size <= PIM_PIM_BUFSIZE_WRITE); + + pim_msg_build_header(pim_msg, pim_msg_size, + PIM_MSG_TYPE_HELLO); + + if (pim_msg_send(pim_ifp->pim_sock_fd, + qpim_all_pim_routers_addr, + pim_msg, + pim_msg_size, + ifp->name)) { + zlog_warn("%s: could not send PIM message on interface %s", + __PRETTY_FUNCTION__, ifp->name); + return -2; + } + + return 0; +} + +static int pim_hello_send(struct interface *ifp, + uint16_t holdtime) +{ + struct pim_interface *pim_ifp; + + zassert(ifp); + pim_ifp = ifp->info; + zassert(pim_ifp); + + if (hello_send(ifp, holdtime)) { + ++pim_ifp->pim_ifstat_hello_sendfail; + + zlog_warn("Could not send PIM hello on interface %s", + ifp->name); + return -1; + } + + ++pim_ifp->pim_ifstat_hello_sent; + + return 0; +} + +static void hello_resched(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + + zassert(ifp); + pim_ifp = ifp->info; + zassert(pim_ifp); + + if (PIM_DEBUG_PIM_TRACE) { + zlog_debug("Rescheduling %d sec hello on interface %s", + pim_ifp->pim_hello_period, ifp->name); + } + THREAD_OFF(pim_ifp->t_pim_hello_timer); + THREAD_TIMER_ON(master, pim_ifp->t_pim_hello_timer, + on_pim_hello_send, + ifp, pim_ifp->pim_hello_period); +} + +/* + Periodic hello timer + */ +static int on_pim_hello_send(struct thread *t) +{ + struct pim_interface *pim_ifp; + struct interface *ifp; + + zassert(t); + ifp = THREAD_ARG(t); + zassert(ifp); + + pim_ifp = ifp->info; + + /* + * Schedule next hello + */ + pim_ifp->t_pim_hello_timer = 0; + hello_resched(ifp); + + /* + * Send hello + */ + return pim_hello_send(ifp, PIM_IF_DEFAULT_HOLDTIME(pim_ifp)); +} + +/* + RFC 4601: 4.3.1. Sending Hello Messages + + Thus, if a router needs to send a Join/Prune or Assert message on an + interface on which it has not yet sent a Hello message with the + currently configured IP address, then it MUST immediately send the + relevant Hello message without waiting for the Hello Timer to + expire, followed by the Join/Prune or Assert message. + */ +void pim_hello_restart_now(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + + zassert(ifp); + pim_ifp = ifp->info; + zassert(pim_ifp); + + /* + * Reset next hello timer + */ + hello_resched(ifp); + + /* + * Immediately send hello + */ + pim_hello_send(ifp, PIM_IF_DEFAULT_HOLDTIME(pim_ifp)); +} + +/* + RFC 4601: 4.3.1. Sending Hello Messages + + To allow new or rebooting routers to learn of PIM neighbors quickly, + when a Hello message is received from a new neighbor, or a Hello + message with a new GenID is received from an existing neighbor, a + new Hello message should be sent on this interface after a + randomized delay between 0 and Triggered_Hello_Delay. + */ +void pim_hello_restart_triggered(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + int triggered_hello_delay_msec; + int random_msec; + + zassert(ifp); + pim_ifp = ifp->info; + zassert(pim_ifp); + + triggered_hello_delay_msec = 1000 * pim_ifp->pim_triggered_hello_delay; + + if (pim_ifp->t_pim_hello_timer) { + long remain_msec = pim_time_timer_remain_msec(pim_ifp->t_pim_hello_timer); + if (remain_msec <= triggered_hello_delay_msec) { + /* Rescheduling hello would increase the delay, then it's faster + to just wait for the scheduled periodic hello. */ + return; + } + + THREAD_OFF(pim_ifp->t_pim_hello_timer); + pim_ifp->t_pim_hello_timer = 0; + } + zassert(!pim_ifp->t_pim_hello_timer); + + random_msec = pim_rand_next(0, triggered_hello_delay_msec); + + if (PIM_DEBUG_PIM_EVENTS) { + zlog_debug("Scheduling %d msec triggered hello on interface %s", + random_msec, ifp->name); + } + + THREAD_TIMER_MSEC_ON(master, pim_ifp->t_pim_hello_timer, + on_pim_hello_send, + ifp, triggered_hello_delay_msec); +} + +int pim_sock_add(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + struct in_addr ifaddr; + + pim_ifp = ifp->info; + zassert(pim_ifp); + + if (pim_ifp->pim_sock_fd >= 0) { + zlog_warn("Can't recreate existing PIM socket fd=%d for interface %s", + pim_ifp->pim_sock_fd, ifp->name); + return -1; + } + + ifaddr = pim_ifp->primary_address; + + pim_ifp->pim_sock_fd = pim_sock_open(ifaddr, ifp->ifindex); + if (pim_ifp->pim_sock_fd < 0) { + zlog_warn("Could not open PIM socket on interface %s", + ifp->name); + return -2; + } + + pim_ifp->t_pim_sock_read = 0; + pim_ifp->pim_sock_creation = pim_time_monotonic_sec(); + + pim_ifp->pim_generation_id = pim_rand() & (int64_t) 0xFFFFFFFF; + + zlog_info("PIM INTERFACE UP: on interface %s", + ifp->name); + + /* + * Start receiving PIM messages + */ + pim_sock_read_on(ifp); + + /* + * Start sending PIM hello's + */ + pim_hello_restart_triggered(ifp); + + return 0; +} diff --git a/pimd/pim_pim.h b/pimd/pim_pim.h new file mode 100644 index 00000000..a2c8fa58 --- /dev/null +++ b/pimd/pim_pim.h @@ -0,0 +1,71 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_PIM_H +#define PIM_PIM_H + +#include + +#include "if.h" + +#define PIM_PIM_BUFSIZE_READ (20000) +#define PIM_PIM_BUFSIZE_WRITE (20000) + +#define PIM_NEXTHOP_IFINDEX_TAB_SIZE (20) + +#define PIM_DEFAULT_HELLO_PERIOD (30) /* seconds, RFC 4601: 4.11 */ +#define PIM_DEFAULT_TRIGGERED_HELLO_DELAY (5) /* seconds, RFC 4601: 4.11 */ +#define PIM_DEFAULT_DR_PRIORITY (1) /* RFC 4601: 4.3.1 */ +#define PIM_DEFAULT_PROPAGATION_DELAY_MSEC (500) /* RFC 4601: 4.11. Timer Values */ +#define PIM_DEFAULT_OVERRIDE_INTERVAL_MSEC (2500) /* RFC 4601: 4.11. Timer Values */ +#define PIM_DEFAULT_CAN_DISABLE_JOIN_SUPPRESSION (0) /* boolean */ +#define PIM_DEFAULT_T_PERIODIC (60) /* RFC 4601: 4.11. Timer Values */ + +#define PIM_MSG_TYPE_HELLO (0) +#define PIM_MSG_TYPE_JOIN_PRUNE (3) +#define PIM_MSG_TYPE_ASSERT (5) + +#define PIM_MSG_HDR_OFFSET_VERSION(pim_msg) (pim_msg) +#define PIM_MSG_HDR_OFFSET_TYPE(pim_msg) (pim_msg) +#define PIM_MSG_HDR_OFFSET_RESERVED(pim_msg) (((char *)(pim_msg)) + 1) +#define PIM_MSG_HDR_OFFSET_CHECKSUM(pim_msg) (((char *)(pim_msg)) + 2) + +#define PIM_MSG_HDR_GET_VERSION(pim_msg) ((*(uint8_t*) PIM_MSG_HDR_OFFSET_VERSION(pim_msg)) >> 4) +#define PIM_MSG_HDR_GET_TYPE(pim_msg) ((*(uint8_t*) PIM_MSG_HDR_OFFSET_TYPE(pim_msg)) & 0xF) +#define PIM_MSG_HDR_GET_CHECKSUM(pim_msg) (*(uint16_t*) PIM_MSG_HDR_OFFSET_CHECKSUM(pim_msg)) + +void pim_ifstat_reset(struct interface *ifp); +void pim_sock_reset(struct interface *ifp); +int pim_sock_add(struct interface *ifp); +void pim_sock_delete(struct interface *ifp, const char *delete_message); +void pim_hello_restart_now(struct interface *ifp); +void pim_hello_restart_triggered(struct interface *ifp); + +int pim_pim_packet(struct interface *ifp, char *buf, size_t len); + +int pim_msg_send(int fd, + struct in_addr dst, + char *pim_msg, + int pim_msg_size, + const char *ifname); + +#endif /* PIM_PIM_H */ diff --git a/pimd/pim_rand.c b/pimd/pim_rand.c new file mode 100644 index 00000000..df2a1111 --- /dev/null +++ b/pimd/pim_rand.c @@ -0,0 +1,60 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include "pim_rand.h" +#include "pim_time.h" + +/* Quick and dirty random number generator from NUMERICAL RECIPES IN C: + THE ART OF SCIENTIFIC COMPUTING (ISBN 0-521-43108-5). */ +/* BEWARE: '_qseed_' is assigned! */ +#define QRANDOM(_qseed_) ((_qseed_) = (((_qseed_) * 1664525L) + 1013904223L)) + +static long qpim_rand_seed; + +void pim_rand_init() +{ + qpim_rand_seed = pim_time_monotonic_sec() ^ getpid(); +} + +long pim_rand() +{ + return QRANDOM(qpim_rand_seed); +} + +int pim_rand_next(int min, int max) +{ + long rand; + + assert(min <= max); + + /* FIXME better random generator ? */ + + rand = QRANDOM(qpim_rand_seed); + if (rand < 0) + rand = -rand; + rand = rand % (1 + max - min) + min; + + assert(rand >= min); + assert(rand <= max); + + return rand; +} diff --git a/pimd/pim_rand.h b/pimd/pim_rand.h new file mode 100644 index 00000000..a1df5054 --- /dev/null +++ b/pimd/pim_rand.h @@ -0,0 +1,30 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_RAND_H +#define PIM_RAND_H + +void pim_rand_init(void); +long pim_rand(void); +int pim_rand_next(int min, int max); + +#endif /* PIM_RAND_H */ diff --git a/pimd/pim_rpf.c b/pimd/pim_rpf.c new file mode 100644 index 00000000..7fdeecfd --- /dev/null +++ b/pimd/pim_rpf.c @@ -0,0 +1,254 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "log.h" +#include "prefix.h" +#include "memory.h" + +#include "pimd.h" +#include "pim_rpf.h" +#include "pim_pim.h" +#include "pim_str.h" +#include "pim_iface.h" +#include "pim_zlookup.h" +#include "pim_ifchannel.h" + +static struct in_addr pim_rpf_find_rpf_addr(struct pim_upstream *up); + +int pim_nexthop_lookup(struct pim_nexthop *nexthop, + struct in_addr addr) +{ + struct pim_zlookup_nexthop nexthop_tab[PIM_NEXTHOP_IFINDEX_TAB_SIZE]; + int num_ifindex; + struct interface *ifp; + int first_ifindex; + + num_ifindex = zclient_lookup_nexthop(qpim_zclient_lookup, nexthop_tab, + PIM_NEXTHOP_IFINDEX_TAB_SIZE, + addr, PIM_NEXTHOP_LOOKUP_MAX); + if (num_ifindex < 1) { + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_warn("%s %s: could not find nexthop ifindex for address %s", + __FILE__, __PRETTY_FUNCTION__, + addr_str); + return -1; + } + + first_ifindex = nexthop_tab[0].ifindex; + + if (num_ifindex > 1) { + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_debug("%s %s: FIXME ignoring multiple nexthop ifindex'es num_ifindex=%d for address %s (using only ifindex=%d)", + __FILE__, __PRETTY_FUNCTION__, + num_ifindex, addr_str, first_ifindex); + /* debug warning only, do not return */ + } + + ifp = if_lookup_by_index(first_ifindex); + if (!ifp) { + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_warn("%s %s: could not find interface for ifindex %d (address %s)", + __FILE__, __PRETTY_FUNCTION__, + first_ifindex, addr_str); + return -2; + } + + if (!ifp->info) { + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_warn("%s: multicast not enabled on input interface %s (ifindex=%d, RPF for source %s)", + __PRETTY_FUNCTION__, + ifp->name, first_ifindex, addr_str); + /* debug warning only, do not return */ + } + + if (PIM_DEBUG_PIM_TRACE) { + char nexthop_str[100]; + char addr_str[100]; + pim_inet4_dump("", nexthop_tab[0].nexthop_addr, nexthop_str, sizeof(nexthop_str)); + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_debug("%s %s: found nexthop %s for address %s: interface %s ifindex=%d metric=%d pref=%d", + __FILE__, __PRETTY_FUNCTION__, + nexthop_str, addr_str, + ifp->name, first_ifindex, + nexthop_tab[0].route_metric, + nexthop_tab[0].protocol_distance); + } + + /* update nextop data */ + nexthop->interface = ifp; + nexthop->mrib_nexthop_addr = nexthop_tab[0].nexthop_addr; + nexthop->mrib_metric_preference = nexthop_tab[0].protocol_distance; + nexthop->mrib_route_metric = nexthop_tab[0].route_metric; + + return 0; +} + +static int nexthop_mismatch(const struct pim_nexthop *nh1, + const struct pim_nexthop *nh2) +{ + return (nh1->interface != nh2->interface) + || + (nh1->mrib_nexthop_addr.s_addr != nh2->mrib_nexthop_addr.s_addr) + || + (nh1->mrib_metric_preference != nh2->mrib_metric_preference) + || + (nh1->mrib_route_metric != nh2->mrib_route_metric); +} + +enum pim_rpf_result pim_rpf_update(struct pim_upstream *up, + struct in_addr *old_rpf_addr) +{ + struct in_addr save_rpf_addr; + struct pim_nexthop save_nexthop; + struct pim_rpf *rpf = &up->rpf; + + save_nexthop = rpf->source_nexthop; /* detect change in pim_nexthop */ + save_rpf_addr = rpf->rpf_addr; /* detect change in RPF'(S,G) */ + + if (pim_nexthop_lookup(&rpf->source_nexthop, + up->source_addr)) { + return PIM_RPF_FAILURE; + } + + rpf->rpf_addr = pim_rpf_find_rpf_addr(up); + if (PIM_INADDR_IS_ANY(rpf->rpf_addr)) { + /* RPF'(S,G) not found */ + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + zlog_warn("%s %s: RPF'(%s,%s) not found: won't send join upstream", + __FILE__, __PRETTY_FUNCTION__, + src_str, grp_str); + /* warning only */ + } + + /* detect change in pim_nexthop */ + if (nexthop_mismatch(&rpf->source_nexthop, &save_nexthop)) { + char src_str[100]; + char grp_str[100]; + char nhaddr_str[100]; + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", rpf->source_nexthop.mrib_nexthop_addr, nhaddr_str, sizeof(nhaddr_str)); + zlog_warn("%s %s: (S,G)=(%s,%s) source nexthop now is: interface=%s address=%s pref=%d metric=%d", + __FILE__, __PRETTY_FUNCTION__, + src_str, grp_str, + rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "", + nhaddr_str, + rpf->source_nexthop.mrib_metric_preference, + rpf->source_nexthop.mrib_route_metric); + /* warning only */ + + pim_upstream_update_join_desired(up); + pim_upstream_update_could_assert(up); + pim_upstream_update_my_assert_metric(up); + } + + /* detect change in RPF_interface(S) */ + if (save_nexthop.interface != rpf->source_nexthop.interface) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + zlog_warn("%s %s: (S,G)=(%s,%s) RPF_interface(S) changed from %s to %s", + __FILE__, __PRETTY_FUNCTION__, + src_str, grp_str, + save_nexthop.interface ? save_nexthop.interface->name : "", + rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : ""); + /* warning only */ + + pim_upstream_rpf_interface_changed(up, save_nexthop.interface); + } + + /* detect change in RPF'(S,G) */ + if (save_rpf_addr.s_addr != rpf->rpf_addr.s_addr) { + + /* return old rpf to caller ? */ + if (old_rpf_addr) + *old_rpf_addr = save_rpf_addr; + + return PIM_RPF_CHANGED; + } + + return PIM_RPF_OK; +} + +/* + RFC 4601: 4.1.6. State Summarization Macros + + neighbor RPF'(S,G) { + if ( I_Am_Assert_Loser(S, G, RPF_interface(S) )) { + return AssertWinner(S, G, RPF_interface(S) ) + } else { + return NBR( RPF_interface(S), MRIB.next_hop( S ) ) + } + } + + RPF'(*,G) and RPF'(S,G) indicate the neighbor from which data + packets should be coming and to which joins should be sent on the RP + tree and SPT, respectively. +*/ +static struct in_addr pim_rpf_find_rpf_addr(struct pim_upstream *up) +{ + struct pim_ifchannel *rpf_ch; + struct pim_neighbor *neigh; + struct in_addr rpf_addr; + + if (!up->rpf.source_nexthop.interface) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + zlog_warn("%s: missing RPF interface for upstream (S,G)=(%s,%s)", + __PRETTY_FUNCTION__, + src_str, grp_str); + + rpf_addr.s_addr = PIM_NET_INADDR_ANY; + return rpf_addr; + } + + rpf_ch = pim_ifchannel_find(up->rpf.source_nexthop.interface, + up->source_addr, up->group_addr); + if (rpf_ch) { + if (rpf_ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) { + return rpf_ch->ifassert_winner; + } + } + + /* return NBR( RPF_interface(S), MRIB.next_hop( S ) ) */ + + neigh = pim_if_find_neighbor(up->rpf.source_nexthop.interface, + up->rpf.source_nexthop.mrib_nexthop_addr); + if (neigh) + rpf_addr = neigh->source_addr; + else + rpf_addr.s_addr = PIM_NET_INADDR_ANY; + + return rpf_addr; +} diff --git a/pimd/pim_rpf.h b/pimd/pim_rpf.h new file mode 100644 index 00000000..078e89f3 --- /dev/null +++ b/pimd/pim_rpf.h @@ -0,0 +1,36 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_RPF_H +#define PIM_RPF_H + +#include + +#include "pim_upstream.h" +#include "pim_neighbor.h" + +int pim_nexthop_lookup(struct pim_nexthop *nexthop, + struct in_addr addr); +enum pim_rpf_result pim_rpf_update(struct pim_upstream *up, + struct in_addr *old_rpf_addr); + +#endif /* PIM_RPF_H */ diff --git a/pimd/pim_signals.c b/pimd/pim_signals.c new file mode 100644 index 00000000..1b146f6f --- /dev/null +++ b/pimd/pim_signals.c @@ -0,0 +1,85 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include +#include "sigevent.h" +#include "log.h" + +#include "pim_signals.h" +#include "pimd.h" + +/* + * Signal handlers + */ + +static void pim_sighup() +{ + zlog_debug ("SIGHUP received, ignoring"); +} + +static void pim_sigint() +{ + zlog_notice("Terminating on signal SIGINT"); + pim_terminate(); + exit(1); +} + +static void pim_sigterm() +{ + zlog_notice("Terminating on signal SIGTERM"); + pim_terminate(); + exit(1); +} + +static void pim_sigusr1() +{ + zlog_debug ("SIGUSR1 received"); + zlog_rotate (NULL); +} + +struct quagga_signal_t pimd_signals[] = +{ + { + .signal = SIGHUP, + .handler = &pim_sighup, + }, + { + .signal = SIGUSR1, + .handler = &pim_sigusr1, + }, + { + .signal = SIGINT, + .handler = &pim_sigint, + }, + { + .signal = SIGTERM, + .handler = &pim_sigterm, + }, +}; + +void pim_signals_init() +{ + signal_init(master, Q_SIGC(pimd_signals), pimd_signals); +} + diff --git a/pimd/pim_signals.h b/pimd/pim_signals.h new file mode 100644 index 00000000..62523c03 --- /dev/null +++ b/pimd/pim_signals.h @@ -0,0 +1,28 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_SIGNALS_H +#define PIM_SIGNALS_H + +void pim_signals_init(void); + +#endif /* PIM_SIGNALS_H */ diff --git a/pimd/pim_sock.c b/pimd/pim_sock.c new file mode 100644 index 00000000..c43cb68e --- /dev/null +++ b/pimd/pim_sock.c @@ -0,0 +1,348 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include "pim_mroute.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "log.h" + +#include "pimd.h" +#include "pim_sock.h" +#include "pim_str.h" + +#ifndef MCAST_JOIN_SOURCE_GROUP +#define MCAST_JOIN_SOURCE_GROUP 46 +struct group_source_req +{ + uint32_t gsr_interface; + struct sockaddr_storage gsr_group; + struct sockaddr_storage gsr_source; +}; +#endif + +int pim_socket_raw(int protocol) +{ + int fd; + + fd = socket(AF_INET, SOCK_RAW, protocol); + if (fd < 0) { + zlog_warn("Could not create raw socket: errno=%d: %s", + errno, strerror(errno)); + return PIM_SOCK_ERR_SOCKET; + } + + return fd; +} + +int pim_socket_mcast(int protocol, struct in_addr ifaddr, int loop) +{ + int fd; + + fd = pim_socket_raw(protocol); + if (fd < 0) { + zlog_warn("Could not create multicast socket: errno=%d: %s", + errno, strerror(errno)); + return PIM_SOCK_ERR_SOCKET; + } + + /* Needed to obtain destination address from recvmsg() */ + { +#if defined(HAVE_IP_PKTINFO) + /* Linux IP_PKTINFO */ + int opt = 1; + if (setsockopt(fd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt))) { + zlog_warn("Could not set IP_PKTINFO on socket fd=%d: errno=%d: %s", + fd, errno, strerror(errno)); + } +#elif defined(HAVE_IP_RECVDSTADDR) + /* BSD IP_RECVDSTADDR */ + int opt = 1; + if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt))) { + zlog_warn("Could not set IP_RECVDSTADDR on socket fd=%d: errno=%d: %s", + fd, errno, strerror(errno)); + } +#else + zlog_err("%s %s: Missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()", + __FILE__, __PRETTY_FUNCTION__); + close(fd); + return PIM_SOCK_ERR_DSTADDR; +#endif + } + + + /* Set router alert (RFC 2113) */ + { + char ra[4]; + ra[0] = 148; + ra[1] = 4; + ra[2] = 0; + ra[3] = 0; + if (setsockopt(fd, IPPROTO_IP, IP_OPTIONS, ra, 4)) { + zlog_warn("Could not set Router Alert Option on socket fd=%d: errno=%d: %s", + fd, errno, strerror(errno)); + close(fd); + return PIM_SOCK_ERR_RA; + } + } + + { + int reuse = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, + (void *) &reuse, sizeof(reuse))) { + zlog_warn("Could not set Reuse Address Option on socket fd=%d: errno=%d: %s", + fd, errno, strerror(errno)); + close(fd); + return PIM_SOCK_ERR_REUSE; + } + } + + { + const int MTTL = 1; + int ttl = MTTL; + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, + (void *) &ttl, sizeof(ttl))) { + zlog_warn("Could not set multicast TTL=%d on socket fd=%d: errno=%d: %s", + MTTL, fd, errno, strerror(errno)); + close(fd); + return PIM_SOCK_ERR_TTL; + } + } + + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, + (void *) &loop, sizeof(loop))) { + zlog_warn("Could not %s Multicast Loopback Option on socket fd=%d: errno=%d: %s", + loop ? "enable" : "disable", + fd, errno, strerror(errno)); + close(fd); + return PIM_SOCK_ERR_LOOP; + } + + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, + (void *) &ifaddr, sizeof(ifaddr))) { + zlog_warn("Could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s", + fd, errno, strerror(errno)); + close(fd); + return PIM_SOCK_ERR_IFACE; + } + + { + long flags; + + flags = fcntl(fd, F_GETFL, 0); + if (flags < 0) { + zlog_warn("Could not get fcntl(F_GETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s", + fd, errno, strerror(errno)); + close(fd); + return PIM_SOCK_ERR_NONBLOCK_GETFL; + } + + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) { + zlog_warn("Could not set fcntl(F_SETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s", + fd, errno, strerror(errno)); + close(fd); + return PIM_SOCK_ERR_NONBLOCK_SETFL; + } + } + + return fd; +} + +int pim_socket_join(int fd, struct in_addr group, + struct in_addr ifaddr, int ifindex) +{ + int ret; + +#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX + struct ip_mreqn opt; +#else + struct ip_mreq opt; +#endif + + opt.imr_multiaddr = group; + +#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX + opt.imr_address = ifaddr; + opt.imr_ifindex = ifindex; +#else + opt.imr_interface = ifaddr; +#endif + + ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &opt, sizeof(opt)); + if (ret) { + char group_str[100]; + char ifaddr_str[100]; + if (!inet_ntop(AF_INET, &group, group_str , sizeof(group_str))) + sprintf(group_str, ""); + if (!inet_ntop(AF_INET, &ifaddr, ifaddr_str , sizeof(ifaddr_str))) + sprintf(ifaddr_str, ""); + + zlog_err("Failure socket joining fd=%d group %s on interface address %s: errno=%d: %s", + fd, group_str, ifaddr_str, errno, strerror(errno)); + return ret; + } + + if (PIM_DEBUG_TRACE) { + char group_str[100]; + char ifaddr_str[100]; + if (!inet_ntop(AF_INET, &group, group_str , sizeof(group_str))) + sprintf(group_str, ""); + if (!inet_ntop(AF_INET, &ifaddr, ifaddr_str , sizeof(ifaddr_str))) + sprintf(ifaddr_str, ""); + + zlog_debug("Socket fd=%d joined group %s on interface address %s", + fd, group_str, ifaddr_str); + } + + return ret; +} + +int pim_socket_join_source(int fd, int ifindex, + struct in_addr group_addr, + struct in_addr source_addr, + const char *ifname) +{ + struct group_source_req req; + struct sockaddr_in *group_sa = (struct sockaddr_in *) &req.gsr_group; + struct sockaddr_in *source_sa = (struct sockaddr_in *) &req.gsr_source; + + memset(group_sa, 0, sizeof(*group_sa)); + group_sa->sin_family = AF_INET; + group_sa->sin_addr = group_addr; + group_sa->sin_port = htons(0); + + memset(source_sa, 0, sizeof(*source_sa)); + source_sa->sin_family = AF_INET; + source_sa->sin_addr = source_addr; + source_sa->sin_port = htons(0); + + req.gsr_interface = ifindex; + + if (setsockopt(fd, SOL_IP, MCAST_JOIN_SOURCE_GROUP, + &req, sizeof(req))) { + int e = errno; + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + zlog_warn("%s: setsockopt(fd=%d) failure for IGMP group %s source %s ifindex %d on interface %s: errno=%d: %s", + __PRETTY_FUNCTION__, + fd, group_str, source_str, ifindex, ifname, + e, strerror(e)); + return -1; + } + + return 0; +} + +int pim_socket_recvfromto(int fd, char *buf, size_t len, + struct sockaddr_in *from, socklen_t *fromlen, + struct sockaddr_in *to, socklen_t *tolen, + int *ifindex) +{ + struct msghdr msgh; + struct cmsghdr *cmsg; + struct iovec iov; + char cbuf[1000]; + int err; + + memset(&msgh, 0, sizeof(struct msghdr)); + iov.iov_base = buf; + iov.iov_len = len; + msgh.msg_control = cbuf; + msgh.msg_controllen = sizeof(cbuf); + msgh.msg_name = from; + msgh.msg_namelen = fromlen ? *fromlen : 0; + msgh.msg_iov = &iov; + msgh.msg_iovlen = 1; + msgh.msg_flags = 0; + + err = recvmsg(fd, &msgh, 0); + if (err < 0) + return err; + + if (fromlen) + *fromlen = msgh.msg_namelen; + + for (cmsg = CMSG_FIRSTHDR(&msgh); + cmsg != NULL; + cmsg = CMSG_NXTHDR(&msgh,cmsg)) { + +#ifdef HAVE_IP_PKTINFO + if ((cmsg->cmsg_level == SOL_IP) && (cmsg->cmsg_type == IP_PKTINFO)) { + struct in_pktinfo *i = (struct in_pktinfo *) CMSG_DATA(cmsg); + if (to) + ((struct sockaddr_in *) to)->sin_addr = i->ipi_addr; + if (tolen) + *tolen = sizeof(struct sockaddr_in); + if (ifindex) + *ifindex = i->ipi_ifindex; + break; + } +#endif + +#ifdef HAVE_IP_RECVDSTADDR + if ((cmsg->cmsg_level == IPPROTO_IP) && (cmsg->cmsg_type == IP_RECVDSTADDR)) { + struct in_addr *i = (struct in_addr *) CMSG_DATA(cmsg); + if (to) + ((struct sockaddr_in *) to)->sin_addr = *i; + if (tolen) + *tolen = sizeof(struct sockaddr_in); + break; + } +#endif + +#if defined(HAVE_IP_RECVIF) && defined(CMSG_IFINDEX) + if (cmsg->cmsg_type == IP_RECVIF) + if (ifindex) + *ifindex = CMSG_IFINDEX(cmsg); +#endif + + } /* for (cmsg) */ + + return err; /* len */ +} + +int pim_socket_mcastloop_get(int fd) +{ + int loop; + socklen_t loop_len = sizeof(loop); + + if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, + &loop, &loop_len)) { + int e = errno; + zlog_warn("Could not get Multicast Loopback Option on socket fd=%d: errno=%d: %s", + fd, errno, strerror(errno)); + errno = e; + return PIM_SOCK_ERR_LOOP; + } + + return loop; +} diff --git a/pimd/pim_sock.h b/pimd/pim_sock.h new file mode 100644 index 00000000..e9d5476f --- /dev/null +++ b/pimd/pim_sock.h @@ -0,0 +1,52 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_SOCK_H +#define PIM_SOCK_H + +#define PIM_SOCK_ERR_NONE (0) /* No error */ +#define PIM_SOCK_ERR_SOCKET (-1) /* socket() */ +#define PIM_SOCK_ERR_RA (-2) /* Router Alert option */ +#define PIM_SOCK_ERR_REUSE (-3) /* Reuse option */ +#define PIM_SOCK_ERR_TTL (-4) /* TTL option */ +#define PIM_SOCK_ERR_LOOP (-5) /* Loopback option */ +#define PIM_SOCK_ERR_IFACE (-6) /* Outgoing interface option */ +#define PIM_SOCK_ERR_DSTADDR (-7) /* Outgoing interface option */ +#define PIM_SOCK_ERR_NONBLOCK_GETFL (-8) /* Get O_NONBLOCK */ +#define PIM_SOCK_ERR_NONBLOCK_SETFL (-9) /* Set O_NONBLOCK */ + +int pim_socket_raw(int protocol); +int pim_socket_mcast(int protocol, struct in_addr ifaddr, int loop); +int pim_socket_join(int fd, struct in_addr group, + struct in_addr ifaddr, int ifindex); +int pim_socket_join_source(int fd, int ifindex, + struct in_addr group_addr, + struct in_addr source_addr, + const char *ifname); +int pim_socket_recvfromto(int fd, char *buf, size_t len, + struct sockaddr_in *from, socklen_t *fromlen, + struct sockaddr_in *to, socklen_t *tolen, + int *ifindex); + +int pim_socket_mcastloop_get(int fd); + +#endif /* PIM_SOCK_H */ diff --git a/pimd/pim_str.c b/pimd/pim_str.c new file mode 100644 index 00000000..7dce7a85 --- /dev/null +++ b/pimd/pim_str.c @@ -0,0 +1,46 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include +#include +#include + +#include + +#include "log.h" + +#include "pim_str.h" + +void pim_inet4_dump(const char *onfail, struct in_addr addr, char *buf, int buf_size) +{ + int save_errno = errno; + + if (!inet_ntop(AF_INET, &addr, buf, buf_size)) { + int e = errno; + zlog_warn("pim_inet4_dump: inet_ntop(AF_INET,buf_size=%d): errno=%d: %s", + buf_size, e, strerror(e)); + if (onfail) + snprintf(buf, buf_size, "%s", onfail); + } + + errno = save_errno; +} diff --git a/pimd/pim_str.h b/pimd/pim_str.h new file mode 100644 index 00000000..925f17f7 --- /dev/null +++ b/pimd/pim_str.h @@ -0,0 +1,32 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_STR_H +#define PIM_STR_H + +#include +#include +#include + +void pim_inet4_dump(const char *onfail, struct in_addr addr, char *buf, int buf_size); + +#endif diff --git a/pimd/pim_time.c b/pimd/pim_time.c new file mode 100644 index 00000000..a837e5b0 --- /dev/null +++ b/pimd/pim_time.c @@ -0,0 +1,151 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include +#include +#include + +#include +#include "log.h" +#include "thread.h" + +#include "pim_time.h" + +/* + see man clock_gettime + */ +static int pim_gettime(clockid_t clk_id, struct timeval *tv) +{ + return quagga_gettime(clk_id, tv); +} + +/* + pim_time_monotonic_sec(): + number of seconds since some unspecified starting point +*/ +int64_t pim_time_monotonic_sec() +{ + struct timeval now_tv; + + if (pim_gettime(CLOCK_MONOTONIC, &now_tv)) { + zlog_err("%s: gettime(CLOCK_MONOTONIC) failure: errno=%d: %s", + __PRETTY_FUNCTION__, + errno, strerror(errno)); + return -1; + } + + return now_tv.tv_sec; +} + +/* + pim_time_monotonic_dsec(): + number of deciseconds since some unspecified starting point +*/ +int64_t pim_time_monotonic_dsec() +{ + struct timeval now_tv; + int64_t now_dsec; + + if (pim_gettime(CLOCK_MONOTONIC, &now_tv)) { + zlog_err("%s: gettime(CLOCK_MONOTONIC) failure: errno=%d: %s", + __PRETTY_FUNCTION__, + errno, strerror(errno)); + return -1; + } + + now_dsec = ((int64_t) now_tv.tv_sec) * 10 + ((int64_t) now_tv.tv_usec) / 100000; + + return now_dsec; +} + +int pim_time_mmss(char *buf, int buf_size, long sec) +{ + long mm; + int wr; + + zassert(buf_size >= 5); + + mm = sec / 60; + sec %= 60; + + wr = snprintf(buf, buf_size, "%02ld:%02ld", mm, sec); + + return wr != 8; +} + +static int pim_time_hhmmss(char *buf, int buf_size, long sec) +{ + long hh; + long mm; + int wr; + + zassert(buf_size >= 8); + + hh = sec / 3600; + sec %= 3600; + mm = sec / 60; + sec %= 60; + + wr = snprintf(buf, buf_size, "%02ld:%02ld:%02ld", hh, mm, sec); + + return wr != 8; +} + +void pim_time_timer_to_mmss(char *buf, int buf_size, struct thread *t_timer) +{ + if (t_timer) { + pim_time_mmss(buf, buf_size, + thread_timer_remain_second(t_timer)); + } + else { + snprintf(buf, buf_size, "--:--"); + } +} + +void pim_time_timer_to_hhmmss(char *buf, int buf_size, struct thread *t_timer) +{ + if (t_timer) { + pim_time_hhmmss(buf, buf_size, + thread_timer_remain_second(t_timer)); + } + else { + snprintf(buf, buf_size, "--:--:--"); + } +} + +void pim_time_uptime(char *buf, int buf_size, int64_t uptime_sec) +{ + zassert(buf_size >= 8); + + pim_time_hhmmss(buf, buf_size, uptime_sec); +} + +long pim_time_timer_remain_msec(struct thread *t_timer) +{ + /* FIXME: Actually fetch msec resolution from thread */ + + /* no timer thread running means timer has expired: return 0 */ + + return t_timer ? + 1000 * thread_timer_remain_second(t_timer) : + 0; +} diff --git a/pimd/pim_time.h b/pimd/pim_time.h new file mode 100644 index 00000000..379eb6cf --- /dev/null +++ b/pimd/pim_time.h @@ -0,0 +1,39 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_TIME_H +#define PIM_TIME_H + +#include + +#include +#include "thread.h" + +int64_t pim_time_monotonic_sec(void); +int64_t pim_time_monotonic_dsec(void); +int pim_time_mmss(char *buf, int buf_size, long sec); +void pim_time_timer_to_mmss(char *buf, int buf_size, struct thread *t); +void pim_time_timer_to_hhmmss(char *buf, int buf_size, struct thread *t); +void pim_time_uptime(char *buf, int buf_size, int64_t uptime_sec); +long pim_time_timer_remain_msec(struct thread *t_timer); + +#endif /* PIM_TIME_H */ diff --git a/pimd/pim_tlv.c b/pimd/pim_tlv.c new file mode 100644 index 00000000..c578a70d --- /dev/null +++ b/pimd/pim_tlv.c @@ -0,0 +1,707 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "log.h" +#include "prefix.h" + +#include "pimd.h" +#include "pim_tlv.h" +#include "pim_str.h" +#include "pim_msg.h" + +char *pim_tlv_append_uint16(char *buf, + const char *buf_pastend, + uint16_t option_type, + uint16_t option_value) +{ + uint16_t option_len = 2; + + if ((buf + PIM_TLV_OPTION_SIZE(option_len)) > buf_pastend) { + zlog_warn("%s: buffer overflow: left=%d needed=%d", + __PRETTY_FUNCTION__, + buf_pastend - buf, PIM_TLV_OPTION_SIZE(option_len)); + return 0; + } + + *(uint16_t *) buf = htons(option_type); + buf += 2; + *(uint16_t *) buf = htons(option_len); + buf += 2; + *(uint16_t *) buf = htons(option_value); + buf += option_len; + + return buf; +} + +char *pim_tlv_append_2uint16(char *buf, + const char *buf_pastend, + uint16_t option_type, + uint16_t option_value1, + uint16_t option_value2) +{ + uint16_t option_len = 4; + + if ((buf + PIM_TLV_OPTION_SIZE(option_len)) > buf_pastend) { + zlog_warn("%s: buffer overflow: left=%d needed=%d", + __PRETTY_FUNCTION__, + buf_pastend - buf, PIM_TLV_OPTION_SIZE(option_len)); + return 0; + } + + *(uint16_t *) buf = htons(option_type); + buf += 2; + *(uint16_t *) buf = htons(option_len); + buf += 2; + *(uint16_t *) buf = htons(option_value1); + buf += 2; + *(uint16_t *) buf = htons(option_value2); + buf += 2; + + return buf; +} + +char *pim_tlv_append_uint32(char *buf, + const char *buf_pastend, + uint16_t option_type, + uint32_t option_value) +{ + uint16_t option_len = 4; + + if ((buf + PIM_TLV_OPTION_SIZE(option_len)) > buf_pastend) { + zlog_warn("%s: buffer overflow: left=%d needed=%d", + __PRETTY_FUNCTION__, + buf_pastend - buf, PIM_TLV_OPTION_SIZE(option_len)); + return 0; + } + + *(uint16_t *) buf = htons(option_type); + buf += 2; + *(uint16_t *) buf = htons(option_len); + buf += 2; + *(uint32_t *) buf = htonl(option_value); + buf += option_len; + + return buf; +} + +#define ucast_ipv4_encoding_len (2 + sizeof(struct in_addr)) + +char *pim_tlv_append_addrlist_ucast(char *buf, + const char *buf_pastend, + struct list *ifconnected) +{ + struct listnode *node; + uint16_t option_len = 0; + + char *curr; + + node = listhead(ifconnected); + + /* Empty address list ? */ + if (!node) { + return buf; + } + + /* Skip first address (primary) */ + node = listnextnode(node); + + /* Scan secondary address list */ + curr = buf + 4; /* skip T and L */ + for (; node; node = listnextnode(node)) { + struct connected *ifc = listgetdata(node); + struct prefix *p = ifc->address; + + if (p->family != AF_INET) + continue; + + if ((curr + ucast_ipv4_encoding_len) > buf_pastend) { + zlog_warn("%s: buffer overflow: left=%d needed=%d", + __PRETTY_FUNCTION__, + buf_pastend - curr, ucast_ipv4_encoding_len); + return 0; + } + + /* Write encoded unicast IPv4 address */ + *(uint8_t *) curr = PIM_MSG_ADDRESS_FAMILY_IPV4; /* notice: AF_INET != PIM_MSG_ADDRESS_FAMILY_IPV4 */ + ++curr; + *(uint8_t *) curr = 0; /* ucast IPv4 native encoding type (RFC 4601: 4.9.1) */ + ++curr; + *(struct in_addr *) curr = p->u.prefix4; + curr += sizeof(struct in_addr); + + option_len += ucast_ipv4_encoding_len; + } + + if (PIM_DEBUG_PIM_TRACE) { + zlog_warn("%s: number of encoded secondary unicast IPv4 addresses: %d", + __PRETTY_FUNCTION__, + option_len / ucast_ipv4_encoding_len); + } + + if (option_len < 1) { + /* Empty secondary unicast IPv4 address list */ + return buf; + } + + /* + * Write T and L + */ + *(uint16_t *) buf = htons(PIM_MSG_OPTION_TYPE_ADDRESS_LIST); + *(uint16_t *) (buf + 2) = htons(option_len); + + return curr; +} + +static int check_tlv_length(const char *label, const char *tlv_name, + const char *ifname, struct in_addr src_addr, + int correct_len, int option_len) +{ + if (option_len != correct_len) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: PIM hello %s TLV with incorrect value size=%d correct=%d from %s on interface %s", + label, tlv_name, + option_len, correct_len, + src_str, ifname); + return -1; + } + + return 0; +} + +static void check_tlv_redefinition_uint16(const char *label, const char *tlv_name, + const char *ifname, struct in_addr src_addr, + pim_hello_options options, + pim_hello_options opt_mask, + uint16_t new, uint16_t old) +{ + if (PIM_OPTION_IS_SET(options, opt_mask)) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: PIM hello TLV redefined %s=%u old=%u from %s on interface %s", + label, tlv_name, + new, old, + src_str, ifname); + } +} + +static void check_tlv_redefinition_uint32(const char *label, const char *tlv_name, + const char *ifname, struct in_addr src_addr, + pim_hello_options options, + pim_hello_options opt_mask, + uint32_t new, uint32_t old) +{ + if (PIM_OPTION_IS_SET(options, opt_mask)) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: PIM hello TLV redefined %s=%u old=%u from %s on interface %s", + label, tlv_name, + new, old, + src_str, ifname); + } +} + +static void check_tlv_redefinition_uint32_hex(const char *label, const char *tlv_name, + const char *ifname, struct in_addr src_addr, + pim_hello_options options, + pim_hello_options opt_mask, + uint32_t new, uint32_t old) +{ + if (PIM_OPTION_IS_SET(options, opt_mask)) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: PIM hello TLV redefined %s=%08x old=%08x from %s on interface %s", + label, tlv_name, + new, old, + src_str, ifname); + } +} + +int pim_tlv_parse_holdtime(const char *ifname, struct in_addr src_addr, + pim_hello_options *hello_options, + uint16_t *hello_option_holdtime, + uint16_t option_len, + const char *tlv_curr) +{ + const char *label = "holdtime"; + + if (check_tlv_length(__PRETTY_FUNCTION__, label, + ifname, src_addr, + sizeof(uint16_t), option_len)) { + return -1; + } + + check_tlv_redefinition_uint16(__PRETTY_FUNCTION__, label, + ifname, src_addr, + *hello_options, PIM_OPTION_MASK_HOLDTIME, + PIM_TLV_GET_HOLDTIME(tlv_curr), + *hello_option_holdtime); + + PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_HOLDTIME); + + *hello_option_holdtime = PIM_TLV_GET_HOLDTIME(tlv_curr); + + return 0; +} + +int pim_tlv_parse_lan_prune_delay(const char *ifname, struct in_addr src_addr, + pim_hello_options *hello_options, + uint16_t *hello_option_propagation_delay, + uint16_t *hello_option_override_interval, + uint16_t option_len, + const char *tlv_curr) +{ + if (check_tlv_length(__PRETTY_FUNCTION__, "lan_prune_delay", + ifname, src_addr, + sizeof(uint32_t), option_len)) { + return -1; + } + + check_tlv_redefinition_uint16(__PRETTY_FUNCTION__, "propagation_delay", + ifname, src_addr, + *hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY, + PIM_TLV_GET_PROPAGATION_DELAY(tlv_curr), + *hello_option_propagation_delay); + + PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY); + + *hello_option_propagation_delay = PIM_TLV_GET_PROPAGATION_DELAY(tlv_curr); + if (PIM_TLV_GET_CAN_DISABLE_JOIN_SUPPRESSION(tlv_curr)) { + PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION); + } + else { + PIM_OPTION_UNSET(*hello_options, PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION); + } + ++tlv_curr; + ++tlv_curr; + *hello_option_override_interval = PIM_TLV_GET_OVERRIDE_INTERVAL(tlv_curr); + + return 0; +} + +int pim_tlv_parse_dr_priority(const char *ifname, struct in_addr src_addr, + pim_hello_options *hello_options, + uint32_t *hello_option_dr_priority, + uint16_t option_len, + const char *tlv_curr) +{ + const char *label = "dr_priority"; + + if (check_tlv_length(__PRETTY_FUNCTION__, label, + ifname, src_addr, + sizeof(uint32_t), option_len)) { + return -1; + } + + check_tlv_redefinition_uint32(__PRETTY_FUNCTION__, label, + ifname, src_addr, + *hello_options, PIM_OPTION_MASK_DR_PRIORITY, + PIM_TLV_GET_DR_PRIORITY(tlv_curr), + *hello_option_dr_priority); + + PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_DR_PRIORITY); + + *hello_option_dr_priority = PIM_TLV_GET_DR_PRIORITY(tlv_curr); + + return 0; +} + +int pim_tlv_parse_generation_id(const char *ifname, struct in_addr src_addr, + pim_hello_options *hello_options, + uint32_t *hello_option_generation_id, + uint16_t option_len, + const char *tlv_curr) +{ + const char *label = "generation_id"; + + if (check_tlv_length(__PRETTY_FUNCTION__, label, + ifname, src_addr, + sizeof(uint32_t), option_len)) { + return -1; + } + + check_tlv_redefinition_uint32_hex(__PRETTY_FUNCTION__, label, + ifname, src_addr, + *hello_options, PIM_OPTION_MASK_GENERATION_ID, + PIM_TLV_GET_GENERATION_ID(tlv_curr), + *hello_option_generation_id); + + PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_GENERATION_ID); + + *hello_option_generation_id = PIM_TLV_GET_GENERATION_ID(tlv_curr); + + return 0; +} + +int pim_parse_addr_ucast(const char *ifname, struct in_addr src_addr, + struct prefix *p, + const char *buf, + int buf_size) +{ + const int ucast_encoding_min_len = 3; /* 1 family + 1 type + 1 addr */ + const char *addr; + const char *pastend; + int family; + int type; + + if (buf_size < ucast_encoding_min_len) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: unicast address encoding overflow: left=%d needed=%d from %s on %s", + __PRETTY_FUNCTION__, + buf_size, ucast_encoding_min_len, + src_str, ifname); + return -1; + } + + addr = buf; + pastend = buf + buf_size; + + family = *(const uint8_t *) addr; + ++addr; + type = *(const uint8_t *) addr; + ++addr; + + switch (family) { + case PIM_MSG_ADDRESS_FAMILY_IPV4: + if (type) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: unknown unicast address encoding type=%d from %s on %s", + __PRETTY_FUNCTION__, + type, src_str, ifname); + return -2; + } + + if ((addr + sizeof(struct in_addr)) > pastend) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: IPv4 unicast address overflow: left=%d needed=%d from %s on %s", + __PRETTY_FUNCTION__, + pastend - addr, sizeof(struct in_addr), + src_str, ifname); + return -3; + } + + p->family = AF_INET; /* notice: AF_INET != PIM_MSG_ADDRESS_FAMILY_IPV4 */ + p->u.prefix4 = *(const struct in_addr *) addr; + addr += sizeof(struct in_addr); + + break; + default: + { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: unknown unicast address encoding family=%d from %s on %s", + __PRETTY_FUNCTION__, + family, src_str, ifname); + return -4; + } + } + + return addr - buf; +} + +int pim_parse_addr_group(const char *ifname, struct in_addr src_addr, + struct prefix *p, + const char *buf, + int buf_size) +{ + const int grp_encoding_min_len = 4; /* 1 family + 1 type + 1 reserved + 1 addr */ + const char *addr; + const char *pastend; + int family; + int type; + int mask_len; + + if (buf_size < grp_encoding_min_len) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: group address encoding overflow: left=%d needed=%d from %s on %s", + __PRETTY_FUNCTION__, + buf_size, grp_encoding_min_len, + src_str, ifname); + return -1; + } + + addr = buf; + pastend = buf + buf_size; + + family = *(const uint8_t *) addr; + ++addr; + type = *(const uint8_t *) addr; + ++addr; + ++addr; /* skip b_reserved_z fields */ + mask_len = *(const uint8_t *) addr; + ++addr; + + switch (family) { + case PIM_MSG_ADDRESS_FAMILY_IPV4: + if (type) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: unknown group address encoding type=%d from %s on %s", + __PRETTY_FUNCTION__, + type, src_str, ifname); + return -2; + } + + if ((addr + sizeof(struct in_addr)) > pastend) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: IPv4 group address overflow: left=%d needed=%d from %s on %s", + __PRETTY_FUNCTION__, + pastend - addr, sizeof(struct in_addr), + src_str, ifname); + return -3; + } + + p->family = AF_INET; /* notice: AF_INET != PIM_MSG_ADDRESS_FAMILY_IPV4 */ + p->u.prefix4 = *(const struct in_addr *) addr; + p->prefixlen = mask_len; + + addr += sizeof(struct in_addr); + + break; + default: + { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: unknown group address encoding family=%d from %s on %s", + __PRETTY_FUNCTION__, + family, src_str, ifname); + return -4; + } + } + + return addr - buf; +} + +int pim_parse_addr_source(const char *ifname, + struct in_addr src_addr, + struct prefix *p, + uint8_t *flags, + const char *buf, + int buf_size) +{ + const int src_encoding_min_len = 4; /* 1 family + 1 type + 1 reserved + 1 addr */ + const char *addr; + const char *pastend; + int family; + int type; + int mask_len; + + if (buf_size < src_encoding_min_len) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: source address encoding overflow: left=%d needed=%d from %s on %s", + __PRETTY_FUNCTION__, + buf_size, src_encoding_min_len, + src_str, ifname); + return -1; + } + + addr = buf; + pastend = buf + buf_size; + + family = *(const uint8_t *) addr; + ++addr; + type = *(const uint8_t *) addr; + ++addr; + *flags = *(const uint8_t *) addr; + ++addr; + mask_len = *(const uint8_t *) addr; + ++addr; + + switch (family) { + case PIM_MSG_ADDRESS_FAMILY_IPV4: + if (type) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: unknown source address encoding type=%d from %s on %s", + __PRETTY_FUNCTION__, + type, src_str, ifname); + return -2; + } + + if ((addr + sizeof(struct in_addr)) > pastend) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: IPv4 source address overflow: left=%d needed=%d from %s on %s", + __PRETTY_FUNCTION__, + pastend - addr, sizeof(struct in_addr), + src_str, ifname); + return -3; + } + + p->family = AF_INET; /* notice: AF_INET != PIM_MSG_ADDRESS_FAMILY_IPV4 */ + p->u.prefix4 = *(const struct in_addr *) addr; + p->prefixlen = mask_len; + + addr += sizeof(struct in_addr); + + break; + default: + { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: unknown source address encoding family=%d from %s on %s", + __PRETTY_FUNCTION__, + family, src_str, ifname); + return -4; + } + } + + return addr - buf; +} + +#define FREE_ADDR_LIST(hello_option_addr_list) \ +{ \ + if (hello_option_addr_list) { \ + list_delete(hello_option_addr_list); \ + hello_option_addr_list = 0; \ + } \ +} + +int pim_tlv_parse_addr_list(const char *ifname, struct in_addr src_addr, + pim_hello_options *hello_options, + struct list **hello_option_addr_list, + uint16_t option_len, + const char *tlv_curr) +{ + const char *addr; + const char *pastend; + + zassert(hello_option_addr_list); + + /* + Scan addr list + */ + addr = tlv_curr; + pastend = tlv_curr + option_len; + while (addr < pastend) { + struct prefix tmp; + int addr_offset; + + /* + Parse ucast addr + */ + addr_offset = pim_parse_addr_ucast(ifname, src_addr, &tmp, + addr, pastend - addr); + if (addr_offset < 1) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s", + __PRETTY_FUNCTION__, + src_str, ifname); + FREE_ADDR_LIST(*hello_option_addr_list); + return -1; + } + addr += addr_offset; + + /* + Debug + */ + if (PIM_DEBUG_PIM_TRACE) { + switch (tmp.family) { + case AF_INET: + { + char addr_str[100]; + char src_str[100]; + pim_inet4_dump("", tmp.u.prefix4, addr_str, sizeof(addr_str)); + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_debug("%s: PIM hello TLV option: list_old_size=%d IPv4 address %s from %s on %s", + __PRETTY_FUNCTION__, + *hello_option_addr_list ? + ((int) listcount(*hello_option_addr_list)) : -1, + addr_str, src_str, ifname); + } + break; + default: + { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_debug("%s: PIM hello TLV option: list_old_size=%d UNKNOWN address family from %s on %s", + __PRETTY_FUNCTION__, + *hello_option_addr_list ? + ((int) listcount(*hello_option_addr_list)) : -1, + src_str, ifname); + } + } + } + + /* + Exclude neighbor's primary address if incorrectly included in + the secondary address list + */ + if (tmp.family == AF_INET) { + if (tmp.u.prefix4.s_addr == src_addr.s_addr) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_warn("%s: ignoring primary address in secondary list from %s on %s", + __PRETTY_FUNCTION__, + src_str, ifname); + continue; + } + } + + /* + Allocate list if needed + */ + if (!*hello_option_addr_list) { + *hello_option_addr_list = list_new(); + if (!*hello_option_addr_list) { + zlog_err("%s %s: failure: hello_option_addr_list=list_new()", + __FILE__, __PRETTY_FUNCTION__); + return -2; + } + (*hello_option_addr_list)->del = (void (*)(void *)) prefix_free; + } + + /* + Attach addr to list + */ + { + struct prefix *p; + p = prefix_new(); + if (!p) { + zlog_err("%s %s: failure: prefix_new()", + __FILE__, __PRETTY_FUNCTION__); + FREE_ADDR_LIST(*hello_option_addr_list); + return -3; + } + p->family = tmp.family; + p->u.prefix4 = tmp.u.prefix4; + listnode_add(*hello_option_addr_list, p); + } + + } /* while (addr < pastend) */ + + /* + Mark hello option + */ + PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_ADDRESS_LIST); + + return 0; +} diff --git a/pimd/pim_tlv.h b/pimd/pim_tlv.h new file mode 100644 index 00000000..9602cb1c --- /dev/null +++ b/pimd/pim_tlv.h @@ -0,0 +1,133 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_TLV_H +#define PIM_TLV_H + +#include + +#include "config.h" +#include "if.h" +#include "linklist.h" + +#ifdef HAVE_INTTYPES_H +#include +#endif /* HAVE_INTTYPES_H */ + +#define PIM_MSG_OPTION_TYPE_HOLDTIME (1) +#define PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY (2) +#define PIM_MSG_OPTION_TYPE_DR_PRIORITY (19) +#define PIM_MSG_OPTION_TYPE_GENERATION_ID (20) +#define PIM_MSG_OPTION_TYPE_DM_STATE_REFRESH (21) +#define PIM_MSG_OPTION_TYPE_ADDRESS_LIST (24) + +typedef uint32_t pim_hello_options; +#define PIM_OPTION_MASK_HOLDTIME (1 << 0) /* recv holdtime */ +#define PIM_OPTION_MASK_LAN_PRUNE_DELAY (1 << 1) /* recv lan_prune_delay */ +#define PIM_OPTION_MASK_DR_PRIORITY (1 << 2) /* recv dr_priority */ +#define PIM_OPTION_MASK_GENERATION_ID (1 << 3) /* recv generation_id */ +#define PIM_OPTION_MASK_ADDRESS_LIST (1 << 4) /* recv secondary address list */ +#define PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION (1 << 5) /* T bit value (valid if recv lan_prune_delay) */ + +#define PIM_RPT_BIT_MASK (1 << 0) +#define PIM_WILDCARD_BIT_MASK (1 << 1) + +#define PIM_OPTION_SET(options, option_mask) ((options) |= (option_mask)) +#define PIM_OPTION_UNSET(options, option_mask) ((options) &= ~(option_mask)) +#define PIM_OPTION_IS_SET(options, option_mask) ((options) & (option_mask)) + +#define PIM_TLV_GET_UINT16(buf) ntohs(*(const uint16_t *)(buf)) +#define PIM_TLV_GET_UINT32(buf) ntohl(*(const uint32_t *)(buf)) +#define PIM_TLV_GET_TYPE(buf) PIM_TLV_GET_UINT16(buf) +#define PIM_TLV_GET_LENGTH(buf) PIM_TLV_GET_UINT16(buf) +#define PIM_TLV_GET_HOLDTIME(buf) PIM_TLV_GET_UINT16(buf) +#define PIM_TLV_GET_PROPAGATION_DELAY(buf) (PIM_TLV_GET_UINT16(buf) & 0x7FFF) +#define PIM_TLV_GET_OVERRIDE_INTERVAL(buf) PIM_TLV_GET_UINT16(buf) +#define PIM_TLV_GET_CAN_DISABLE_JOIN_SUPPRESSION(buf) ((*(const uint8_t *)(buf)) & 0x80) +#define PIM_TLV_GET_DR_PRIORITY(buf) PIM_TLV_GET_UINT32(buf) +#define PIM_TLV_GET_GENERATION_ID(buf) PIM_TLV_GET_UINT32(buf) + +#define PIM_TLV_TYPE_SIZE (2) +#define PIM_TLV_LENGTH_SIZE (2) +#define PIM_TLV_MIN_SIZE (PIM_TLV_TYPE_SIZE + PIM_TLV_LENGTH_SIZE) +#define PIM_TLV_OPTION_SIZE(option_len) (PIM_TLV_MIN_SIZE + (option_len)) + +char *pim_tlv_append_uint16(char *buf, + const char *buf_pastend, + uint16_t option_type, + uint16_t option_value); +char *pim_tlv_append_2uint16(char *buf, + const char *buf_pastend, + uint16_t option_type, + uint16_t option_value1, + uint16_t option_value2); +char *pim_tlv_append_uint32(char *buf, + const char *buf_pastend, + uint16_t option_type, + uint32_t option_value); +char *pim_tlv_append_addrlist_ucast(char *buf, + const char *buf_pastend, + struct list *ifconnected); + +int pim_tlv_parse_holdtime(const char *ifname, struct in_addr src_addr, + pim_hello_options *hello_options, + uint16_t *hello_option_holdtime, + uint16_t option_len, + const char *tlv_curr); +int pim_tlv_parse_lan_prune_delay(const char *ifname, struct in_addr src_addr, + pim_hello_options *hello_options, + uint16_t *hello_option_propagation_delay, + uint16_t *hello_option_override_interval, + uint16_t option_len, + const char *tlv_curr); +int pim_tlv_parse_dr_priority(const char *ifname, struct in_addr src_addr, + pim_hello_options *hello_options, + uint32_t *hello_option_dr_priority, + uint16_t option_len, + const char *tlv_curr); +int pim_tlv_parse_generation_id(const char *ifname, struct in_addr src_addr, + pim_hello_options *hello_options, + uint32_t *hello_option_generation_id, + uint16_t option_len, + const char *tlv_curr); +int pim_tlv_parse_addr_list(const char *ifname, struct in_addr src_addr, + pim_hello_options *hello_options, + struct list **hello_option_addr_list, + uint16_t option_len, + const char *tlv_curr); + +int pim_parse_addr_ucast(const char *ifname, struct in_addr src_addr, + struct prefix *p, + const char *buf, + int buf_size); +int pim_parse_addr_group(const char *ifname, struct in_addr src_addr, + struct prefix *p, + const char *buf, + int buf_size); +int pim_parse_addr_source(const char *ifname, + struct in_addr src_addr, + struct prefix *p, + uint8_t *flags, + const char *buf, + int buf_size); + +#endif /* PIM_TLV_H */ diff --git a/pimd/pim_upstream.c b/pimd/pim_upstream.c new file mode 100644 index 00000000..b9cf1e52 --- /dev/null +++ b/pimd/pim_upstream.c @@ -0,0 +1,686 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "zebra/rib.h" + +#include "log.h" +#include "zclient.h" +#include "memory.h" +#include "thread.h" +#include "linklist.h" + +#include "pimd.h" +#include "pim_pim.h" +#include "pim_str.h" +#include "pim_time.h" +#include "pim_iface.h" +#include "pim_join.h" +#include "pim_zlookup.h" +#include "pim_upstream.h" +#include "pim_ifchannel.h" +#include "pim_neighbor.h" +#include "pim_rpf.h" +#include "pim_zebra.h" +#include "pim_oil.h" +#include "pim_macro.h" + +static void join_timer_start(struct pim_upstream *up); +static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up); + +void pim_upstream_free(struct pim_upstream *up) +{ + XFREE(MTYPE_PIM_UPSTREAM, up); +} + +static void upstream_channel_oil_detach(struct pim_upstream *up) +{ + if (up->channel_oil) { + pim_channel_oil_del(up->channel_oil); + up->channel_oil = 0; + } +} + +void pim_upstream_delete(struct pim_upstream *up) +{ + THREAD_OFF(up->t_join_timer); + + upstream_channel_oil_detach(up); + + /* + notice that listnode_delete() can't be moved + into pim_upstream_free() because the later is + called by list_delete_all_node() + */ + listnode_delete(qpim_upstream_list, up); + + pim_upstream_free(up); +} + +static void send_join(struct pim_upstream *up) +{ + zassert(up->join_state == PIM_UPSTREAM_JOINED); + + + if (PIM_DEBUG_PIM_TRACE) { + if (PIM_INADDR_IS_ANY(up->rpf.rpf_addr)) { + char src_str[100]; + char grp_str[100]; + char rpf_str[100]; + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", up->rpf.rpf_addr, rpf_str, sizeof(rpf_str)); + zlog_warn("%s: can't send join upstream: RPF'(%s,%s)=%s", + __PRETTY_FUNCTION__, + src_str, grp_str, rpf_str); + /* warning only */ + } + } + + /* send Join(S,G) to the current upstream neighbor */ + pim_joinprune_send(up->rpf.source_nexthop.interface, + up->rpf.rpf_addr, + up->source_addr, + up->group_addr, + 1 /* join */); +} + +static int on_join_timer(struct thread *t) +{ + struct pim_upstream *up; + + zassert(t); + up = THREAD_ARG(t); + zassert(up); + + send_join(up); + + up->t_join_timer = 0; + join_timer_start(up); + + return 0; +} + +static void join_timer_start(struct pim_upstream *up) +{ + if (PIM_DEBUG_PIM_EVENTS) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + zlog_debug("%s: starting %d sec timer for upstream (S,G)=(%s,%s)", + __PRETTY_FUNCTION__, + qpim_t_periodic, + src_str, grp_str); + } + + zassert(!up->t_join_timer); + + THREAD_TIMER_ON(master, up->t_join_timer, + on_join_timer, + up, qpim_t_periodic); +} + +void pim_upstream_join_timer_restart(struct pim_upstream *up) +{ + THREAD_OFF(up->t_join_timer); + join_timer_start(up); +} + +static void pim_upstream_join_timer_restart_msec(struct pim_upstream *up, + int interval_msec) +{ + if (PIM_DEBUG_PIM_EVENTS) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + zlog_debug("%s: restarting %d msec timer for upstream (S,G)=(%s,%s)", + __PRETTY_FUNCTION__, + interval_msec, + src_str, grp_str); + } + + THREAD_OFF(up->t_join_timer); + THREAD_TIMER_MSEC_ON(master, up->t_join_timer, + on_join_timer, + up, interval_msec); +} + +void pim_upstream_join_suppress(struct pim_upstream *up, + struct in_addr rpf_addr, + int holdtime) +{ + long t_joinsuppress_msec; + long join_timer_remain_msec; + + t_joinsuppress_msec = MIN(pim_if_t_suppressed_msec(up->rpf.source_nexthop.interface), + 1000 * holdtime); + + join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer); + + if (PIM_DEBUG_PIM_TRACE) { + char src_str[100]; + char grp_str[100]; + char rpf_str[100]; + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", rpf_addr, rpf_str, sizeof(rpf_str)); + zlog_debug("%s %s: detected Join(%s,%s) to RPF'(S,G)=%s: join_timer=%ld msec t_joinsuppress=%ld msec", + __FILE__, __PRETTY_FUNCTION__, + src_str, grp_str, + rpf_str, + join_timer_remain_msec, t_joinsuppress_msec); + } + + if (join_timer_remain_msec < t_joinsuppress_msec) { + if (PIM_DEBUG_PIM_TRACE) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + zlog_debug("%s %s: suppressing Join(S,G)=(%s,%s) for %ld msec", + __FILE__, __PRETTY_FUNCTION__, + src_str, grp_str, t_joinsuppress_msec); + } + + pim_upstream_join_timer_restart_msec(up, t_joinsuppress_msec); + } +} + +void pim_upstream_join_timer_decrease_to_t_override(const char *debug_label, + struct pim_upstream *up, + struct in_addr rpf_addr) +{ + long join_timer_remain_msec; + int t_override_msec; + + join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer); + t_override_msec = pim_if_t_override_msec(up->rpf.source_nexthop.interface); + + if (PIM_DEBUG_PIM_TRACE) { + char src_str[100]; + char grp_str[100]; + char rpf_str[100]; + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", rpf_addr, rpf_str, sizeof(rpf_str)); + zlog_debug("%s: to RPF'(%s,%s)=%s: join_timer=%ld msec t_override=%d msec", + debug_label, + src_str, grp_str, rpf_str, + join_timer_remain_msec, t_override_msec); + } + + if (join_timer_remain_msec > t_override_msec) { + if (PIM_DEBUG_PIM_TRACE) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + zlog_debug("%s: decreasing (S,G)=(%s,%s) join timer to t_override=%d msec", + debug_label, + src_str, grp_str, + t_override_msec); + } + + pim_upstream_join_timer_restart_msec(up, t_override_msec); + } +} + +static void forward_on(struct pim_upstream *up) +{ + struct listnode *ifnode; + struct listnode *ifnextnode; + struct listnode *chnode; + struct listnode *chnextnode; + struct interface *ifp; + struct pim_interface *pim_ifp; + struct pim_ifchannel *ch; + struct in_addr ifaddr; + + /* scan all interfaces */ + for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) { + pim_ifp = ifp->info; + if (!pim_ifp) + continue; + + ifaddr = pim_ifp->primary_address; + + /* scan per-interface (S,G) state */ + for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) { + + if (ch->upstream != up) + continue; + + if (pim_macro_chisin_oiflist(ch)) + pim_forward_start(ch); + + } /* scan iface channel list */ + } /* scan iflist */ +} + +static void forward_off(struct pim_upstream *up) +{ + struct listnode *ifnode; + struct listnode *ifnextnode; + struct listnode *chnode; + struct listnode *chnextnode; + struct interface *ifp; + struct pim_interface *pim_ifp; + struct pim_ifchannel *ch; + + /* scan all interfaces */ + for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) { + pim_ifp = ifp->info; + if (!pim_ifp) + continue; + + /* scan per-interface (S,G) state */ + for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) { + + if (ch->upstream != up) + continue; + + pim_forward_stop(ch); + + } /* scan iface channel list */ + } /* scan iflist */ +} + +static void pim_upstream_switch(struct pim_upstream *up, + enum pim_upstream_state new_state) +{ + enum pim_upstream_state old_state = up->join_state; + + zassert(old_state != new_state); + + up->join_state = new_state; + up->state_transition = pim_time_monotonic_sec(); + + if (PIM_DEBUG_PIM_EVENTS) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + zlog_debug("%s: PIM_UPSTREAM_%s: (S,G)=(%s,%s)", + __PRETTY_FUNCTION__, + ((new_state == PIM_UPSTREAM_JOINED) ? "JOINED" : "NOTJOINED"), + src_str, grp_str); + } + + pim_upstream_update_assert_tracking_desired(up); + + if (new_state == PIM_UPSTREAM_JOINED) { + forward_on(up); + send_join(up); + join_timer_start(up); + } + else { + forward_off(up); + pim_joinprune_send(up->rpf.source_nexthop.interface, + up->rpf.rpf_addr, + up->source_addr, + up->group_addr, + 0 /* prune */); + zassert(up->t_join_timer); + THREAD_OFF(up->t_join_timer); + } + +} + +static struct pim_upstream *pim_upstream_new(struct in_addr source_addr, + struct in_addr group_addr) +{ + struct pim_upstream *up; + + up = XMALLOC(MTYPE_PIM_UPSTREAM, sizeof(*up)); + if (!up) { + zlog_err("%s: PIM XMALLOC(%d) failure", + __PRETTY_FUNCTION__, sizeof(*up)); + return 0; + } + + up->source_addr = source_addr; + up->group_addr = group_addr; + up->flags = 0; + up->ref_count = 1; + up->t_join_timer = 0; + up->join_state = 0; + up->state_transition = pim_time_monotonic_sec(); + up->channel_oil = 0; + + up->rpf.source_nexthop.interface = 0; + up->rpf.source_nexthop.mrib_nexthop_addr.s_addr = PIM_NET_INADDR_ANY; + up->rpf.source_nexthop.mrib_metric_preference = qpim_infinite_assert_metric.metric_preference; + up->rpf.source_nexthop.mrib_route_metric = qpim_infinite_assert_metric.route_metric; + up->rpf.rpf_addr.s_addr = PIM_NET_INADDR_ANY; + + pim_rpf_update(up, 0); + + listnode_add(qpim_upstream_list, up); + + return up; +} + +struct pim_upstream *pim_upstream_find(struct in_addr source_addr, + struct in_addr group_addr) +{ + struct listnode *up_node; + struct pim_upstream *up; + + for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, up_node, up)) { + if ( + (source_addr.s_addr == up->source_addr.s_addr) && + (group_addr.s_addr == up->group_addr.s_addr) + ) { + return up; + } + } + + return 0; +} + +struct pim_upstream *pim_upstream_add(struct in_addr source_addr, + struct in_addr group_addr) +{ + struct pim_upstream *up; + + up = pim_upstream_find(source_addr, group_addr); + if (up) { + ++up->ref_count; + } + else { + up = pim_upstream_new(source_addr, group_addr); + } + + return up; +} + +void pim_upstream_del(struct pim_upstream *up) +{ + --up->ref_count; + + if (up->ref_count < 1) { + pim_upstream_delete(up); + } +} + +/* + Evaluate JoinDesired(S,G): + + JoinDesired(S,G) is true if there is a downstream (S,G) interface I + in the set: + + inherited_olist(S,G) = + joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G) + + JoinDesired(S,G) may be affected by changes in the following: + + pim_ifp->primary_address + pim_ifp->pim_dr_addr + ch->ifassert_winner_metric + ch->ifassert_winner + ch->local_ifmembership + ch->ifjoin_state + ch->upstream->rpf.source_nexthop.mrib_metric_preference + ch->upstream->rpf.source_nexthop.mrib_route_metric + ch->upstream->rpf.source_nexthop.interface + + See also pim_upstream_update_join_desired() below. + */ +int pim_upstream_evaluate_join_desired(struct pim_upstream *up) +{ + struct listnode *ifnode; + struct listnode *ifnextnode; + struct listnode *chnode; + struct listnode *chnextnode; + struct interface *ifp; + struct pim_interface *pim_ifp; + struct pim_ifchannel *ch; + + /* scan all interfaces */ + for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) { + pim_ifp = ifp->info; + if (!pim_ifp) + continue; + + /* scan per-interface (S,G) state */ + for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) { + if (ch->upstream != up) + continue; + + if (pim_macro_ch_lost_assert(ch)) + continue; /* keep searching */ + + if (pim_macro_chisin_joins_or_include(ch)) + return 1; /* true */ + } /* scan iface channel list */ + } /* scan iflist */ + + return 0; /* false */ +} + +/* + See also pim_upstream_evaluate_join_desired() above. +*/ +void pim_upstream_update_join_desired(struct pim_upstream *up) +{ + int was_join_desired; /* boolean */ + int is_join_desired; /* boolean */ + + was_join_desired = PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(up->flags); + + is_join_desired = pim_upstream_evaluate_join_desired(up); + if (is_join_desired) + PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(up->flags); + else + PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(up->flags); + + /* switched from false to true */ + if (is_join_desired && !was_join_desired) { + zassert(up->join_state == PIM_UPSTREAM_NOTJOINED); + pim_upstream_switch(up, PIM_UPSTREAM_JOINED); + return; + } + + /* switched from true to false */ + if (!is_join_desired && was_join_desired) { + zassert(up->join_state == PIM_UPSTREAM_JOINED); + pim_upstream_switch(up, PIM_UPSTREAM_NOTJOINED); + return; + } +} + +/* + RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages + Transitions from Joined State + RPF'(S,G) GenID changes + + The upstream (S,G) state machine remains in Joined state. If the + Join Timer is set to expire in more than t_override seconds, reset + it so that it expires after t_override seconds. +*/ +void pim_upstream_rpf_genid_changed(struct in_addr neigh_addr) +{ + struct listnode *up_node; + struct listnode *up_nextnode; + struct pim_upstream *up; + + /* + Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr + */ + for (ALL_LIST_ELEMENTS(qpim_upstream_list, up_node, up_nextnode, up)) { + + if (PIM_DEBUG_PIM_TRACE) { + char neigh_str[100]; + char src_str[100]; + char grp_str[100]; + char rpf_addr_str[100]; + pim_inet4_dump("", neigh_addr, neigh_str, sizeof(neigh_str)); + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", up->rpf.rpf_addr, rpf_addr_str, sizeof(rpf_addr_str)); + zlog_debug("%s: matching neigh=%s against upstream (S,G)=(%s,%s) joined=%d rpf_addr=%s", + __PRETTY_FUNCTION__, + neigh_str, src_str, grp_str, + up->join_state == PIM_UPSTREAM_JOINED, + rpf_addr_str); + } + + /* consider only (S,G) upstream in Joined state */ + if (up->join_state != PIM_UPSTREAM_JOINED) + continue; + + /* match RPF'(S,G)=neigh_addr */ + if (up->rpf.rpf_addr.s_addr != neigh_addr.s_addr) + continue; + + pim_upstream_join_timer_decrease_to_t_override("RPF'(S,G) GenID change", + up, neigh_addr); + } +} + + +void pim_upstream_rpf_interface_changed(struct pim_upstream *up, + struct interface *old_rpf_ifp) +{ + struct listnode *ifnode; + struct listnode *ifnextnode; + struct interface *ifp; + + /* scan all interfaces */ + for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) { + struct listnode *chnode; + struct listnode *chnextnode; + struct pim_ifchannel *ch; + struct pim_interface *pim_ifp; + + pim_ifp = ifp->info; + if (!pim_ifp) + continue; + + /* search all ifchannels */ + for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) { + if (ch->upstream != up) + continue; + + if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) { + if ( + /* RPF_interface(S) was NOT I */ + (old_rpf_ifp == ch->interface) + && + /* RPF_interface(S) stopped being I */ + (ch->upstream->rpf.source_nexthop.interface != ch->interface) + ) { + assert_action_a5(ch); + } + } /* PIM_IFASSERT_I_AM_LOSER */ + + pim_ifchannel_update_assert_tracking_desired(ch); + } + } +} + +void pim_upstream_update_could_assert(struct pim_upstream *up) +{ + struct listnode *ifnode; + struct listnode *ifnextnode; + struct listnode *chnode; + struct listnode *chnextnode; + struct interface *ifp; + struct pim_interface *pim_ifp; + struct pim_ifchannel *ch; + + /* scan all interfaces */ + for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) { + pim_ifp = ifp->info; + if (!pim_ifp) + continue; + + /* scan per-interface (S,G) state */ + for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) { + + if (ch->upstream != up) + continue; + + pim_ifchannel_update_could_assert(ch); + + } /* scan iface channel list */ + } /* scan iflist */ +} + +void pim_upstream_update_my_assert_metric(struct pim_upstream *up) +{ + struct listnode *ifnode; + struct listnode *ifnextnode; + struct listnode *chnode; + struct listnode *chnextnode; + struct interface *ifp; + struct pim_interface *pim_ifp; + struct pim_ifchannel *ch; + + /* scan all interfaces */ + for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) { + pim_ifp = ifp->info; + if (!pim_ifp) + continue; + + /* scan per-interface (S,G) state */ + for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) { + + if (ch->upstream != up) + continue; + + pim_ifchannel_update_my_assert_metric(ch); + + } /* scan iface channel list */ + } /* scan iflist */ +} + +static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up) +{ + struct listnode *ifnode; + struct listnode *ifnextnode; + struct listnode *chnode; + struct listnode *chnextnode; + struct interface *ifp; + struct pim_interface *pim_ifp; + struct pim_ifchannel *ch; + + /* scan all interfaces */ + for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) { + pim_ifp = ifp->info; + if (!pim_ifp) + continue; + + /* scan per-interface (S,G) state */ + for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) { + + if (ch->upstream != up) + continue; + + pim_ifchannel_update_assert_tracking_desired(ch); + + } /* scan iface channel list */ + } /* scan iflist */ +} diff --git a/pimd/pim_upstream.h b/pimd/pim_upstream.h new file mode 100644 index 00000000..5b5182dd --- /dev/null +++ b/pimd/pim_upstream.h @@ -0,0 +1,122 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_UPSTREAM_H +#define PIM_UPSTREAM_H + +#include + +#define PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED (1 << 0) +#define PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED (2 << 0) + +#define PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED) +#define PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED_UPDATED(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED) + +#define PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED) +#define PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED_UPDATED(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED) + +#define PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED) +#define PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED_UPDATED(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED) + +/* + RFC 4601: + + Metric Preference + Preference value assigned to the unicast routing protocol that + provided the route to the multicast source or Rendezvous-Point. + + Metric + The unicast routing table metric associated with the route used to + reach the multicast source or Rendezvous-Point. The metric is in + units applicable to the unicast routing protocol used. +*/ +struct pim_nexthop { + struct interface *interface; /* RPF_interface(S) */ + struct in_addr mrib_nexthop_addr; /* MRIB.next_hop(S) */ + uint32_t mrib_metric_preference; /* MRIB.pref(S) */ + uint32_t mrib_route_metric; /* MRIB.metric(S) */ +}; + +struct pim_rpf { + struct pim_nexthop source_nexthop; + struct in_addr rpf_addr; /* RPF'(S,G) */ +}; + +enum pim_rpf_result { + PIM_RPF_OK = 0, + PIM_RPF_CHANGED, + PIM_RPF_FAILURE +}; + +enum pim_upstream_state { + PIM_UPSTREAM_NOTJOINED, + PIM_UPSTREAM_JOINED +}; + +/* + Upstream (S,G) channel in Joined state + + (S,G) in the "Not Joined" state is not represented + + See RFC 4601: 4.5.7. Sending (S,G) Join/Prune Message +*/ +struct pim_upstream { + struct in_addr source_addr; /* (S,G) source key */ + struct in_addr group_addr; /* (S,G) group key */ + uint32_t flags; + struct channel_oil *channel_oil; + + enum pim_upstream_state join_state; + int ref_count; + + struct pim_rpf rpf; + + struct thread *t_join_timer; + int64_t state_transition; /* Record current state uptime */ +}; + +void pim_upstream_free(struct pim_upstream *up); +void pim_upstream_delete(struct pim_upstream *up); +struct pim_upstream *pim_upstream_find(struct in_addr source_addr, + struct in_addr group_addr); +struct pim_upstream *pim_upstream_add(struct in_addr source_addr, + struct in_addr group_addr); +void pim_upstream_del(struct pim_upstream *up); + +int pim_upstream_evaluate_join_desired(struct pim_upstream *up); +void pim_upstream_update_join_desired(struct pim_upstream *up); + +void pim_upstream_join_suppress(struct pim_upstream *up, + struct in_addr rpf_addr, + int holdtime); +void pim_upstream_join_timer_decrease_to_t_override(const char *debug_label, + struct pim_upstream *up, + struct in_addr rpf_addr); +void pim_upstream_join_timer_restart(struct pim_upstream *up); +void pim_upstream_rpf_genid_changed(struct in_addr neigh_addr); +void pim_upstream_rpf_interface_changed(struct pim_upstream *up, + struct interface *old_rpf_ifp); + +void pim_upstream_update_could_assert(struct pim_upstream *up); +void pim_upstream_update_my_assert_metric(struct pim_upstream *up); + +#endif /* PIM_UPSTREAM_H */ diff --git a/pimd/pim_util.c b/pimd/pim_util.c new file mode 100644 index 00000000..5bc8d07e --- /dev/null +++ b/pimd/pim_util.c @@ -0,0 +1,132 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include "pim_util.h" + +/* + RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code) + + If QQIC < 128, QQI = QQIC + If QQIC >= 128, QQI = (mant | 0x10) << (exp + 3) + + 0 1 2 3 4 5 6 7 + +-+-+-+-+-+-+-+-+ + |1| exp | mant | + +-+-+-+-+-+-+-+-+ + + Since exp=0..7 then (exp+3)=3..10, then QQI has + one of the following bit patterns: + + exp=0: QQI = 0000.0000.1MMM.M000 + exp=1: QQI = 0000.0001.MMMM.0000 + ... + exp=6: QQI = 001M.MMM0.0000.0000 + exp=7: QQI = 01MM.MM00.0000.0000 + --------- --------- + 0x4 0x0 0x0 0x0 +*/ +uint8_t igmp_msg_encode16to8(uint16_t value) +{ + uint8_t code; + + if (value < 128) { + code = value; + } + else { + uint16_t mask = 0x4000; + uint8_t exp; + uint16_t mant; + for (exp = 7; exp > 0; --exp) { + if (mask & value) + break; + mask >>= 1; + } + mant = 0x000F & (value >> (exp + 3)); + code = ((uint8_t) 1 << 7) | ((uint8_t) exp << 4) | (uint8_t) mant; + } + + return code; +} + +/* + RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code) + + If QQIC < 128, QQI = QQIC + If QQIC >= 128, QQI = (mant | 0x10) << (exp + 3) + + 0 1 2 3 4 5 6 7 + +-+-+-+-+-+-+-+-+ + |1| exp | mant | + +-+-+-+-+-+-+-+-+ +*/ +uint16_t igmp_msg_decode8to16(uint8_t code) +{ + uint16_t value; + + if (code < 128) { + value = code; + } + else { + uint16_t mant = (code & 0x0F); + uint8_t exp = (code & 0x70) >> 4; + value = (mant | 0x10) << (exp + 3); + } + + return value; +} + +#ifndef PIM_USE_QUAGGA_INET_CHECKSUM +/* + RFC 3376: 4.1.2. Checksum + + The Checksum is the 16-bit one's complement of the one's complement + sum of the whole IGMP message (the entire IP payload). For + computing the checksum, the Checksum field is set to zero. When + receiving packets, the checksum MUST be verified before processing a + packet. [RFC-1071] +*/ +uint16_t pim_inet_checksum(const char *buf, int size) +{ + const uint16_t *ptr; + uint32_t sum; + uint16_t checksum; + + ptr = (const uint16_t *) buf; + sum = 0; + while (size > 1) { + sum += *ptr; + ++ptr; + size -= 2; + } + + /* Add left-over byte, if any */ + if (size > 0) + sum += (uint16_t) *(const uint8_t *) ptr; + + /* Fold 32-bit sum to 16 bits */ + sum = (sum & 0xffff) + (sum >> 16); + + checksum = ~sum; + + return checksum; +} +#endif /* PIM_USE_QUAGGA_INET_CHECKSUM */ diff --git a/pimd/pim_util.h b/pimd/pim_util.h new file mode 100644 index 00000000..0fcdce0c --- /dev/null +++ b/pimd/pim_util.h @@ -0,0 +1,41 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_UTIL_H +#define PIM_UTIL_H + +#include + +#include + +#include "checksum.h" + +uint8_t igmp_msg_encode16to8(uint16_t value); +uint16_t igmp_msg_decode8to16(uint8_t code); + +#ifdef PIM_USE_QUAGGA_INET_CHECKSUM +#define pim_inet_checksum(buf,size) in_cksum(buf,size) +#else +uint16_t pim_inet_checksum(const char *buf, int size); +#endif /* PIM_USE_QUAGGA_INET_CHECKSUM */ + +#endif /* PIM_UTIL_H */ diff --git a/pimd/pim_version.c b/pimd/pim_version.c new file mode 100644 index 00000000..fe7e5634 --- /dev/null +++ b/pimd/pim_version.c @@ -0,0 +1,25 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include "pim_version.h" + +const char * const PIMD_VERSION = PIMD_VERSION_STR; diff --git a/pimd/pim_version.h b/pimd/pim_version.h new file mode 100644 index 00000000..796db663 --- /dev/null +++ b/pimd/pim_version.h @@ -0,0 +1,30 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_VERSION_H +#define PIM_VERSION_H + +#define PIMD_VERSION_STR "0.155" + +const char * const PIMD_VERSION; + +#endif /* PIM_VERSION_H */ diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c new file mode 100644 index 00000000..7d69b5b1 --- /dev/null +++ b/pimd/pim_vty.c @@ -0,0 +1,145 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "if.h" +#include "linklist.h" + +#include "pimd.h" +#include "pim_vty.h" +#include "pim_iface.h" +#include "pim_cmd.h" +#include "pim_str.h" + +int pim_debug_config_write(struct vty *vty) +{ + int writes = 0; + + if (PIM_DEBUG_IGMP_EVENTS) { + vty_out(vty, "debug igmp events%s", VTY_NEWLINE); + ++writes; + } + if (PIM_DEBUG_IGMP_PACKETS) { + vty_out(vty, "debug igmp packets%s", VTY_NEWLINE); + ++writes; + } + if (PIM_DEBUG_IGMP_TRACE) { + vty_out(vty, "debug igmp trace%s", VTY_NEWLINE); + ++writes; + } + + if (PIM_DEBUG_PIM_EVENTS) { + vty_out(vty, "debug pim events%s", VTY_NEWLINE); + ++writes; + } + if (PIM_DEBUG_PIM_PACKETS) { + vty_out(vty, "debug pim packets%s", VTY_NEWLINE); + ++writes; + } + if (PIM_DEBUG_PIM_TRACE) { + vty_out(vty, "debug pim trace%s", VTY_NEWLINE); + ++writes; + } + + if (PIM_DEBUG_ZEBRA) { + vty_out(vty, "debug pim zebra%s", VTY_NEWLINE); + ++writes; + } + + return writes; +} + +int pim_global_config_write(struct vty *vty) +{ + int writes = 0; + + if (PIM_MROUTE_IS_ENABLED) { + vty_out(vty, "%s%s", PIM_CMD_IP_MULTICAST_ROUTING, VTY_NEWLINE); + ++writes; + } + + return writes; +} + +int pim_interface_config_write(struct vty *vty) +{ + int writes = 0; + struct listnode *node; + struct interface *ifp; + + for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) { + + /* IF name */ + vty_out(vty, "interface %s%s", ifp->name, VTY_NEWLINE); + writes++; + + if (ifp->info) { + struct pim_interface *pim_ifp = ifp->info; + + /* IF ip pim ssm */ + if (PIM_IF_TEST_PIM(pim_ifp->options)) { + vty_out(vty, " ip pim ssm%s", VTY_NEWLINE); + writes++; + } + + /* IF ip igmp */ + if (PIM_IF_TEST_IGMP(pim_ifp->options)) { + vty_out(vty, " ip igmp%s", VTY_NEWLINE); + writes++; + } + + /* IF ip igmp query-interval */ + vty_out(vty, " %s %d%s", + PIM_CMD_IP_IGMP_QUERY_INTERVAL, + pim_ifp->igmp_default_query_interval, + VTY_NEWLINE); + writes++; + + /* IF ip igmp query-max-response-time */ + vty_out(vty, " %s %d%s", + PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC, + pim_ifp->igmp_query_max_response_time_dsec, + VTY_NEWLINE); + writes++; + + /* IF ip igmp join */ + if (pim_ifp->igmp_join_list) { + struct listnode *node; + struct igmp_join *ij; + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_join_list, node, ij)) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", ij->group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", ij->source_addr, source_str, sizeof(source_str)); + vty_out(vty, " ip igmp join %s %s%s", + group_str, source_str, + VTY_NEWLINE); + writes++; + } + } + } + vty_out(vty, "!%s", VTY_NEWLINE); + } + + return writes; +} diff --git a/pimd/pim_vty.h b/pimd/pim_vty.h new file mode 100644 index 00000000..904ee553 --- /dev/null +++ b/pimd/pim_vty.h @@ -0,0 +1,32 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_VTY_H +#define PIM_VTY_H + +#include "vty.h" + +int pim_debug_config_write(struct vty *vty); +int pim_global_config_write(struct vty *vty); +int pim_interface_config_write(struct vty *vty); + +#endif /* PIM_VTY_H */ diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c new file mode 100644 index 00000000..d03cc540 --- /dev/null +++ b/pimd/pim_zebra.c @@ -0,0 +1,1172 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "zebra/rib.h" + +#include "log.h" +#include "prefix.h" +#include "zclient.h" +#include "stream.h" +#include "network.h" + +#include "pimd.h" +#include "pim_pim.h" +#include "pim_zebra.h" +#include "pim_iface.h" +#include "pim_str.h" +#include "pim_oil.h" +#include "pim_rpf.h" +#include "pim_time.h" +#include "pim_join.h" +#include "pim_zlookup.h" +#include "pim_ifchannel.h" + +#undef PIM_DEBUG_IFADDR_DUMP +#define PIM_DEBUG_IFADDR_DUMP + +static int fib_lookup_if_vif_index(struct in_addr addr); +static int del_oif(struct channel_oil *channel_oil, + struct interface *oif, + uint32_t proto_mask); + +/* Router-id update message from zebra. */ +static int pim_router_id_update_zebra(int command, struct zclient *zclient, + zebra_size_t length) +{ + struct prefix router_id; + + /* FIXME: actually use router_id for anything ? */ + zebra_router_id_update_read(zclient->ibuf, &router_id); + + return 0; +} + +static int pim_zebra_if_add(int command, struct zclient *zclient, + zebra_size_t length) +{ + struct interface *ifp; + + /* + zebra api adds/dels interfaces using the same call + interface_add_read below, see comments in lib/zclient.c + */ + ifp = zebra_interface_add_read(zclient->ibuf); + if (!ifp) + return 0; + + if (PIM_DEBUG_ZEBRA) { + zlog_debug("%s: %s index %d flags %ld metric %d mtu %d operative %d", + __PRETTY_FUNCTION__, + ifp->name, ifp->ifindex, (long)ifp->flags, ifp->metric, + ifp->mtu, if_is_operative(ifp)); + } + + if (if_is_operative(ifp)) + pim_if_addr_add_all(ifp); + + return 0; +} + +static int pim_zebra_if_del(int command, struct zclient *zclient, + zebra_size_t length) +{ + struct interface *ifp; + + /* + zebra api adds/dels interfaces using the same call + interface_add_read below, see comments in lib/zclient.c + */ + ifp = zebra_interface_add_read(zclient->ibuf); + if (!ifp) + return 0; + + if (PIM_DEBUG_ZEBRA) { + zlog_debug("%s: %s index %d flags %ld metric %d mtu %d operative %d", + __PRETTY_FUNCTION__, + ifp->name, ifp->ifindex, (long)ifp->flags, ifp->metric, + ifp->mtu, if_is_operative(ifp)); + } + + if (!if_is_operative(ifp)) + pim_if_addr_del_all(ifp); + + return 0; +} + +static int pim_zebra_if_state_up(int command, struct zclient *zclient, + zebra_size_t length) +{ + struct interface *ifp; + + /* + zebra api notifies interface up/down events by using the same call + interface_add_read below, see comments in lib/zclient.c + */ + ifp = zebra_interface_state_read(zclient->ibuf); + if (!ifp) + return 0; + + if (PIM_DEBUG_ZEBRA) { + zlog_debug("%s: %s index %d flags %ld metric %d mtu %d operative %d", + __PRETTY_FUNCTION__, + ifp->name, ifp->ifindex, (long)ifp->flags, ifp->metric, + ifp->mtu, if_is_operative(ifp)); + } + + if (if_is_operative(ifp)) { + /* + pim_if_addr_add_all() suffices for bringing up both IGMP and PIM + */ + pim_if_addr_add_all(ifp); + } + + return 0; +} + +static int pim_zebra_if_state_down(int command, struct zclient *zclient, + zebra_size_t length) +{ + struct interface *ifp; + + /* + zebra api notifies interface up/down events by using the same call + interface_add_read below, see comments in lib/zclient.c + */ + ifp = zebra_interface_state_read(zclient->ibuf); + if (!ifp) + return 0; + + if (PIM_DEBUG_ZEBRA) { + zlog_debug("%s: %s index %d flags %ld metric %d mtu %d operative %d", + __PRETTY_FUNCTION__, + ifp->name, ifp->ifindex, (long)ifp->flags, ifp->metric, + ifp->mtu, if_is_operative(ifp)); + } + + if (!if_is_operative(ifp)) { + /* + pim_if_addr_del_all() suffices for shutting down IGMP, + but not for shutting down PIM + */ + pim_if_addr_del_all(ifp); + + /* + pim_sock_delete() closes the socket, stops read and timer threads, + and kills all neighbors. + */ + pim_sock_delete(ifp, "link down"); + } + + return 0; +} + +#ifdef PIM_DEBUG_IFADDR_DUMP +static void dump_if_address(struct interface *ifp) +{ + struct connected *ifc; + struct listnode *node; + + zlog_debug("%s %s: interface %s addresses:", + __FILE__, __PRETTY_FUNCTION__, + ifp->name); + + for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { + struct prefix *p = ifc->address; + + if (p->family != AF_INET) + continue; + + zlog_debug("%s %s: interface %s address %s", + __FILE__, __PRETTY_FUNCTION__, + ifp->name, + inet_ntoa(p->u.prefix4)); + } +} +#endif + +static int pim_zebra_if_address_add(int command, struct zclient *zclient, + zebra_size_t length) +{ + struct connected *c; + struct prefix *p; + + zassert(command == ZEBRA_INTERFACE_ADDRESS_ADD); + + /* + zebra api notifies address adds/dels events by using the same call + interface_add_read below, see comments in lib/zclient.c + + zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_ADD, ...) + will add address to interface list by calling + connected_add_by_prefix() + */ + c = zebra_interface_address_read(command, zclient->ibuf); + if (!c) + return 0; + + p = c->address; + if (p->family != AF_INET) + return 0; + + if (PIM_DEBUG_ZEBRA) { + char buf[BUFSIZ]; + prefix2str(p, buf, BUFSIZ); + zlog_debug("%s: %s connected IP address %s flags %u", + __PRETTY_FUNCTION__, + c->ifp->name, buf, c->flags); + +#ifdef PIM_DEBUG_IFADDR_DUMP + dump_if_address(c->ifp); +#endif + } + + pim_if_addr_add(c); + + return 0; +} + +static int pim_zebra_if_address_del(int command, struct zclient *client, + zebra_size_t length) +{ + struct connected *c; + struct prefix *p; + + zassert(command == ZEBRA_INTERFACE_ADDRESS_DELETE); + + /* + zebra api notifies address adds/dels events by using the same call + interface_add_read below, see comments in lib/zclient.c + + zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_DELETE, ...) + will remove address from interface list by calling + connected_delete_by_prefix() + */ + c = zebra_interface_address_read(command, client->ibuf); + if (!c) + return 0; + + p = c->address; + if (p->family != AF_INET) + return 0; + + if (PIM_DEBUG_ZEBRA) { + char buf[BUFSIZ]; + prefix2str(p, buf, BUFSIZ); + zlog_debug("%s: %s disconnected IP address %s flags %u", + __PRETTY_FUNCTION__, + c->ifp->name, buf, c->flags); + +#ifdef PIM_DEBUG_IFADDR_DUMP + dump_if_address(c->ifp); +#endif + } + + pim_if_addr_del(c); + + return 0; +} + +static void scan_upstream_rpf_cache() +{ + struct listnode *up_node; + struct listnode *up_nextnode; + struct pim_upstream *up; + + for (ALL_LIST_ELEMENTS(qpim_upstream_list, up_node, up_nextnode, up)) { + struct in_addr old_rpf_addr; + enum pim_rpf_result rpf_result; + + rpf_result = pim_rpf_update(up, &old_rpf_addr); + if (rpf_result == PIM_RPF_FAILURE) + continue; + + if (rpf_result == PIM_RPF_CHANGED) { + + if (up->join_state == PIM_UPSTREAM_JOINED) { + + /* + RFC 4601: 4.5.7. Sending (S,G) Join/Prune Messages + + Transitions from Joined State + + RPF'(S,G) changes not due to an Assert + + The upstream (S,G) state machine remains in Joined + state. Send Join(S,G) to the new upstream neighbor, which is + the new value of RPF'(S,G). Send Prune(S,G) to the old + upstream neighbor, which is the old value of RPF'(S,G). Set + the Join Timer (JT) to expire after t_periodic seconds. + */ + + + /* send Prune(S,G) to the old upstream neighbor */ + pim_joinprune_send(up->rpf.source_nexthop.interface, + old_rpf_addr, + up->source_addr, + up->group_addr, + 0 /* prune */); + + /* send Join(S,G) to the current upstream neighbor */ + pim_joinprune_send(up->rpf.source_nexthop.interface, + up->rpf.rpf_addr, + up->source_addr, + up->group_addr, + 1 /* join */); + + pim_upstream_join_timer_restart(up); + } /* up->join_state == PIM_UPSTREAM_JOINED */ + + /* FIXME can join_desired actually be changed by pim_rpf_update() + returning PIM_RPF_CHANGED ? */ + pim_upstream_update_join_desired(up); + + } /* PIM_RPF_CHANGED */ + + } /* for (qpim_upstream_list) */ + +} + +static void scan_oil() +{ + struct listnode *node; + struct listnode *nextnode; + struct channel_oil *c_oil; + + for (ALL_LIST_ELEMENTS(qpim_channel_oil_list, node, nextnode, c_oil)) { + int old_vif_index; + int input_iface_vif_index = fib_lookup_if_vif_index(c_oil->oil.mfcc_origin); + if (input_iface_vif_index < 1) { + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + zlog_warn("%s %s: could not find input interface for (S,G)=(%s,%s)", + __FILE__, __PRETTY_FUNCTION__, + source_str, group_str); + continue; + } + + if (input_iface_vif_index == c_oil->oil.mfcc_parent) { + /* RPF unchanged */ + continue; + } + + if (PIM_DEBUG_ZEBRA) { + struct interface *old_iif = pim_if_find_by_vif_index(c_oil->oil.mfcc_parent); + struct interface *new_iif = pim_if_find_by_vif_index(input_iface_vif_index); + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + zlog_debug("%s %s: (S,G)=(%s,%s) input interface changed from %s vif_index=%d to %s vif_index=%d", + __FILE__, __PRETTY_FUNCTION__, + source_str, group_str, + old_iif ? old_iif->name : "", c_oil->oil.mfcc_parent, + new_iif ? new_iif->name : "", input_iface_vif_index); + } + + /* new iif loops to existing oif ? */ + if (c_oil->oil.mfcc_ttls[input_iface_vif_index]) { + struct interface *new_iif = pim_if_find_by_vif_index(input_iface_vif_index); + + if (PIM_DEBUG_ZEBRA) { + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + zlog_debug("%s %s: (S,G)=(%s,%s) new iif loops to existing oif: %s vif_index=%d", + __FILE__, __PRETTY_FUNCTION__, + source_str, group_str, + new_iif ? new_iif->name : "", input_iface_vif_index); + } + + del_oif(c_oil, new_iif, PIM_OIF_FLAG_PROTO_ANY); + } + + /* update iif vif_index */ + old_vif_index = c_oil->oil.mfcc_parent; + c_oil->oil.mfcc_parent = input_iface_vif_index; + + /* update kernel multicast forwarding cache (MFC) */ + if (pim_mroute_add(&c_oil->oil)) { + /* just log warning */ + struct interface *old_iif = pim_if_find_by_vif_index(old_vif_index); + struct interface *new_iif = pim_if_find_by_vif_index(input_iface_vif_index); + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + zlog_warn("%s %s: (S,G)=(%s,%s) failure updating input interface from %s vif_index=%d to %s vif_index=%d", + __FILE__, __PRETTY_FUNCTION__, + source_str, group_str, + old_iif ? old_iif->name : "", c_oil->oil.mfcc_parent, + new_iif ? new_iif->name : "", input_iface_vif_index); + continue; + } + + } /* for (qpim_channel_oil_list) */ +} + +static int on_rpf_cache_refresh(struct thread *t) +{ + zassert(t); + zassert(qpim_rpf_cache_refresher); + + qpim_rpf_cache_refresher = 0; + + /* update PIM protocol state */ + scan_upstream_rpf_cache(); + + /* update kernel multicast forwarding cache (MFC) */ + scan_oil(); + + return 0; +} + +static void sched_rpf_cache_refresh() +{ + if (qpim_rpf_cache_refresher) + return; + + if (PIM_DEBUG_ZEBRA) { + zlog_debug("%s: triggering %ld msec timer", + __PRETTY_FUNCTION__, + qpim_rpf_cache_refresh_delay_msec); + } + + THREAD_TIMER_MSEC_ON(master, qpim_rpf_cache_refresher, + on_rpf_cache_refresh, + 0, qpim_rpf_cache_refresh_delay_msec); +} + +static int redist_read_ipv4_route(int command, struct zclient *zclient, + zebra_size_t length) +{ + struct stream *s; + struct zapi_ipv4 api; + unsigned long ifindex; + struct in_addr nexthop; + struct prefix_ipv4 p; + int min_len = 4; + + if (length < min_len) { + zlog_warn("%s %s: short buffer: length=%d min=%d", + __FILE__, __PRETTY_FUNCTION__, + length, min_len); + return -1; + } + + s = zclient->ibuf; + ifindex = 0; + nexthop.s_addr = 0; + + /* Type, flags, message. */ + api.type = stream_getc(s); + api.flags = stream_getc(s); + api.message = stream_getc(s); + + /* IPv4 prefix length. */ + memset(&p, 0, sizeof(struct prefix_ipv4)); + p.family = AF_INET; + p.prefixlen = stream_getc(s); + + min_len += + PSIZE(p.prefixlen) + + CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP) ? 5 : 0 + + CHECK_FLAG(api.message, ZAPI_MESSAGE_IFINDEX) ? 5 : 0 + + CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ? 1 : 0 + + CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? 4 : 0; + + if (PIM_DEBUG_ZEBRA) { + zlog_debug("%s %s: length=%d min_len=%d flags=%s%s%s%s", + __FILE__, __PRETTY_FUNCTION__, + length, min_len, + CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP) ? "nh" : "", + CHECK_FLAG(api.message, ZAPI_MESSAGE_IFINDEX) ? " ifi" : "", + CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ? " dist" : "", + CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? " metr" : ""); + } + + if (length < min_len) { + zlog_warn("%s %s: short buffer: length=%d min_len=%d flags=%s%s%s%s", + __FILE__, __PRETTY_FUNCTION__, + length, min_len, + CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP) ? "nh" : "", + CHECK_FLAG(api.message, ZAPI_MESSAGE_IFINDEX) ? " ifi" : "", + CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ? " dist" : "", + CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? " metr" : ""); + return -1; + } + + /* IPv4 prefix. */ + stream_get(&p.prefix, s, PSIZE(p.prefixlen)); + + /* Nexthop, ifindex, distance, metric. */ + if (CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP)) { + api.nexthop_num = stream_getc(s); + nexthop.s_addr = stream_get_ipv4(s); + } + if (CHECK_FLAG(api.message, ZAPI_MESSAGE_IFINDEX)) { + api.ifindex_num = stream_getc(s); + ifindex = stream_getl(s); + } + + api.distance = CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ? + api.distance = stream_getc(s) : + 0; + + api.metric = CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? + stream_getl(s) : + 0; + + switch (command) { + case ZEBRA_IPV4_ROUTE_ADD: + if (PIM_DEBUG_ZEBRA) { + char buf[2][INET_ADDRSTRLEN]; + zlog_debug("%s: add %s %s/%d " + "nexthop %s ifindex %ld metric%s %u distance%s %u", + __PRETTY_FUNCTION__, + zebra_route_string(api.type), + inet_ntop(AF_INET, &p.prefix, buf[0], sizeof(buf[0])), + p.prefixlen, + inet_ntop(AF_INET, &nexthop, buf[1], sizeof(buf[1])), + ifindex, + CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? "-recv" : "-miss", + api.metric, + CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ? "-recv" : "-miss", + api.distance); + } + break; + case ZEBRA_IPV4_ROUTE_DELETE: + if (PIM_DEBUG_ZEBRA) { + char buf[2][INET_ADDRSTRLEN]; + zlog_debug("%s: delete %s %s/%d " + "nexthop %s ifindex %ld metric%s %u distance%s %u", + __PRETTY_FUNCTION__, + zebra_route_string(api.type), + inet_ntop(AF_INET, &p.prefix, buf[0], sizeof(buf[0])), + p.prefixlen, + inet_ntop(AF_INET, &nexthop, buf[1], sizeof(buf[1])), + ifindex, + CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? "-recv" : "-miss", + api.metric, + CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ? "-recv" : "-miss", + api.distance); + } + break; + default: + zlog_warn("%s: unknown command=%d", __PRETTY_FUNCTION__, command); + return -1; + } + + sched_rpf_cache_refresh(); + + return 0; +} + +void pim_zebra_init() +{ + struct zclient *zclient; + int i; + +#ifdef HAVE_TCP_ZEBRA + zlog_notice("zclient update contacting ZEBRA daemon at socket TCP %s,%d", "127.0.0.1", ZEBRA_PORT); +#else + zlog_notice("zclient update contacting ZEBRA daemon at socket UNIX %s", ZEBRA_SERV_PATH); +#endif + + /* Socket for receiving updates from Zebra daemon */ + zclient = zclient_new(); + + zclient->router_id_update = pim_router_id_update_zebra; + zclient->interface_add = pim_zebra_if_add; + zclient->interface_delete = pim_zebra_if_del; + zclient->interface_up = pim_zebra_if_state_up; + zclient->interface_down = pim_zebra_if_state_down; + zclient->interface_address_add = pim_zebra_if_address_add; + zclient->interface_address_delete = pim_zebra_if_address_del; + zclient->ipv4_route_add = redist_read_ipv4_route; + zclient->ipv4_route_delete = redist_read_ipv4_route; + + zclient_init(zclient, ZEBRA_ROUTE_PIM); + zlog_info("zclient_init cleared redistribution request"); + + zassert(zclient->redist_default == ZEBRA_ROUTE_PIM); + + /* Request all redistribution */ + for (i = 0; i < ZEBRA_ROUTE_MAX; i++) { + if (i == zclient->redist_default) + continue; + zclient->redist[i] = 1; + zlog_info("%s: requesting redistribution for %s (%i)", + __PRETTY_FUNCTION__, zebra_route_string(i), i); + } + + /* Request default information */ + zclient->default_information = 1; + zlog_info("%s: requesting default information redistribution", + __PRETTY_FUNCTION__); + + zlog_notice("%s: zclient update socket initialized", + __PRETTY_FUNCTION__); + + zassert(!qpim_zclient_lookup); + qpim_zclient_lookup = zclient_lookup_new(); + zassert(qpim_zclient_lookup); +} + +void igmp_anysource_forward_start(struct igmp_group *group) +{ + /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */ + zassert(group->group_filtermode_isexcl); + zassert(listcount(group->group_source_list) < 1); + + if (PIM_DEBUG_IGMP_TRACE) { + zlog_debug("%s %s: UNIMPLEMENTED", + __FILE__, __PRETTY_FUNCTION__); + } +} + +void igmp_anysource_forward_stop(struct igmp_group *group) +{ + /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */ + zassert((!group->group_filtermode_isexcl) || (listcount(group->group_source_list) > 0)); + + if (PIM_DEBUG_IGMP_TRACE) { + zlog_debug("%s %s: UNIMPLEMENTED", + __FILE__, __PRETTY_FUNCTION__); + } +} + +static int fib_lookup_if_vif_index(struct in_addr addr) +{ + struct pim_zlookup_nexthop nexthop_tab[PIM_NEXTHOP_IFINDEX_TAB_SIZE]; + int num_ifindex; + int vif_index; + int first_ifindex; + + num_ifindex = zclient_lookup_nexthop(qpim_zclient_lookup, nexthop_tab, + PIM_NEXTHOP_IFINDEX_TAB_SIZE, addr, + PIM_NEXTHOP_LOOKUP_MAX); + if (num_ifindex < 1) { + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_warn("%s %s: could not find nexthop ifindex for address %s", + __FILE__, __PRETTY_FUNCTION__, + addr_str); + return -1; + } + + first_ifindex = nexthop_tab[0].ifindex; + + if (num_ifindex > 1) { + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_debug("%s %s: FIXME ignoring multiple nexthop ifindex'es num_ifindex=%d for address %s (using only ifindex=%d)", + __FILE__, __PRETTY_FUNCTION__, + num_ifindex, addr_str, first_ifindex); + /* debug warning only, do not return */ + } + + if (PIM_DEBUG_ZEBRA) { + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_debug("%s %s: found nexthop ifindex=%d (interface %s) for address %s", + __FILE__, __PRETTY_FUNCTION__, + first_ifindex, ifindex2ifname(first_ifindex), addr_str); + } + + vif_index = pim_if_find_vifindex_by_ifindex(first_ifindex); + + if (vif_index < 1) { + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_warn("%s %s: low vif_index=%d < 1 nexthop for address %s", + __FILE__, __PRETTY_FUNCTION__, + vif_index, addr_str); + return -2; + } + + zassert(qpim_mroute_oif_highest_vif_index < MAXVIFS); + + if (vif_index > qpim_mroute_oif_highest_vif_index) { + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_warn("%s %s: high vif_index=%d > highest_vif_index=%d nexthop for address %s", + __FILE__, __PRETTY_FUNCTION__, + vif_index, qpim_mroute_oif_highest_vif_index, addr_str); + + zlog_warn("%s %s: pim disabled on interface %s vif_index=%d ?", + __FILE__, __PRETTY_FUNCTION__, + ifindex2ifname(vif_index), + vif_index); + + return -3; + } + + return vif_index; +} + +static int add_oif(struct channel_oil *channel_oil, + struct interface *oif, + uint32_t proto_mask) +{ + struct pim_interface *pim_ifp; + int old_ttl; + + zassert(channel_oil); + + pim_ifp = oif->info; + + if (pim_ifp->mroute_vif_index < 1) { + zlog_warn("%s %s: interface %s vif_index=%d < 1", + __FILE__, __PRETTY_FUNCTION__, + oif->name, pim_ifp->mroute_vif_index); + return -1; + } + +#ifdef PIM_ENFORCE_LOOPFREE_MFC + /* + Prevent creating MFC entry with OIF=IIF. + + This is a protection against implementation mistakes. + + PIM protocol implicitely ensures loopfree multicast topology. + + IGMP must be protected against adding looped MFC entries created + by both source and receiver attached to the same interface. See + TODO T22. + */ + if (pim_ifp->mroute_vif_index == channel_oil->oil.mfcc_parent) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + zlog_warn("%s %s: refusing protocol mask %u request for IIF=OIF=%s (vif_index=%d) for channel (S,G)=(%s,%s)", + __FILE__, __PRETTY_FUNCTION__, + proto_mask, oif->name, pim_ifp->mroute_vif_index, + source_str, group_str); + return -2; + } +#endif + + zassert(qpim_mroute_oif_highest_vif_index < MAXVIFS); + zassert(pim_ifp->mroute_vif_index <= qpim_mroute_oif_highest_vif_index); + + /* Prevent single protocol from subscribing same interface to + channel (S,G) multiple times */ + if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + zlog_warn("%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)", + __FILE__, __PRETTY_FUNCTION__, + proto_mask, oif->name, pim_ifp->mroute_vif_index, + channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index], + source_str, group_str); + return -3; + } + + /* Allow other protocol to request subscription of same interface to + channel (S,G) multiple times, by silently ignoring further + requests */ + if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & PIM_OIF_FLAG_PROTO_ANY) { + + /* Check the OIF really exists before returning, and only log + warning otherwise */ + if (channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] < 1) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + zlog_warn("%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)", + __FILE__, __PRETTY_FUNCTION__, + proto_mask, oif->name, pim_ifp->mroute_vif_index, + channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index], + source_str, group_str); + } + + return 0; + } + + old_ttl = channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index]; + + if (old_ttl > 0) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + zlog_warn("%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%s,%s)", + __FILE__, __PRETTY_FUNCTION__, + oif->name, pim_ifp->mroute_vif_index, + source_str, group_str); + return -4; + } + + channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = PIM_MROUTE_MIN_TTL; + + if (pim_mroute_add(&channel_oil->oil)) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + zlog_warn("%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)", + __FILE__, __PRETTY_FUNCTION__, + oif->name, pim_ifp->mroute_vif_index, + source_str, group_str); + + channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = old_ttl; + return -5; + } + + channel_oil->oif_creation[pim_ifp->mroute_vif_index] = pim_time_monotonic_sec(); + ++channel_oil->oil_size; + channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= proto_mask; + + return 0; +} + +static int del_oif(struct channel_oil *channel_oil, + struct interface *oif, + uint32_t proto_mask) +{ + struct pim_interface *pim_ifp; + int old_ttl; + + zassert(channel_oil); + + pim_ifp = oif->info; + + zassert(pim_ifp->mroute_vif_index >= 1); + zassert(qpim_mroute_oif_highest_vif_index < MAXVIFS); + zassert(pim_ifp->mroute_vif_index <= qpim_mroute_oif_highest_vif_index); + + /* Prevent single protocol from unsubscribing same interface from + channel (S,G) multiple times */ + if (!(channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask)) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + zlog_warn("%s %s: nonexistent protocol mask %u removed OIF %s (vif_index=%d, min_ttl=%d) from channel (S,G)=(%s,%s)", + __FILE__, __PRETTY_FUNCTION__, + proto_mask, oif->name, pim_ifp->mroute_vif_index, + channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index], + source_str, group_str); + return -2; + } + + /* Mark that protocol is no longer interested in this OIF */ + channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~proto_mask; + + /* Allow multiple protocols to unsubscribe same interface from + channel (S,G) multiple times, by silently ignoring requests while + there is at least one protocol interested in the channel */ + if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & PIM_OIF_FLAG_PROTO_ANY) { + + /* Check the OIF keeps existing before returning, and only log + warning otherwise */ + if (channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] < 1) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + zlog_warn("%s %s: protocol mask %u removing nonexistent OIF %s (vif_index=%d, min_ttl=%d) from channel (S,G)=(%s,%s)", + __FILE__, __PRETTY_FUNCTION__, + proto_mask, oif->name, pim_ifp->mroute_vif_index, + channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index], + source_str, group_str); + } + + return 0; + } + + old_ttl = channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index]; + + if (old_ttl < 1) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + zlog_warn("%s %s: interface %s (vif_index=%d) is not output for channel (S,G)=(%s,%s)", + __FILE__, __PRETTY_FUNCTION__, + oif->name, pim_ifp->mroute_vif_index, + source_str, group_str); + return -3; + } + + channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = 0; + + if (pim_mroute_add(&channel_oil->oil)) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + zlog_warn("%s %s: could not remove output interface %s (vif_index=%d) from channel (S,G)=(%s,%s)", + __FILE__, __PRETTY_FUNCTION__, + oif->name, pim_ifp->mroute_vif_index, + source_str, group_str); + + channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = old_ttl; + return -4; + } + + --channel_oil->oil_size; + + if (channel_oil->oil_size < 1) { + if (pim_mroute_del(&channel_oil->oil)) { + /* just log a warning in case of failure */ + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + zlog_warn("%s %s: failure removing OIL for channel (S,G)=(%s,%s)", + __FILE__, __PRETTY_FUNCTION__, + source_str, group_str); + } + } + + return 0; +} + +void igmp_source_forward_start(struct igmp_source *source) +{ + struct igmp_group *group; + + if (PIM_DEBUG_IGMP_TRACE) { + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", source->source_group->group_addr, group_str, sizeof(group_str)); + zlog_debug("%s: (S,G)=(%s,%s) igmp_sock=%d oif=%s fwd=%d", + __PRETTY_FUNCTION__, + source_str, group_str, + source->source_group->group_igmp_sock->fd, + source->source_group->group_igmp_sock->interface->name, + IGMP_SOURCE_TEST_FORWARDING(source->source_flags)); + } + + /* Prevent IGMP interface from installing multicast route multiple + times */ + if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) { + return; + } + + group = source->source_group; + + if (!source->source_channel_oil) { + struct pim_interface *pim_oif; + int input_iface_vif_index = fib_lookup_if_vif_index(source->source_addr); + if (input_iface_vif_index < 1) { + char source_str[100]; + pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); + zlog_warn("%s %s: could not find input interface for source %s", + __FILE__, __PRETTY_FUNCTION__, + source_str); + return; + } + + /* + Protect IGMP against adding looped MFC entries created by both + source and receiver attached to the same interface. See TODO + T22. + */ + pim_oif = source->source_group->group_igmp_sock->interface->info; + if (!pim_oif) { + zlog_warn("%s: multicast not enabled on oif=%s ?", + __PRETTY_FUNCTION__, + source->source_group->group_igmp_sock->interface->name); + return; + } + if (pim_oif->mroute_vif_index < 1) { + zlog_warn("%s %s: oif=%s vif_index=%d < 1", + __FILE__, __PRETTY_FUNCTION__, + source->source_group->group_igmp_sock->interface->name, + pim_oif->mroute_vif_index); + return; + } + if (input_iface_vif_index == pim_oif->mroute_vif_index) { + /* ignore request for looped MFC entry */ + if (PIM_DEBUG_IGMP_TRACE) { + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", source->source_group->group_addr, group_str, sizeof(group_str)); + zlog_debug("%s: ignoring request for looped MFC entry (S,G)=(%s,%s): igmp_sock=%d oif=%s vif_index=%d", + __PRETTY_FUNCTION__, + source_str, group_str, + source->source_group->group_igmp_sock->fd, + source->source_group->group_igmp_sock->interface->name, + input_iface_vif_index); + } + return; + } + + source->source_channel_oil = pim_channel_oil_add(group->group_addr, + source->source_addr, + input_iface_vif_index); + if (!source->source_channel_oil) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); + zlog_warn("%s %s: could not create OIL for channel (S,G)=(%s,%s)", + __FILE__, __PRETTY_FUNCTION__, + source_str, group_str); + return; + } + } + + if (add_oif(source->source_channel_oil, + group->group_igmp_sock->interface, + PIM_OIF_FLAG_PROTO_IGMP)) { + return; + } + + /* + Feed IGMPv3-gathered local membership information into PIM + per-interface (S,G) state. + */ + pim_ifchannel_local_membership_add(group->group_igmp_sock->interface, + source->source_addr, group->group_addr); + + IGMP_SOURCE_DO_FORWARDING(source->source_flags); +} + +void igmp_source_forward_stop(struct igmp_source *source) +{ + struct igmp_group *group; + + if (PIM_DEBUG_IGMP_TRACE) { + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", source->source_group->group_addr, group_str, sizeof(group_str)); + zlog_debug("%s: (S,G)=(%s,%s) igmp_sock=%d oif=%s fwd=%d", + __PRETTY_FUNCTION__, + source_str, group_str, + source->source_group->group_igmp_sock->fd, + source->source_group->group_igmp_sock->interface->name, + IGMP_SOURCE_TEST_FORWARDING(source->source_flags)); + } + + /* Prevent IGMP interface from removing multicast route multiple + times */ + if (!IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) { + return; + } + + group = source->source_group; + + if (del_oif(source->source_channel_oil, + group->group_igmp_sock->interface, + PIM_OIF_FLAG_PROTO_IGMP)) { + return; + } + + /* + Feed IGMPv3-gathered local membership information into PIM + per-interface (S,G) state. + */ + pim_ifchannel_local_membership_del(group->group_igmp_sock->interface, + source->source_addr, group->group_addr); + + IGMP_SOURCE_DONT_FORWARDING(source->source_flags); +} + +void pim_forward_start(struct pim_ifchannel *ch) +{ + struct pim_upstream *up = ch->upstream; + + if (PIM_DEBUG_PIM_TRACE) { + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", ch->source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", ch->group_addr, group_str, sizeof(group_str)); + zlog_debug("%s: (S,G)=(%s,%s) oif=%s", + __PRETTY_FUNCTION__, + source_str, group_str, ch->interface->name); + } + + if (!up->channel_oil) { + int input_iface_vif_index = fib_lookup_if_vif_index(up->source_addr); + if (input_iface_vif_index < 1) { + char source_str[100]; + pim_inet4_dump("", up->source_addr, source_str, sizeof(source_str)); + zlog_warn("%s %s: could not find input interface for source %s", + __FILE__, __PRETTY_FUNCTION__, + source_str); + return; + } + + up->channel_oil = pim_channel_oil_add(up->group_addr, up->source_addr, + input_iface_vif_index); + if (!up->channel_oil) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", up->group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", up->source_addr, source_str, sizeof(source_str)); + zlog_warn("%s %s: could not create OIL for channel (S,G)=(%s,%s)", + __FILE__, __PRETTY_FUNCTION__, + source_str, group_str); + return; + } + } + + add_oif(up->channel_oil, + ch->interface, + PIM_OIF_FLAG_PROTO_PIM); +} + +void pim_forward_stop(struct pim_ifchannel *ch) +{ + struct pim_upstream *up = ch->upstream; + + if (PIM_DEBUG_PIM_TRACE) { + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", ch->source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", ch->group_addr, group_str, sizeof(group_str)); + zlog_debug("%s: (S,G)=(%s,%s) oif=%s", + __PRETTY_FUNCTION__, + source_str, group_str, ch->interface->name); + } + + if (!up->channel_oil) { + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", ch->source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", ch->group_addr, group_str, sizeof(group_str)); + zlog_warn("%s: (S,G)=(%s,%s) oif=%s missing channel OIL", + __PRETTY_FUNCTION__, + source_str, group_str, ch->interface->name); + + return; + } + + del_oif(up->channel_oil, + ch->interface, + PIM_OIF_FLAG_PROTO_PIM); +} diff --git a/pimd/pim_zebra.h b/pimd/pim_zebra.h new file mode 100644 index 00000000..e0c9bdcb --- /dev/null +++ b/pimd/pim_zebra.h @@ -0,0 +1,40 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_ZEBRA_H +#define PIM_ZEBRA_H + +#include "pim_igmp.h" +#include "pim_ifchannel.h" + +void pim_zebra_init(void); + +void igmp_anysource_forward_start(struct igmp_group *group); +void igmp_anysource_forward_stop(struct igmp_group *group); + +void igmp_source_forward_start(struct igmp_source *source); +void igmp_source_forward_stop(struct igmp_source *source); + +void pim_forward_start(struct pim_ifchannel *ch); +void pim_forward_stop(struct pim_ifchannel *ch); + +#endif /* PIM_ZEBRA_H */ diff --git a/pimd/pim_zlookup.c b/pimd/pim_zlookup.c new file mode 100644 index 00000000..98548e79 --- /dev/null +++ b/pimd/pim_zlookup.c @@ -0,0 +1,455 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include +#include "zebra/rib.h" + +#include "log.h" +#include "prefix.h" +#include "zclient.h" +#include "stream.h" +#include "network.h" +#include "thread.h" + +#include "pimd.h" +#include "pim_pim.h" +#include "pim_str.h" +#include "pim_zlookup.h" + +extern int zclient_debug; + +static void zclient_lookup_sched(struct zclient *zlookup, int delay); + +/* Connect to zebra for nexthop lookup. */ +static int zclient_lookup_connect(struct thread *t) +{ + struct zclient *zlookup; + + zlookup = THREAD_ARG(t); + zlookup->t_connect = NULL; + + if (zlookup->sock >= 0) { + return 0; + } + +#ifdef HAVE_TCP_ZEBRA + zlog_debug("%s: FIXME blocking connect: zclient_socket()", + __PRETTY_FUNCTION__); + zlookup->sock = zclient_socket(); + if (zlookup->sock < 0) { + zlog_warn("%s: failure connecting TCP socket %s,%d", + __PRETTY_FUNCTION__, "127.0.0.1", ZEBRA_PORT); + } + else if (zclient_debug) { + zlog_notice("%s: connected TCP socket %s,%d", + __PRETTY_FUNCTION__, "127.0.0.1", ZEBRA_PORT); + } +#else + zlog_debug("%s: FIXME blocking connect: zclient_socket_un()", + __PRETTY_FUNCTION__); + zlookup->sock = zclient_socket_un(ZEBRA_SERV_PATH); + if (zlookup->sock < 0) { + zlog_warn("%s: failure connecting UNIX socket %s", + __PRETTY_FUNCTION__, ZEBRA_SERV_PATH); + } + else if (zclient_debug) { + zlog_notice("%s: connected UNIX socket %s", + __PRETTY_FUNCTION__, ZEBRA_SERV_PATH); + } +#endif /* HAVE_TCP_ZEBRA */ + + zassert(!zlookup->t_connect); + if (zlookup->sock < 0) { + /* Since last connect failed, retry within 10 secs */ + zclient_lookup_sched(zlookup, 10); + return -1; + } + + return 0; +} + +/* Schedule connection with delay. */ +static void zclient_lookup_sched(struct zclient *zlookup, int delay) +{ + zassert(!zlookup->t_connect); + + THREAD_TIMER_ON(master, zlookup->t_connect, + zclient_lookup_connect, + zlookup, delay); + + zlog_notice("%s: zclient lookup connection scheduled for %d seconds", + __PRETTY_FUNCTION__, delay); +} + +/* Schedule connection for now. */ +static void zclient_lookup_sched_now(struct zclient *zlookup) +{ + zassert(!zlookup->t_connect); + + zlookup->t_connect = thread_add_event(master, zclient_lookup_connect, + zlookup, 0); + + zlog_notice("%s: zclient lookup immediate connection scheduled", + __PRETTY_FUNCTION__); +} + +/* Schedule reconnection, if needed. */ +static void zclient_lookup_reconnect(struct zclient *zlookup) +{ + if (zlookup->t_connect) { + return; + } + + zclient_lookup_sched_now(zlookup); +} + +struct zclient *zclient_lookup_new() +{ + struct zclient *zlookup; + + zlookup = zclient_new(); + if (!zlookup) { + zlog_err("%s: zclient_new() failure", + __PRETTY_FUNCTION__); + return 0; + } + + zlookup->sock = -1; + zlookup->ibuf = stream_new(ZEBRA_MAX_PACKET_SIZ); + zlookup->obuf = stream_new(ZEBRA_MAX_PACKET_SIZ); + zlookup->t_connect = 0; + + zclient_lookup_sched_now(zlookup); + + zlog_notice("%s: zclient lookup socket initialized", + __PRETTY_FUNCTION__); + + return zlookup; +} + +static int zclient_read_nexthop(struct zclient *zlookup, + struct pim_zlookup_nexthop nexthop_tab[], + const int tab_size, + struct in_addr addr) +{ + int num_ifindex = 0; + struct stream *s; + const uint16_t MIN_LEN = 14; /* getc=1 getc=1 getw=2 getipv4=4 getc=1 getl=4 getc=1 */ + uint16_t length, len; + u_char marker; + u_char version; + uint16_t command; + int nbytes; + struct in_addr raddr; + uint8_t distance; + uint32_t metric; + int nexthop_num; + int i; + + if (PIM_DEBUG_ZEBRA) { + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_debug("%s: addr=%s", + __PRETTY_FUNCTION__, + addr_str); + } + + s = zlookup->ibuf; + stream_reset(s); + + nbytes = stream_read(s, zlookup->sock, 2); + if (nbytes < 2) { + zlog_err("%s %s: failure reading zclient lookup socket: nbytes=%d", + __FILE__, __PRETTY_FUNCTION__, nbytes); + close(zlookup->sock); + zlookup->sock = -1; + zclient_lookup_reconnect(zlookup); + return -1; + } + length = stream_getw(s); + + len = length - 2; + + if (len < MIN_LEN) { + zlog_err("%s %s: failure reading zclient lookup socket: len=%d < MIN_LEN=%d", + __FILE__, __PRETTY_FUNCTION__, len, MIN_LEN); + close(zlookup->sock); + zlookup->sock = -1; + zclient_lookup_reconnect(zlookup); + return -2; + } + + nbytes = stream_read(s, zlookup->sock, len); + if (nbytes < (length - 2)) { + zlog_err("%s %s: failure reading zclient lookup socket: nbytes=%d < len=%d", + __FILE__, __PRETTY_FUNCTION__, nbytes, len); + close(zlookup->sock); + zlookup->sock = -1; + zclient_lookup_reconnect(zlookup); + return -3; + } + marker = stream_getc(s); + version = stream_getc(s); + + if (version != ZSERV_VERSION || marker != ZEBRA_HEADER_MARKER) { + zlog_err("%s: socket %d version mismatch, marker %d, version %d", + __func__, zlookup->sock, marker, version); + return -4; + } + + command = stream_getw(s); + if (command != ZEBRA_IPV4_NEXTHOP_LOOKUP_V2) { + zlog_err("%s: socket %d command mismatch: %d", + __func__, zlookup->sock, command); + return -5; + } + + raddr.s_addr = stream_get_ipv4(s); + + if (raddr.s_addr != addr.s_addr) { + char addr_str[100]; + char raddr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + pim_inet4_dump("", raddr, raddr_str, sizeof(raddr_str)); + zlog_warn("%s: address mismatch: addr=%s raddr=%s", + __PRETTY_FUNCTION__, + addr_str, raddr_str); + /* warning only */ + } + + distance = stream_getc(s); + metric = stream_getl(s); + nexthop_num = stream_getc(s); + + if (nexthop_num < 1) { + zlog_err("%s: socket %d bad nexthop_num=%d", + __func__, zlookup->sock, nexthop_num); + return -6; + } + + len -= MIN_LEN; + + for (i = 0; i < nexthop_num; ++i) { + enum nexthop_types_t nexthop_type; + + if (len < 1) { + zlog_err("%s: socket %d empty input expecting nexthop_type: len=%d", + __func__, zlookup->sock, len); + return -7; + } + + nexthop_type = stream_getc(s); + --len; + + switch (nexthop_type) { + case ZEBRA_NEXTHOP_IFINDEX: + case ZEBRA_NEXTHOP_IFNAME: + case ZEBRA_NEXTHOP_IPV4_IFINDEX: + if (num_ifindex >= tab_size) { + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_warn("%s %s: found too many nexthop ifindexes (%d > %d) for address %s", + __FILE__, __PRETTY_FUNCTION__, + (num_ifindex + 1), tab_size, addr_str); + return num_ifindex; + } + if (nexthop_type == ZEBRA_NEXTHOP_IPV4_IFINDEX) { + if (len < 4) { + zlog_err("%s: socket %d short input expecting nexthop IPv4-addr: len=%d", + __func__, zlookup->sock, len); + return -8; + } + nexthop_tab[num_ifindex].nexthop_addr.s_addr = stream_get_ipv4(s); + len -= 4; + } + else { + nexthop_tab[num_ifindex].nexthop_addr.s_addr = PIM_NET_INADDR_ANY; + } + nexthop_tab[num_ifindex].ifindex = stream_getl(s); + nexthop_tab[num_ifindex].protocol_distance = distance; + nexthop_tab[num_ifindex].route_metric = metric; + ++num_ifindex; + break; + case ZEBRA_NEXTHOP_IPV4: + if (num_ifindex >= tab_size) { + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_warn("%s %s: found too many nexthop ifindexes (%d > %d) for address %s", + __FILE__, __PRETTY_FUNCTION__, + (num_ifindex + 1), tab_size, addr_str); + return num_ifindex; + } + nexthop_tab[num_ifindex].nexthop_addr.s_addr = stream_get_ipv4(s); + len -= 4; + nexthop_tab[num_ifindex].ifindex = 0; + nexthop_tab[num_ifindex].protocol_distance = distance; + nexthop_tab[num_ifindex].route_metric = metric; + { + char addr_str[100]; + char nexthop_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + pim_inet4_dump("", nexthop_tab[num_ifindex].nexthop_addr, nexthop_str, sizeof(nexthop_str)); + zlog_warn("%s %s: zebra returned recursive nexthop %s for address %s", + __FILE__, __PRETTY_FUNCTION__, + nexthop_str, addr_str); + } + ++num_ifindex; + break; + default: + /* do nothing */ + { + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_warn("%s %s: found non-ifindex nexthop type=%d for address %s", + __FILE__, __PRETTY_FUNCTION__, + nexthop_type, addr_str); + } + break; + } + } + + return num_ifindex; +} + +static int zclient_lookup_nexthop_once(struct zclient *zlookup, + struct pim_zlookup_nexthop nexthop_tab[], + const int tab_size, + struct in_addr addr) +{ + struct stream *s; + int ret; + + if (PIM_DEBUG_ZEBRA) { + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_debug("%s: addr=%s", + __PRETTY_FUNCTION__, + addr_str); + } + + /* Check socket. */ + if (zlookup->sock < 0) { + zlog_err("%s %s: zclient lookup socket is not connected", + __FILE__, __PRETTY_FUNCTION__); + zclient_lookup_reconnect(zlookup); + return -1; + } + + s = zlookup->obuf; + stream_reset(s); + zclient_create_header(s, ZEBRA_IPV4_NEXTHOP_LOOKUP_V2); + stream_put_in_addr(s, &addr); + stream_putw_at(s, 0, stream_get_endp(s)); + + ret = writen(zlookup->sock, s->data, stream_get_endp(s)); + if (ret < 0) { + zlog_err("%s %s: writen() failure writing to zclient lookup socket", + __FILE__, __PRETTY_FUNCTION__); + close(zlookup->sock); + zlookup->sock = -1; + zclient_lookup_reconnect(zlookup); + return -2; + } + if (ret == 0) { + zlog_err("%s %s: connection closed on zclient lookup socket", + __FILE__, __PRETTY_FUNCTION__); + close(zlookup->sock); + zlookup->sock = -1; + zclient_lookup_reconnect(zlookup); + return -3; + } + + return zclient_read_nexthop(zlookup, nexthop_tab, + tab_size, addr); +} + +int zclient_lookup_nexthop(struct zclient *zlookup, + struct pim_zlookup_nexthop nexthop_tab[], + const int tab_size, + struct in_addr addr, + int max_lookup) +{ + int lookup; + + for (lookup = 0; lookup < max_lookup; ++lookup) { + int num_ifindex; + int first_ifindex; + struct in_addr nexthop_addr; + + num_ifindex = zclient_lookup_nexthop_once(qpim_zclient_lookup, nexthop_tab, + PIM_NEXTHOP_IFINDEX_TAB_SIZE, addr); + if (num_ifindex < 1) { + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_warn("%s %s: lookup=%d/%d: could not find nexthop ifindex for address %s", + __FILE__, __PRETTY_FUNCTION__, + lookup, max_lookup, addr_str); + return -1; + } + + /* + FIXME: Non-recursive nexthop ensured only for first ifindex. + However, recursive route lookup should really be fixed in zebra daemon. + See also TODO T24. + */ + first_ifindex = nexthop_tab[0].ifindex; + nexthop_addr = nexthop_tab[0].nexthop_addr; + if (first_ifindex > 0) { + /* found: first ifindex is non-recursive nexthop */ + + if (lookup > 0) { + /* Report non-recursive success after first lookup */ + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_info("%s %s: lookup=%d/%d: found non-recursive ifindex=%d for address %s", + __FILE__, __PRETTY_FUNCTION__, + lookup, max_lookup, first_ifindex, addr_str); + + /* use last address as nexthop address */ + nexthop_tab[0].nexthop_addr = addr; + } + + return num_ifindex; + } + + { + char addr_str[100]; + char nexthop_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + pim_inet4_dump("", nexthop_addr, nexthop_str, sizeof(nexthop_str)); + zlog_warn("%s %s: lookup=%d/%d: zebra returned recursive nexthop %s for address %s", + __FILE__, __PRETTY_FUNCTION__, + lookup, max_lookup, nexthop_str, addr_str); + } + + addr = nexthop_addr; /* use nexthop addr for recursive lookup */ + + } /* for (max_lookup) */ + + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_warn("%s %s: lookup=%d/%d: failure searching recursive nexthop ifindex for address %s", + __FILE__, __PRETTY_FUNCTION__, + lookup, max_lookup, addr_str); + + return -2; +} diff --git a/pimd/pim_zlookup.h b/pimd/pim_zlookup.h new file mode 100644 index 00000000..1f184942 --- /dev/null +++ b/pimd/pim_zlookup.h @@ -0,0 +1,47 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_ZLOOKUP_H +#define PIM_ZLOOKUP_H + +#include + +#include "zclient.h" + +#define PIM_NEXTHOP_LOOKUP_MAX (3) /* max. recursive route lookup */ + +struct pim_zlookup_nexthop { + struct in_addr nexthop_addr; + int ifindex; + uint32_t route_metric; + uint8_t protocol_distance; +}; + +struct zclient *zclient_lookup_new(void); + +int zclient_lookup_nexthop(struct zclient *zlookup, + struct pim_zlookup_nexthop nexthop_tab[], + const int tab_size, + struct in_addr addr, + int max_lookup); + +#endif /* PIM_ZLOOKUP_H */ diff --git a/pimd/pimd.c b/pimd/pimd.c new file mode 100644 index 00000000..cbcd0648 --- /dev/null +++ b/pimd/pimd.c @@ -0,0 +1,125 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "log.h" +#include "memory.h" + +#include "pimd.h" +#include "pim_cmd.h" +#include "pim_iface.h" +#include "pim_zebra.h" +#include "pim_str.h" +#include "pim_oil.h" +#include "pim_pim.h" +#include "pim_upstream.h" +#include "pim_rand.h" +#include "pim_rpf.h" + +const char *const PIM_ALL_SYSTEMS = MCAST_ALL_SYSTEMS; +const char *const PIM_ALL_ROUTERS = MCAST_ALL_ROUTERS; +const char *const PIM_ALL_PIM_ROUTERS = MCAST_ALL_PIM_ROUTERS; +const char *const PIM_ALL_IGMP_ROUTERS = MCAST_ALL_IGMP_ROUTERS; + +struct thread_master *master = 0; +uint32_t qpim_debugs = 0; +int qpim_mroute_socket_fd = -1; +int64_t qpim_mroute_socket_creation = 0; /* timestamp of creation */ +struct thread *qpim_mroute_socket_reader = 0; +int qpim_mroute_oif_highest_vif_index = -1; +struct list *qpim_channel_oil_list = 0; +int qpim_t_periodic = PIM_DEFAULT_T_PERIODIC; /* Period between Join/Prune Messages */ +struct list *qpim_upstream_list = 0; +struct zclient *qpim_zclient_lookup = 0; +struct pim_assert_metric qpim_infinite_assert_metric; +long qpim_rpf_cache_refresh_delay_msec = 10000; +struct thread *qpim_rpf_cache_refresher = 0; +struct in_addr qpim_inaddr_any; + +static void pim_free() +{ + if (qpim_channel_oil_list) + list_free(qpim_channel_oil_list); + + if (qpim_upstream_list) + list_free(qpim_upstream_list); +} + +void pim_init() +{ + pim_rand_init(); + + if (!inet_aton(PIM_ALL_PIM_ROUTERS, &qpim_all_pim_routers_addr)) { + zlog_err("%s %s: could not solve %s to group address: errno=%d: %s", + __FILE__, __PRETTY_FUNCTION__, + PIM_ALL_PIM_ROUTERS, errno, strerror(errno)); + zassert(0); + return; + } + + qpim_channel_oil_list = list_new(); + if (!qpim_channel_oil_list) { + zlog_err("%s %s: failure: channel_oil_list=list_new()", + __FILE__, __PRETTY_FUNCTION__); + return; + } + qpim_channel_oil_list->del = (void (*)(void *)) pim_channel_oil_free; + + qpim_upstream_list = list_new(); + if (!qpim_upstream_list) { + zlog_err("%s %s: failure: upstream_list=list_new()", + __FILE__, __PRETTY_FUNCTION__); + pim_free(); + return; + } + qpim_upstream_list->del = (void (*)(void *)) pim_upstream_free; + + qpim_mroute_socket_fd = -1; /* mark mroute as disabled */ + qpim_mroute_oif_highest_vif_index = -1; + + zassert(!qpim_debugs); + zassert(!PIM_MROUTE_IS_ENABLED); + + qpim_inaddr_any.s_addr = PIM_NET_INADDR_ANY; + + /* + RFC 4601: 4.6.3. Assert Metrics + + assert_metric + infinite_assert_metric() { + return {1,infinity,infinity,0} + } + */ + qpim_infinite_assert_metric.rpt_bit_flag = 1; + qpim_infinite_assert_metric.metric_preference = PIM_ASSERT_METRIC_PREFERENCE_MAX; + qpim_infinite_assert_metric.route_metric = PIM_ASSERT_ROUTE_METRIC_MAX; + qpim_infinite_assert_metric.ip_address = qpim_inaddr_any; + + pim_if_init(); + pim_cmd_init(); +} + +void pim_terminate() +{ + pim_free(); +} diff --git a/pimd/pimd.conf.sample b/pimd/pimd.conf.sample new file mode 100644 index 00000000..2f4543ec --- /dev/null +++ b/pimd/pimd.conf.sample @@ -0,0 +1,35 @@ +! +! pimd sample configuration file +! $QuaggaId: $Format:%an, %ai, %h$ $ +! +hostname quagga-pimd-router +password zebra +!enable password zebra +! +!log file pimd.log +log stdout +! +line vty + exec-timeout 60 +! +!debug igmp +!debug pim +!debug pim zebra +! +ip multicast-routing +! +! ! HINTS: +! ! - Enable "ip pim ssm" on the interface directly attached to the +! ! multicast source host (if this is the first-hop router) +! ! - Enable "ip pim ssm" on pim-routers-facing interfaces +! ! - Enable "ip igmp" on IGMPv3-hosts-facing interfaces +! ! - In order to inject IGMPv3 local membership information in the +! ! PIM protocol state, enable both "ip pim ssm" and "ip igmp" on +! ! the same interface; otherwise PIM won't advertise +! ! IGMPv3-learned membership to other PIM routers +! +interface eth0 + ip pim ssm + ip igmp + +! -x- diff --git a/pimd/pimd.h b/pimd/pimd.h new file mode 100644 index 00000000..10f8518a --- /dev/null +++ b/pimd/pimd.h @@ -0,0 +1,121 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIMD_H +#define PIMD_H + +#include + +#include "pim_mroute.h" +#include "pim_assert.h" + +#define PIMD_PROGNAME "pimd" +#define PIMD_DEFAULT_CONFIG "pimd.conf" +#define PIMD_VTY_PORT 2611 +#define PIMD_BUG_ADDRESS "http://savannah.nongnu.org/projects/qpimd" + +#define PIM_IP_HEADER_MIN_LEN (20) +#define PIM_IP_HEADER_MAX_LEN (60) +#define PIM_IP_PROTO_IGMP (2) +#define PIM_IP_PROTO_PIM (103) +#define PIM_IGMP_MIN_LEN (8) +#define PIM_MSG_HEADER_LEN (4) +#define PIM_PIM_MIN_LEN PIM_MSG_HEADER_LEN +#define PIM_PROTO_VERSION (2) + +#define MCAST_ALL_SYSTEMS "224.0.0.1" +#define MCAST_ALL_ROUTERS "224.0.0.2" +#define MCAST_ALL_PIM_ROUTERS "224.0.0.13" +#define MCAST_ALL_IGMP_ROUTERS "224.0.0.22" + +#define PIM_FORCE_BOOLEAN(expr) ((expr) != 0) + +#define PIM_NET_INADDR_ANY (htonl(INADDR_ANY)) +#define PIM_INADDR_IS_ANY(addr) ((addr).s_addr == PIM_NET_INADDR_ANY) /* struct in_addr addr */ +#define PIM_INADDR_ISNOT_ANY(addr) ((addr).s_addr != PIM_NET_INADDR_ANY) /* struct in_addr addr */ + +#define PIM_MASK_PIM_EVENTS (1 << 0) +#define PIM_MASK_PIM_PACKETS (1 << 1) +#define PIM_MASK_PIM_TRACE (1 << 2) +#define PIM_MASK_IGMP_EVENTS (1 << 3) +#define PIM_MASK_IGMP_PACKETS (1 << 4) +#define PIM_MASK_IGMP_TRACE (1 << 5) +#define PIM_MASK_ZEBRA (1 << 6) + +const char *const PIM_ALL_SYSTEMS; +const char *const PIM_ALL_ROUTERS; +const char *const PIM_ALL_PIM_ROUTERS; +const char *const PIM_ALL_IGMP_ROUTERS; + +struct thread_master *master; +uint32_t qpim_debugs; +int qpim_mroute_socket_fd; +int64_t qpim_mroute_socket_creation; /* timestamp of creation */ +struct thread *qpim_mroute_socket_reader; +int qpim_mroute_oif_highest_vif_index; +struct list *qpim_channel_oil_list; /* list of struct channel_oil */ +struct in_addr qpim_all_pim_routers_addr; +int qpim_t_periodic; /* Period between Join/Prune Messages */ +struct list *qpim_upstream_list; /* list of struct pim_upstream */ +struct zclient *qpim_zclient_lookup; +struct pim_assert_metric qpim_infinite_assert_metric; +long qpim_rpf_cache_refresh_delay_msec; +struct thread *qpim_rpf_cache_refresher; +struct in_addr qpim_inaddr_any; + +#define PIM_JP_HOLDTIME (qpim_t_periodic * 7 / 2) + +#define PIM_MROUTE_IS_ENABLED (qpim_mroute_socket_fd >= 0) +#define PIM_MROUTE_IS_DISABLED (qpim_mroute_socket_fd < 0) + +#define PIM_DEBUG_PIM_EVENTS (qpim_debugs & PIM_MASK_PIM_EVENTS) +#define PIM_DEBUG_PIM_PACKETS (qpim_debugs & PIM_MASK_PIM_PACKETS) +#define PIM_DEBUG_PIM_TRACE (qpim_debugs & PIM_MASK_PIM_TRACE) +#define PIM_DEBUG_IGMP_EVENTS (qpim_debugs & PIM_MASK_IGMP_EVENTS) +#define PIM_DEBUG_IGMP_PACKETS (qpim_debugs & PIM_MASK_IGMP_PACKETS) +#define PIM_DEBUG_IGMP_TRACE (qpim_debugs & PIM_MASK_IGMP_TRACE) +#define PIM_DEBUG_ZEBRA (qpim_debugs & PIM_MASK_ZEBRA) + +#define PIM_DEBUG_EVENTS (qpim_debugs & (PIM_MASK_PIM_EVENTS | PIM_MASK_IGMP_EVENTS)) +#define PIM_DEBUG_PACKETS (qpim_debugs & (PIM_MASK_PIM_PACKETS | PIM_MASK_IGMP_PACKETS)) +#define PIM_DEBUG_TRACE (qpim_debugs & (PIM_MASK_PIM_TRACE | PIM_MASK_IGMP_TRACE)) + +#define PIM_DO_DEBUG_PIM_EVENTS (qpim_debugs |= PIM_MASK_PIM_EVENTS) +#define PIM_DO_DEBUG_PIM_PACKETS (qpim_debugs |= PIM_MASK_PIM_PACKETS) +#define PIM_DO_DEBUG_PIM_TRACE (qpim_debugs |= PIM_MASK_PIM_TRACE) +#define PIM_DO_DEBUG_IGMP_EVENTS (qpim_debugs |= PIM_MASK_IGMP_EVENTS) +#define PIM_DO_DEBUG_IGMP_PACKETS (qpim_debugs |= PIM_MASK_IGMP_PACKETS) +#define PIM_DO_DEBUG_IGMP_TRACE (qpim_debugs |= PIM_MASK_IGMP_TRACE) +#define PIM_DO_DEBUG_ZEBRA (qpim_debugs |= PIM_MASK_ZEBRA) + +#define PIM_DONT_DEBUG_PIM_EVENTS (qpim_debugs &= ~PIM_MASK_PIM_EVENTS) +#define PIM_DONT_DEBUG_PIM_PACKETS (qpim_debugs &= ~PIM_MASK_PIM_PACKETS) +#define PIM_DONT_DEBUG_PIM_TRACE (qpim_debugs &= ~PIM_MASK_PIM_TRACE) +#define PIM_DONT_DEBUG_IGMP_EVENTS (qpim_debugs &= ~PIM_MASK_IGMP_EVENTS) +#define PIM_DONT_DEBUG_IGMP_PACKETS (qpim_debugs &= ~PIM_MASK_IGMP_PACKETS) +#define PIM_DONT_DEBUG_IGMP_TRACE (qpim_debugs &= ~PIM_MASK_IGMP_TRACE) +#define PIM_DONT_DEBUG_ZEBRA (qpim_debugs &= ~PIM_MASK_ZEBRA) + +void pim_init(void); +void pim_terminate(void); + +#endif /* PIMD_H */ diff --git a/pimd/quagga-bootstrap.sh b/pimd/quagga-bootstrap.sh new file mode 100755 index 00000000..1ad9a121 --- /dev/null +++ b/pimd/quagga-bootstrap.sh @@ -0,0 +1,21 @@ +#! /bin/bash +# +# Bootstrap Quagga autotools for pimd. +# +# Run from quagga's top dir as: +# ./pimd/quagga-bootstrap.sh +# +# $QuaggaId: $Format:%an, %ai, %h$ $ + +me=`basename $0` +msg () { + echo >&2 $me: $* +} + +if [ -f ./bootstrap.sh ]; then + msg found ./bootstrap.sh from quagga + ./bootstrap.sh +else + msg missing ./bootstrap.sh from quagga + autoreconf -i --force +fi diff --git a/pimd/quagga-build.sh b/pimd/quagga-build.sh new file mode 100755 index 00000000..2ad476d0 --- /dev/null +++ b/pimd/quagga-build.sh @@ -0,0 +1,10 @@ +#! /bin/bash +# +# Build minimum Quagga needed for pimd. +# +# Run from quagga's top dir as: +# ./pimd/quagga-build.sh +# +# $QuaggaId: $Format:%an, %ai, %h$ $ + +./pimd/quagga-memtypes.sh && ./pimd/quagga-bootstrap.sh && ./pimd/quagga-configure.sh && make diff --git a/pimd/quagga-configure.sh b/pimd/quagga-configure.sh new file mode 100755 index 00000000..a1cdd587 --- /dev/null +++ b/pimd/quagga-configure.sh @@ -0,0 +1,10 @@ +#! /bin/bash +# +# Configure for minimum Quagga build needed for pimd. +# +# Run from quagga's top dir as: +# . pimd/quagga-configure.sh +# +# $QuaggaId: $Format:%an, %ai, %h$ $ + +./configure --disable-bgpd --disable-ripd --disable-ripngd --disable-ospfd --disable-ospf6d --disable-watchquagga --disable-bgp-announce --disable-ospfapi --disable-ospfclient --disable-rtadv --disable-irdp --enable-pimd --enable-tcp-zebra --enable-ipv6 diff --git a/pimd/quagga-git-add.sh b/pimd/quagga-git-add.sh new file mode 100755 index 00000000..3824e984 --- /dev/null +++ b/pimd/quagga-git-add.sh @@ -0,0 +1,12 @@ +#! /bin/bash +# +# Add to git new files created by qpimd patch +# +# Run from quagga's top dir as: +# ./pimd/quagga-git-add.sh +# +# $QuaggaId: $Format:%an, %ai, %h$ $ + +chmod a+rx pimd/*.sh +git add doc/pimd.8 +git add pimd diff --git a/pimd/quagga-memtypes.sh b/pimd/quagga-memtypes.sh new file mode 100755 index 00000000..e86f414b --- /dev/null +++ b/pimd/quagga-memtypes.sh @@ -0,0 +1,22 @@ +#! /bin/bash +# +# Check lib/memtypes.h from Quagga +# +# Run from quagga's top dir as: +# ./pimd/quagga-memtypes.sh +# +# $QuaggaId: $Format:%an, %ai, %h$ $ + +me=`basename $0` +msg () { + echo >&2 $me: $* +} + +memtypes_h=lib/memtypes.h +if [ -e $memtypes_h ]; then + memtypes_h_size=`ls -s $memtypes_h | cut -d' ' -f1` + if [ "$memtypes_h_size" -lt 1 ]; then + msg WARNING: removing empty file: $memtypes_h -- awk failed? + rm $memtypes_h + fi +fi diff --git a/pimd/savannah-git-clone.sh b/pimd/savannah-git-clone.sh new file mode 100755 index 00000000..68fd608b --- /dev/null +++ b/pimd/savannah-git-clone.sh @@ -0,0 +1,16 @@ +#! /bin/bash +# +# Savannah Developer Git Checkout +# +# Delete remote branch qpimd: git push origin :qpimd +# (git push origin :refs/heads/branch_to_delete) +# Delete remote tag v0.139: git push origin :v0.139 +# (git push origin :refs/tags/tag_to_delete) +# Create remote-tracking branch: git checkout -b pim0.142 origin/pim0.142 +# Rename branch qpimd to pim: git branch -m qpimd pim +# Commit changes: git commit -a +# Send changes: git push --all +# +# $QuaggaId: $Format:%an, %ai, %h$ $ + +git clone ssh://evertonm@git.sv.gnu.org/srv/git/qpimd.git quagga diff --git a/ports/Makefile b/ports/Makefile index d085d06e..86f77bd0 100644 --- a/ports/Makefile +++ b/ports/Makefile @@ -54,5 +54,6 @@ post-install: @echo " ripngd 2603/tcp # RIPngd vty"; @echo " ospfd 2604/tcp # OSPFd vty"; @echo " bgpd 2605/tcp # BGPd vty"; + @echo " pimd 2611/tcp # PIMd vty"; .include diff --git a/ports/pkg/DESCR b/ports/pkg/DESCR index 0c8d01b7..aeb1950e 100644 --- a/ports/pkg/DESCR +++ b/ports/pkg/DESCR @@ -47,6 +47,7 @@ ripd 2602/tcp # RIPd vty ripngd 2603/tcp # RIPngd vty ospfd 2604/tcp # OSPFd vty bgpd 2605/tcp # BGPd vty +pimd 2611/tcp # PIMd vty I recommend you to add upper list to /etc/services. diff --git a/redhat/quagga.spec.in b/redhat/quagga.spec.in index 0ce25ca3..35d691ba 100644 --- a/redhat/quagga.spec.in +++ b/redhat/quagga.spec.in @@ -48,16 +48,16 @@ %define quagga_buildreqs %{quagga_buildreqs} patch libcap-devel # FC4 and 5 split texi2html out of tetex package. -%if "%dist" != "fc2" || "%dist" != "fc3" +%if "%dist" == "fc4" || "%dist" == "fc5" %define quagga_buildreqs %{quagga_buildreqs} texi2html %endif # pam_stack is deprecated in FC5 # default to pam_stack, default should be changed later. -%if "%dist" == "fc4" || "%dist" == "fc3" -%define quagga_pam_source quagga.pam.stack -%else +%if "%dist" == "fc5" %define quagga_pam_source quagga.pam +%else +%define quagga_pam_source quagga.pam.stack %endif ############################################################################ @@ -303,6 +303,9 @@ zebra_spec_add_service ospfapi 2607/tcp "OSPF-API" %if %{with_isisd} zebra_spec_add_service isisd 2608/tcp "ISISd vty" %endif +%if %{with_pimd} +zebra_spec_add_service pimd 2611/tcp "PIMd vty" +%endif for daemon in %daemon_list ; do /sbin/chkconfig --add ${daemon} diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index effe2338..d6ec9147 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -496,6 +496,17 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, break; } + if (IS_ZEBRA_DEBUG_RIB) { + char buf[INET6_ADDRSTRLEN]; + inet_ntop(rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); + zlog_debug("%s: %s/%d: nexthop match %p: %s type=%d sel=%d int=%d", + __func__, buf, rn->p.prefixlen, match, + match ? zebra_route_string(match->type) : "", + match ? match->type : -1, + match ? CHECK_FLAG(match->flags, ZEBRA_FLAG_SELECTED) : -1, + match ? CHECK_FLAG(match->flags, ZEBRA_FLAG_INTERNAL) : -1); + } + /* If there is no selected route or matched route is EGP, go up tree. */ if (! match @@ -524,7 +535,8 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, return 1; } - else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL)) + else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL) || + match->type == ZEBRA_ROUTE_KERNEL) { resolved = 0; for (newhop = match->nexthop; newhop; newhop = newhop->next) @@ -1042,6 +1054,43 @@ rib_match_ipv6 (struct in6_addr *addr) } #endif /* HAVE_IPV6 */ +static void nexthop_dump(struct nexthop *nexthop, + char *type_str_buf, + int type_str_buf_size, + char *addr_str_buf, + int addr_str_buf_size, + char *via_str_buf, + int via_str_buf_size) +{ + switch (nexthop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + snprintf(type_str_buf, type_str_buf_size, "ipv4"); + snprintf(addr_str_buf, addr_str_buf_size, "%s", inet_ntoa(nexthop->gate.ipv4)); + snprintf(via_str_buf, via_str_buf_size, "%s", nexthop->ifindex ? ifindex2ifname(nexthop->ifindex) : ""); + break; + case NEXTHOP_TYPE_IFINDEX: + snprintf(type_str_buf, type_str_buf_size, "connected"); + snprintf(addr_str_buf, addr_str_buf_size, ""); + snprintf(via_str_buf, via_str_buf_size, "%s", ifindex2ifname(nexthop->ifindex)); + break; + case NEXTHOP_TYPE_IFNAME: + snprintf(type_str_buf, type_str_buf_size, "connected"); + snprintf(addr_str_buf, addr_str_buf_size, ""); + snprintf(via_str_buf, via_str_buf_size, "%s", nexthop->ifname); + break; + case NEXTHOP_TYPE_BLACKHOLE: + snprintf(type_str_buf, type_str_buf_size, "blackhole"); + snprintf(addr_str_buf, addr_str_buf_size, ""); + snprintf(via_str_buf, via_str_buf_size, "Null0"); + break; + default: + snprintf(type_str_buf, type_str_buf_size, "unknown"); + snprintf(addr_str_buf, addr_str_buf_size, ""); + snprintf(via_str_buf, via_str_buf_size, ""); + } +} + #define RIB_SYSTEM_ROUTE(R) \ ((R)->type == ZEBRA_ROUTE_KERNEL || (R)->type == ZEBRA_ROUTE_CONNECT) @@ -1096,10 +1145,27 @@ nexthop_active_check (struct route_node *rn, struct rib *rib, case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV4_IFINDEX: family = AFI_IP; - if (nexthop_active_ipv4 (rib, nexthop, set, rn)) - SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); - else - UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + { + int nh_active = nexthop_active_ipv4 (rib, nexthop, set, rn); + if (IS_ZEBRA_DEBUG_RIB) { + char type_str_buf[100]; + char addr_str_buf[100]; + char via_str_buf[100]; + nexthop_dump(nexthop, + type_str_buf, sizeof(type_str_buf), + addr_str_buf, sizeof(addr_str_buf), + via_str_buf, sizeof(via_str_buf)); + zlog_debug("%s: rib %p nexthop %p type=%d %s %s via %s ifindex=%d nexthop_active_ipv4=%d", + __func__, rib, nexthop, + nexthop->type, type_str_buf, + addr_str_buf, via_str_buf, nexthop->ifindex, + nh_active); + } + if (nh_active) + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + else + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + } break; #ifdef HAVE_IPV6 case NEXTHOP_TYPE_IPV6: @@ -1190,8 +1256,23 @@ nexthop_active_update (struct route_node *rn, struct rib *rib, int set) { prev_active = CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); prev_index = nexthop->ifindex; - if ((new_active = nexthop_active_check (rn, rib, nexthop, set))) + new_active = nexthop_active_check (rn, rib, nexthop, set); + if (new_active) rib->nexthop_active_num++; + if (IS_ZEBRA_DEBUG_RIB) { + char type_str_buf[100]; + char addr_str_buf[100]; + char via_str_buf[100]; + nexthop_dump(nexthop, + type_str_buf, sizeof(type_str_buf), + addr_str_buf, sizeof(addr_str_buf), + via_str_buf, sizeof(via_str_buf)); + zlog_debug("%s: rib %p nexthop %p type=%d %s %s via %s ifindex=%d act=%d total_act=%d", + __func__, rib, nexthop, + nexthop->type, type_str_buf, + addr_str_buf, via_str_buf, nexthop->ifindex, + new_active, rib->nexthop_active_num); + } if (prev_active != new_active || prev_index != nexthop->ifindex) SET_FLAG (rib->flags, ZEBRA_FLAG_CHANGED); diff --git a/zebra/zserv.c b/zebra/zserv.c index afd722a1..261c49a3 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -1,5 +1,6 @@ /* Zebra daemon server routine. * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro + * Portions Copyright (c) 2008 Everton da Silva Marques * * This file is part of GNU Zebra. * From 596470f2a410fb58109fd880f04362984ffd7c69 Mon Sep 17 00:00:00 2001 From: Leonard Herve Date: Tue, 11 Aug 2009 15:45:26 -0300 Subject: [PATCH 308/482] [pim] pim commands added to vtysh --- pimd/pim_cmd.c | 302 +++++++++++++++++++++++----------------------- pimd/pim_main.c | 25 ++++ pimd/pim_mroute.c | 13 ++ pimd/pim_sock.c | 13 ++ vtysh/Makefile.am | 1 + vtysh/vtysh.c | 2 + vtysh/vtysh.h | 4 +- 7 files changed, 207 insertions(+), 153 deletions(-) diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index b1349ed3..339f9da0 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -54,9 +54,9 @@ static struct cmd_node pim_global_node = { 1 /* vtysh ? yes */ }; -static struct cmd_node pim_interface_node = { +static struct cmd_node interface_node = { INTERFACE_NODE, - "%s(config-if-pim)# ", + "%s(config-if)# ", 1 /* vtysh ? yes */ }; @@ -3767,157 +3767,157 @@ DEFUN (test_pim_receive_upcall, void pim_cmd_init() { - install_node(&pim_global_node, pim_global_config_write); /* PIM_NODE */ - install_node(&pim_interface_node, pim_interface_config_write); /* INTERFACE_NODE */ + install_node (&pim_global_node, pim_global_config_write); /* PIM_NODE */ + install_node (&interface_node, pim_interface_config_write); /* INTERFACE_NODE */ - install_element(CONFIG_NODE, &ip_multicast_routing_cmd); - install_element(CONFIG_NODE, &no_ip_multicast_routing_cmd); + install_element (CONFIG_NODE, &ip_multicast_routing_cmd); + install_element (CONFIG_NODE, &no_ip_multicast_routing_cmd); #if 0 - install_element(CONFIG_NODE, &interface_cmd); /* from if.h */ + install_element (CONFIG_NODE, &interface_cmd); /* from if.h */ #else - install_element(CONFIG_NODE, &pim_interface_cmd); + install_element (CONFIG_NODE, &pim_interface_cmd); #endif - install_element(CONFIG_NODE, &no_interface_cmd); /* from if.h */ - - install_default(INTERFACE_NODE); - install_element(INTERFACE_NODE, &interface_ip_igmp_cmd); - install_element(INTERFACE_NODE, &interface_no_ip_igmp_cmd); - install_element(INTERFACE_NODE, &interface_ip_igmp_join_cmd); - install_element(INTERFACE_NODE, &interface_no_ip_igmp_join_cmd); - install_element(INTERFACE_NODE, &interface_ip_igmp_query_interval_cmd); - install_element(INTERFACE_NODE, &interface_no_ip_igmp_query_interval_cmd); - install_element(INTERFACE_NODE, &interface_ip_igmp_query_max_response_time_cmd); - install_element(INTERFACE_NODE, &interface_no_ip_igmp_query_max_response_time_cmd); - install_element(INTERFACE_NODE, &interface_ip_igmp_query_max_response_time_dsec_cmd); - install_element(INTERFACE_NODE, &interface_no_ip_igmp_query_max_response_time_dsec_cmd); - install_element(INTERFACE_NODE, &interface_ip_pim_ssm_cmd); - install_element(INTERFACE_NODE, &interface_no_ip_pim_ssm_cmd); - - install_element(VIEW_NODE, &show_ip_igmp_interface_cmd); - install_element(VIEW_NODE, &show_ip_igmp_parameters_cmd); - install_element(VIEW_NODE, &show_ip_igmp_groups_cmd); - install_element(VIEW_NODE, &show_ip_igmp_groups_retransmissions_cmd); - install_element(VIEW_NODE, &show_ip_igmp_sources_cmd); - install_element(VIEW_NODE, &show_ip_igmp_sources_retransmissions_cmd); - install_element(VIEW_NODE, &show_ip_igmp_querier_cmd); - install_element(VIEW_NODE, &show_ip_pim_assert_cmd); - install_element(VIEW_NODE, &show_ip_pim_assert_internal_cmd); - install_element(VIEW_NODE, &show_ip_pim_assert_metric_cmd); - install_element(VIEW_NODE, &show_ip_pim_assert_winner_metric_cmd); - install_element(VIEW_NODE, &show_ip_pim_dr_cmd); - install_element(VIEW_NODE, &show_ip_pim_hello_cmd); - install_element(VIEW_NODE, &show_ip_pim_interface_cmd); - install_element(VIEW_NODE, &show_ip_pim_join_cmd); - install_element(VIEW_NODE, &show_ip_pim_jp_override_interval_cmd); - install_element(VIEW_NODE, &show_ip_pim_lan_prune_delay_cmd); - install_element(VIEW_NODE, &show_ip_pim_local_membership_cmd); - install_element(VIEW_NODE, &show_ip_pim_neighbor_cmd); - install_element(VIEW_NODE, &show_ip_pim_rpf_cmd); - install_element(VIEW_NODE, &show_ip_pim_secondary_cmd); - install_element(VIEW_NODE, &show_ip_pim_upstream_cmd); - install_element(VIEW_NODE, &show_ip_pim_upstream_join_desired_cmd); - install_element(VIEW_NODE, &show_ip_pim_upstream_rpf_cmd); - install_element(VIEW_NODE, &show_ip_multicast_cmd); - install_element(VIEW_NODE, &show_ip_mroute_cmd); - install_element(VIEW_NODE, &show_ip_mroute_count_cmd); - install_element(VIEW_NODE, &show_ip_route_cmd); - install_element(VIEW_NODE, &show_debugging_cmd); - - install_element(ENABLE_NODE, &clear_ip_interfaces_cmd); - install_element(ENABLE_NODE, &clear_ip_igmp_interfaces_cmd); - install_element(ENABLE_NODE, &clear_ip_pim_interfaces_cmd); - - install_element(ENABLE_NODE, &show_ip_igmp_interface_cmd); - install_element(ENABLE_NODE, &show_ip_igmp_parameters_cmd); - install_element(ENABLE_NODE, &show_ip_igmp_groups_cmd); - install_element(ENABLE_NODE, &show_ip_igmp_groups_retransmissions_cmd); - install_element(ENABLE_NODE, &show_ip_igmp_sources_cmd); - install_element(ENABLE_NODE, &show_ip_igmp_sources_retransmissions_cmd); - install_element(ENABLE_NODE, &show_ip_igmp_querier_cmd); - install_element(ENABLE_NODE, &show_ip_pim_address_cmd); - install_element(ENABLE_NODE, &show_ip_pim_assert_cmd); - install_element(ENABLE_NODE, &show_ip_pim_assert_internal_cmd); - install_element(ENABLE_NODE, &show_ip_pim_assert_metric_cmd); - install_element(ENABLE_NODE, &show_ip_pim_assert_winner_metric_cmd); - install_element(ENABLE_NODE, &show_ip_pim_dr_cmd); - install_element(ENABLE_NODE, &show_ip_pim_hello_cmd); - install_element(ENABLE_NODE, &show_ip_pim_interface_cmd); - install_element(ENABLE_NODE, &show_ip_pim_join_cmd); - install_element(ENABLE_NODE, &show_ip_pim_jp_override_interval_cmd); - install_element(ENABLE_NODE, &show_ip_pim_lan_prune_delay_cmd); - install_element(ENABLE_NODE, &show_ip_pim_local_membership_cmd); - install_element(ENABLE_NODE, &show_ip_pim_neighbor_cmd); - install_element(ENABLE_NODE, &show_ip_pim_rpf_cmd); - install_element(ENABLE_NODE, &show_ip_pim_secondary_cmd); - install_element(ENABLE_NODE, &show_ip_pim_upstream_cmd); - install_element(ENABLE_NODE, &show_ip_pim_upstream_join_desired_cmd); - install_element(ENABLE_NODE, &show_ip_pim_upstream_rpf_cmd); - install_element(ENABLE_NODE, &show_ip_multicast_cmd); - install_element(ENABLE_NODE, &show_ip_mroute_cmd); - install_element(ENABLE_NODE, &show_ip_mroute_count_cmd); - install_element(ENABLE_NODE, &show_ip_route_cmd); - install_element(ENABLE_NODE, &show_debugging_cmd); - - install_element(ENABLE_NODE, &test_igmp_receive_report_cmd); - install_element(ENABLE_NODE, &test_pim_receive_assert_cmd); - install_element(ENABLE_NODE, &test_pim_receive_hello_cmd); - install_element(ENABLE_NODE, &test_pim_receive_join_cmd); - install_element(ENABLE_NODE, &test_pim_receive_prune_cmd); - install_element(ENABLE_NODE, &test_pim_receive_upcall_cmd); - - install_element(ENABLE_NODE, &debug_igmp_cmd); - install_element(ENABLE_NODE, &no_debug_igmp_cmd); - install_element(ENABLE_NODE, &undebug_igmp_cmd); - install_element(ENABLE_NODE, &debug_igmp_events_cmd); - install_element(ENABLE_NODE, &no_debug_igmp_events_cmd); - install_element(ENABLE_NODE, &undebug_igmp_events_cmd); - install_element(ENABLE_NODE, &debug_igmp_packets_cmd); - install_element(ENABLE_NODE, &no_debug_igmp_packets_cmd); - install_element(ENABLE_NODE, &undebug_igmp_packets_cmd); - install_element(ENABLE_NODE, &debug_igmp_trace_cmd); - install_element(ENABLE_NODE, &no_debug_igmp_trace_cmd); - install_element(ENABLE_NODE, &undebug_igmp_trace_cmd); - install_element(ENABLE_NODE, &debug_pim_cmd); - install_element(ENABLE_NODE, &no_debug_pim_cmd); - install_element(ENABLE_NODE, &undebug_pim_cmd); - install_element(ENABLE_NODE, &debug_pim_events_cmd); - install_element(ENABLE_NODE, &no_debug_pim_events_cmd); - install_element(ENABLE_NODE, &undebug_pim_events_cmd); - install_element(ENABLE_NODE, &debug_pim_packets_cmd); - install_element(ENABLE_NODE, &no_debug_pim_packets_cmd); - install_element(ENABLE_NODE, &undebug_pim_packets_cmd); - install_element(ENABLE_NODE, &debug_pim_trace_cmd); - install_element(ENABLE_NODE, &no_debug_pim_trace_cmd); - install_element(ENABLE_NODE, &undebug_pim_trace_cmd); - install_element(ENABLE_NODE, &debug_pim_zebra_cmd); - install_element(ENABLE_NODE, &no_debug_pim_zebra_cmd); - install_element(ENABLE_NODE, &undebug_pim_zebra_cmd); - - install_element(CONFIG_NODE, &debug_igmp_cmd); - install_element(CONFIG_NODE, &no_debug_igmp_cmd); - install_element(CONFIG_NODE, &undebug_igmp_cmd); - install_element(CONFIG_NODE, &debug_igmp_events_cmd); - install_element(CONFIG_NODE, &no_debug_igmp_events_cmd); - install_element(CONFIG_NODE, &undebug_igmp_events_cmd); - install_element(CONFIG_NODE, &debug_igmp_packets_cmd); - install_element(CONFIG_NODE, &no_debug_igmp_packets_cmd); - install_element(CONFIG_NODE, &undebug_igmp_packets_cmd); - install_element(CONFIG_NODE, &debug_igmp_trace_cmd); - install_element(CONFIG_NODE, &no_debug_igmp_trace_cmd); - install_element(CONFIG_NODE, &undebug_igmp_trace_cmd); - install_element(CONFIG_NODE, &debug_pim_cmd); - install_element(CONFIG_NODE, &no_debug_pim_cmd); - install_element(CONFIG_NODE, &undebug_pim_cmd); - install_element(CONFIG_NODE, &debug_pim_events_cmd); - install_element(CONFIG_NODE, &no_debug_pim_events_cmd); - install_element(CONFIG_NODE, &undebug_pim_events_cmd); - install_element(CONFIG_NODE, &debug_pim_packets_cmd); - install_element(CONFIG_NODE, &no_debug_pim_packets_cmd); - install_element(CONFIG_NODE, &undebug_pim_packets_cmd); - install_element(CONFIG_NODE, &debug_pim_trace_cmd); - install_element(CONFIG_NODE, &no_debug_pim_trace_cmd); - install_element(CONFIG_NODE, &undebug_pim_trace_cmd); - install_element(CONFIG_NODE, &debug_pim_zebra_cmd); - install_element(CONFIG_NODE, &no_debug_pim_zebra_cmd); - install_element(CONFIG_NODE, &undebug_pim_zebra_cmd); + install_element (CONFIG_NODE, &no_interface_cmd); /* from if.h */ + + install_default (INTERFACE_NODE); + install_element (INTERFACE_NODE, &interface_ip_igmp_cmd); + install_element (INTERFACE_NODE, &interface_no_ip_igmp_cmd); + install_element (INTERFACE_NODE, &interface_ip_igmp_join_cmd); + install_element (INTERFACE_NODE, &interface_no_ip_igmp_join_cmd); + install_element (INTERFACE_NODE, &interface_ip_igmp_query_interval_cmd); + install_element (INTERFACE_NODE, &interface_no_ip_igmp_query_interval_cmd); + install_element (INTERFACE_NODE, &interface_ip_igmp_query_max_response_time_cmd); + install_element (INTERFACE_NODE, &interface_no_ip_igmp_query_max_response_time_cmd); + install_element (INTERFACE_NODE, &interface_ip_igmp_query_max_response_time_dsec_cmd); + install_element (INTERFACE_NODE, &interface_no_ip_igmp_query_max_response_time_dsec_cmd); + install_element (INTERFACE_NODE, &interface_ip_pim_ssm_cmd); + install_element (INTERFACE_NODE, &interface_no_ip_pim_ssm_cmd); + + install_element (VIEW_NODE, &show_ip_igmp_interface_cmd); + install_element (VIEW_NODE, &show_ip_igmp_parameters_cmd); + install_element (VIEW_NODE, &show_ip_igmp_groups_cmd); + install_element (VIEW_NODE, &show_ip_igmp_groups_retransmissions_cmd); + install_element (VIEW_NODE, &show_ip_igmp_sources_cmd); + install_element (VIEW_NODE, &show_ip_igmp_sources_retransmissions_cmd); + install_element (VIEW_NODE, &show_ip_igmp_querier_cmd); + install_element (VIEW_NODE, &show_ip_pim_assert_cmd); + install_element (VIEW_NODE, &show_ip_pim_assert_internal_cmd); + install_element (VIEW_NODE, &show_ip_pim_assert_metric_cmd); + install_element (VIEW_NODE, &show_ip_pim_assert_winner_metric_cmd); + install_element (VIEW_NODE, &show_ip_pim_dr_cmd); + install_element (VIEW_NODE, &show_ip_pim_hello_cmd); + install_element (VIEW_NODE, &show_ip_pim_interface_cmd); + install_element (VIEW_NODE, &show_ip_pim_join_cmd); + install_element (VIEW_NODE, &show_ip_pim_jp_override_interval_cmd); + install_element (VIEW_NODE, &show_ip_pim_lan_prune_delay_cmd); + install_element (VIEW_NODE, &show_ip_pim_local_membership_cmd); + install_element (VIEW_NODE, &show_ip_pim_neighbor_cmd); + install_element (VIEW_NODE, &show_ip_pim_rpf_cmd); + install_element (VIEW_NODE, &show_ip_pim_secondary_cmd); + install_element (VIEW_NODE, &show_ip_pim_upstream_cmd); + install_element (VIEW_NODE, &show_ip_pim_upstream_join_desired_cmd); + install_element (VIEW_NODE, &show_ip_pim_upstream_rpf_cmd); + install_element (VIEW_NODE, &show_ip_multicast_cmd); + install_element (VIEW_NODE, &show_ip_mroute_cmd); + install_element (VIEW_NODE, &show_ip_mroute_count_cmd); + install_element (VIEW_NODE, &show_ip_route_cmd); + install_element (VIEW_NODE, &show_debugging_cmd); + + install_element (ENABLE_NODE, &clear_ip_interfaces_cmd); + install_element (ENABLE_NODE, &clear_ip_igmp_interfaces_cmd); + install_element (ENABLE_NODE, &clear_ip_pim_interfaces_cmd); + + install_element (ENABLE_NODE, &show_ip_igmp_interface_cmd); + install_element (ENABLE_NODE, &show_ip_igmp_parameters_cmd); + install_element (ENABLE_NODE, &show_ip_igmp_groups_cmd); + install_element (ENABLE_NODE, &show_ip_igmp_groups_retransmissions_cmd); + install_element (ENABLE_NODE, &show_ip_igmp_sources_cmd); + install_element (ENABLE_NODE, &show_ip_igmp_sources_retransmissions_cmd); + install_element (ENABLE_NODE, &show_ip_igmp_querier_cmd); + install_element (ENABLE_NODE, &show_ip_pim_address_cmd); + install_element (ENABLE_NODE, &show_ip_pim_assert_cmd); + install_element (ENABLE_NODE, &show_ip_pim_assert_internal_cmd); + install_element (ENABLE_NODE, &show_ip_pim_assert_metric_cmd); + install_element (ENABLE_NODE, &show_ip_pim_assert_winner_metric_cmd); + install_element (ENABLE_NODE, &show_ip_pim_dr_cmd); + install_element (ENABLE_NODE, &show_ip_pim_hello_cmd); + install_element (ENABLE_NODE, &show_ip_pim_interface_cmd); + install_element (ENABLE_NODE, &show_ip_pim_join_cmd); + install_element (ENABLE_NODE, &show_ip_pim_jp_override_interval_cmd); + install_element (ENABLE_NODE, &show_ip_pim_lan_prune_delay_cmd); + install_element (ENABLE_NODE, &show_ip_pim_local_membership_cmd); + install_element (ENABLE_NODE, &show_ip_pim_neighbor_cmd); + install_element (ENABLE_NODE, &show_ip_pim_rpf_cmd); + install_element (ENABLE_NODE, &show_ip_pim_secondary_cmd); + install_element (ENABLE_NODE, &show_ip_pim_upstream_cmd); + install_element (ENABLE_NODE, &show_ip_pim_upstream_join_desired_cmd); + install_element (ENABLE_NODE, &show_ip_pim_upstream_rpf_cmd); + install_element (ENABLE_NODE, &show_ip_multicast_cmd); + install_element (ENABLE_NODE, &show_ip_mroute_cmd); + install_element (ENABLE_NODE, &show_ip_mroute_count_cmd); + install_element (ENABLE_NODE, &show_ip_route_cmd); + install_element (ENABLE_NODE, &show_debugging_cmd); + + install_element (ENABLE_NODE, &test_igmp_receive_report_cmd); + install_element (ENABLE_NODE, &test_pim_receive_assert_cmd); + install_element (ENABLE_NODE, &test_pim_receive_hello_cmd); + install_element (ENABLE_NODE, &test_pim_receive_join_cmd); + install_element (ENABLE_NODE, &test_pim_receive_prune_cmd); + install_element (ENABLE_NODE, &test_pim_receive_upcall_cmd); + + install_element (ENABLE_NODE, &debug_igmp_cmd); + install_element (ENABLE_NODE, &no_debug_igmp_cmd); + install_element (ENABLE_NODE, &undebug_igmp_cmd); + install_element (ENABLE_NODE, &debug_igmp_events_cmd); + install_element (ENABLE_NODE, &no_debug_igmp_events_cmd); + install_element (ENABLE_NODE, &undebug_igmp_events_cmd); + install_element (ENABLE_NODE, &debug_igmp_packets_cmd); + install_element (ENABLE_NODE, &no_debug_igmp_packets_cmd); + install_element (ENABLE_NODE, &undebug_igmp_packets_cmd); + install_element (ENABLE_NODE, &debug_igmp_trace_cmd); + install_element (ENABLE_NODE, &no_debug_igmp_trace_cmd); + install_element (ENABLE_NODE, &undebug_igmp_trace_cmd); + install_element (ENABLE_NODE, &debug_pim_cmd); + install_element (ENABLE_NODE, &no_debug_pim_cmd); + install_element (ENABLE_NODE, &undebug_pim_cmd); + install_element (ENABLE_NODE, &debug_pim_events_cmd); + install_element (ENABLE_NODE, &no_debug_pim_events_cmd); + install_element (ENABLE_NODE, &undebug_pim_events_cmd); + install_element (ENABLE_NODE, &debug_pim_packets_cmd); + install_element (ENABLE_NODE, &no_debug_pim_packets_cmd); + install_element (ENABLE_NODE, &undebug_pim_packets_cmd); + install_element (ENABLE_NODE, &debug_pim_trace_cmd); + install_element (ENABLE_NODE, &no_debug_pim_trace_cmd); + install_element (ENABLE_NODE, &undebug_pim_trace_cmd); + install_element (ENABLE_NODE, &debug_pim_zebra_cmd); + install_element (ENABLE_NODE, &no_debug_pim_zebra_cmd); + install_element (ENABLE_NODE, &undebug_pim_zebra_cmd); + + install_element (CONFIG_NODE, &debug_igmp_cmd); + install_element (CONFIG_NODE, &no_debug_igmp_cmd); + install_element (CONFIG_NODE, &undebug_igmp_cmd); + install_element (CONFIG_NODE, &debug_igmp_events_cmd); + install_element (CONFIG_NODE, &no_debug_igmp_events_cmd); + install_element (CONFIG_NODE, &undebug_igmp_events_cmd); + install_element (CONFIG_NODE, &debug_igmp_packets_cmd); + install_element (CONFIG_NODE, &no_debug_igmp_packets_cmd); + install_element (CONFIG_NODE, &undebug_igmp_packets_cmd); + install_element (CONFIG_NODE, &debug_igmp_trace_cmd); + install_element (CONFIG_NODE, &no_debug_igmp_trace_cmd); + install_element (CONFIG_NODE, &undebug_igmp_trace_cmd); + install_element (CONFIG_NODE, &debug_pim_cmd); + install_element (CONFIG_NODE, &no_debug_pim_cmd); + install_element (CONFIG_NODE, &undebug_pim_cmd); + install_element (CONFIG_NODE, &debug_pim_events_cmd); + install_element (CONFIG_NODE, &no_debug_pim_events_cmd); + install_element (CONFIG_NODE, &undebug_pim_events_cmd); + install_element (CONFIG_NODE, &debug_pim_packets_cmd); + install_element (CONFIG_NODE, &no_debug_pim_packets_cmd); + install_element (CONFIG_NODE, &undebug_pim_packets_cmd); + install_element (CONFIG_NODE, &debug_pim_trace_cmd); + install_element (CONFIG_NODE, &no_debug_pim_trace_cmd); + install_element (CONFIG_NODE, &undebug_pim_trace_cmd); + install_element (CONFIG_NODE, &debug_pim_zebra_cmd); + install_element (CONFIG_NODE, &no_debug_pim_zebra_cmd); + install_element (CONFIG_NODE, &undebug_pim_zebra_cmd); } diff --git a/pimd/pim_main.c b/pimd/pim_main.c index 1206b551..3cf1869c 100644 --- a/pimd/pim_main.c +++ b/pimd/pim_main.c @@ -23,6 +23,7 @@ #include #include "log.h" +#include "privs.h" #include "version.h" #include #include "command.h" @@ -61,6 +62,29 @@ struct option longopts[] = { { 0 } }; +/* pimd privileges */ +zebra_capabilities_t _caps_p [] = +{ + ZCAP_NET_ADMIN, + ZCAP_SYS_ADMIN, + ZCAP_NET_RAW, +}; + +/* pimd privileges to run with */ +struct zebra_privs_t pimd_privs = +{ +#if defined(QUAGGA_USER) && defined(QUAGGA_GROUP) + .user = QUAGGA_USER, + .group = QUAGGA_GROUP, +#endif +#ifdef VTY_GROUP + .vty_group = VTY_GROUP, +#endif + .caps_p = _caps_p, + .cap_num_p = sizeof(_caps_p)/sizeof(_caps_p[0]), + .cap_num_i = 0 +}; + char* progname; const char *pid_file = PATH_PIMD_PID; @@ -170,6 +194,7 @@ int main(int argc, char** argv, char** envp) { /* * Initializations */ + zprivs_init (&pimd_privs); pim_signals_init(); cmd_init(1); vty_init(master); diff --git a/pimd/pim_mroute.c b/pimd/pim_mroute.c index f021abaa..48213b0b 100644 --- a/pimd/pim_mroute.c +++ b/pimd/pim_mroute.c @@ -22,6 +22,7 @@ #include #include "log.h" +#include "privs.h" #include "pimd.h" #include "pim_mroute.h" @@ -30,6 +31,9 @@ #include "pim_iface.h" #include "pim_macro.h" +/* GLOBAL VARS */ +extern struct zebra_privs_t pimd_privs; + static void mroute_read_on(void); static int pim_mroute_set(int fd, int enable) @@ -259,7 +263,16 @@ int pim_mroute_socket_enable() if (PIM_MROUTE_IS_ENABLED) return -1; + if ( pimd_privs.change (ZPRIVS_RAISE) ) + zlog_err ("pim_mroute_socket_enable: could not raise privs, %s", + safe_strerror (errno) ); + fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP); + + if ( pimd_privs.change (ZPRIVS_LOWER) ) + zlog_err ("pim_mroute_socket_enable: could not lower privs, %s", + safe_strerror (errno) ); + if (fd < 0) { zlog_warn("Could not create mroute socket: errno=%d: %s", errno, strerror(errno)); diff --git a/pimd/pim_sock.c b/pimd/pim_sock.c index c43cb68e..9f346411 100644 --- a/pimd/pim_sock.c +++ b/pimd/pim_sock.c @@ -33,11 +33,15 @@ #include #include "log.h" +#include "privs.h" #include "pimd.h" #include "pim_sock.h" #include "pim_str.h" +/* GLOBAL VARS */ +extern struct zebra_privs_t pimd_privs; + #ifndef MCAST_JOIN_SOURCE_GROUP #define MCAST_JOIN_SOURCE_GROUP 46 struct group_source_req @@ -52,7 +56,16 @@ int pim_socket_raw(int protocol) { int fd; + if ( pimd_privs.change (ZPRIVS_RAISE) ) + zlog_err ("pim_sockek_raw: could not raise privs, %s", + safe_strerror (errno) ); + fd = socket(AF_INET, SOCK_RAW, protocol); + + if ( pimd_privs.change (ZPRIVS_LOWER) ) + zlog_err ("pim_socket_raw: could not lower privs, %s", + safe_strerror (errno) ); + if (fd < 0) { zlog_warn("Could not create raw socket: errno=%d: %s", errno, strerror(errno)); diff --git a/vtysh/Makefile.am b/vtysh/Makefile.am index 5c325ec2..f9dea2de 100644 --- a/vtysh/Makefile.am +++ b/vtysh/Makefile.am @@ -25,6 +25,7 @@ vtysh_cmd_FILES = $(top_srcdir)/bgpd/*.c $(top_srcdir)/isisd/*.c \ $(top_srcdir)/ospfd/*.c $(top_srcdir)/ospf6d/*.c \ $(top_srcdir)/ripd/*.c $(top_srcdir)/ripngd/*.c \ $(top_srcdir)/babeld/*.c \ + $(top_srcdir)/pimd/pim_cmd.c \ $(top_srcdir)/lib/keychain.c $(top_srcdir)/lib/routemap.c \ $(top_srcdir)/lib/filter.c $(top_srcdir)/lib/plist.c \ $(top_srcdir)/lib/distribute.c $(top_srcdir)/lib/if_rmap.c \ diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 89b9b257..a5e29dc9 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -59,6 +59,8 @@ struct vtysh_client { .fd = -1, .name = "bgpd", .flag = VTYSH_BGPD, .path = BGP_VTYSH_PATH}, { .fd = -1, .name = "isisd", .flag = VTYSH_ISISD, .path = ISIS_VTYSH_PATH}, { .fd = -1, .name = "babeld", .flag = VTYSH_BABELD, .path = BABEL_VTYSH_PATH}, + { .fd = -1, .name = "pimd", .flag = VTYSH_PIMD, .path = PIM_VTYSH_PATH}, +>>>>>>> [pim] pim commands added to vtysh }; diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h index 3cc7bafe..5d513c8d 100644 --- a/vtysh/vtysh.h +++ b/vtysh/vtysh.h @@ -30,9 +30,9 @@ #define VTYSH_BGPD 0x20 #define VTYSH_ISISD 0x40 #define VTYSH_BABELD 0x80 -#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_BABELD +#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_BABELD|VTYSH_PIMD #define VTYSH_RMAP VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_BABELD -#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_BABELD +#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_BABELD|VTYSH_PIMD /* vtysh local configuration file. */ #define VTYSH_DEFAULT_CONFIG "vtysh.conf" From e96f0af2679e3c91518f62b3a86d811cafba1adc Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Tue, 11 Aug 2009 15:48:02 -0300 Subject: [PATCH 309/482] [pim] Log physical interface up/down [pim] Replace strerror with safe_strerror [pim] Fix PIM socket removal from non-PIM interfaces [pim] show ip igmp querier: left-align Querier string [pim] Version up to 0.157 [pim] Recipe to re-sync with Quagga repository [pim] Build vtysh in development script --- pimd/pim_cmd.c | 42 +++++++++++++++++++------------------- pimd/pim_iface.c | 2 +- pimd/pim_igmp.c | 10 ++++----- pimd/pim_igmpv3.c | 2 +- pimd/pim_mroute.c | 20 +++++++++--------- pimd/pim_pim.c | 12 ++++++++--- pimd/pim_sock.c | 28 ++++++++++++------------- pimd/pim_str.c | 2 +- pimd/pim_time.c | 4 ++-- pimd/pim_version.h | 2 +- pimd/pim_zebra.c | 8 +++++++- pimd/pimd.c | 2 +- pimd/quagga-configure.sh | 2 +- pimd/savannah-git-clone.sh | 11 ++++++++++ 14 files changed, 85 insertions(+), 62 deletions(-) diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 339f9da0..c845a895 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -1084,7 +1084,7 @@ static void igmp_show_querier(struct vty *vty) pim_time_timer_to_hhmmss(query_hhmmss, sizeof(query_hhmmss), igmp->t_igmp_query_timer); pim_time_timer_to_hhmmss(other_hhmmss, sizeof(other_hhmmss), igmp->t_other_querier_timer); - vty_out(vty, "%-9s %-15s %7s %10d %11s %11s%s", + vty_out(vty, "%-9s %-15s %-7s %10d %11s %11s%s", ifp->name, inet_ntoa(igmp->ifaddr), igmp->t_igmp_query_timer ? "THIS" : "OTHER", @@ -1871,7 +1871,7 @@ static void show_multicast_interfaces(struct vty *vty) ifp->name, pim_ifp->mroute_vif_index, e, - strerror(e), + safe_strerror(e), VTY_NEWLINE); continue; } @@ -2047,7 +2047,7 @@ static void show_mroute_count(struct vty *vty) source_str, group_str, e, - strerror(e), + safe_strerror(e), VTY_NEWLINE); continue; } @@ -2093,7 +2093,7 @@ DEFUN (show_ip_route, result = inet_pton(AF_INET, addr_str, &addr); if (result <= 0) { vty_out(vty, "Bad unicast address %s: errno=%d: %s%s", - addr_str, errno, strerror(errno), VTY_NEWLINE); + addr_str, errno, safe_strerror(errno), VTY_NEWLINE); return CMD_WARNING; } @@ -2265,7 +2265,7 @@ DEFUN (interface_ip_igmp_join, result = inet_pton(AF_INET, group_str, &group_addr); if (result <= 0) { vty_out(vty, "Bad group address %s: errno=%d: %s%s", - group_str, errno, strerror(errno), VTY_NEWLINE); + group_str, errno, safe_strerror(errno), VTY_NEWLINE); return CMD_WARNING; } @@ -2274,7 +2274,7 @@ DEFUN (interface_ip_igmp_join, result = inet_pton(AF_INET, source_str, &source_addr); if (result <= 0) { vty_out(vty, "Bad source address %s: errno=%d: %s%s", - source_str, errno, strerror(errno), VTY_NEWLINE); + source_str, errno, safe_strerror(errno), VTY_NEWLINE); return CMD_WARNING; } @@ -2312,7 +2312,7 @@ DEFUN (interface_no_ip_igmp_join, result = inet_pton(AF_INET, group_str, &group_addr); if (result <= 0) { vty_out(vty, "Bad group address %s: errno=%d: %s%s", - group_str, errno, strerror(errno), VTY_NEWLINE); + group_str, errno, safe_strerror(errno), VTY_NEWLINE); return CMD_WARNING; } @@ -2321,7 +2321,7 @@ DEFUN (interface_no_ip_igmp_join, result = inet_pton(AF_INET, source_str, &source_addr); if (result <= 0) { vty_out(vty, "Bad source address %s: errno=%d: %s%s", - source_str, errno, strerror(errno), VTY_NEWLINE); + source_str, errno, safe_strerror(errno), VTY_NEWLINE); return CMD_WARNING; } @@ -3168,7 +3168,7 @@ DEFUN (test_igmp_receive_report, result = inet_pton(AF_INET, grp_str, &grp_addr); if (result <= 0) { vty_out(vty, "Bad group address %s: errno=%d: %s%s", - grp_str, errno, strerror(errno), VTY_NEWLINE); + grp_str, errno, safe_strerror(errno), VTY_NEWLINE); return CMD_WARNING; } @@ -3204,7 +3204,7 @@ DEFUN (test_igmp_receive_report, result = inet_pton(AF_INET, src_str, src_addr); if (result <= 0) { vty_out(vty, "Bad source address %s: errno=%d: %s%s", - src_str, errno, strerror(errno), VTY_NEWLINE); + src_str, errno, safe_strerror(errno), VTY_NEWLINE); return CMD_WARNING; } } @@ -3281,7 +3281,7 @@ DEFUN (test_pim_receive_hello, result = inet_pton(AF_INET, neigh_str, &neigh_addr); if (result <= 0) { vty_out(vty, "Bad neighbor address %s: errno=%d: %s%s", - neigh_str, errno, strerror(errno), VTY_NEWLINE); + neigh_str, errno, safe_strerror(errno), VTY_NEWLINE); return CMD_WARNING; } @@ -3314,7 +3314,7 @@ DEFUN (test_pim_receive_hello, result = inet_pton(AF_INET, sec_str, &sec_addr); if (result <= 0) { vty_out(vty, "Bad neighbor secondary address %s: errno=%d: %s%s", - sec_str, errno, strerror(errno), VTY_NEWLINE); + sec_str, errno, safe_strerror(errno), VTY_NEWLINE); return CMD_WARNING; } @@ -3407,7 +3407,7 @@ DEFUN (test_pim_receive_assert, result = inet_pton(AF_INET, neigh_str, &neigh_addr); if (result <= 0) { vty_out(vty, "Bad neighbor address %s: errno=%d: %s%s", - neigh_str, errno, strerror(errno), VTY_NEWLINE); + neigh_str, errno, safe_strerror(errno), VTY_NEWLINE); return CMD_WARNING; } @@ -3416,7 +3416,7 @@ DEFUN (test_pim_receive_assert, result = inet_pton(AF_INET, group_str, &group_addr); if (result <= 0) { vty_out(vty, "Bad group address %s: errno=%d: %s%s", - group_str, errno, strerror(errno), VTY_NEWLINE); + group_str, errno, safe_strerror(errno), VTY_NEWLINE); return CMD_WARNING; } @@ -3425,7 +3425,7 @@ DEFUN (test_pim_receive_assert, result = inet_pton(AF_INET, source_str, &source_addr); if (result <= 0) { vty_out(vty, "Bad source address %s: errno=%d: %s%s", - source_str, errno, strerror(errno), VTY_NEWLINE); + source_str, errno, safe_strerror(errno), VTY_NEWLINE); return CMD_WARNING; } @@ -3523,7 +3523,7 @@ static int recv_joinprune(struct vty *vty, result = inet_pton(AF_INET, neigh_dst_str, &neigh_dst_addr); if (result <= 0) { vty_out(vty, "Bad neighbor destination address %s: errno=%d: %s%s", - neigh_dst_str, errno, strerror(errno), VTY_NEWLINE); + neigh_dst_str, errno, safe_strerror(errno), VTY_NEWLINE); return CMD_WARNING; } @@ -3532,7 +3532,7 @@ static int recv_joinprune(struct vty *vty, result = inet_pton(AF_INET, neigh_src_str, &neigh_src_addr); if (result <= 0) { vty_out(vty, "Bad neighbor source address %s: errno=%d: %s%s", - neigh_src_str, errno, strerror(errno), VTY_NEWLINE); + neigh_src_str, errno, safe_strerror(errno), VTY_NEWLINE); return CMD_WARNING; } @@ -3541,7 +3541,7 @@ static int recv_joinprune(struct vty *vty, result = inet_pton(AF_INET, group_str, &group_addr); if (result <= 0) { vty_out(vty, "Bad group address %s: errno=%d: %s%s", - group_str, errno, strerror(errno), VTY_NEWLINE); + group_str, errno, safe_strerror(errno), VTY_NEWLINE); return CMD_WARNING; } @@ -3550,7 +3550,7 @@ static int recv_joinprune(struct vty *vty, result = inet_pton(AF_INET, source_str, &source_addr); if (result <= 0) { vty_out(vty, "Bad source address %s: errno=%d: %s%s", - source_str, errno, strerror(errno), VTY_NEWLINE); + source_str, errno, safe_strerror(errno), VTY_NEWLINE); return CMD_WARNING; } @@ -3740,7 +3740,7 @@ DEFUN (test_pim_receive_upcall, result = inet_pton(AF_INET, group_str, &msg.im_dst); if (result <= 0) { vty_out(vty, "Bad group address %s: errno=%d: %s%s", - group_str, errno, strerror(errno), VTY_NEWLINE); + group_str, errno, safe_strerror(errno), VTY_NEWLINE); return CMD_WARNING; } @@ -3749,7 +3749,7 @@ DEFUN (test_pim_receive_upcall, result = inet_pton(AF_INET, source_str, &msg.im_src); if (result <= 0) { vty_out(vty, "Bad source address %s: errno=%d: %s%s", - source_str, errno, strerror(errno), VTY_NEWLINE); + source_str, errno, safe_strerror(errno), VTY_NEWLINE); return CMD_WARNING; } diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index b4fbaec5..b3df0189 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -1020,7 +1020,7 @@ int pim_if_igmp_join_del(struct interface *ifp, pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); zlog_warn("%s: failure closing sock_fd=%d for IGMP group %s source %s on interface %s: errno=%d: %s", __PRETTY_FUNCTION__, - ij->sock_fd, group_str, source_str, ifp->name, e, strerror(e)); + ij->sock_fd, group_str, source_str, ifp->name, e, safe_strerror(e)); /* warning only */ } listnode_delete(pim_ifp->igmp_join_list, ij); diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c index e38ac96a..b8f25814 100644 --- a/pimd/pim_igmp.c +++ b/pimd/pim_igmp.c @@ -65,7 +65,7 @@ static int igmp_sock_open(struct in_addr ifaddr, int ifindex, uint32_t pim_optio else { zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s", __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr), - PIM_ALL_ROUTERS, errno, strerror(errno)); + PIM_ALL_ROUTERS, errno, safe_strerror(errno)); } } @@ -80,7 +80,7 @@ static int igmp_sock_open(struct in_addr ifaddr, int ifindex, uint32_t pim_optio else { zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s", __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr), - PIM_ALL_SYSTEMS, errno, strerror(errno)); + PIM_ALL_SYSTEMS, errno, safe_strerror(errno)); } if (inet_aton(PIM_ALL_IGMP_ROUTERS, &group)) { @@ -91,7 +91,7 @@ static int igmp_sock_open(struct in_addr ifaddr, int ifindex, uint32_t pim_optio else { zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s", __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr), - PIM_ALL_IGMP_ROUTERS, errno, strerror(errno)); + PIM_ALL_IGMP_ROUTERS, errno, safe_strerror(errno)); } if (!join) { @@ -949,7 +949,7 @@ static int pim_igmp_read(struct thread *t) &ifindex); if (len < 0) { zlog_warn("Failure receiving IP IGMP packet on fd=%d: errno=%d: %s", - fd, errno, strerror(errno)); + fd, errno, safe_strerror(errno)); goto done; } @@ -1023,7 +1023,7 @@ static void sock_close(struct igmp_sock *igmp) if (close(igmp->fd)) { zlog_err("Failure closing IGMP socket %s fd=%d on interface %s: errno=%d: %s", inet_ntoa(igmp->ifaddr), igmp->fd, igmp->interface->name, - errno, strerror(errno)); + errno, safe_strerror(errno)); } if (PIM_DEBUG_IGMP_TRACE) { diff --git a/pimd/pim_igmpv3.c b/pimd/pim_igmpv3.c index f9fa123f..3ff04c91 100644 --- a/pimd/pim_igmpv3.c +++ b/pimd/pim_igmpv3.c @@ -1679,7 +1679,7 @@ void pim_igmp_send_membership_query(struct igmp_group *group, zlog_warn("%s: sendto() failure to %s on %s: group=%s msg_size=%d: errno=%d: %s", __PRETTY_FUNCTION__, dst_str, ifname, group_str, msg_size, - e, strerror(e)); + e, safe_strerror(e)); } else { zlog_warn("%s: sendto() partial to %s on %s: group=%s msg_size=%d: sent=%d", diff --git a/pimd/pim_mroute.c b/pimd/pim_mroute.c index 48213b0b..c76ba525 100644 --- a/pimd/pim_mroute.c +++ b/pimd/pim_mroute.c @@ -47,7 +47,7 @@ static int pim_mroute_set(int fd, int enable) int e = errno; zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,%s=%d): errno=%d: %s", __FILE__, __PRETTY_FUNCTION__, - fd, enable ? "MRT_INIT" : "MRT_DONE", opt, e, strerror(e)); + fd, enable ? "MRT_INIT" : "MRT_DONE", opt, e, safe_strerror(e)); errno = e; return -1; } @@ -209,7 +209,7 @@ static int mroute_read_msg(int fd) rd = read(fd, buf, sizeof(buf)); if (rd < 0) { zlog_warn("%s: failure reading fd=%d: errno=%d: %s", - __PRETTY_FUNCTION__, fd, errno, strerror(errno)); + __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno)); return -2; } @@ -275,13 +275,13 @@ int pim_mroute_socket_enable() if (fd < 0) { zlog_warn("Could not create mroute socket: errno=%d: %s", - errno, strerror(errno)); + errno, safe_strerror(errno)); return -2; } if (pim_mroute_set(fd, 1)) { zlog_warn("Could not enable mroute on socket fd=%d: errno=%d: %s", - fd, errno, strerror(errno)); + fd, errno, safe_strerror(errno)); close(fd); return -3; } @@ -302,13 +302,13 @@ int pim_mroute_socket_disable() if (pim_mroute_set(qpim_mroute_socket_fd, 0)) { zlog_warn("Could not disable mroute on socket fd=%d: errno=%d: %s", - qpim_mroute_socket_fd, errno, strerror(errno)); + qpim_mroute_socket_fd, errno, safe_strerror(errno)); return -2; } if (close(qpim_mroute_socket_fd)) { zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s", - qpim_mroute_socket_fd, errno, strerror(errno)); + qpim_mroute_socket_fd, errno, safe_strerror(errno)); return -3; } @@ -359,7 +359,7 @@ int pim_mroute_add_vif(int vif_index, struct in_addr ifaddr) zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s): errno=%d: %s", __FILE__, __PRETTY_FUNCTION__, qpim_mroute_socket_fd, vif_index, ifaddr_str, - e, strerror(e)); + e, safe_strerror(e)); errno = e; return -2; } @@ -387,7 +387,7 @@ int pim_mroute_del_vif(int vif_index) zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s", __FILE__, __PRETTY_FUNCTION__, qpim_mroute_socket_fd, vif_index, - e, strerror(e)); + e, safe_strerror(e)); errno = e; return -2; } @@ -412,7 +412,7 @@ int pim_mroute_add(struct mfcctl *mc) zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s", __FILE__, __PRETTY_FUNCTION__, qpim_mroute_socket_fd, - e, strerror(e)); + e, safe_strerror(e)); errno = e; return -2; } @@ -436,7 +436,7 @@ int pim_mroute_del(struct mfcctl *mc) zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s", __FILE__, __PRETTY_FUNCTION__, qpim_mroute_socket_fd, - e, strerror(e)); + e, safe_strerror(e)); errno = e; return -2; } diff --git a/pimd/pim_pim.c b/pimd/pim_pim.c index bbf67763..dd78b904 100644 --- a/pimd/pim_pim.c +++ b/pimd/pim_pim.c @@ -74,7 +74,7 @@ static void sock_close(struct interface *ifp) if (close(pim_ifp->pim_sock_fd)) { zlog_warn("Failure closing PIM socket fd=%d on interface %s: errno=%d: %s", pim_ifp->pim_sock_fd, ifp->name, - errno, strerror(errno)); + errno, safe_strerror(errno)); } pim_ifp->pim_sock_fd = -1; @@ -91,6 +91,12 @@ void pim_sock_delete(struct interface *ifp, const char *delete_message) zlog_info("PIM INTERFACE DOWN: on interface %s: %s", ifp->name, delete_message); + if (!ifp->info) { + zlog_err("%s: %s: but PIM not enabled on interface %s (!)", + __PRETTY_FUNCTION__, delete_message, ifp->name); + return; + } + /* RFC 4601: 4.3.1. Sending Hello Messages @@ -266,7 +272,7 @@ static int pim_sock_read(struct thread *t) &ifindex); if (len < 0) { zlog_warn("Failure receiving IP PIM packet on fd=%d: errno=%d: %s", - fd, errno, strerror(errno)); + fd, errno, safe_strerror(errno)); goto done; } @@ -459,7 +465,7 @@ int pim_msg_send(int fd, zlog_warn("%s: sendto() failure to %s on %s: fd=%d msg_size=%d: errno=%d: %s", __PRETTY_FUNCTION__, dst_str, ifname, fd, pim_msg_size, - e, strerror(e)); + e, safe_strerror(e)); } else { zlog_warn("%s: sendto() partial to %s on %s: fd=%d msg_size=%d: sent=%d", diff --git a/pimd/pim_sock.c b/pimd/pim_sock.c index 9f346411..8e6c559a 100644 --- a/pimd/pim_sock.c +++ b/pimd/pim_sock.c @@ -68,7 +68,7 @@ int pim_socket_raw(int protocol) if (fd < 0) { zlog_warn("Could not create raw socket: errno=%d: %s", - errno, strerror(errno)); + errno, safe_strerror(errno)); return PIM_SOCK_ERR_SOCKET; } @@ -82,7 +82,7 @@ int pim_socket_mcast(int protocol, struct in_addr ifaddr, int loop) fd = pim_socket_raw(protocol); if (fd < 0) { zlog_warn("Could not create multicast socket: errno=%d: %s", - errno, strerror(errno)); + errno, safe_strerror(errno)); return PIM_SOCK_ERR_SOCKET; } @@ -93,14 +93,14 @@ int pim_socket_mcast(int protocol, struct in_addr ifaddr, int loop) int opt = 1; if (setsockopt(fd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt))) { zlog_warn("Could not set IP_PKTINFO on socket fd=%d: errno=%d: %s", - fd, errno, strerror(errno)); + fd, errno, safe_strerror(errno)); } #elif defined(HAVE_IP_RECVDSTADDR) /* BSD IP_RECVDSTADDR */ int opt = 1; if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt))) { zlog_warn("Could not set IP_RECVDSTADDR on socket fd=%d: errno=%d: %s", - fd, errno, strerror(errno)); + fd, errno, safe_strerror(errno)); } #else zlog_err("%s %s: Missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()", @@ -120,7 +120,7 @@ int pim_socket_mcast(int protocol, struct in_addr ifaddr, int loop) ra[3] = 0; if (setsockopt(fd, IPPROTO_IP, IP_OPTIONS, ra, 4)) { zlog_warn("Could not set Router Alert Option on socket fd=%d: errno=%d: %s", - fd, errno, strerror(errno)); + fd, errno, safe_strerror(errno)); close(fd); return PIM_SOCK_ERR_RA; } @@ -131,7 +131,7 @@ int pim_socket_mcast(int protocol, struct in_addr ifaddr, int loop) if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *) &reuse, sizeof(reuse))) { zlog_warn("Could not set Reuse Address Option on socket fd=%d: errno=%d: %s", - fd, errno, strerror(errno)); + fd, errno, safe_strerror(errno)); close(fd); return PIM_SOCK_ERR_REUSE; } @@ -143,7 +143,7 @@ int pim_socket_mcast(int protocol, struct in_addr ifaddr, int loop) if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (void *) &ttl, sizeof(ttl))) { zlog_warn("Could not set multicast TTL=%d on socket fd=%d: errno=%d: %s", - MTTL, fd, errno, strerror(errno)); + MTTL, fd, errno, safe_strerror(errno)); close(fd); return PIM_SOCK_ERR_TTL; } @@ -153,7 +153,7 @@ int pim_socket_mcast(int protocol, struct in_addr ifaddr, int loop) (void *) &loop, sizeof(loop))) { zlog_warn("Could not %s Multicast Loopback Option on socket fd=%d: errno=%d: %s", loop ? "enable" : "disable", - fd, errno, strerror(errno)); + fd, errno, safe_strerror(errno)); close(fd); return PIM_SOCK_ERR_LOOP; } @@ -161,7 +161,7 @@ int pim_socket_mcast(int protocol, struct in_addr ifaddr, int loop) if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, (void *) &ifaddr, sizeof(ifaddr))) { zlog_warn("Could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s", - fd, errno, strerror(errno)); + fd, errno, safe_strerror(errno)); close(fd); return PIM_SOCK_ERR_IFACE; } @@ -172,14 +172,14 @@ int pim_socket_mcast(int protocol, struct in_addr ifaddr, int loop) flags = fcntl(fd, F_GETFL, 0); if (flags < 0) { zlog_warn("Could not get fcntl(F_GETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s", - fd, errno, strerror(errno)); + fd, errno, safe_strerror(errno)); close(fd); return PIM_SOCK_ERR_NONBLOCK_GETFL; } if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) { zlog_warn("Could not set fcntl(F_SETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s", - fd, errno, strerror(errno)); + fd, errno, safe_strerror(errno)); close(fd); return PIM_SOCK_ERR_NONBLOCK_SETFL; } @@ -218,7 +218,7 @@ int pim_socket_join(int fd, struct in_addr group, sprintf(ifaddr_str, ""); zlog_err("Failure socket joining fd=%d group %s on interface address %s: errno=%d: %s", - fd, group_str, ifaddr_str, errno, strerror(errno)); + fd, group_str, ifaddr_str, errno, safe_strerror(errno)); return ret; } @@ -268,7 +268,7 @@ int pim_socket_join_source(int fd, int ifindex, zlog_warn("%s: setsockopt(fd=%d) failure for IGMP group %s source %s ifindex %d on interface %s: errno=%d: %s", __PRETTY_FUNCTION__, fd, group_str, source_str, ifindex, ifname, - e, strerror(e)); + e, safe_strerror(e)); return -1; } @@ -352,7 +352,7 @@ int pim_socket_mcastloop_get(int fd) &loop, &loop_len)) { int e = errno; zlog_warn("Could not get Multicast Loopback Option on socket fd=%d: errno=%d: %s", - fd, errno, strerror(errno)); + fd, errno, safe_strerror(errno)); errno = e; return PIM_SOCK_ERR_LOOP; } diff --git a/pimd/pim_str.c b/pimd/pim_str.c index 7dce7a85..af5a184d 100644 --- a/pimd/pim_str.c +++ b/pimd/pim_str.c @@ -37,7 +37,7 @@ void pim_inet4_dump(const char *onfail, struct in_addr addr, char *buf, int buf_ if (!inet_ntop(AF_INET, &addr, buf, buf_size)) { int e = errno; zlog_warn("pim_inet4_dump: inet_ntop(AF_INET,buf_size=%d): errno=%d: %s", - buf_size, e, strerror(e)); + buf_size, e, safe_strerror(e)); if (onfail) snprintf(buf, buf_size, "%s", onfail); } diff --git a/pimd/pim_time.c b/pimd/pim_time.c index a837e5b0..63861e5c 100644 --- a/pimd/pim_time.c +++ b/pimd/pim_time.c @@ -49,7 +49,7 @@ int64_t pim_time_monotonic_sec() if (pim_gettime(CLOCK_MONOTONIC, &now_tv)) { zlog_err("%s: gettime(CLOCK_MONOTONIC) failure: errno=%d: %s", __PRETTY_FUNCTION__, - errno, strerror(errno)); + errno, safe_strerror(errno)); return -1; } @@ -68,7 +68,7 @@ int64_t pim_time_monotonic_dsec() if (pim_gettime(CLOCK_MONOTONIC, &now_tv)) { zlog_err("%s: gettime(CLOCK_MONOTONIC) failure: errno=%d: %s", __PRETTY_FUNCTION__, - errno, strerror(errno)); + errno, safe_strerror(errno)); return -1; } diff --git a/pimd/pim_version.h b/pimd/pim_version.h index 796db663..fc5bca2f 100644 --- a/pimd/pim_version.h +++ b/pimd/pim_version.h @@ -23,7 +23,7 @@ #ifndef PIM_VERSION_H #define PIM_VERSION_H -#define PIMD_VERSION_STR "0.155" +#define PIMD_VERSION_STR "0.157" const char * const PIMD_VERSION; diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index d03cc540..56e4dba1 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -127,6 +127,8 @@ static int pim_zebra_if_state_up(int command, struct zclient *zclient, if (!ifp) return 0; + zlog_info("INTERFACE UP: %s", ifp->name); + if (PIM_DEBUG_ZEBRA) { zlog_debug("%s: %s index %d flags %ld metric %d mtu %d operative %d", __PRETTY_FUNCTION__, @@ -157,6 +159,8 @@ static int pim_zebra_if_state_down(int command, struct zclient *zclient, if (!ifp) return 0; + zlog_info("INTERFACE DOWN: %s", ifp->name); + if (PIM_DEBUG_ZEBRA) { zlog_debug("%s: %s index %d flags %ld metric %d mtu %d operative %d", __PRETTY_FUNCTION__, @@ -175,7 +179,9 @@ static int pim_zebra_if_state_down(int command, struct zclient *zclient, pim_sock_delete() closes the socket, stops read and timer threads, and kills all neighbors. */ - pim_sock_delete(ifp, "link down"); + if (ifp->info) { + pim_sock_delete(ifp, "link down"); + } } return 0; diff --git a/pimd/pimd.c b/pimd/pimd.c index cbcd0648..f255a28f 100644 --- a/pimd/pimd.c +++ b/pimd/pimd.c @@ -72,7 +72,7 @@ void pim_init() if (!inet_aton(PIM_ALL_PIM_ROUTERS, &qpim_all_pim_routers_addr)) { zlog_err("%s %s: could not solve %s to group address: errno=%d: %s", __FILE__, __PRETTY_FUNCTION__, - PIM_ALL_PIM_ROUTERS, errno, strerror(errno)); + PIM_ALL_PIM_ROUTERS, errno, safe_strerror(errno)); zassert(0); return; } diff --git a/pimd/quagga-configure.sh b/pimd/quagga-configure.sh index a1cdd587..45d7be08 100755 --- a/pimd/quagga-configure.sh +++ b/pimd/quagga-configure.sh @@ -7,4 +7,4 @@ # # $QuaggaId: $Format:%an, %ai, %h$ $ -./configure --disable-bgpd --disable-ripd --disable-ripngd --disable-ospfd --disable-ospf6d --disable-watchquagga --disable-bgp-announce --disable-ospfapi --disable-ospfclient --disable-rtadv --disable-irdp --enable-pimd --enable-tcp-zebra --enable-ipv6 +./configure --disable-bgpd --disable-ripd --disable-ripngd --disable-ospfd --disable-ospf6d --disable-watchquagga --disable-bgp-announce --disable-ospfapi --disable-ospfclient --disable-rtadv --disable-irdp --enable-pimd --enable-tcp-zebra --enable-ipv6 --enable-vtysh diff --git a/pimd/savannah-git-clone.sh b/pimd/savannah-git-clone.sh index 68fd608b..1aad51bb 100755 --- a/pimd/savannah-git-clone.sh +++ b/pimd/savannah-git-clone.sh @@ -11,6 +11,17 @@ # Commit changes: git commit -a # Send changes: git push --all # +# Recipe to re-sync with Quagga repository: +# git clone ssh://evertonm@git.sv.gnu.org/srv/git/qpimd.git quagga +# cd quagga +# git checkout master +# git pull git://code.quagga.net/quagga.git master +# git checkout -b pim origin/pim +# git rebase master pim +# # Test, then push back into Savannah repository: +# git push origin :pim ;# delete remote branch pim +# git push --all +# # $QuaggaId: $Format:%an, %ai, %h$ $ git clone ssh://evertonm@git.sv.gnu.org/srv/git/qpimd.git quagga From 236b01556122fba479118797163c44849073ff46 Mon Sep 17 00:00:00 2001 From: Leonard Herve Date: Tue, 11 Aug 2009 15:51:52 -0300 Subject: [PATCH 310/482] [pim] igmpv3: specific query interval set to 1 second (RFC 3376 8.8.) [pim] pim messages: encoded source address format with Sparse bit=1 (RFC 4601 4.9.1.) [pim] and Mask Len MUST be equal to 32 [pim] dr election: new traces [pim] fix triggered_hello_delay_msec randomization --- pimd/pim_iface.c | 7 ++++--- pimd/pim_iface.h | 11 ++++++----- pimd/pim_igmp.h | 9 ++++++--- pimd/pim_igmpv3.c | 18 +++++++++--------- pimd/pim_join.c | 26 ++++++++++++++++++++++++++ pimd/pim_msg.c | 2 +- pimd/pim_neighbor.c | 34 +++++++++++++++++++++++++++------- pimd/pim_pim.c | 2 +- 8 files changed, 80 insertions(+), 29 deletions(-) diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index b3df0189..009f87c1 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -85,9 +85,10 @@ struct pim_interface *pim_if_new(struct interface *ifp, int igmp, int pim) pim_ifp->options = 0; pim_ifp->mroute_vif_index = -1; - pim_ifp->igmp_default_robustness_variable = IGMP_DEFAULT_ROBUSTNESS_VARIABLE; - pim_ifp->igmp_default_query_interval = IGMP_GENERAL_QUERY_INTERVAL; - pim_ifp->igmp_query_max_response_time_dsec = IGMP_QUERY_MAX_RESPONSE_TIME_DSEC; + pim_ifp->igmp_default_robustness_variable = IGMP_DEFAULT_ROBUSTNESS_VARIABLE; + pim_ifp->igmp_default_query_interval = IGMP_GENERAL_QUERY_INTERVAL; + pim_ifp->igmp_query_max_response_time_dsec = IGMP_QUERY_MAX_RESPONSE_TIME_DSEC; + pim_ifp->igmp_specific_query_max_response_time_dsec = IGMP_SPECIFIC_QUERY_MAX_RESPONSE_TIME_DSEC; /* RFC 3376: 8.3. Query Response Interval diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h index 6ce866ba..bc99aafb 100644 --- a/pimd/pim_iface.h +++ b/pimd/pim_iface.h @@ -57,11 +57,12 @@ struct pim_interface { int mroute_vif_index; struct in_addr primary_address; /* remember addr to detect change */ - int igmp_default_robustness_variable; /* IGMPv3 QRV */ - int igmp_default_query_interval; /* IGMPv3 secs between general queries */ - int igmp_query_max_response_time_dsec; /* IGMPv3 Max Response Time in dsecs */ - struct list *igmp_socket_list; /* list of struct igmp_sock */ - struct list *igmp_join_list; /* list of struct igmp_join */ + int igmp_default_robustness_variable; /* IGMPv3 QRV */ + int igmp_default_query_interval; /* IGMPv3 secs between general queries */ + int igmp_query_max_response_time_dsec; /* IGMPv3 Max Response Time in dsecs for general queries */ + int igmp_specific_query_max_response_time_dsec; /* IGMPv3 Max Response Time in dsecs for specific queries */ + struct list *igmp_socket_list; /* list of struct igmp_sock */ + struct list *igmp_join_list; /* list of struct igmp_join */ int pim_sock_fd; /* PIM socket file descriptor */ struct thread *t_pim_sock_read; /* thread for reading PIM socket */ diff --git a/pimd/pim_igmp.h b/pimd/pim_igmp.h index 656147f2..95e60bf2 100644 --- a/pimd/pim_igmp.h +++ b/pimd/pim_igmp.h @@ -55,13 +55,16 @@ #define IGMP_V3_GROUP_RECORD_SOURCE_OFFSET (8) /* RFC 3376: 8.1. Robustness Variable - Default: 2 */ -#define IGMP_DEFAULT_ROBUSTNESS_VARIABLE (2) +#define IGMP_DEFAULT_ROBUSTNESS_VARIABLE (2) /* RFC 3376: 8.2. Query Interval - Default: 125 seconds */ -#define IGMP_GENERAL_QUERY_INTERVAL (125) +#define IGMP_GENERAL_QUERY_INTERVAL (125) /* RFC 3376: 8.3. Query Response Interval - Default: 100 deciseconds */ -#define IGMP_QUERY_MAX_RESPONSE_TIME_DSEC (100) +#define IGMP_QUERY_MAX_RESPONSE_TIME_DSEC (100) + +/* RFC 3376: 8.8. Last Member Query Interval - Default: 10 deciseconds */ +#define IGMP_SPECIFIC_QUERY_MAX_RESPONSE_TIME_DSEC (10) struct igmp_join { struct in_addr group_addr; diff --git a/pimd/pim_igmpv3.c b/pimd/pim_igmpv3.c index 3ff04c91..3d3ee7ac 100644 --- a/pimd/pim_igmpv3.c +++ b/pimd/pim_igmpv3.c @@ -1006,7 +1006,7 @@ static void group_retransmit_group(struct igmp_group *group) pim_ifp = igmp->interface->info; lmqc = igmp->querier_robustness_variable; - lmqi_msec = 100 * pim_ifp->igmp_query_max_response_time_dsec; + lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec; lmqt_msec = lmqc * lmqi_msec; /* @@ -1042,7 +1042,7 @@ static void group_retransmit_group(struct igmp_group *group) 0 /* num_sources_tosend */, group->group_addr /* dst_addr */, group->group_addr /* group_addr */, - pim_ifp->igmp_query_max_response_time_dsec, + pim_ifp->igmp_specific_query_max_response_time_dsec, s_flag, igmp->querier_robustness_variable, igmp->querier_query_interval); @@ -1090,7 +1090,7 @@ static int group_retransmit_sources(struct igmp_group *group, pim_ifp = igmp->interface->info; lmqc = igmp->querier_robustness_variable; - lmqi_msec = 100 * pim_ifp->igmp_query_max_response_time_dsec; + lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec; lmqt_msec = lmqc * lmqi_msec; /* Scan all group sources */ @@ -1163,7 +1163,7 @@ static int group_retransmit_sources(struct igmp_group *group, num_sources_tosend1, group->group_addr, group->group_addr, - pim_ifp->igmp_query_max_response_time_dsec, + pim_ifp->igmp_specific_query_max_response_time_dsec, 1 /* s_flag */, igmp->querier_robustness_variable, igmp->querier_query_interval); @@ -1205,7 +1205,7 @@ static int group_retransmit_sources(struct igmp_group *group, num_sources_tosend2, group->group_addr, group->group_addr, - pim_ifp->igmp_query_max_response_time_dsec, + pim_ifp->igmp_specific_query_max_response_time_dsec, 0 /* s_flag */, igmp->querier_robustness_variable, igmp->querier_query_interval); @@ -1290,7 +1290,7 @@ static void group_retransmit_timer_on(struct igmp_group *group) igmp = group->group_igmp_sock; pim_ifp = igmp->interface->info; - lmqi_msec = 100 * pim_ifp->igmp_query_max_response_time_dsec; + lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec; if (PIM_DEBUG_IGMP_TRACE) { char group_str[100]; @@ -1359,7 +1359,7 @@ static void source_query_send_by_flag(struct igmp_group *group, pim_ifp = igmp->interface->info; lmqc = igmp->querier_robustness_variable; - lmqi_msec = 100 * pim_ifp->igmp_query_max_response_time_dsec; + lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec; lmqt_msec = lmqc * lmqi_msec; /* @@ -1519,7 +1519,7 @@ void igmp_group_timer_lower_to_lmqt(struct igmp_group *group) pim_ifp = ifp->info; ifname = ifp->name; - lmqi_dsec = pim_ifp->igmp_query_max_response_time_dsec; + lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec; lmqc = igmp->querier_robustness_variable; lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */ @@ -1554,7 +1554,7 @@ void igmp_source_timer_lower_to_lmqt(struct igmp_source *source) pim_ifp = ifp->info; ifname = ifp->name; - lmqi_dsec = pim_ifp->igmp_query_max_response_time_dsec; + lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec; lmqc = igmp->querier_robustness_variable; lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */ diff --git a/pimd/pim_join.c b/pimd/pim_join.c index ce4ec4e6..6b46759a 100644 --- a/pimd/pim_join.c +++ b/pimd/pim_join.c @@ -239,6 +239,19 @@ int pim_joinprune_recv(struct interface *ifp, if (addr_offset < 1) { return -7; } + + /* + RFC 4601: 4.9.1 Encoded Source and Group Address Formats + + Encoded-Source Address + (...) + The mask length MUST be equal to the mask length in bits for the + given Address Family and Encoding Type (32 for IPv4 native and + 128 for IPv6 native). A router SHOULD ignore any messages + received with any other mask length. + */ + if (msg_source_addr.prefixlen!=32) return; + buf += addr_offset; recv_join(ifp, neigh, msg_holdtime, @@ -257,6 +270,19 @@ int pim_joinprune_recv(struct interface *ifp, if (addr_offset < 1) { return -8; } + + /* + RFC 4601: 4.9.1 Encoded Source and Group Address Formats + + Encoded-Source Address + (...) + The mask length MUST be equal to the mask length in bits for the + given Address Family and Encoding Type (32 for IPv4 native and + 128 for IPv6 native). A router SHOULD ignore any messages + received with any other mask length. + */ + if (msg_source_addr.prefixlen!=32) return; + buf += addr_offset; recv_prune(ifp, neigh, msg_holdtime, diff --git a/pimd/pim_msg.c b/pimd/pim_msg.c index 76f78f8a..1105eaca 100644 --- a/pimd/pim_msg.c +++ b/pimd/pim_msg.c @@ -98,7 +98,7 @@ char *pim_msg_addr_encode_ipv4_source(char *buf, buf[0] = PIM_MSG_ADDRESS_FAMILY_IPV4; /* addr family */ buf[1] = '\0'; /* native encoding */ - buf[2] = '\0'; /* reserved */ + buf[2] = 4; /* reserved = 0 | S bit = 1 | W bit = 0 | R bit = 0 */ buf[3] = 32; /* mask len */ *(struct in_addr *)(buf + 4) = addr; diff --git a/pimd/pim_neighbor.c b/pimd/pim_neighbor.c index 67aa9d08..b4112edf 100644 --- a/pimd/pim_neighbor.c +++ b/pimd/pim_neighbor.c @@ -46,6 +46,10 @@ static void dr_election_by_addr(struct interface *ifp) pim_ifp->pim_dr_addr = pim_ifp->primary_address; + zlog_info("%s on interface %s", + __PRETTY_FUNCTION__, + ifp->name); + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, node, neigh)) { if (ntohl(neigh->source_addr.s_addr) > ntohl(pim_ifp->pim_dr_addr.s_addr)) { pim_ifp->pim_dr_addr = neigh->source_addr; @@ -66,7 +70,15 @@ static void dr_election_by_pri(struct interface *ifp) pim_ifp->pim_dr_addr = pim_ifp->primary_address; dr_pri = pim_ifp->pim_dr_priority; + zlog_info("%s: dr pri %u on interface %s", + __PRETTY_FUNCTION__, + dr_pri, ifp->name); + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, node, neigh)) { + zlog_info("%s: neigh pri %u addr %x if dr addr %x", + __PRETTY_FUNCTION__, + neigh->dr_priority, ntohl(neigh->source_addr.s_addr) , ntohl(pim_ifp->pim_dr_addr.s_addr) ); if ( (neigh->dr_priority > dr_pri) || ( @@ -91,6 +103,8 @@ void pim_if_dr_election(struct interface *ifp) { struct pim_interface *pim_ifp = ifp->info; struct in_addr old_dr_addr; + char dr_old_str[100]; + char dr_new_str[100]; pim_ifp->pim_dr_election_last = pim_time_monotonic_sec(); /* timestamp */ ++pim_ifp->pim_dr_election_count; @@ -104,16 +118,22 @@ void pim_if_dr_election(struct interface *ifp) dr_election_by_pri(ifp); } - /* DR changed ? */ - if (old_dr_addr.s_addr != pim_ifp->pim_dr_addr.s_addr) { - char dr_old_str[100]; - char dr_new_str[100]; - pim_inet4_dump("", old_dr_addr, dr_old_str, sizeof(dr_old_str)); - pim_inet4_dump("", pim_ifp->pim_dr_addr, dr_new_str, sizeof(dr_new_str)); - zlog_info("%s: DR was %s now is %s on interface %s", + pim_inet4_dump("", old_dr_addr, dr_old_str, sizeof(dr_old_str)); + pim_inet4_dump("", pim_ifp->pim_dr_addr, dr_new_str, sizeof(dr_new_str)); + zlog_info("%s: DR was %s now is %s on interface %s", __PRETTY_FUNCTION__, dr_old_str, dr_new_str, ifp->name); + /* DR changed ? */ + if (old_dr_addr.s_addr != pim_ifp->pim_dr_addr.s_addr) { + /* char dr_old_str[100]; */ + /* char dr_new_str[100]; */ + /* pim_inet4_dump("", old_dr_addr, dr_old_str, sizeof(dr_old_str)); */ + /* pim_inet4_dump("", pim_ifp->pim_dr_addr, dr_new_str, sizeof(dr_new_str)); */ + /* zlog_info("%s: DR was %s now is %s on interface %s", */ + /* __PRETTY_FUNCTION__, */ + /* dr_old_str, dr_new_str, ifp->name); */ + pim_if_update_join_desired(pim_ifp); pim_if_update_could_assert(ifp); pim_if_update_assert_tracking_desired(ifp); diff --git a/pimd/pim_pim.c b/pimd/pim_pim.c index dd78b904..9b6dc359 100644 --- a/pimd/pim_pim.c +++ b/pimd/pim_pim.c @@ -674,7 +674,7 @@ void pim_hello_restart_triggered(struct interface *ifp) THREAD_TIMER_MSEC_ON(master, pim_ifp->t_pim_hello_timer, on_pim_hello_send, - ifp, triggered_hello_delay_msec); + ifp, random_msec); } int pim_sock_add(struct interface *ifp) From d12beab1b9ce09c50672adb3c980e64ccd11edb4 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Wed, 12 Aug 2009 10:52:22 -0300 Subject: [PATCH 311/482] [pim] Move encoded source address length check to pim_parse_addr_source --- pimd/pim_igmp.c | 2 +- pimd/pim_join.c | 24 ------------------------ pimd/pim_tlv.c | 20 +++++++++++++++++++- 3 files changed, 20 insertions(+), 26 deletions(-) diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c index b8f25814..82db37f6 100644 --- a/pimd/pim_igmp.c +++ b/pimd/pim_igmp.c @@ -392,7 +392,7 @@ static int recv_igmp_query(struct igmp_sock *igmp, int query_version, /* this is a general query */ /* log that general query should have the s_flag set */ - zlog_warn("General IGMP query v%d from %s on %s: Router-Side Processing flag is clear", + zlog_warn("General IGMP query v%d from %s on %s: Suppress Router-Side Processing flag is clear", query_version, from_str, ifp->name); } else { diff --git a/pimd/pim_join.c b/pimd/pim_join.c index 6b46759a..aa3aa789 100644 --- a/pimd/pim_join.c +++ b/pimd/pim_join.c @@ -240,18 +240,6 @@ int pim_joinprune_recv(struct interface *ifp, return -7; } - /* - RFC 4601: 4.9.1 Encoded Source and Group Address Formats - - Encoded-Source Address - (...) - The mask length MUST be equal to the mask length in bits for the - given Address Family and Encoding Type (32 for IPv4 native and - 128 for IPv6 native). A router SHOULD ignore any messages - received with any other mask length. - */ - if (msg_source_addr.prefixlen!=32) return; - buf += addr_offset; recv_join(ifp, neigh, msg_holdtime, @@ -271,18 +259,6 @@ int pim_joinprune_recv(struct interface *ifp, return -8; } - /* - RFC 4601: 4.9.1 Encoded Source and Group Address Formats - - Encoded-Source Address - (...) - The mask length MUST be equal to the mask length in bits for the - given Address Family and Encoding Type (32 for IPv4 native and - 128 for IPv6 native). A router SHOULD ignore any messages - received with any other mask length. - */ - if (msg_source_addr.prefixlen!=32) return; - buf += addr_offset; recv_prune(ifp, neigh, msg_holdtime, diff --git a/pimd/pim_tlv.c b/pimd/pim_tlv.c index c578a70d..fc48c888 100644 --- a/pimd/pim_tlv.c +++ b/pimd/pim_tlv.c @@ -559,6 +559,24 @@ int pim_parse_addr_source(const char *ifname, p->u.prefix4 = *(const struct in_addr *) addr; p->prefixlen = mask_len; + /* + RFC 4601: 4.9.1 Encoded Source and Group Address Formats + + Encoded-Source Address + + The mask length MUST be equal to the mask length in bits for + the given Address Family and Encoding Type (32 for IPv4 native + and 128 for IPv6 native). A router SHOULD ignore any messages + received with any other mask length. + */ + if (p->prefixlen != 32) { + char src_str[100]; + pim_inet4_dump("", p->u.prefix4, src_str, sizeof(src_str)); + zlog_warn("%s: IPv4 bad source address mask: %s/%d", + __PRETTY_FUNCTION__, src_str, p->prefixlen); + return -4; + } + addr += sizeof(struct in_addr); break; @@ -569,7 +587,7 @@ int pim_parse_addr_source(const char *ifname, zlog_warn("%s: unknown source address encoding family=%d from %s on %s", __PRETTY_FUNCTION__, family, src_str, ifname); - return -4; + return -5; } } From 613938d48abb863660691641a5761f10402cf3f3 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Thu, 13 Aug 2009 15:39:31 -0300 Subject: [PATCH 312/482] [pim] RPF cache refresh statistics --- pimd/pim_cmd.c | 30 +++++++++++++++++++++--------- pimd/pim_zebra.c | 10 +++++++++- pimd/pimd.c | 2 ++ pimd/pimd.h | 2 ++ 4 files changed, 34 insertions(+), 10 deletions(-) diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index c845a895..df6897e3 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -1022,16 +1022,32 @@ static void pim_show_upstream_rpf(struct vty *vty) } } +static void show_rpf_refresh_stats(struct vty *vty) +{ + vty_out(vty, + "RPF Cache Refresh Delay: %ld msecs%s" + "RPF Cache Refresh Timer: %ld msecs%s" + "RPF Cache Refresh Requests: %lld%s" + "RPF Cache Refresh Events: %lld%s", + qpim_rpf_cache_refresh_delay_msec, VTY_NEWLINE, + pim_time_timer_remain_msec(qpim_rpf_cache_refresher), VTY_NEWLINE, + qpim_rpf_cache_refresh_requests, VTY_NEWLINE, + qpim_rpf_cache_refresh_events, VTY_NEWLINE); +} + static void pim_show_rpf(struct vty *vty) { struct listnode *up_node; struct pim_upstream *up; + show_rpf_refresh_stats(vty); + + vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, "Source Group RpfIface RpfAddress RibNextHop Metric Pref%s", VTY_NEWLINE); - for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, up_node, up)) { char src_str[100]; char grp_str[100]; @@ -1847,7 +1863,7 @@ static void show_multicast_interfaces(struct vty *vty) vty_out(vty, "%s", VTY_NEWLINE); - vty_out(vty, "Interface Address ifIndex VifIndex PktsIn PktsOut BytesIn BytesOut%s", + vty_out(vty, "Interface Address ifi Vif PktsIn PktsOut BytesIn BytesOut%s", VTY_NEWLINE); for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) { @@ -1878,7 +1894,7 @@ static void show_multicast_interfaces(struct vty *vty) ifaddr = pim_ifp->primary_address; - vty_out(vty, "%-9s %-15s %7d %8d %6lu %7lu %7lu %8lu%s", + vty_out(vty, "%-9s %-15s %3d %3d %7lu %7lu %10lu %10lu%s", ifp->name, inet_ntoa(ifaddr), ifp->ifindex, @@ -1934,12 +1950,8 @@ DEFUN (show_ip_multicast, VTY_NEWLINE); vty_out(vty, "%s", VTY_NEWLINE); - vty_out(vty, "RPF Cache Refresh Delay: %ld msecs%s", - qpim_rpf_cache_refresh_delay_msec, - VTY_NEWLINE); - vty_out(vty, "RPF Cache Refresh Timer: %ld msecs%s", - pim_time_timer_remain_msec(qpim_rpf_cache_refresher), - VTY_NEWLINE); + + show_rpf_refresh_stats(vty); show_multicast_interfaces(vty); diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index 56e4dba1..9764532b 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -447,13 +447,21 @@ static int on_rpf_cache_refresh(struct thread *t) /* update kernel multicast forwarding cache (MFC) */ scan_oil(); + ++qpim_rpf_cache_refresh_events; + return 0; } static void sched_rpf_cache_refresh() { - if (qpim_rpf_cache_refresher) + ++qpim_rpf_cache_refresh_requests; + + if (qpim_rpf_cache_refresher) { + /* Refresh timer is already running */ return; + } + + /* Start refresh timer */ if (PIM_DEBUG_ZEBRA) { zlog_debug("%s: triggering %ld msec timer", diff --git a/pimd/pimd.c b/pimd/pimd.c index f255a28f..e8afe779 100644 --- a/pimd/pimd.c +++ b/pimd/pimd.c @@ -54,6 +54,8 @@ struct zclient *qpim_zclient_lookup = 0; struct pim_assert_metric qpim_infinite_assert_metric; long qpim_rpf_cache_refresh_delay_msec = 10000; struct thread *qpim_rpf_cache_refresher = 0; +int64_t qpim_rpf_cache_refresh_requests = 0; +int64_t qpim_rpf_cache_refresh_events = 0; struct in_addr qpim_inaddr_any; static void pim_free() diff --git a/pimd/pimd.h b/pimd/pimd.h index 10f8518a..aaf5faae 100644 --- a/pimd/pimd.h +++ b/pimd/pimd.h @@ -80,6 +80,8 @@ struct zclient *qpim_zclient_lookup; struct pim_assert_metric qpim_infinite_assert_metric; long qpim_rpf_cache_refresh_delay_msec; struct thread *qpim_rpf_cache_refresher; +int64_t qpim_rpf_cache_refresh_requests; +int64_t qpim_rpf_cache_refresh_events; struct in_addr qpim_inaddr_any; #define PIM_JP_HOLDTIME (qpim_t_periodic * 7 / 2) From df4044b2cbe0015d06d25de25e640fca231243dd Mon Sep 17 00:00:00 2001 From: Leonard Herve Date: Fri, 14 Aug 2009 10:38:52 +0200 Subject: [PATCH 313/482] Router Alert option for IGMP packets but not for PIM packets --- pimd/pim_sock.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pimd/pim_sock.c b/pimd/pim_sock.c index 8e6c559a..00fc8eda 100644 --- a/pimd/pim_sock.c +++ b/pimd/pim_sock.c @@ -111,8 +111,8 @@ int pim_socket_mcast(int protocol, struct in_addr ifaddr, int loop) } - /* Set router alert (RFC 2113) */ - { + /* Set router alert (RFC 2113) for all IGMP messages (RFC 3376 4. Message Formats)*/ + if (protocol == IPPROTO_IGMP) { char ra[4]; ra[0] = 148; ra[1] = 4; From 942b0fdcc18c3841c6781f6a3f36aa47a604ba1f Mon Sep 17 00:00:00 2001 From: Leonard Herve Date: Fri, 14 Aug 2009 15:49:06 +0200 Subject: [PATCH 314/482] [pim] Correction for cross-compilation error with this syntax --- pimd/pim_igmp.c | 6 ++++-- pimd/pim_igmpv3.c | 29 ++++++++++++++++++++--------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c index 82db37f6..a0a4aa81 100644 --- a/pimd/pim_igmp.c +++ b/pimd/pim_igmp.c @@ -286,6 +286,7 @@ static int recv_igmp_query(struct igmp_sock *igmp, int query_version, struct in_addr group_addr; uint16_t recv_checksum; uint16_t checksum; + int i; group_addr = *(struct in_addr *)(igmp_msg + 4); @@ -419,7 +420,7 @@ static int recv_igmp_query(struct igmp_sock *igmp, int query_version, /* Scan sources in query and lower their timers to LMQT */ struct in_addr *sources = (struct in_addr *)(igmp_msg + IGMP_V3_SOURCES_OFFSET); - for (int i = 0; i < recv_num_sources; ++i) { + for (i = 0; i < recv_num_sources; ++i) { struct in_addr src_addr = sources[i]; struct igmp_source *src = igmp_find_source_by_addr(group, src_addr); if (src) { @@ -451,6 +452,7 @@ static int igmp_v3_report(struct igmp_sock *igmp, uint8_t *group_record; uint8_t *report_pastend = (uint8_t *) igmp_msg + igmp_msg_len; struct interface *ifp = igmp->interface; + int i; if (igmp_msg_len < IGMP_V3_MSG_MIN_SIZE) { zlog_warn("Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d", @@ -485,7 +487,7 @@ static int igmp_v3_report(struct igmp_sock *igmp, group_record = (uint8_t *) igmp_msg + IGMP_V3_REPORT_GROUPPRECORD_OFFSET; /* Scan groups */ - for (int i = 0; i < num_groups; ++i) { + for (i = 0; i < num_groups; ++i) { struct in_addr rec_group; uint8_t *sources; uint8_t *src; diff --git a/pimd/pim_igmpv3.c b/pimd/pim_igmpv3.c index 3d3ee7ac..7e76f442 100644 --- a/pimd/pim_igmpv3.c +++ b/pimd/pim_igmpv3.c @@ -531,6 +531,7 @@ static void allow(struct igmp_sock *igmp, struct in_addr from, struct interface *ifp = igmp->interface; struct pim_interface *pim_ifp; struct igmp_group *group; + int i; pim_ifp = ifp->info; @@ -541,7 +542,7 @@ static void allow(struct igmp_sock *igmp, struct in_addr from, } /* scan received sources */ - for (int i = 0; i < num_sources; ++i) { + for (i = 0; i < num_sources; ++i) { struct igmp_source *source; struct in_addr *src_addr; @@ -580,6 +581,8 @@ void igmpv3_report_isin(struct igmp_sock *igmp, struct in_addr from, static void isex_excl(struct igmp_group *group, int num_sources, struct in_addr *sources) { + int i; + /* EXCLUDE mode */ zassert(group->group_filtermode_isexcl); @@ -587,7 +590,7 @@ static void isex_excl(struct igmp_group *group, source_mark_delete_flag(group->group_source_list); /* scan received sources (A) */ - for (int i = 0; i < num_sources; ++i) { + for (i = 0; i < num_sources; ++i) { struct igmp_source *source; struct in_addr *src_addr; @@ -621,6 +624,8 @@ static void isex_excl(struct igmp_group *group, static void isex_incl(struct igmp_group *group, int num_sources, struct in_addr *sources) { + int i; + /* INCLUDE mode */ zassert(!group->group_filtermode_isexcl); @@ -628,7 +633,7 @@ static void isex_incl(struct igmp_group *group, source_mark_delete_flag(group->group_source_list); /* scan received sources (B) */ - for (int i = 0; i < num_sources; ++i) { + for (i = 0; i < num_sources; ++i) { struct igmp_source *source; struct in_addr *src_addr; @@ -702,12 +707,13 @@ static void toin_incl(struct igmp_group *group, { struct igmp_sock *igmp = group->group_igmp_sock; int num_sources_tosend = listcount(group->group_source_list); + int i; /* Set SEND flag for all known sources (A) */ source_mark_send_flag(group->group_source_list); /* Scan received sources (B) */ - for (int i = 0; i < num_sources; ++i) { + for (i = 0; i < num_sources; ++i) { struct igmp_source *source; struct in_addr *src_addr; @@ -745,12 +751,13 @@ static void toin_excl(struct igmp_group *group, { struct igmp_sock *igmp = group->group_igmp_sock; int num_sources_tosend; + int i; /* Set SEND flag for X (sources with timer > 0) */ num_sources_tosend = source_mark_send_flag_by_timer(group->group_source_list); /* Scan received sources (A) */ - for (int i = 0; i < num_sources; ++i) { + for (i = 0; i < num_sources; ++i) { struct igmp_source *source; struct in_addr *src_addr; @@ -821,6 +828,7 @@ static void toex_incl(struct igmp_group *group, int num_sources, struct in_addr *sources) { int num_sources_tosend = 0; + int i; zassert(!group->group_filtermode_isexcl); @@ -831,7 +839,7 @@ static void toex_incl(struct igmp_group *group, source_clear_send_flag(group->group_source_list); /* Scan received sources (B) */ - for (int i = 0; i < num_sources; ++i) { + for (i = 0; i < num_sources; ++i) { struct igmp_source *source; struct in_addr *src_addr; @@ -878,6 +886,7 @@ static void toex_excl(struct igmp_group *group, int num_sources, struct in_addr *sources) { int num_sources_tosend = 0; + int i; /* set DELETE flag for all known sources (X,Y) */ source_mark_delete_flag(group->group_source_list); @@ -886,7 +895,7 @@ static void toex_excl(struct igmp_group *group, source_clear_send_flag(group->group_source_list); /* scan received sources (A) */ - for (int i = 0; i < num_sources; ++i) { + for (i = 0; i < num_sources; ++i) { struct igmp_source *source; struct in_addr *src_addr; @@ -1392,12 +1401,13 @@ static void block_excl(struct igmp_group *group, int num_sources, struct in_addr *sources) { int num_sources_tosend = 0; + int i; /* 1. clear off SEND flag from all known sources (X,Y) */ source_clear_send_flag(group->group_source_list); /* 2. scan received sources (A) */ - for (int i = 0; i < num_sources; ++i) { + for (i = 0; i < num_sources; ++i) { struct igmp_source *source; struct in_addr *src_addr; @@ -1438,12 +1448,13 @@ static void block_incl(struct igmp_group *group, int num_sources, struct in_addr *sources) { int num_sources_tosend = 0; + int i; /* 1. clear off SEND flag from all known sources (B) */ source_clear_send_flag(group->group_source_list); /* 2. scan received sources (A) */ - for (int i = 0; i < num_sources; ++i) { + for (i = 0; i < num_sources; ++i) { struct igmp_source *source; struct in_addr *src_addr; From bcc4abe09d3faa9b392be2d46e3f6a29b75e46d9 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Mon, 17 Aug 2009 18:18:59 -0300 Subject: [PATCH 315/482] [pim] More RPF cache refresh statistics --- pimd/pim_cmd.c | 25 +++++++++++++++---------- pimd/pim_zebra.c | 1 + pimd/pimd.c | 1 + pimd/pimd.h | 1 + 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index df6897e3..ea396bcf 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -1022,25 +1022,32 @@ static void pim_show_upstream_rpf(struct vty *vty) } } -static void show_rpf_refresh_stats(struct vty *vty) +static void show_rpf_refresh_stats(struct vty *vty, time_t now) { + char refresh_uptime[10]; + + pim_time_uptime(refresh_uptime, sizeof(refresh_uptime), now - qpim_rpf_cache_refresh_last); + vty_out(vty, "RPF Cache Refresh Delay: %ld msecs%s" "RPF Cache Refresh Timer: %ld msecs%s" "RPF Cache Refresh Requests: %lld%s" - "RPF Cache Refresh Events: %lld%s", + "RPF Cache Refresh Events: %lld%s" + "RPF Cache Refresh Last: %s%s", qpim_rpf_cache_refresh_delay_msec, VTY_NEWLINE, pim_time_timer_remain_msec(qpim_rpf_cache_refresher), VTY_NEWLINE, qpim_rpf_cache_refresh_requests, VTY_NEWLINE, - qpim_rpf_cache_refresh_events, VTY_NEWLINE); + qpim_rpf_cache_refresh_events, VTY_NEWLINE, + refresh_uptime, VTY_NEWLINE); } static void pim_show_rpf(struct vty *vty) { struct listnode *up_node; struct pim_upstream *up; + time_t now = pim_time_monotonic_sec(); - show_rpf_refresh_stats(vty); + show_rpf_refresh_stats(vty, now); vty_out(vty, "%s", VTY_NEWLINE); @@ -1079,9 +1086,7 @@ static void igmp_show_querier(struct vty *vty) { struct listnode *node; struct interface *ifp; - time_t now; - - now = pim_time_monotonic_sec(); + time_t now = pim_time_monotonic_sec(); vty_out(vty, "Interface Address Querier StartCount Query-Timer Other-Timer%s", VTY_NEWLINE); @@ -1914,15 +1919,15 @@ DEFUN (show_ip_multicast, IP_STR "Multicast global information\n") { + time_t now = pim_time_monotonic_sec(); + if (PIM_MROUTE_IS_ENABLED) { - time_t now; char uptime[10]; vty_out(vty, "Mroute socket descriptor: %d%s", qpim_mroute_socket_fd, VTY_NEWLINE); - now = pim_time_monotonic_sec(); pim_time_uptime(uptime, sizeof(uptime), now - qpim_mroute_socket_creation); vty_out(vty, "Mroute socket uptime: %s%s", uptime, @@ -1951,7 +1956,7 @@ DEFUN (show_ip_multicast, vty_out(vty, "%s", VTY_NEWLINE); - show_rpf_refresh_stats(vty); + show_rpf_refresh_stats(vty, now); show_multicast_interfaces(vty); diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index 9764532b..3530434d 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -447,6 +447,7 @@ static int on_rpf_cache_refresh(struct thread *t) /* update kernel multicast forwarding cache (MFC) */ scan_oil(); + qpim_rpf_cache_refresh_last = pim_time_monotonic_sec(); ++qpim_rpf_cache_refresh_events; return 0; diff --git a/pimd/pimd.c b/pimd/pimd.c index e8afe779..f1a084f1 100644 --- a/pimd/pimd.c +++ b/pimd/pimd.c @@ -56,6 +56,7 @@ long qpim_rpf_cache_refresh_delay_msec = 10000; struct thread *qpim_rpf_cache_refresher = 0; int64_t qpim_rpf_cache_refresh_requests = 0; int64_t qpim_rpf_cache_refresh_events = 0; +int64_t qpim_rpf_cache_refresh_last = 0; struct in_addr qpim_inaddr_any; static void pim_free() diff --git a/pimd/pimd.h b/pimd/pimd.h index aaf5faae..6ee91e7f 100644 --- a/pimd/pimd.h +++ b/pimd/pimd.h @@ -82,6 +82,7 @@ long qpim_rpf_cache_refresh_delay_msec; struct thread *qpim_rpf_cache_refresher; int64_t qpim_rpf_cache_refresh_requests; int64_t qpim_rpf_cache_refresh_events; +int64_t qpim_rpf_cache_refresh_last; struct in_addr qpim_inaddr_any; #define PIM_JP_HOLDTIME (qpim_t_periodic * 7 / 2) From 5f35a5236435522f49f9a95d3b59009f947b9130 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Thu, 20 Aug 2009 11:57:41 -0300 Subject: [PATCH 316/482] [pim] show ip pim lan-prune-delay: fix cosmetic alignment --- pimd/pim_cmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index ea396bcf..140812c2 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -797,7 +797,7 @@ static void pim_show_lan_prune_delay(struct vty *vty) pim_inet4_dump("", neigh->source_addr, neigh_src_str, sizeof(neigh_src_str)); - vty_out(vty, "%-9s %-15s %5u %5u %5u %5u %5u %1u %-15s %s %5u %5u %1u%s", + vty_out(vty, "%-9s %-15s %5u %5u %5u %5u %5u %1u %-15s %-3s %5u %5u %1u%s", ifp->name, inet_ntoa(ifaddr), pim_ifp->pim_propagation_delay_msec, From 54d6c57db53a626e21766ce608e69f30396a95f0 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Thu, 20 Aug 2009 18:31:03 -0300 Subject: [PATCH 317/482] [pim] Fixed doc on CAVEAT C7 --- pimd/CAVEATS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pimd/CAVEATS b/pimd/CAVEATS index 7dc950fe..c436d552 100644 --- a/pimd/CAVEATS +++ b/pimd/CAVEATS @@ -26,7 +26,7 @@ C6 PIM implementation currently does not support IPv6. PIM-SSM requires IGMPv3 for IPv4 and MLDv2 for IPv6. MLDv2 is currently missing. See also CAVEAT C9. -C7 (S,G) Assert state machine (RFC 4601, section 4.6.1) is not +C7 FIXED (S,G) Assert state machine (RFC 4601, section 4.6.1) is not implemented. See also TODO T6. See also CAVEAT C10. C8 It is not possible to disable join suppression in order to From 47afa6e19b69b433c0c0d0b73837118e0a1284cc Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Thu, 27 Aug 2009 18:23:02 -0300 Subject: [PATCH 318/482] [pim] Version up to 0.158 --- pimd/pim_version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pimd/pim_version.h b/pimd/pim_version.h index fc5bca2f..fa6dd552 100644 --- a/pimd/pim_version.h +++ b/pimd/pim_version.h @@ -23,7 +23,7 @@ #ifndef PIM_VERSION_H #define PIM_VERSION_H -#define PIMD_VERSION_STR "0.157" +#define PIMD_VERSION_STR "0.158" const char * const PIMD_VERSION; From b471196e812d98b3ce42bcc186d3a381080d423a Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Fri, 11 Sep 2009 11:15:42 -0300 Subject: [PATCH 319/482] [pim] ip mroute show: can display the MFC --- pimd/LINUX_KERNEL_MROUTE_MFC | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pimd/LINUX_KERNEL_MROUTE_MFC b/pimd/LINUX_KERNEL_MROUTE_MFC index 7dc44b0d..e87e567f 100644 --- a/pimd/LINUX_KERNEL_MROUTE_MFC +++ b/pimd/LINUX_KERNEL_MROUTE_MFC @@ -19,4 +19,8 @@ Interface BytesIn PktsIn BytesOut PktsOut Flags Local Remote cat /proc/net/ip_mr_cache Group Origin Iif Pkts Bytes Wrong Oifs +# iproute2 can display the MFC: +ip mroute show +(2.2.2.2, 239.2.2.2) Iif: eth1 Oifs: eth0 + # -- end-of-file -- From 465185300b26042e1813d53f179616a17154d37a Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Fri, 11 Sep 2009 15:05:40 -0300 Subject: [PATCH 320/482] [pim] clean-up --- pimd/pim_cmd.c | 1 - pimd/pim_iface.h | 2 -- 2 files changed, 3 deletions(-) diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 140812c2..5b47f3bb 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -1086,7 +1086,6 @@ static void igmp_show_querier(struct vty *vty) { struct listnode *node; struct interface *ifp; - time_t now = pim_time_monotonic_sec(); vty_out(vty, "Interface Address Querier StartCount Query-Timer Other-Timer%s", VTY_NEWLINE); diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h index bc99aafb..cfdb8eb1 100644 --- a/pimd/pim_iface.h +++ b/pimd/pim_iface.h @@ -110,8 +110,6 @@ void pim_if_init(void); struct pim_interface *pim_if_new(struct interface *ifp, int igmp, int pim); void pim_if_delete(struct interface *ifp); -int pim_if_igmp_listen(struct vty *vty, - struct interface *ifp); void pim_if_addr_add(struct connected *ifc); void pim_if_addr_del(struct connected *ifc); void pim_if_addr_add_all(struct interface *ifp); From 40765fe45f39767ad545ca693fb5985fa90ce4ca Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Wed, 30 Sep 2009 17:10:11 -0300 Subject: [PATCH 321/482] [pim] Command line tool to test IGMPv3 join. --- pimd/Makefile.am | 10 ++- pimd/pim_igmp_join.c | 61 ++++++++++++++++ pimd/pim_igmp_join.h | 32 +++++++++ pimd/pim_sock.c | 30 +------- pimd/pim_sock.h | 2 + pimd/test_igmpv3_join.c | 149 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 254 insertions(+), 30 deletions(-) create mode 100644 pimd/pim_igmp_join.c create mode 100644 pimd/pim_igmp_join.h create mode 100644 pimd/test_igmpv3_join.c diff --git a/pimd/Makefile.am b/pimd/Makefile.am index 80df2e6b..c722b7ec 100644 --- a/pimd/Makefile.am +++ b/pimd/Makefile.am @@ -44,6 +44,7 @@ INSTALL_SDATA=@INSTALL@ -m 600 LIBS = @LIBS@ noinst_LIBRARIES = libpim.a sbin_PROGRAMS = pimd +bin_PROGRAMS = test_igmpv3_join libpim_a_SOURCES = \ pimd.c pim_version.c pim_cmd.c pim_signals.c pim_iface.c \ @@ -51,7 +52,8 @@ libpim_a_SOURCES = \ pim_igmpv3.c pim_str.c pim_mroute.c pim_util.c pim_time.c \ pim_oil.c pim_zlookup.c pim_pim.c pim_tlv.c pim_neighbor.c \ pim_hello.c pim_ifchannel.c pim_join.c pim_assert.c \ - pim_msg.c pim_upstream.c pim_rpf.c pim_rand.c pim_macro.c + pim_msg.c pim_upstream.c pim_rpf.c pim_rand.c pim_macro.c \ + pim_igmp_join.c noinst_HEADERS = \ pimd.h pim_version.h pim_cmd.h pim_signals.h pim_iface.h \ @@ -59,11 +61,15 @@ noinst_HEADERS = \ pim_igmpv3.h pim_str.h pim_mroute.h pim_util.h pim_time.h \ pim_oil.h pim_zlookup.h pim_pim.h pim_tlv.h pim_neighbor.h \ pim_hello.h pim_ifchannel.h pim_join.h pim_assert.h \ - pim_msg.h pim_upstream.h pim_rpf.h pim_rand.h pim_macro.h + pim_msg.h pim_upstream.h pim_rpf.h pim_rand.h pim_macro.h \ + pim_igmp_join.h pimd_SOURCES = \ pim_main.c $(libpim_a_SOURCES) +test_igmpv3_join_SOURCES = \ + test_igmpv3_join.c pim_igmp_join.c + pimd_LDADD = ../lib/libzebra.la @LIBCAP@ examplesdir = $(exampledir) diff --git a/pimd/pim_igmp_join.c b/pimd/pim_igmp_join.c new file mode 100644 index 00000000..7183997c --- /dev/null +++ b/pimd/pim_igmp_join.c @@ -0,0 +1,61 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include + +#include "pim_igmp_join.h" + +#ifndef MCAST_JOIN_SOURCE_GROUP +#define MCAST_JOIN_SOURCE_GROUP 46 +struct group_source_req +{ + uint32_t gsr_interface; + struct sockaddr_storage gsr_group; + struct sockaddr_storage gsr_source; +}; +#endif + +int pim_igmp_join_source(int fd, int ifindex, + struct in_addr group_addr, + struct in_addr source_addr) +{ + struct group_source_req req; + struct sockaddr_in *group_sa = (struct sockaddr_in *) &req.gsr_group; + struct sockaddr_in *source_sa = (struct sockaddr_in *) &req.gsr_source; + + memset(group_sa, 0, sizeof(*group_sa)); + group_sa->sin_family = AF_INET; + group_sa->sin_addr = group_addr; + group_sa->sin_port = htons(0); + + memset(source_sa, 0, sizeof(*source_sa)); + source_sa->sin_family = AF_INET; + source_sa->sin_addr = source_addr; + source_sa->sin_port = htons(0); + + req.gsr_interface = ifindex; + + return setsockopt(fd, SOL_IP, MCAST_JOIN_SOURCE_GROUP, + &req, sizeof(req)); + + return 0; +} diff --git a/pimd/pim_igmp_join.h b/pimd/pim_igmp_join.h new file mode 100644 index 00000000..1127af12 --- /dev/null +++ b/pimd/pim_igmp_join.h @@ -0,0 +1,32 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_IGMP_JOIN_H +#define PIM_IGMP_JOIN_H + +#include + +int pim_igmp_join_source(int fd, int ifindex, + struct in_addr group_addr, + struct in_addr source_addr); + +#endif /* PIM_IGMP_JOIN_H */ diff --git a/pimd/pim_sock.c b/pimd/pim_sock.c index 00fc8eda..c908e8b8 100644 --- a/pimd/pim_sock.c +++ b/pimd/pim_sock.c @@ -38,20 +38,11 @@ #include "pimd.h" #include "pim_sock.h" #include "pim_str.h" +#include "pim_igmp_join.h" /* GLOBAL VARS */ extern struct zebra_privs_t pimd_privs; -#ifndef MCAST_JOIN_SOURCE_GROUP -#define MCAST_JOIN_SOURCE_GROUP 46 -struct group_source_req -{ - uint32_t gsr_interface; - struct sockaddr_storage gsr_group; - struct sockaddr_storage gsr_source; -}; -#endif - int pim_socket_raw(int protocol) { int fd; @@ -242,24 +233,7 @@ int pim_socket_join_source(int fd, int ifindex, struct in_addr source_addr, const char *ifname) { - struct group_source_req req; - struct sockaddr_in *group_sa = (struct sockaddr_in *) &req.gsr_group; - struct sockaddr_in *source_sa = (struct sockaddr_in *) &req.gsr_source; - - memset(group_sa, 0, sizeof(*group_sa)); - group_sa->sin_family = AF_INET; - group_sa->sin_addr = group_addr; - group_sa->sin_port = htons(0); - - memset(source_sa, 0, sizeof(*source_sa)); - source_sa->sin_family = AF_INET; - source_sa->sin_addr = source_addr; - source_sa->sin_port = htons(0); - - req.gsr_interface = ifindex; - - if (setsockopt(fd, SOL_IP, MCAST_JOIN_SOURCE_GROUP, - &req, sizeof(req))) { + if (pim_igmp_join_source(fd, ifindex, group_addr, source_addr)) { int e = errno; char group_str[100]; char source_str[100]; diff --git a/pimd/pim_sock.h b/pimd/pim_sock.h index e9d5476f..d3557809 100644 --- a/pimd/pim_sock.h +++ b/pimd/pim_sock.h @@ -23,6 +23,8 @@ #ifndef PIM_SOCK_H #define PIM_SOCK_H +#include + #define PIM_SOCK_ERR_NONE (0) /* No error */ #define PIM_SOCK_ERR_SOCKET (-1) /* socket() */ #define PIM_SOCK_ERR_RA (-2) /* Router Alert option */ diff --git a/pimd/test_igmpv3_join.c b/pimd/test_igmpv3_join.c new file mode 100644 index 00000000..af93ab60 --- /dev/null +++ b/pimd/test_igmpv3_join.c @@ -0,0 +1,149 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pim_igmp_join.h" + +const char *prog_name = 0; + +static int iface_solve_index(const char *ifname) +{ + struct if_nameindex *ini; + int ifindex = -1; + int i; + + if (!ifname) + return -1; + + ini = if_nameindex(); + if (!ini) { + int err = errno; + fprintf(stderr, + "%s: interface=%s: failure solving index: errno=%d: %s\n", + prog_name, ifname, err, strerror(err)); + errno = err; + return -1; + } + + for (i = 0; ini[i].if_index; ++i) { +#if 0 + fprintf(stderr, + "%s: interface=%s matching against local ifname=%s ifindex=%d\n", + prog_name, ifname, ini[i].if_name, ini[i].if_index); +#endif + if (!strcmp(ini[i].if_name, ifname)) { + ifindex = ini[i].if_index; + break; + } + } + + if_freenameindex(ini); + + return ifindex; +} + +int main(int argc, const char *argv[]) +{ + struct in_addr group_addr; + struct in_addr source_addr; + const char *ifname; + const char *group; + const char *source; + int ifindex; + int result; + int fd; + + prog_name = argv[0]; + + fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (fd < 0) { + fprintf(stderr, + "%s: could not create socket: socket(): errno=%d: %s\n", + prog_name, errno, strerror(errno)); + exit(1); + } + + if (argc != 4) { + fprintf(stderr, + "usage: %s interface group source\n" + "example: %s eth0 232.1.1.1 1.1.1.1\n", + prog_name, prog_name); + exit(1); + } + + ifname = argv[1]; + group = argv[2]; + source = argv[3]; + + ifindex = iface_solve_index(ifname); + if (ifindex < 0) { + fprintf(stderr, "%s: could not find interface: %s\n", + prog_name, ifname); + exit(1); + } + + result = inet_pton(AF_INET, group, &group_addr); + if (result <= 0) { + fprintf(stderr, "%s: bad group address: %s\n", + prog_name, group); + exit(1); + } + + result = inet_pton(AF_INET, source, &source_addr); + if (result <= 0) { + fprintf(stderr, "%s: bad source address: %s\n", + prog_name, source); + exit(1); + } + + result = pim_igmp_join_source(fd, ifindex, group_addr, source_addr); + if (result) { + fprintf(stderr, + "%s: setsockopt(fd=%d) failure for IGMP group %s source %s ifindex %d on interface %s: errno=%d: %s\n", + prog_name, fd, group, source, ifindex, ifname, + errno, strerror(errno)); + exit(1); + } + + printf("%s: joined channel (S,G)=(%s,%s) on interface %s\n", + prog_name, source, group, ifname); + + printf("%s: waiting...\n", prog_name); + + getchar(); + + close(fd); + + printf("%s: left channel (S,G)=(%s,%s) on interface %s\n", + prog_name, source, group, ifname); + + exit(0); +} From 31894370d26de0b91b5c003ca6c592f0a487d60f Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Thu, 1 Oct 2009 10:04:02 -0300 Subject: [PATCH 322/482] [pim] Hint for test_igmpv3_join command-line utility --- pimd/DEBUG | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pimd/DEBUG b/pimd/DEBUG index aeed9da1..119c46f5 100644 --- a/pimd/DEBUG +++ b/pimd/DEBUG @@ -8,6 +8,18 @@ DEBUG HINTS - Check the multicast packets are not being dropped due to fragmentation problems. + - Two easy options to test IGMPv3 joins from the receiver host: + + 1) Configure pimd on the receiver host with "ip igmp join": + + interface eth0 + ip pim ssm + ip igmp join 239.1.1.1 1.1.1.1 + + 2) Use the test_igmpv3_join command-line utility: + + test_igmpv3_join eth0 239.1.1.1 1.1.1.1 + - The following command generates a 100-kbps multicast stream for channel 1.1.1.1,239.1.1.1 with TTL 10 and 1000-byte payload per UDP packet (to avoid fragmentation): From ccc5d2bb9b7a395375a34cb6a6b703d78e885b1d Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Fri, 2 Oct 2009 14:50:08 -0300 Subject: [PATCH 323/482] [pim] Ref. on LW-MLDv2 --- pimd/TODO | 1 + 1 file changed, 1 insertion(+) diff --git a/pimd/TODO b/pimd/TODO index 7a2ca0b6..6ed98c06 100644 --- a/pimd/TODO +++ b/pimd/TODO @@ -350,6 +350,7 @@ T39 DONE AssertTrackingDesired: flags is not matching evaluation # T40 Lightweight MLDv2 + http://tools.ietf.org/html/draft-ietf-mboned-lightweight-igmpv3-mldv2-05 http://www.ietf.org/internet-drafts/draft-ietf-mboned-lightweight-igmpv3-mldv2-05.txt http://www.ietf.org/html.charters/mboned-charter.html From 96f91aefc06477e73d0e93008b51fc6e87fa2bc4 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Wed, 7 Oct 2009 18:41:45 -0300 Subject: [PATCH 324/482] [pim] Skeleton for ssmpingd support --- pimd/Makefile.am | 4 +-- pimd/TODO | 3 +++ pimd/pim_cmd.c | 60 +++++++++++++++++++++++++++++++++++++++++++++ pimd/pim_cmd.h | 1 + pimd/pim_iface.c | 1 + pimd/pim_iface.h | 1 + pimd/pim_ssmpingd.c | 45 ++++++++++++++++++++++++++++++++++ pimd/pim_ssmpingd.h | 45 ++++++++++++++++++++++++++++++++++ pimd/pimd.c | 5 ++++ pimd/pimd.h | 1 + 10 files changed, 164 insertions(+), 2 deletions(-) create mode 100644 pimd/pim_ssmpingd.c create mode 100644 pimd/pim_ssmpingd.h diff --git a/pimd/Makefile.am b/pimd/Makefile.am index c722b7ec..57bd31ae 100644 --- a/pimd/Makefile.am +++ b/pimd/Makefile.am @@ -53,7 +53,7 @@ libpim_a_SOURCES = \ pim_oil.c pim_zlookup.c pim_pim.c pim_tlv.c pim_neighbor.c \ pim_hello.c pim_ifchannel.c pim_join.c pim_assert.c \ pim_msg.c pim_upstream.c pim_rpf.c pim_rand.c pim_macro.c \ - pim_igmp_join.c + pim_igmp_join.c pim_ssmpingd.c noinst_HEADERS = \ pimd.h pim_version.h pim_cmd.h pim_signals.h pim_iface.h \ @@ -62,7 +62,7 @@ noinst_HEADERS = \ pim_oil.h pim_zlookup.h pim_pim.h pim_tlv.h pim_neighbor.h \ pim_hello.h pim_ifchannel.h pim_join.h pim_assert.h \ pim_msg.h pim_upstream.h pim_rpf.h pim_rand.h pim_macro.h \ - pim_igmp_join.h + pim_igmp_join.h pim_ssmpingd.h pimd_SOURCES = \ pim_main.c $(libpim_a_SOURCES) diff --git a/pimd/TODO b/pimd/TODO index 6ed98c06..dc2ece5f 100644 --- a/pimd/TODO +++ b/pimd/TODO @@ -354,4 +354,7 @@ T40 Lightweight MLDv2 http://www.ietf.org/internet-drafts/draft-ietf-mboned-lightweight-igmpv3-mldv2-05.txt http://www.ietf.org/html.charters/mboned-charter.html +T41 ssmping + http://www.venaas.no/multicast/ssmping/ + -x- diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 5b47f3bb..692c2fcc 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -47,6 +47,7 @@ #include "pim_upstream.h" #include "pim_rpf.h" #include "pim_macro.h" +#include "pim_ssmpingd.h" static struct cmd_node pim_global_node = { PIM_NODE, @@ -2200,6 +2201,63 @@ DEFUN (no_ip_multicast_routing, return CMD_SUCCESS; } +DEFUN (ip_ssmpingd, + ip_ssmpingd_cmd, + "ip ssmpingd [A.B.C.D]", + IP_STR + SSMPINGD_STR + "Source address\n") +{ + int result; + struct in_addr source_addr; + const char *source_str = (argc > 0) ? argv[0] : "0.0.0.0"; + + result = inet_pton(AF_INET, source_str, &source_addr); + if (result <= 0) { + vty_out(vty, "%% Bad source address %s: errno=%d: %s%s", + source_str, errno, safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + result = pim_ssmpingd_start(source_addr); + if (result) { + vty_out(vty, "%% Failure starting ssmpingd for source %s: %d%s", + source_str, result, VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN (no_ip_ssmpingd, + no_ip_ssmpingd_cmd, + "no ip ssmpingd [A.B.C.D]", + NO_STR + IP_STR + SSMPINGD_STR + "Source address\n") +{ + int result; + struct in_addr source_addr; + const char *source_str = (argc > 0) ? argv[0] : "0.0.0.0"; + + result = inet_pton(AF_INET, source_str, &source_addr); + if (result <= 0) { + vty_out(vty, "%% Bad source address %s: errno=%d: %s%s", + source_str, errno, safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + result = pim_ssmpingd_stop(source_addr); + if (result) { + vty_out(vty, "%% Failure stopping ssmpingd for source %s: %d%s", + source_str, result, VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + DEFUN (interface_ip_igmp, interface_ip_igmp_cmd, "ip igmp", @@ -3788,6 +3846,8 @@ void pim_cmd_init() install_element (CONFIG_NODE, &ip_multicast_routing_cmd); install_element (CONFIG_NODE, &no_ip_multicast_routing_cmd); + install_element (CONFIG_NODE, &ip_ssmpingd_cmd); + install_element (CONFIG_NODE, &no_ip_ssmpingd_cmd); #if 0 install_element (CONFIG_NODE, &interface_cmd); /* from if.h */ #else diff --git a/pimd/pim_cmd.h b/pimd/pim_cmd.h index c2bb61bb..f3b2f96c 100644 --- a/pimd/pim_cmd.h +++ b/pimd/pim_cmd.h @@ -27,6 +27,7 @@ #define IGMP_STR "IGMP information\n" #define IGMP_GROUP_STR "IGMP groups information\n" #define IGMP_SOURCE_STR "IGMP sources information\n" +#define SSMPINGD_STR "Enable ssmpingd operation\n" #define IFACE_PIM_STR "Enable PIM SSM operation\n" #define IFACE_IGMP_STR "Enable IGMP operation\n" #define IFACE_IGMP_QUERY_INTERVAL_STR "IGMP host query interval\n" diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index 009f87c1..7806c805 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -38,6 +38,7 @@ #include "pim_ifchannel.h" #include "pim_rand.h" #include "pim_sock.h" +#include "pim_ssmpingd.h" static void pim_if_igmp_join_del_all(struct interface *ifp); diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h index cfdb8eb1..0a702c27 100644 --- a/pimd/pim_iface.h +++ b/pimd/pim_iface.h @@ -29,6 +29,7 @@ #include "vty.h" #include "pim_igmp.h" +#include "pim_upstream.h" #define PIM_IF_MASK_PIM (1 << 0) #define PIM_IF_MASK_IGMP (1 << 1) diff --git a/pimd/pim_ssmpingd.c b/pimd/pim_ssmpingd.c new file mode 100644 index 00000000..e4b16aa3 --- /dev/null +++ b/pimd/pim_ssmpingd.c @@ -0,0 +1,45 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include "pim_ssmpingd.h" +#include "pim_time.h" +#include "pimd.h" + +void pim_ssmpingd_init() +{ +} + +void pim_ssmpingd_destroy() +{ + if (qpim_ssmpingd_list) + list_free(qpim_ssmpingd_list); +} + +int pim_ssmpingd_start(struct in_addr source_addr) +{ + return 0; +} + +int pim_ssmpingd_stop(struct in_addr source_addr) +{ + return 0; +} diff --git a/pimd/pim_ssmpingd.h b/pimd/pim_ssmpingd.h new file mode 100644 index 00000000..32563aeb --- /dev/null +++ b/pimd/pim_ssmpingd.h @@ -0,0 +1,45 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_SSMPINGD_H +#define PIM_SSMPINGD_H + +#include + +#include "if.h" + +#include "pim_iface.h" + +struct ssmpingd_sock { + int sock_fd; /* socket */ + struct thread *t_sock_read; /* thread for reading socket */ + struct in_addr source; /* source address */ + int64_t creation; /* timestamp of socket creation */ + int64_t requests; /* counter */ +}; + +void pim_ssmpingd_init(void); +void pim_ssmpingd_destroy(void); +int pim_ssmpingd_start(struct in_addr source_addr); +int pim_ssmpingd_stop(struct in_addr source_addr); + +#endif /* PIM_SSMPINGD_H */ diff --git a/pimd/pimd.c b/pimd/pimd.c index f1a084f1..220604d9 100644 --- a/pimd/pimd.c +++ b/pimd/pimd.c @@ -35,6 +35,7 @@ #include "pim_upstream.h" #include "pim_rand.h" #include "pim_rpf.h" +#include "pim_ssmpingd.h" const char *const PIM_ALL_SYSTEMS = MCAST_ALL_SYSTEMS; const char *const PIM_ALL_ROUTERS = MCAST_ALL_ROUTERS; @@ -58,9 +59,12 @@ int64_t qpim_rpf_cache_refresh_requests = 0; int64_t qpim_rpf_cache_refresh_events = 0; int64_t qpim_rpf_cache_refresh_last = 0; struct in_addr qpim_inaddr_any; +struct list *qpim_ssmpingd_list = 0; static void pim_free() { + pim_ssmpingd_destroy(); + if (qpim_channel_oil_list) list_free(qpim_channel_oil_list); @@ -120,6 +124,7 @@ void pim_init() pim_if_init(); pim_cmd_init(); + pim_ssmpingd_init(); } void pim_terminate() diff --git a/pimd/pimd.h b/pimd/pimd.h index 6ee91e7f..bdf83b43 100644 --- a/pimd/pimd.h +++ b/pimd/pimd.h @@ -84,6 +84,7 @@ int64_t qpim_rpf_cache_refresh_requests; int64_t qpim_rpf_cache_refresh_events; int64_t qpim_rpf_cache_refresh_last; struct in_addr qpim_inaddr_any; +struct list *qpim_ssmpingd_list; /* list of struct ssmpingd_sock */ #define PIM_JP_HOLDTIME (qpim_t_periodic * 7 / 2) From 824adbea2d8d78f626f32d5b7900121fdebf6937 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Thu, 8 Oct 2009 09:16:27 -0300 Subject: [PATCH 325/482] [pim] Hooks for ssmpingd support --- lib/memtypes.c | 1 + pimd/pim_cmd.c | 82 +++++++++++- pimd/pim_cmd.h | 4 +- pimd/pim_ssmpingd.c | 304 +++++++++++++++++++++++++++++++++++++++++++- pimd/pim_ssmpingd.h | 2 +- pimd/pim_vty.c | 32 ++++- pimd/pimd.h | 4 + 7 files changed, 418 insertions(+), 11 deletions(-) diff --git a/lib/memtypes.c b/lib/memtypes.c index 26e27e72..4ed1e895 100644 --- a/lib/memtypes.c +++ b/lib/memtypes.c @@ -268,6 +268,7 @@ struct memory_list memory_list_pim[] = { MTYPE_PIM_NEIGHBOR, "PIM interface neighbor" }, { MTYPE_PIM_IFCHANNEL, "PIM interface (S,G) state" }, { MTYPE_PIM_UPSTREAM, "PIM upstream (S,G) state" }, + { MTYPE_PIM_SSMPINGD, "PIM sspimgd socket" }, { -1, NULL }, }; diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 692c2fcc..a4073f50 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -2137,6 +2137,47 @@ DEFUN (show_ip_route, return CMD_SUCCESS; } +static void show_ssmpingd(struct vty *vty) +{ + struct listnode *node; + struct ssmpingd_sock *ss; + time_t now; + + vty_out(vty, "Source Socket Uptime Requests%s", + VTY_NEWLINE); + + if (!qpim_ssmpingd_list) + return; + + now = pim_time_monotonic_sec(); + + for (ALL_LIST_ELEMENTS_RO(qpim_ssmpingd_list, node, ss)) { + char source_str[100]; + char ss_uptime[10]; + + pim_inet4_dump("", ss->source_addr, source_str, sizeof(source_str)); + pim_time_uptime(ss_uptime, sizeof(ss_uptime), now - ss->creation); + + vty_out(vty, "%-15s %6d %8s %8lld%s", + source_str, + ss->sock_fd, + ss_uptime, + ss->requests, + VTY_NEWLINE); + } +} + +DEFUN (show_ip_ssmpingd, + show_ip_ssmpingd_cmd, + "show ip ssmpingd", + SHOW_STR + IP_STR + SHOW_SSMPINGD_STR) +{ + show_ssmpingd(vty); + return CMD_SUCCESS; +} + static void mroute_add_all() { struct listnode *node; @@ -2205,7 +2246,7 @@ DEFUN (ip_ssmpingd, ip_ssmpingd_cmd, "ip ssmpingd [A.B.C.D]", IP_STR - SSMPINGD_STR + CONF_SSMPINGD_STR "Source address\n") { int result; @@ -2234,7 +2275,7 @@ DEFUN (no_ip_ssmpingd, "no ip ssmpingd [A.B.C.D]", NO_STR IP_STR - SSMPINGD_STR + CONF_SSMPINGD_STR "Source address\n") { int result; @@ -3132,6 +3173,36 @@ ALIAS (no_debug_pim_trace, DEBUG_PIM_STR DEBUG_PIM_TRACE_STR) +DEFUN (debug_ssmpingd, + debug_ssmpingd_cmd, + "debug ssmpingd", + DEBUG_STR + DEBUG_PIM_STR + DEBUG_SSMPINGD_STR) +{ + PIM_DO_DEBUG_SSMPINGD; + return CMD_SUCCESS; +} + +DEFUN (no_debug_ssmpingd, + no_debug_ssmpingd_cmd, + "no debug ssmpingd", + NO_STR + DEBUG_STR + DEBUG_PIM_STR + DEBUG_SSMPINGD_STR) +{ + PIM_DONT_DEBUG_SSMPINGD; + return CMD_SUCCESS; +} + +ALIAS (no_debug_ssmpingd, + undebug_ssmpingd_cmd, + "undebug ssmpingd", + UNDEBUG_STR + DEBUG_PIM_STR + DEBUG_SSMPINGD_STR) + DEFUN (debug_pim_zebra, debug_pim_zebra_cmd, "debug pim zebra", @@ -3897,6 +3968,7 @@ void pim_cmd_init() install_element (VIEW_NODE, &show_ip_mroute_cmd); install_element (VIEW_NODE, &show_ip_mroute_count_cmd); install_element (VIEW_NODE, &show_ip_route_cmd); + install_element (VIEW_NODE, &show_ip_ssmpingd_cmd); install_element (VIEW_NODE, &show_debugging_cmd); install_element (ENABLE_NODE, &clear_ip_interfaces_cmd); @@ -3965,6 +4037,9 @@ void pim_cmd_init() install_element (ENABLE_NODE, &debug_pim_trace_cmd); install_element (ENABLE_NODE, &no_debug_pim_trace_cmd); install_element (ENABLE_NODE, &undebug_pim_trace_cmd); + install_element (ENABLE_NODE, &debug_ssmpingd_cmd); + install_element (ENABLE_NODE, &no_debug_ssmpingd_cmd); + install_element (ENABLE_NODE, &undebug_ssmpingd_cmd); install_element (ENABLE_NODE, &debug_pim_zebra_cmd); install_element (ENABLE_NODE, &no_debug_pim_zebra_cmd); install_element (ENABLE_NODE, &undebug_pim_zebra_cmd); @@ -3993,6 +4068,9 @@ void pim_cmd_init() install_element (CONFIG_NODE, &debug_pim_trace_cmd); install_element (CONFIG_NODE, &no_debug_pim_trace_cmd); install_element (CONFIG_NODE, &undebug_pim_trace_cmd); + install_element (CONFIG_NODE, &debug_ssmpingd_cmd); + install_element (CONFIG_NODE, &no_debug_ssmpingd_cmd); + install_element (CONFIG_NODE, &undebug_ssmpingd_cmd); install_element (CONFIG_NODE, &debug_pim_zebra_cmd); install_element (CONFIG_NODE, &no_debug_pim_zebra_cmd); install_element (CONFIG_NODE, &undebug_pim_zebra_cmd); diff --git a/pimd/pim_cmd.h b/pimd/pim_cmd.h index f3b2f96c..800bfcb4 100644 --- a/pimd/pim_cmd.h +++ b/pimd/pim_cmd.h @@ -27,7 +27,8 @@ #define IGMP_STR "IGMP information\n" #define IGMP_GROUP_STR "IGMP groups information\n" #define IGMP_SOURCE_STR "IGMP sources information\n" -#define SSMPINGD_STR "Enable ssmpingd operation\n" +#define CONF_SSMPINGD_STR "Enable ssmpingd operation\n" +#define SHOW_SSMPINGD_STR "ssmpingd operation\n" #define IFACE_PIM_STR "Enable PIM SSM operation\n" #define IFACE_IGMP_STR "Enable IGMP operation\n" #define IFACE_IGMP_QUERY_INTERVAL_STR "IGMP host query interval\n" @@ -42,6 +43,7 @@ #define DEBUG_PIM_PACKETS_STR "PIM protocol packets\n" #define DEBUG_PIM_TRACE_STR "PIM internal daemon activity\n" #define DEBUG_PIM_ZEBRA_STR "ZEBRA protocol activity\n" +#define DEBUG_SSMPINGD_STR "ssmpingd activity\n" #define CLEAR_IP_IGMP_STR "IGMP clear commands\n" #define CLEAR_IP_PIM_STR "PIM clear commands\n" #define MROUTE_STR "IP multicast routing table\n" diff --git a/pimd/pim_ssmpingd.c b/pimd/pim_ssmpingd.c index e4b16aa3..4eb5c8dc 100644 --- a/pimd/pim_ssmpingd.c +++ b/pimd/pim_ssmpingd.c @@ -20,26 +20,328 @@ $QuaggaId: $Format:%an, %ai, %h$ $ */ +#include + +#include "if.h" +#include "log.h" +#include "memory.h" + #include "pim_ssmpingd.h" #include "pim_time.h" +#include "pim_sock.h" +#include "pim_str.h" #include "pimd.h" +static void ssmpingd_read_on(struct ssmpingd_sock *ss); + void pim_ssmpingd_init() { + zassert(!qpim_ssmpingd_list); } void pim_ssmpingd_destroy() { - if (qpim_ssmpingd_list) + if (qpim_ssmpingd_list) { list_free(qpim_ssmpingd_list); + qpim_ssmpingd_list = 0; + } +} + +static struct ssmpingd_sock *ssmpingd_find(struct in_addr source_addr) +{ + struct listnode *node; + struct ssmpingd_sock *ss; + + if (!qpim_ssmpingd_list) + return 0; + + for (ALL_LIST_ELEMENTS_RO(qpim_ssmpingd_list, node, ss)) + if (source_addr.s_addr == ss->source_addr.s_addr) + return ss; + + return 0; +} + +static void ssmpingd_free(struct ssmpingd_sock *ss) +{ + XFREE(MTYPE_PIM_SSMPINGD, ss); +} + +static int ssmpingd_socket(struct in_addr addr, int mttl) +{ + int fd; + + fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (fd < 0) { + zlog_err("%s: could not create socket: errno=%d: %s", + __PRETTY_FUNCTION__, errno, safe_strerror(errno)); + return -1; + } + + /* Needed to obtain destination address from recvmsg() */ + { +#if defined(HAVE_IP_PKTINFO) + /* Linux IP_PKTINFO */ + int opt = 1; + if (setsockopt(fd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt))) { + zlog_warn("Could not set IP_PKTINFO on socket fd=%d: errno=%d: %s", + fd, errno, safe_strerror(errno)); + } +#elif defined(HAVE_IP_RECVDSTADDR) + /* BSD IP_RECVDSTADDR */ + int opt = 1; + if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt))) { + zlog_warn("Could not set IP_RECVDSTADDR on socket fd=%d: errno=%d: %s", + fd, errno, safe_strerror(errno)); + } +#else + zlog_err("%s %s: missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()", + __FILE__, __PRETTY_FUNCTION__); + close(fd); + return -1; +#endif + } + + { + int reuse = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, + (void *) &reuse, sizeof(reuse))) { + zlog_warn("%s: could not set Reuse Address Option on socket fd=%d: errno=%d: %s", + __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno)); + close(fd); + return -1; + } + } + + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, + (void *) &mttl, sizeof(mttl))) { + zlog_warn("%s: could not set multicast TTL=%d on socket fd=%d: errno=%d: %s", + __PRETTY_FUNCTION__, mttl, fd, errno, safe_strerror(errno)); + close(fd); + return -1; + } + + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, + (void *) &addr, sizeof(addr))) { + zlog_warn("%s: could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s", + __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno)); + close(fd); + return -1; + } + + { + long flags; + + flags = fcntl(fd, F_GETFL, 0); + if (flags < 0) { + zlog_warn("%s: could not get fcntl(F_GETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s", + __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno)); + close(fd); + return -1; + } + + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) { + zlog_warn("%s: could not set fcntl(F_SETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s", + __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno)); + close(fd); + return -1; + } + } + + return fd; +} + +static void ssmpingd_delete(struct ssmpingd_sock *ss) +{ + zassert(ss); + zassert(qpim_ssmpingd_list); + + THREAD_OFF(ss->t_sock_read); + + if (close(ss->sock_fd)) { + int e = errno; + char source_str[100]; + pim_inet4_dump("", ss->source_addr, source_str, sizeof(source_str)); + zlog_warn("%s: failure closing ssmpingd sock_fd=%d for source %s: errno=%d: %s", + __PRETTY_FUNCTION__, + ss->sock_fd, source_str, e, safe_strerror(e)); + /* warning only */ + } + + listnode_delete(qpim_ssmpingd_list, ss); + ssmpingd_free(ss); +} + +static int ssmpingd_read_msg(struct ssmpingd_sock *ss) +{ + struct interface *ifp; + struct sockaddr_in from; + struct sockaddr_in to; + socklen_t fromlen = sizeof(from); + socklen_t tolen = sizeof(to); + int ifindex = -1; + char buf[1000]; + int len; + + len = pim_socket_recvfromto(ss->sock_fd, buf, sizeof(buf), + &from, &fromlen, + &to, &tolen, + &ifindex); + if (len < 0) { + char source_str[100]; + pim_inet4_dump("", ss->source_addr, source_str, sizeof(source_str)); + zlog_warn("%s: failure receiving ssmping for source %s on fd=%d: errno=%d: %s", + __PRETTY_FUNCTION__, source_str, ss->sock_fd, errno, safe_strerror(errno)); + return -1; + } + + ifp = if_lookup_by_index(ifindex); + + if (PIM_DEBUG_SSMPINGD) { + char source_str[100]; + char from_str[100]; + char to_str[100]; + pim_inet4_dump("", ss->source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", from.sin_addr, from_str, sizeof(from_str)); + pim_inet4_dump("", to.sin_addr, to_str, sizeof(to_str)); + zlog_debug("%s: ssmpingd on source %s: interface %s ifindex=%d received ssmping from %s to %s on fd=%d", + __PRETTY_FUNCTION__, + source_str, + ifp ? ifp->name : "", + ifindex, from_str, to_str, ss->sock_fd); + } + + return 0; +} + +static int ssmpingd_sock_read(struct thread *t) +{ + struct ssmpingd_sock *ss; + int sock_fd; + int result; + + zassert(t); + + ss = THREAD_ARG(t); + zassert(ss); + + sock_fd = THREAD_FD(t); + zassert(sock_fd == ss->sock_fd); + + result = ssmpingd_read_msg(ss); + + /* Keep reading */ + ss->t_sock_read = 0; + ssmpingd_read_on(ss); + + return result; +} + +static void ssmpingd_read_on(struct ssmpingd_sock *ss) +{ + zassert(!ss->t_sock_read); + THREAD_READ_ON(master, ss->t_sock_read, + ssmpingd_sock_read, ss, ss->sock_fd); +} + +static struct ssmpingd_sock *ssmpingd_new(struct in_addr source_addr) +{ + struct ssmpingd_sock *ss; + int sock_fd; + + if (!qpim_ssmpingd_list) { + qpim_ssmpingd_list = list_new(); + if (!qpim_ssmpingd_list) { + zlog_err("%s %s: failure: qpim_ssmpingd_list=list_new()", + __FILE__, __PRETTY_FUNCTION__); + return 0; + } + qpim_ssmpingd_list->del = (void (*)(void *)) ssmpingd_free; + } + + sock_fd = ssmpingd_socket(source_addr, 64 /* ttl */); + if (sock_fd < 0) { + char source_str[100]; + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + zlog_warn("%s: ssmpingd_socket() failure for source %s", + __PRETTY_FUNCTION__, source_str); + return 0; + } + + ss = XMALLOC(MTYPE_PIM_SSMPINGD, sizeof(*ss)); + if (!ss) { + char source_str[100]; + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + zlog_err("%s: XMALLOC(%d) failure for ssmpingd source %s", + __PRETTY_FUNCTION__, + sizeof(*ss), source_str); + close(sock_fd); + return 0; + } + + ss->sock_fd = sock_fd; + ss->t_sock_read = 0; + ss->source_addr = source_addr; + ss->creation = pim_time_monotonic_sec(); + ss->requests = 0; + + listnode_add(qpim_ssmpingd_list, ss); + + ssmpingd_read_on(ss); + + return ss; } int pim_ssmpingd_start(struct in_addr source_addr) { + struct ssmpingd_sock *ss; + + ss = ssmpingd_find(source_addr); + if (ss) { + /* silently ignore request to recreate entry */ + return 0; + } + + { + char source_str[100]; + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + zlog_info("%s: starting ssmpingd for source %s", + __PRETTY_FUNCTION__, source_str); + } + + ss = ssmpingd_new(source_addr); + if (!ss) { + char source_str[100]; + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + zlog_warn("%s: ssmpingd_new() failure for source %s", + __PRETTY_FUNCTION__, source_str); + return -1; + } + return 0; } int pim_ssmpingd_stop(struct in_addr source_addr) { + struct ssmpingd_sock *ss; + + ss = ssmpingd_find(source_addr); + if (!ss) { + char source_str[100]; + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + zlog_warn("%s: could not find ssmpingd for source %s", + __PRETTY_FUNCTION__, source_str); + return -1; + } + + { + char source_str[100]; + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + zlog_info("%s: stopping ssmpingd for source %s", + __PRETTY_FUNCTION__, source_str); + } + + ssmpingd_delete(ss); + return 0; } diff --git a/pimd/pim_ssmpingd.h b/pimd/pim_ssmpingd.h index 32563aeb..4bef20b2 100644 --- a/pimd/pim_ssmpingd.h +++ b/pimd/pim_ssmpingd.h @@ -32,7 +32,7 @@ struct ssmpingd_sock { int sock_fd; /* socket */ struct thread *t_sock_read; /* thread for reading socket */ - struct in_addr source; /* source address */ + struct in_addr source_addr; /* source address */ int64_t creation; /* timestamp of socket creation */ int64_t requests; /* counter */ }; diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c index 7d69b5b1..0b06d0ec 100644 --- a/pimd/pim_vty.c +++ b/pimd/pim_vty.c @@ -30,6 +30,7 @@ #include "pim_iface.h" #include "pim_cmd.h" #include "pim_str.h" +#include "pim_ssmpingd.h" int pim_debug_config_write(struct vty *vty) { @@ -66,6 +67,11 @@ int pim_debug_config_write(struct vty *vty) ++writes; } + if (PIM_DEBUG_SSMPINGD) { + vty_out(vty, "debug ssmpingd%s", VTY_NEWLINE); + ++writes; + } + return writes; } @@ -78,6 +84,19 @@ int pim_global_config_write(struct vty *vty) ++writes; } + if (qpim_ssmpingd_list) { + struct listnode *node; + struct ssmpingd_sock *ss; + vty_out(vty, "!%s", VTY_NEWLINE); + ++writes; + for (ALL_LIST_ELEMENTS_RO(qpim_ssmpingd_list, node, ss)) { + char source_str[100]; + pim_inet4_dump("", ss->source_addr, source_str, sizeof(source_str)); + vty_out(vty, "ip ssmpingd %s%s", source_str, VTY_NEWLINE); + ++writes; + } + } + return writes; } @@ -91,7 +110,7 @@ int pim_interface_config_write(struct vty *vty) /* IF name */ vty_out(vty, "interface %s%s", ifp->name, VTY_NEWLINE); - writes++; + ++writes; if (ifp->info) { struct pim_interface *pim_ifp = ifp->info; @@ -99,13 +118,13 @@ int pim_interface_config_write(struct vty *vty) /* IF ip pim ssm */ if (PIM_IF_TEST_PIM(pim_ifp->options)) { vty_out(vty, " ip pim ssm%s", VTY_NEWLINE); - writes++; + ++writes; } /* IF ip igmp */ if (PIM_IF_TEST_IGMP(pim_ifp->options)) { vty_out(vty, " ip igmp%s", VTY_NEWLINE); - writes++; + ++writes; } /* IF ip igmp query-interval */ @@ -113,14 +132,14 @@ int pim_interface_config_write(struct vty *vty) PIM_CMD_IP_IGMP_QUERY_INTERVAL, pim_ifp->igmp_default_query_interval, VTY_NEWLINE); - writes++; + ++writes; /* IF ip igmp query-max-response-time */ vty_out(vty, " %s %d%s", PIM_CMD_IP_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC, pim_ifp->igmp_query_max_response_time_dsec, VTY_NEWLINE); - writes++; + ++writes; /* IF ip igmp join */ if (pim_ifp->igmp_join_list) { @@ -134,11 +153,12 @@ int pim_interface_config_write(struct vty *vty) vty_out(vty, " ip igmp join %s %s%s", group_str, source_str, VTY_NEWLINE); - writes++; + ++writes; } } } vty_out(vty, "!%s", VTY_NEWLINE); + ++writes; } return writes; diff --git a/pimd/pimd.h b/pimd/pimd.h index bdf83b43..a2dc6430 100644 --- a/pimd/pimd.h +++ b/pimd/pimd.h @@ -60,6 +60,7 @@ #define PIM_MASK_IGMP_PACKETS (1 << 4) #define PIM_MASK_IGMP_TRACE (1 << 5) #define PIM_MASK_ZEBRA (1 << 6) +#define PIM_MASK_SSMPINGD (1 << 7) const char *const PIM_ALL_SYSTEMS; const char *const PIM_ALL_ROUTERS; @@ -98,6 +99,7 @@ struct list *qpim_ssmpingd_list; /* list of struct ssmpingd_sock */ #define PIM_DEBUG_IGMP_PACKETS (qpim_debugs & PIM_MASK_IGMP_PACKETS) #define PIM_DEBUG_IGMP_TRACE (qpim_debugs & PIM_MASK_IGMP_TRACE) #define PIM_DEBUG_ZEBRA (qpim_debugs & PIM_MASK_ZEBRA) +#define PIM_DEBUG_SSMPINGD (qpim_debugs & PIM_MASK_SSMPINGD) #define PIM_DEBUG_EVENTS (qpim_debugs & (PIM_MASK_PIM_EVENTS | PIM_MASK_IGMP_EVENTS)) #define PIM_DEBUG_PACKETS (qpim_debugs & (PIM_MASK_PIM_PACKETS | PIM_MASK_IGMP_PACKETS)) @@ -110,6 +112,7 @@ struct list *qpim_ssmpingd_list; /* list of struct ssmpingd_sock */ #define PIM_DO_DEBUG_IGMP_PACKETS (qpim_debugs |= PIM_MASK_IGMP_PACKETS) #define PIM_DO_DEBUG_IGMP_TRACE (qpim_debugs |= PIM_MASK_IGMP_TRACE) #define PIM_DO_DEBUG_ZEBRA (qpim_debugs |= PIM_MASK_ZEBRA) +#define PIM_DO_DEBUG_SSMPINGD (qpim_debugs |= PIM_MASK_SSMPINGD) #define PIM_DONT_DEBUG_PIM_EVENTS (qpim_debugs &= ~PIM_MASK_PIM_EVENTS) #define PIM_DONT_DEBUG_PIM_PACKETS (qpim_debugs &= ~PIM_MASK_PIM_PACKETS) @@ -118,6 +121,7 @@ struct list *qpim_ssmpingd_list; /* list of struct ssmpingd_sock */ #define PIM_DONT_DEBUG_IGMP_PACKETS (qpim_debugs &= ~PIM_MASK_IGMP_PACKETS) #define PIM_DONT_DEBUG_IGMP_TRACE (qpim_debugs &= ~PIM_MASK_IGMP_TRACE) #define PIM_DONT_DEBUG_ZEBRA (qpim_debugs &= ~PIM_MASK_ZEBRA) +#define PIM_DONT_DEBUG_SSMPINGD (qpim_debugs &= ~PIM_MASK_SSMPINGD) void pim_init(void); void pim_terminate(void); From e8c11bbf75c881e3beaadb85d5485161855424a7 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Thu, 8 Oct 2009 15:06:32 -0300 Subject: [PATCH 326/482] [pim] T41 DONE ssmping support --- pimd/TODO | 13 ++++- pimd/pim_cmd.c | 17 ++++++- pimd/pim_sock.c | 54 ++++++++++++++++++++ pimd/pim_sock.h | 3 ++ pimd/pim_ssmpingd.c | 119 ++++++++++++++++++++++++++++++++++++++++---- pimd/pimd.c | 1 + pimd/pimd.h | 1 + 7 files changed, 195 insertions(+), 13 deletions(-) diff --git a/pimd/TODO b/pimd/TODO index dc2ece5f..0818d9c1 100644 --- a/pimd/TODO +++ b/pimd/TODO @@ -354,7 +354,16 @@ T40 Lightweight MLDv2 http://www.ietf.org/internet-drafts/draft-ietf-mboned-lightweight-igmpv3-mldv2-05.txt http://www.ietf.org/html.charters/mboned-charter.html -T41 ssmping - http://www.venaas.no/multicast/ssmping/ +T41 DONE ssmping support + See http://www.venaas.no/multicast/ssmping/ + + Example: + + debug ssmpingd + + conf t + ip ssmpingd 1.1.1.1 + + show ip ssmpingd -x- diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index a4073f50..eed74927 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -2143,7 +2143,7 @@ static void show_ssmpingd(struct vty *vty) struct ssmpingd_sock *ss; time_t now; - vty_out(vty, "Source Socket Uptime Requests%s", + vty_out(vty, "Source Socket Address Port Uptime Requests%s", VTY_NEWLINE); if (!qpim_ssmpingd_list) @@ -2154,13 +2154,25 @@ static void show_ssmpingd(struct vty *vty) for (ALL_LIST_ELEMENTS_RO(qpim_ssmpingd_list, node, ss)) { char source_str[100]; char ss_uptime[10]; + struct sockaddr_in bind_addr; + int len = sizeof(bind_addr); + char bind_addr_str[100]; pim_inet4_dump("", ss->source_addr, source_str, sizeof(source_str)); + + if (pim_socket_getsockname(ss->sock_fd, (struct sockaddr *) &bind_addr, &len)) { + vty_out(vty, "%% Failure reading socket name for ssmpingd source %s on fd=%d%s", + source_str, ss->sock_fd, VTY_NEWLINE); + } + + pim_inet4_dump("", bind_addr.sin_addr, bind_addr_str, sizeof(bind_addr_str)); pim_time_uptime(ss_uptime, sizeof(ss_uptime), now - ss->creation); - vty_out(vty, "%-15s %6d %8s %8lld%s", + vty_out(vty, "%-15s %6d %-15s %5d %8s %8lld%s", source_str, ss->sock_fd, + bind_addr_str, + ntohs(bind_addr.sin_port), ss_uptime, ss->requests, VTY_NEWLINE); @@ -4004,6 +4016,7 @@ void pim_cmd_init() install_element (ENABLE_NODE, &show_ip_mroute_cmd); install_element (ENABLE_NODE, &show_ip_mroute_count_cmd); install_element (ENABLE_NODE, &show_ip_route_cmd); + install_element (ENABLE_NODE, &show_ip_ssmpingd_cmd); install_element (ENABLE_NODE, &show_debugging_cmd); install_element (ENABLE_NODE, &test_igmp_receive_report_cmd); diff --git a/pimd/pim_sock.c b/pimd/pim_sock.c index c908e8b8..2e786054 100644 --- a/pimd/pim_sock.c +++ b/pimd/pim_sock.c @@ -260,6 +260,29 @@ int pim_socket_recvfromto(int fd, char *buf, size_t len, char cbuf[1000]; int err; + /* + * IP_PKTINFO / IP_RECVDSTADDR don't yield sin_port. + * Use getsockname() to get sin_port. + */ + if (to) { + struct sockaddr_in si; + socklen_t si_len = sizeof(si); + + ((struct sockaddr_in *) to)->sin_family = AF_INET; + + if (pim_socket_getsockname(fd, (struct sockaddr *) &si, &si_len)) { + ((struct sockaddr_in *) to)->sin_port = ntohs(0); + ((struct sockaddr_in *) to)->sin_addr.s_addr = ntohl(0); + } + else { + ((struct sockaddr_in *) to)->sin_port = si.sin_port; + ((struct sockaddr_in *) to)->sin_addr = si.sin_addr; + } + + if (tolen) + *tolen = sizeof(si); + } + memset(&msgh, 0, sizeof(struct msghdr)); iov.iov_base = buf; iov.iov_len = len; @@ -291,6 +314,15 @@ int pim_socket_recvfromto(int fd, char *buf, size_t len, *tolen = sizeof(struct sockaddr_in); if (ifindex) *ifindex = i->ipi_ifindex; + + if (to && PIM_DEBUG_PACKETS) { + char to_str[100]; + pim_inet4_dump("", to->sin_addr, to_str, sizeof(to_str)); + zlog_debug("%s: HAVE_IP_PKTINFO to=%s,%d", + __PRETTY_FUNCTION__, + to_str, ntohs(to->sin_port)); + } + break; } #endif @@ -302,6 +334,15 @@ int pim_socket_recvfromto(int fd, char *buf, size_t len, ((struct sockaddr_in *) to)->sin_addr = *i; if (tolen) *tolen = sizeof(struct sockaddr_in); + + if (to && PIM_DEBUG_PACKETS) { + char to_str[100]; + pim_inet4_dump("", to->sin_addr, to_str, sizeof(to_str)); + zlog_debug("%s: HAVE_IP_RECVDSTADDR to=%s,%d", + __PRETTY_FUNCTION__, + to_str, ntohs(to->sin_port)); + } + break; } #endif @@ -333,3 +374,16 @@ int pim_socket_mcastloop_get(int fd) return loop; } + +int pim_socket_getsockname(int fd, struct sockaddr *name, int *namelen) +{ + if (getsockname(fd, name, namelen)) { + int e = errno; + zlog_warn("Could not get Socket Name for socket fd=%d: errno=%d: %s", + fd, errno, safe_strerror(errno)); + errno = e; + return PIM_SOCK_ERR_NAME; + } + + return PIM_SOCK_ERR_NONE; +} diff --git a/pimd/pim_sock.h b/pimd/pim_sock.h index d3557809..3f026dcd 100644 --- a/pimd/pim_sock.h +++ b/pimd/pim_sock.h @@ -35,6 +35,7 @@ #define PIM_SOCK_ERR_DSTADDR (-7) /* Outgoing interface option */ #define PIM_SOCK_ERR_NONBLOCK_GETFL (-8) /* Get O_NONBLOCK */ #define PIM_SOCK_ERR_NONBLOCK_SETFL (-9) /* Set O_NONBLOCK */ +#define PIM_SOCK_ERR_NAME (-10) /* Socket name (getsockname) */ int pim_socket_raw(int protocol); int pim_socket_mcast(int protocol, struct in_addr ifaddr, int loop); @@ -51,4 +52,6 @@ int pim_socket_recvfromto(int fd, char *buf, size_t len, int pim_socket_mcastloop_get(int fd); +int pim_socket_getsockname(int fd, struct sockaddr *name, int *namelen); + #endif /* PIM_SOCK_H */ diff --git a/pimd/pim_ssmpingd.c b/pimd/pim_ssmpingd.c index 4eb5c8dc..6422cf62 100644 --- a/pimd/pim_ssmpingd.c +++ b/pimd/pim_ssmpingd.c @@ -32,11 +32,24 @@ #include "pim_str.h" #include "pimd.h" +static const char * const PIM_SSMPINGD_REPLY_GROUP = "232.43.211.234"; + +enum { + PIM_SSMPINGD_REQUEST = 'Q', + PIM_SSMPINGD_REPLY = 'A' +}; + static void ssmpingd_read_on(struct ssmpingd_sock *ss); void pim_ssmpingd_init() { + int result; + zassert(!qpim_ssmpingd_list); + + result = inet_pton(AF_INET, PIM_SSMPINGD_REPLY_GROUP, &qpim_ssmpingd_group_addr); + + zassert(result > 0); } void pim_ssmpingd_destroy() @@ -67,8 +80,9 @@ static void ssmpingd_free(struct ssmpingd_sock *ss) XFREE(MTYPE_PIM_SSMPINGD, ss); } -static int ssmpingd_socket(struct in_addr addr, int mttl) +static int ssmpingd_socket(struct in_addr addr, int port, int mttl) { + struct sockaddr_in sockaddr; int fd; fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); @@ -78,21 +92,36 @@ static int ssmpingd_socket(struct in_addr addr, int mttl) return -1; } + sockaddr.sin_family = AF_INET; + sockaddr.sin_addr = addr; + sockaddr.sin_port = htons(port); + + if (bind(fd, &sockaddr, sizeof(sockaddr))) { + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_warn("%s: bind(fd=%d,addr=%s,port=%d,len=%d) failure: errno=%d: %s", + __PRETTY_FUNCTION__, + fd, addr_str, port, sizeof(sockaddr), + errno, safe_strerror(errno)); + close(fd); + return -1; + } + /* Needed to obtain destination address from recvmsg() */ { #if defined(HAVE_IP_PKTINFO) /* Linux IP_PKTINFO */ int opt = 1; if (setsockopt(fd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt))) { - zlog_warn("Could not set IP_PKTINFO on socket fd=%d: errno=%d: %s", - fd, errno, safe_strerror(errno)); + zlog_warn("%s: could not set IP_PKTINFO on socket fd=%d: errno=%d: %s", + __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno)); } #elif defined(HAVE_IP_RECVDSTADDR) /* BSD IP_RECVDSTADDR */ int opt = 1; if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt))) { - zlog_warn("Could not set IP_RECVDSTADDR on socket fd=%d: errno=%d: %s", - fd, errno, safe_strerror(errno)); + zlog_warn("%s: could not set IP_RECVDSTADDR on socket fd=%d: errno=%d: %s", + __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno)); } #else zlog_err("%s %s: missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()", @@ -121,6 +150,19 @@ static int ssmpingd_socket(struct in_addr addr, int mttl) return -1; } + { + int loop = 0; + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, + (void *) &loop, sizeof(loop))) { + zlog_warn("%s: could not %s Multicast Loopback Option on socket fd=%d: errno=%d: %s", + __PRETTY_FUNCTION__, + loop ? "enable" : "disable", + fd, errno, safe_strerror(errno)); + close(fd); + return PIM_SOCK_ERR_LOOP; + } + } + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, (void *) &addr, sizeof(addr))) { zlog_warn("%s: could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s", @@ -172,6 +214,34 @@ static void ssmpingd_delete(struct ssmpingd_sock *ss) ssmpingd_free(ss); } +static void ssmpingd_sendto(struct ssmpingd_sock *ss, + const char *buf, + int len, + struct sockaddr_in to) +{ + socklen_t tolen = sizeof(to); + int sent; + + sent = sendto(ss->sock_fd, buf, len, MSG_DONTWAIT, &to, tolen); + if (sent != len) { + int e = errno; + char to_str[100]; + pim_inet4_dump("", to.sin_addr, to_str, sizeof(to_str)); + if (sent < 0) { + zlog_warn("%s: sendto() failure to %s,%d: fd=%d len=%d: errno=%d: %s", + __PRETTY_FUNCTION__, + to_str, ntohs(to.sin_port), ss->sock_fd, len, + e, safe_strerror(e)); + } + else { + zlog_warn("%s: sendto() partial to %s,%d: fd=%d len=%d: sent=%d", + __PRETTY_FUNCTION__, + to_str, ntohs(to.sin_port), ss->sock_fd, + len, sent); + } + } +} + static int ssmpingd_read_msg(struct ssmpingd_sock *ss) { struct interface *ifp; @@ -183,6 +253,8 @@ static int ssmpingd_read_msg(struct ssmpingd_sock *ss) char buf[1000]; int len; + ++ss->requests; + len = pim_socket_recvfromto(ss->sock_fd, buf, sizeof(buf), &from, &fromlen, &to, &tolen, @@ -197,6 +269,24 @@ static int ssmpingd_read_msg(struct ssmpingd_sock *ss) ifp = if_lookup_by_index(ifindex); + if (buf[0] != PIM_SSMPINGD_REQUEST) { + char source_str[100]; + char from_str[100]; + char to_str[100]; + pim_inet4_dump("", ss->source_addr, source_str, sizeof(source_str)); + pim_inet4_dump("", from.sin_addr, from_str, sizeof(from_str)); + pim_inet4_dump("", to.sin_addr, to_str, sizeof(to_str)); + zlog_warn("%s: bad ssmping type=%d from %s,%d to %s,%d on interface %s ifindex=%d fd=%d src=%s", + __PRETTY_FUNCTION__, + buf[0], + from_str, ntohs(from.sin_port), + to_str, ntohs(to.sin_port), + ifp ? ifp->name : "", + ifindex, ss->sock_fd, + source_str); + return 0; + } + if (PIM_DEBUG_SSMPINGD) { char source_str[100]; char from_str[100]; @@ -204,13 +294,24 @@ static int ssmpingd_read_msg(struct ssmpingd_sock *ss) pim_inet4_dump("", ss->source_addr, source_str, sizeof(source_str)); pim_inet4_dump("", from.sin_addr, from_str, sizeof(from_str)); pim_inet4_dump("", to.sin_addr, to_str, sizeof(to_str)); - zlog_debug("%s: ssmpingd on source %s: interface %s ifindex=%d received ssmping from %s to %s on fd=%d", + zlog_debug("%s: recv ssmping from %s,%d to %s,%d on interface %s ifindex=%d fd=%d src=%s", __PRETTY_FUNCTION__, - source_str, + from_str, ntohs(from.sin_port), + to_str, ntohs(to.sin_port), ifp ? ifp->name : "", - ifindex, from_str, to_str, ss->sock_fd); + ifindex, ss->sock_fd, + source_str); } + buf[0] = PIM_SSMPINGD_REPLY; + + /* unicast reply */ + ssmpingd_sendto(ss, buf, len, from); + + /* multicast reply */ + from.sin_addr = qpim_ssmpingd_group_addr; + ssmpingd_sendto(ss, buf, len, from); + return 0; } @@ -259,7 +360,7 @@ static struct ssmpingd_sock *ssmpingd_new(struct in_addr source_addr) qpim_ssmpingd_list->del = (void (*)(void *)) ssmpingd_free; } - sock_fd = ssmpingd_socket(source_addr, 64 /* ttl */); + sock_fd = ssmpingd_socket(source_addr, /* port: */ 4321, /* mTTL: */ 64); if (sock_fd < 0) { char source_str[100]; pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); diff --git a/pimd/pimd.c b/pimd/pimd.c index 220604d9..71b74e97 100644 --- a/pimd/pimd.c +++ b/pimd/pimd.c @@ -60,6 +60,7 @@ int64_t qpim_rpf_cache_refresh_events = 0; int64_t qpim_rpf_cache_refresh_last = 0; struct in_addr qpim_inaddr_any; struct list *qpim_ssmpingd_list = 0; +struct in_addr qpim_ssmpingd_group_addr; static void pim_free() { diff --git a/pimd/pimd.h b/pimd/pimd.h index a2dc6430..b42c54cf 100644 --- a/pimd/pimd.h +++ b/pimd/pimd.h @@ -86,6 +86,7 @@ int64_t qpim_rpf_cache_refresh_events; int64_t qpim_rpf_cache_refresh_last; struct in_addr qpim_inaddr_any; struct list *qpim_ssmpingd_list; /* list of struct ssmpingd_sock */ +struct in_addr qpim_ssmpingd_group_addr; #define PIM_JP_HOLDTIME (qpim_t_periodic * 7 / 2) From 8bc0b34a2bee1627de04415fe2fddecb3d71e165 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Thu, 8 Oct 2009 15:29:59 -0300 Subject: [PATCH 327/482] [pim] Sample config for ssmpingd support --- pimd/pimd.conf.sample | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pimd/pimd.conf.sample b/pimd/pimd.conf.sample index 2f4543ec..67530856 100644 --- a/pimd/pimd.conf.sample +++ b/pimd/pimd.conf.sample @@ -18,6 +18,12 @@ line vty ! ip multicast-routing ! +! ! You may want to enable ssmpingd for troubleshooting +! ! See http://www.venaas.no/multicast/ssmping/ +! ! +! ip ssmpingd 1.1.1.1 +! ip ssmpingd 2.2.2.2 +! ! ! HINTS: ! ! - Enable "ip pim ssm" on the interface directly attached to the ! ! multicast source host (if this is the first-hop router) From e85a9371c01751273858db94aa5b0f99b11dbf1f Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Thu, 8 Oct 2009 16:05:44 -0300 Subject: [PATCH 328/482] [pim] ssmpingd commands --- pimd/COMMANDS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pimd/COMMANDS b/pimd/COMMANDS index 5679e95a..60a4dbe7 100644 --- a/pimd/COMMANDS +++ b/pimd/COMMANDS @@ -2,6 +2,7 @@ global configuration commands: ip multicast-routing Enable IP multicast forwarding + ip ssmpingd Enable ssmpingd operation interface configuration commands: ip igmp Enable IGMP operation @@ -40,6 +41,7 @@ verification commands: show ip mroute IP multicast routing table show ip mroute count Route and packet count data show ip route IP routing table + show ip ssmpingd ssmpingd operation debug commands: clear ip interfaces Reset interfaces @@ -48,6 +50,7 @@ debug commands: debug igmp IGMP protocol activity debug pim PIM protocol activity debug pim zebra ZEBRA protocol activity + debug ssmpingd ssmpingd activity show debugging State of each debugging option test igmp receive report Test reception of IGMPv3 report test pim receive assert Test reception of PIM assert From 779e220f30ace11acb23ba81ed6eaef51db6f1e4 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Fri, 16 Oct 2009 08:40:11 -0300 Subject: [PATCH 329/482] [pim] Version up to 0.159 --- pimd/pim_version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pimd/pim_version.h b/pimd/pim_version.h index fa6dd552..546ace3b 100644 --- a/pimd/pim_version.h +++ b/pimd/pim_version.h @@ -23,7 +23,7 @@ #ifndef PIM_VERSION_H #define PIM_VERSION_H -#define PIMD_VERSION_STR "0.158" +#define PIMD_VERSION_STR "0.159" const char * const PIMD_VERSION; From 3466dae420942b3a5b342c95d5667e1927409e8a Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Wed, 21 Oct 2009 11:33:47 -0200 Subject: [PATCH 330/482] [pim] Reference about draft mboned-ssmping --- pimd/TODO | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pimd/TODO b/pimd/TODO index 0818d9c1..1dc561dd 100644 --- a/pimd/TODO +++ b/pimd/TODO @@ -355,7 +355,11 @@ T40 Lightweight MLDv2 http://www.ietf.org/html.charters/mboned-charter.html T41 DONE ssmping support - See http://www.venaas.no/multicast/ssmping/ + + See also: + http://www.venaas.no/multicast/ssmping/ + draft-ietf-mboned-ssmping-07 + http://tools.ietf.org/html/draft-ietf-mboned-ssmping-07 Example: From e0b8b9b6e465bc8ae4a416e5297fa682d7feb39e Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Tue, 17 Nov 2009 10:17:21 -0200 Subject: [PATCH 331/482] [pim] Clean-up log messages --- pimd/pim_hello.c | 2 +- pimd/pim_neighbor.c | 51 ++++++++++++++++++++++----------------------- 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/pimd/pim_hello.c b/pimd/pim_hello.c index 8b710b8c..545b3b12 100644 --- a/pimd/pim_hello.c +++ b/pimd/pim_hello.c @@ -197,7 +197,7 @@ int pim_hello_recv(struct interface *ifp, if ((tlv_curr + option_len) > tlv_pastend) { char src_str[100]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); - zlog_warn("%s: long PIM hello TLV type=%d length=%d > max=%d from %s on interface %s", + zlog_warn("%s: long PIM hello TLV type=%d length=%d > left=%d from %s on interface %s", __PRETTY_FUNCTION__, option_type, option_len, tlv_pastend - tlv_curr, src_str, ifp->name); diff --git a/pimd/pim_neighbor.c b/pimd/pim_neighbor.c index b4112edf..9013b50c 100644 --- a/pimd/pim_neighbor.c +++ b/pimd/pim_neighbor.c @@ -46,9 +46,11 @@ static void dr_election_by_addr(struct interface *ifp) pim_ifp->pim_dr_addr = pim_ifp->primary_address; - zlog_info("%s on interface %s", - __PRETTY_FUNCTION__, - ifp->name); + if (PIM_DEBUG_PIM_TRACE) { + zlog_debug("%s: on interface %s", + __PRETTY_FUNCTION__, + ifp->name); + } for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, node, neigh)) { if (ntohl(neigh->source_addr.s_addr) > ntohl(pim_ifp->pim_dr_addr.s_addr)) { @@ -70,15 +72,20 @@ static void dr_election_by_pri(struct interface *ifp) pim_ifp->pim_dr_addr = pim_ifp->primary_address; dr_pri = pim_ifp->pim_dr_priority; - zlog_info("%s: dr pri %u on interface %s", - __PRETTY_FUNCTION__, - dr_pri, ifp->name); - + if (PIM_DEBUG_PIM_TRACE) { + zlog_debug("%s: dr pri %u on interface %s", + __PRETTY_FUNCTION__, + dr_pri, ifp->name); + } for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, node, neigh)) { - zlog_info("%s: neigh pri %u addr %x if dr addr %x", - __PRETTY_FUNCTION__, - neigh->dr_priority, ntohl(neigh->source_addr.s_addr) , ntohl(pim_ifp->pim_dr_addr.s_addr) ); + if (PIM_DEBUG_PIM_TRACE) { + zlog_info("%s: neigh pri %u addr %x if dr addr %x", + __PRETTY_FUNCTION__, + neigh->dr_priority, + ntohl(neigh->source_addr.s_addr), + ntohl(pim_ifp->pim_dr_addr.s_addr)); + } if ( (neigh->dr_priority > dr_pri) || ( @@ -103,8 +110,6 @@ void pim_if_dr_election(struct interface *ifp) { struct pim_interface *pim_ifp = ifp->info; struct in_addr old_dr_addr; - char dr_old_str[100]; - char dr_new_str[100]; pim_ifp->pim_dr_election_last = pim_time_monotonic_sec(); /* timestamp */ ++pim_ifp->pim_dr_election_count; @@ -118,22 +123,16 @@ void pim_if_dr_election(struct interface *ifp) dr_election_by_pri(ifp); } - pim_inet4_dump("", old_dr_addr, dr_old_str, sizeof(dr_old_str)); - pim_inet4_dump("", pim_ifp->pim_dr_addr, dr_new_str, sizeof(dr_new_str)); - zlog_info("%s: DR was %s now is %s on interface %s", - __PRETTY_FUNCTION__, - dr_old_str, dr_new_str, ifp->name); - /* DR changed ? */ if (old_dr_addr.s_addr != pim_ifp->pim_dr_addr.s_addr) { - /* char dr_old_str[100]; */ - /* char dr_new_str[100]; */ - /* pim_inet4_dump("", old_dr_addr, dr_old_str, sizeof(dr_old_str)); */ - /* pim_inet4_dump("", pim_ifp->pim_dr_addr, dr_new_str, sizeof(dr_new_str)); */ - /* zlog_info("%s: DR was %s now is %s on interface %s", */ - /* __PRETTY_FUNCTION__, */ - /* dr_old_str, dr_new_str, ifp->name); */ - + char dr_old_str[100]; + char dr_new_str[100]; + pim_inet4_dump("", old_dr_addr, dr_old_str, sizeof(dr_old_str)); + pim_inet4_dump("", pim_ifp->pim_dr_addr, dr_new_str, sizeof(dr_new_str)); + zlog_info("%s: DR was %s now is %s on interface %s", + __PRETTY_FUNCTION__, + dr_old_str, dr_new_str, ifp->name); + pim_if_update_join_desired(pim_ifp); pim_if_update_could_assert(ifp); pim_if_update_assert_tracking_desired(ifp); From 627380420e60a4b944646eb1ce2b1552e6e03565 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Wed, 18 Nov 2009 10:44:13 -0200 Subject: [PATCH 332/482] [pim] Packet dump debugging --- pimd/pim_cmd.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++ pimd/pim_cmd.h | 3 ++ pimd/pim_pim.c | 12 ++++++++ pimd/pim_util.c | 27 ++++++++++++++++++ pimd/pim_util.h | 2 ++ pimd/pim_vty.c | 8 ++++++ pimd/pimd.h | 74 ++++++++++++++++++++++++++--------------------- 7 files changed, 169 insertions(+), 33 deletions(-) diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index eed74927..d42f3bf8 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -3086,6 +3086,10 @@ DEFUN (no_debug_pim, PIM_DONT_DEBUG_PIM_EVENTS; PIM_DONT_DEBUG_PIM_PACKETS; PIM_DONT_DEBUG_PIM_TRACE; + + PIM_DONT_DEBUG_PIM_PACKETDUMP_SEND; + PIM_DONT_DEBUG_PIM_PACKETDUMP_RECV; + return CMD_SUCCESS; } @@ -3155,6 +3159,72 @@ ALIAS (no_debug_pim_packets, DEBUG_PIM_STR DEBUG_PIM_PACKETS_STR) +DEFUN (debug_pim_packetdump_send, + debug_pim_packetdump_send_cmd, + "debug pim packet-dump send", + DEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_PACKETDUMP_STR + DEBUG_PIM_PACKETDUMP_SEND_STR) +{ + PIM_DO_DEBUG_PIM_PACKETDUMP_SEND; + return CMD_SUCCESS; +} + +DEFUN (no_debug_pim_packetdump_send, + no_debug_pim_packetdump_send_cmd, + "no debug pim packet-dump send", + NO_STR + DEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_PACKETDUMP_STR + DEBUG_PIM_PACKETDUMP_SEND_STR) +{ + PIM_DONT_DEBUG_PIM_PACKETDUMP_SEND; + return CMD_SUCCESS; +} + +ALIAS (no_debug_pim_packetdump_send, + undebug_pim_packetdump_send_cmd, + "undebug pim packet-dump send", + UNDEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_PACKETDUMP_STR + DEBUG_PIM_PACKETDUMP_SEND_STR) + +DEFUN (debug_pim_packetdump_recv, + debug_pim_packetdump_recv_cmd, + "debug pim packet-dump receive", + DEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_PACKETDUMP_STR + DEBUG_PIM_PACKETDUMP_RECV_STR) +{ + PIM_DO_DEBUG_PIM_PACKETDUMP_RECV; + return CMD_SUCCESS; +} + +DEFUN (no_debug_pim_packetdump_recv, + no_debug_pim_packetdump_recv_cmd, + "no debug pim packet-dump receive", + NO_STR + DEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_PACKETDUMP_STR + DEBUG_PIM_PACKETDUMP_RECV_STR) +{ + PIM_DONT_DEBUG_PIM_PACKETDUMP_RECV; + return CMD_SUCCESS; +} + +ALIAS (no_debug_pim_packetdump_recv, + undebug_pim_packetdump_recv_cmd, + "undebug pim packet-dump receive", + UNDEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_PACKETDUMP_STR + DEBUG_PIM_PACKETDUMP_RECV_STR) + DEFUN (debug_pim_trace, debug_pim_trace_cmd, "debug pim trace", @@ -4047,6 +4117,12 @@ void pim_cmd_init() install_element (ENABLE_NODE, &debug_pim_packets_cmd); install_element (ENABLE_NODE, &no_debug_pim_packets_cmd); install_element (ENABLE_NODE, &undebug_pim_packets_cmd); + install_element (ENABLE_NODE, &debug_pim_packetdump_send_cmd); + install_element (ENABLE_NODE, &no_debug_pim_packetdump_send_cmd); + install_element (ENABLE_NODE, &undebug_pim_packetdump_send_cmd); + install_element (ENABLE_NODE, &debug_pim_packetdump_recv_cmd); + install_element (ENABLE_NODE, &no_debug_pim_packetdump_recv_cmd); + install_element (ENABLE_NODE, &undebug_pim_packetdump_recv_cmd); install_element (ENABLE_NODE, &debug_pim_trace_cmd); install_element (ENABLE_NODE, &no_debug_pim_trace_cmd); install_element (ENABLE_NODE, &undebug_pim_trace_cmd); diff --git a/pimd/pim_cmd.h b/pimd/pim_cmd.h index 800bfcb4..fd1a62fa 100644 --- a/pimd/pim_cmd.h +++ b/pimd/pim_cmd.h @@ -41,6 +41,9 @@ #define DEBUG_PIM_STR "PIM protocol activity\n" #define DEBUG_PIM_EVENTS_STR "PIM protocol events\n" #define DEBUG_PIM_PACKETS_STR "PIM protocol packets\n" +#define DEBUG_PIM_PACKETDUMP_STR "PIM packet dump\n" +#define DEBUG_PIM_PACKETDUMP_SEND_STR "Dump sent packets\n" +#define DEBUG_PIM_PACKETDUMP_RECV_STR "Dump received packets\n" #define DEBUG_PIM_TRACE_STR "PIM internal daemon activity\n" #define DEBUG_PIM_ZEBRA_STR "ZEBRA protocol activity\n" #define DEBUG_SSMPINGD_STR "ssmpingd activity\n" diff --git a/pimd/pim_pim.c b/pimd/pim_pim.c index 9b6dc359..9595d2d7 100644 --- a/pimd/pim_pim.c +++ b/pimd/pim_pim.c @@ -169,6 +169,10 @@ int pim_pim_packet(struct interface *ifp, char *buf, size_t len) pim_msg = buf + ip_hlen; pim_msg_len = len - ip_hlen; + if (PIM_DEBUG_PIM_PACKETDUMP_RECV) { + pim_pkt_dump(__PRETTY_FUNCTION__, pim_msg, pim_msg_len); + } + if (pim_msg_len < PIM_PIM_MIN_LEN) { zlog_warn("PIM message size=%d shorter than minimum=%d", pim_msg_len, PIM_PIM_MIN_LEN); @@ -289,6 +293,10 @@ static int pim_sock_read(struct thread *t) len, from_str, to_str, fd, ifindex, ifp->ifindex); } + if (PIM_DEBUG_PIM_PACKETDUMP_RECV) { + pim_pkt_dump(__PRETTY_FUNCTION__, buf, len); + } + #ifdef PIM_CHECK_RECV_IFINDEX_SANITY /* ifindex sanity check */ if (ifindex != (int) ifp->ifindex) { @@ -456,6 +464,10 @@ int pim_msg_send(int fd, #endif tolen = sizeof(to); + if (PIM_DEBUG_PIM_PACKETDUMP_SEND) { + pim_pkt_dump(__PRETTY_FUNCTION__, pim_msg, pim_msg_size); + } + sent = sendto(fd, pim_msg, pim_msg_size, MSG_DONTWAIT, &to, tolen); if (sent != (ssize_t) pim_msg_size) { int e = errno; diff --git a/pimd/pim_util.c b/pimd/pim_util.c index 5bc8d07e..13230c0c 100644 --- a/pimd/pim_util.c +++ b/pimd/pim_util.c @@ -20,6 +20,10 @@ $QuaggaId: $Format:%an, %ai, %h$ $ */ +#include + +#include "log.h" + #include "pim_util.h" /* @@ -130,3 +134,26 @@ uint16_t pim_inet_checksum(const char *buf, int size) return checksum; } #endif /* PIM_USE_QUAGGA_INET_CHECKSUM */ + +void pim_pkt_dump(const char *label, const char *buf, int size) +{ + char dump_buf[1000]; + int i = 0; + int j = 0; + + for (; i < size; ++i, j += 3) { + int left = sizeof(dump_buf) - j; + if (left < 4) { + if (left > 1) { + strcat(dump_buf + j, "!"); /* mark as truncated */ + } + break; + } + snprintf(dump_buf + j, left, " %02x", buf[i]); + } + + zlog_debug("%s: pkt dump size=%d:%s", + label, + size, + dump_buf); +} diff --git a/pimd/pim_util.h b/pimd/pim_util.h index 0fcdce0c..5863d5f2 100644 --- a/pimd/pim_util.h +++ b/pimd/pim_util.h @@ -38,4 +38,6 @@ uint16_t igmp_msg_decode8to16(uint8_t code); uint16_t pim_inet_checksum(const char *buf, int size); #endif /* PIM_USE_QUAGGA_INET_CHECKSUM */ +void pim_pkt_dump(const char *label, const char *buf, int size); + #endif /* PIM_UTIL_H */ diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c index 0b06d0ec..3a1abf13 100644 --- a/pimd/pim_vty.c +++ b/pimd/pim_vty.c @@ -57,6 +57,14 @@ int pim_debug_config_write(struct vty *vty) vty_out(vty, "debug pim packets%s", VTY_NEWLINE); ++writes; } + if (PIM_DEBUG_PIM_PACKETDUMP_SEND) { + vty_out(vty, "debug pim packet-dump send%s", VTY_NEWLINE); + ++writes; + } + if (PIM_DEBUG_PIM_PACKETDUMP_RECV) { + vty_out(vty, "debug pim packet-dump receive%s", VTY_NEWLINE); + ++writes; + } if (PIM_DEBUG_PIM_TRACE) { vty_out(vty, "debug pim trace%s", VTY_NEWLINE); ++writes; diff --git a/pimd/pimd.h b/pimd/pimd.h index b42c54cf..1dcd8654 100644 --- a/pimd/pimd.h +++ b/pimd/pimd.h @@ -53,14 +53,16 @@ #define PIM_INADDR_IS_ANY(addr) ((addr).s_addr == PIM_NET_INADDR_ANY) /* struct in_addr addr */ #define PIM_INADDR_ISNOT_ANY(addr) ((addr).s_addr != PIM_NET_INADDR_ANY) /* struct in_addr addr */ -#define PIM_MASK_PIM_EVENTS (1 << 0) -#define PIM_MASK_PIM_PACKETS (1 << 1) -#define PIM_MASK_PIM_TRACE (1 << 2) -#define PIM_MASK_IGMP_EVENTS (1 << 3) -#define PIM_MASK_IGMP_PACKETS (1 << 4) -#define PIM_MASK_IGMP_TRACE (1 << 5) -#define PIM_MASK_ZEBRA (1 << 6) -#define PIM_MASK_SSMPINGD (1 << 7) +#define PIM_MASK_PIM_EVENTS (1 << 0) +#define PIM_MASK_PIM_PACKETS (1 << 1) +#define PIM_MASK_PIM_PACKETDUMP_SEND (1 << 2) +#define PIM_MASK_PIM_PACKETDUMP_RECV (1 << 3) +#define PIM_MASK_PIM_TRACE (1 << 4) +#define PIM_MASK_IGMP_EVENTS (1 << 5) +#define PIM_MASK_IGMP_PACKETS (1 << 6) +#define PIM_MASK_IGMP_TRACE (1 << 7) +#define PIM_MASK_ZEBRA (1 << 8) +#define PIM_MASK_SSMPINGD (1 << 9) const char *const PIM_ALL_SYSTEMS; const char *const PIM_ALL_ROUTERS; @@ -93,36 +95,42 @@ struct in_addr qpim_ssmpingd_group_addr; #define PIM_MROUTE_IS_ENABLED (qpim_mroute_socket_fd >= 0) #define PIM_MROUTE_IS_DISABLED (qpim_mroute_socket_fd < 0) -#define PIM_DEBUG_PIM_EVENTS (qpim_debugs & PIM_MASK_PIM_EVENTS) -#define PIM_DEBUG_PIM_PACKETS (qpim_debugs & PIM_MASK_PIM_PACKETS) -#define PIM_DEBUG_PIM_TRACE (qpim_debugs & PIM_MASK_PIM_TRACE) -#define PIM_DEBUG_IGMP_EVENTS (qpim_debugs & PIM_MASK_IGMP_EVENTS) -#define PIM_DEBUG_IGMP_PACKETS (qpim_debugs & PIM_MASK_IGMP_PACKETS) -#define PIM_DEBUG_IGMP_TRACE (qpim_debugs & PIM_MASK_IGMP_TRACE) -#define PIM_DEBUG_ZEBRA (qpim_debugs & PIM_MASK_ZEBRA) -#define PIM_DEBUG_SSMPINGD (qpim_debugs & PIM_MASK_SSMPINGD) +#define PIM_DEBUG_PIM_EVENTS (qpim_debugs & PIM_MASK_PIM_EVENTS) +#define PIM_DEBUG_PIM_PACKETS (qpim_debugs & PIM_MASK_PIM_PACKETS) +#define PIM_DEBUG_PIM_PACKETDUMP_SEND (qpim_debugs & PIM_MASK_PIM_PACKETDUMP_SEND) +#define PIM_DEBUG_PIM_PACKETDUMP_RECV (qpim_debugs & PIM_MASK_PIM_PACKETDUMP_RECV) +#define PIM_DEBUG_PIM_TRACE (qpim_debugs & PIM_MASK_PIM_TRACE) +#define PIM_DEBUG_IGMP_EVENTS (qpim_debugs & PIM_MASK_IGMP_EVENTS) +#define PIM_DEBUG_IGMP_PACKETS (qpim_debugs & PIM_MASK_IGMP_PACKETS) +#define PIM_DEBUG_IGMP_TRACE (qpim_debugs & PIM_MASK_IGMP_TRACE) +#define PIM_DEBUG_ZEBRA (qpim_debugs & PIM_MASK_ZEBRA) +#define PIM_DEBUG_SSMPINGD (qpim_debugs & PIM_MASK_SSMPINGD) #define PIM_DEBUG_EVENTS (qpim_debugs & (PIM_MASK_PIM_EVENTS | PIM_MASK_IGMP_EVENTS)) #define PIM_DEBUG_PACKETS (qpim_debugs & (PIM_MASK_PIM_PACKETS | PIM_MASK_IGMP_PACKETS)) #define PIM_DEBUG_TRACE (qpim_debugs & (PIM_MASK_PIM_TRACE | PIM_MASK_IGMP_TRACE)) -#define PIM_DO_DEBUG_PIM_EVENTS (qpim_debugs |= PIM_MASK_PIM_EVENTS) -#define PIM_DO_DEBUG_PIM_PACKETS (qpim_debugs |= PIM_MASK_PIM_PACKETS) -#define PIM_DO_DEBUG_PIM_TRACE (qpim_debugs |= PIM_MASK_PIM_TRACE) -#define PIM_DO_DEBUG_IGMP_EVENTS (qpim_debugs |= PIM_MASK_IGMP_EVENTS) -#define PIM_DO_DEBUG_IGMP_PACKETS (qpim_debugs |= PIM_MASK_IGMP_PACKETS) -#define PIM_DO_DEBUG_IGMP_TRACE (qpim_debugs |= PIM_MASK_IGMP_TRACE) -#define PIM_DO_DEBUG_ZEBRA (qpim_debugs |= PIM_MASK_ZEBRA) -#define PIM_DO_DEBUG_SSMPINGD (qpim_debugs |= PIM_MASK_SSMPINGD) - -#define PIM_DONT_DEBUG_PIM_EVENTS (qpim_debugs &= ~PIM_MASK_PIM_EVENTS) -#define PIM_DONT_DEBUG_PIM_PACKETS (qpim_debugs &= ~PIM_MASK_PIM_PACKETS) -#define PIM_DONT_DEBUG_PIM_TRACE (qpim_debugs &= ~PIM_MASK_PIM_TRACE) -#define PIM_DONT_DEBUG_IGMP_EVENTS (qpim_debugs &= ~PIM_MASK_IGMP_EVENTS) -#define PIM_DONT_DEBUG_IGMP_PACKETS (qpim_debugs &= ~PIM_MASK_IGMP_PACKETS) -#define PIM_DONT_DEBUG_IGMP_TRACE (qpim_debugs &= ~PIM_MASK_IGMP_TRACE) -#define PIM_DONT_DEBUG_ZEBRA (qpim_debugs &= ~PIM_MASK_ZEBRA) -#define PIM_DONT_DEBUG_SSMPINGD (qpim_debugs &= ~PIM_MASK_SSMPINGD) +#define PIM_DO_DEBUG_PIM_EVENTS (qpim_debugs |= PIM_MASK_PIM_EVENTS) +#define PIM_DO_DEBUG_PIM_PACKETS (qpim_debugs |= PIM_MASK_PIM_PACKETS) +#define PIM_DO_DEBUG_PIM_PACKETDUMP_SEND (qpim_debugs |= PIM_MASK_PIM_PACKETDUMP_SEND) +#define PIM_DO_DEBUG_PIM_PACKETDUMP_RECV (qpim_debugs |= PIM_MASK_PIM_PACKETDUMP_RECV) +#define PIM_DO_DEBUG_PIM_TRACE (qpim_debugs |= PIM_MASK_PIM_TRACE) +#define PIM_DO_DEBUG_IGMP_EVENTS (qpim_debugs |= PIM_MASK_IGMP_EVENTS) +#define PIM_DO_DEBUG_IGMP_PACKETS (qpim_debugs |= PIM_MASK_IGMP_PACKETS) +#define PIM_DO_DEBUG_IGMP_TRACE (qpim_debugs |= PIM_MASK_IGMP_TRACE) +#define PIM_DO_DEBUG_ZEBRA (qpim_debugs |= PIM_MASK_ZEBRA) +#define PIM_DO_DEBUG_SSMPINGD (qpim_debugs |= PIM_MASK_SSMPINGD) + +#define PIM_DONT_DEBUG_PIM_EVENTS (qpim_debugs &= ~PIM_MASK_PIM_EVENTS) +#define PIM_DONT_DEBUG_PIM_PACKETS (qpim_debugs &= ~PIM_MASK_PIM_PACKETS) +#define PIM_DONT_DEBUG_PIM_PACKETDUMP_SEND (qpim_debugs &= ~PIM_MASK_PIM_PACKETDUMP_SEND) +#define PIM_DONT_DEBUG_PIM_PACKETDUMP_RECV (qpim_debugs &= ~PIM_MASK_PIM_PACKETDUMP_RECV) +#define PIM_DONT_DEBUG_PIM_TRACE (qpim_debugs &= ~PIM_MASK_PIM_TRACE) +#define PIM_DONT_DEBUG_IGMP_EVENTS (qpim_debugs &= ~PIM_MASK_IGMP_EVENTS) +#define PIM_DONT_DEBUG_IGMP_PACKETS (qpim_debugs &= ~PIM_MASK_IGMP_PACKETS) +#define PIM_DONT_DEBUG_IGMP_TRACE (qpim_debugs &= ~PIM_MASK_IGMP_TRACE) +#define PIM_DONT_DEBUG_ZEBRA (qpim_debugs &= ~PIM_MASK_ZEBRA) +#define PIM_DONT_DEBUG_SSMPINGD (qpim_debugs &= ~PIM_MASK_SSMPINGD) void pim_init(void); void pim_terminate(void); From 929bb95587fe43db9a904464d973c0605d160644 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Wed, 18 Nov 2009 11:55:13 -0200 Subject: [PATCH 333/482] [pim] Fix dump byte type --- pimd/pim_util.c | 2 +- pimd/pim_util.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pimd/pim_util.c b/pimd/pim_util.c index 13230c0c..d97b599c 100644 --- a/pimd/pim_util.c +++ b/pimd/pim_util.c @@ -135,7 +135,7 @@ uint16_t pim_inet_checksum(const char *buf, int size) } #endif /* PIM_USE_QUAGGA_INET_CHECKSUM */ -void pim_pkt_dump(const char *label, const char *buf, int size) +void pim_pkt_dump(const char *label, const uint8_t *buf, int size) { char dump_buf[1000]; int i = 0; diff --git a/pimd/pim_util.h b/pimd/pim_util.h index 5863d5f2..6f5bf223 100644 --- a/pimd/pim_util.h +++ b/pimd/pim_util.h @@ -38,6 +38,6 @@ uint16_t igmp_msg_decode8to16(uint8_t code); uint16_t pim_inet_checksum(const char *buf, int size); #endif /* PIM_USE_QUAGGA_INET_CHECKSUM */ -void pim_pkt_dump(const char *label, const char *buf, int size); +void pim_pkt_dump(const char *label, const uint8_t *buf, int size); #endif /* PIM_UTIL_H */ From 3e92c456b01bb9364a0e68e0124c9ff36edc0b4b Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Wed, 18 Nov 2009 16:26:38 -0200 Subject: [PATCH 334/482] [pim] test pim receive dump --- pimd/COMMANDS | 1 + pimd/pim_cmd.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) diff --git a/pimd/COMMANDS b/pimd/COMMANDS index 60a4dbe7..8cfa671e 100644 --- a/pimd/COMMANDS +++ b/pimd/COMMANDS @@ -54,6 +54,7 @@ debug commands: show debugging State of each debugging option test igmp receive report Test reception of IGMPv3 report test pim receive assert Test reception of PIM assert + test pim receive dump Test reception of PIM packet dump test pim receive hello Test reception of PIM hello test pim receive join Test reception of PIM join test pim receive prune Test reception of PIM prune diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index d42f3bf8..7ccd5cd5 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -3457,6 +3457,97 @@ DEFUN (test_igmp_receive_report, return CMD_SUCCESS; } +DEFUN (test_pim_receive_dump, + test_pim_receive_dump_cmd, + "test pim receive dump INTERFACE A.B.C.D .LINE", + "Test\n" + "Test PIM protocol\n" + "Test PIM message reception\n" + "Test PIM packet dump reception from neighbor\n" + "Interface\n" + "Neighbor address\n" + "Packet dump\n") +{ + char buf[1000]; + char *pim_msg; + struct ip *ip_hdr; + size_t ip_hlen; /* ip header length in bytes */ + int ip_msg_len; + int pim_msg_size; + const char *neigh_str; + struct in_addr neigh_addr; + const char *ifname; + struct interface *ifp; + int argi; + int result; + + /* Find interface */ + ifname = argv[0]; + ifp = if_lookup_by_name(ifname); + if (!ifp) { + vty_out(vty, "No such interface name %s%s", + ifname, VTY_NEWLINE); + return CMD_WARNING; + } + + /* Neighbor address */ + neigh_str = argv[1]; + result = inet_pton(AF_INET, neigh_str, &neigh_addr); + if (result <= 0) { + vty_out(vty, "Bad neighbor address %s: errno=%d: %s%s", + neigh_str, errno, safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + /* + Tweak IP header + */ + ip_hdr = (struct ip *) buf; + ip_hdr->ip_p = PIM_IP_PROTO_PIM; + ip_hlen = PIM_IP_HEADER_MIN_LEN; /* ip header length in bytes */ + ip_hdr->ip_hl = ip_hlen >> 2; /* ip header length in 4-byte words */ + ip_hdr->ip_src = neigh_addr; + ip_hdr->ip_dst = qpim_all_pim_routers_addr; + + /* + Build PIM hello message + */ + pim_msg = buf + ip_hlen; + pim_msg_size = 0; + + /* Scan LINE dump into buffer */ + for (argi = 2; argi < argc; ++argi, ++pim_msg_size) { + const char *hex = argv[argi]; + uint8_t octet = strtol(hex, 0, 16); + int left; + + left = sizeof(buf) - ip_hlen - pim_msg_size; + if (left < 1) { + vty_out(vty, "%% Overflow buf_size=%d buf_left=%d at dump arg %d byte %d value %s=%02x%s", + sizeof(buf), left, argi, argi - 2, hex, octet, VTY_NEWLINE); + return CMD_WARNING; + } + + pim_msg[pim_msg_size] = octet; + } + + ip_msg_len = ip_hlen + pim_msg_size; + + vty_out(vty, "Receiving: buf_size=%d ip_msg_size=%d pim_msg_size=%d%s", + sizeof(buf), ip_msg_len, pim_msg_size, VTY_NEWLINE); + + /* "receive" message */ + + result = pim_pim_packet(ifp, buf, ip_msg_len); + if (result) { + vty_out(vty, "%% pim_pim_packet(len=%d) returned failure: %d%s", + ip_msg_len, result, VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + DEFUN (test_pim_receive_hello, test_pim_receive_hello_cmd, "test pim receive hello INTERFACE A.B.C.D <0-65535> <0-65535> <0-65535> <0-32767> <0-65535> <0-1>[LINE]", @@ -4091,6 +4182,7 @@ void pim_cmd_init() install_element (ENABLE_NODE, &test_igmp_receive_report_cmd); install_element (ENABLE_NODE, &test_pim_receive_assert_cmd); + install_element (ENABLE_NODE, &test_pim_receive_dump_cmd); install_element (ENABLE_NODE, &test_pim_receive_hello_cmd); install_element (ENABLE_NODE, &test_pim_receive_join_cmd); install_element (ENABLE_NODE, &test_pim_receive_prune_cmd); From 42e3078a1e4a0bbb033351ad5a65729a17c1fa19 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Wed, 18 Nov 2009 17:19:43 -0200 Subject: [PATCH 335/482] [pim] Clean-up --- lib/command.c | 2 ++ lib/vty.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/lib/command.c b/lib/command.c index d1af7fa2..8870a421 100644 --- a/lib/command.c +++ b/lib/command.c @@ -2872,6 +2872,7 @@ DEFUN (config_exit, case KEYCHAIN_NODE: case MASC_NODE: case RMAP_NODE: + case PIM_NODE: case VTY_NODE: vty->node = CONFIG_NODE; break; @@ -2929,6 +2930,7 @@ DEFUN (config_end, case KEYCHAIN_NODE: case KEYCHAIN_KEY_NODE: case MASC_NODE: + case PIM_NODE: case VTY_NODE: vty_config_unlock (vty); vty->node = ENABLE_NODE; diff --git a/lib/vty.c b/lib/vty.c index 3f08b9e9..b8db7c89 100644 --- a/lib/vty.c +++ b/lib/vty.c @@ -713,6 +713,7 @@ vty_end_config (struct vty *vty) case KEYCHAIN_NODE: case KEYCHAIN_KEY_NODE: case MASC_NODE: + case PIM_NODE: case VTY_NODE: vty_config_unlock (vty); vty->node = ENABLE_NODE; @@ -1117,6 +1118,7 @@ vty_stop_input (struct vty *vty) case KEYCHAIN_NODE: case KEYCHAIN_KEY_NODE: case MASC_NODE: + case PIM_NODE: case VTY_NODE: vty_config_unlock (vty); vty->node = ENABLE_NODE; From dba7758a7fa712fc712cd3063a2724bb15c92e60 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Thu, 19 Nov 2009 10:32:19 -0200 Subject: [PATCH 336/482] [pim] test pim receive dump --- pimd/pim_cmd.c | 46 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 7ccd5cd5..23ad11f6 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -3457,6 +3457,11 @@ DEFUN (test_igmp_receive_report, return CMD_SUCCESS; } +static int hexval(uint8_t ch) +{ + return isdigit(ch) ? (ch - '0') : (10 + tolower(ch) - 'a'); +} + DEFUN (test_pim_receive_dump, test_pim_receive_dump_cmd, "test pim receive dump INTERFACE A.B.C.D .LINE", @@ -3516,19 +3521,40 @@ DEFUN (test_pim_receive_dump, pim_msg_size = 0; /* Scan LINE dump into buffer */ - for (argi = 2; argi < argc; ++argi, ++pim_msg_size) { - const char *hex = argv[argi]; - uint8_t octet = strtol(hex, 0, 16); - int left; - - left = sizeof(buf) - ip_hlen - pim_msg_size; - if (left < 1) { - vty_out(vty, "%% Overflow buf_size=%d buf_left=%d at dump arg %d byte %d value %s=%02x%s", - sizeof(buf), left, argi, argi - 2, hex, octet, VTY_NEWLINE); + for (argi = 2; argi < argc; ++argi) { + const char *str = argv[argi]; + int str_len = strlen(str); + int str_last = str_len - 1; + int i; + + if (str_len % 2) { + vty_out(vty, "%% Uneven hex array arg %d=%s%s", + argi, str, VTY_NEWLINE); return CMD_WARNING; } - pim_msg[pim_msg_size] = octet; + for (i = 0; i < str_last; i += 2) { + uint8_t octet; + int left; + uint8_t h1 = str[i]; + uint8_t h2 = str[i + 1]; + + if (!isxdigit(h1) || !isxdigit(h2)) { + vty_out(vty, "%% Non-hex octet %c%c at hex array arg %d=%s%s", + h1, h2, argi, str, VTY_NEWLINE); + return CMD_WARNING; + } + octet = (hexval(h1) << 4) + hexval(h2); + + left = sizeof(buf) - ip_hlen - pim_msg_size; + if (left < 1) { + vty_out(vty, "%% Overflow buf_size=%d buf_left=%d at hex array arg %d=%s octet %02x%s", + sizeof(buf), left, argi, str, octet, VTY_NEWLINE); + return CMD_WARNING; + } + + pim_msg[pim_msg_size++] = octet; + } } ip_msg_len = ip_hlen + pim_msg_size; From 7c5f50125d9ef099336660330b7432fb848b7e75 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Thu, 19 Nov 2009 17:00:23 -0200 Subject: [PATCH 337/482] [pim] Fix net/host byte order --- pimd/Makefile.am | 4 ++-- pimd/pim_assert.c | 19 ++++++++++--------- pimd/pim_assert.h | 2 +- pimd/pim_int.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ pimd/pim_int.h | 31 +++++++++++++++++++++++++++++++ pimd/pim_tlv.c | 23 ++++++++++++----------- pimd/pim_tlv.h | 16 ++++++++-------- 7 files changed, 108 insertions(+), 31 deletions(-) create mode 100644 pimd/pim_int.c create mode 100644 pimd/pim_int.h diff --git a/pimd/Makefile.am b/pimd/Makefile.am index 57bd31ae..4aae6378 100644 --- a/pimd/Makefile.am +++ b/pimd/Makefile.am @@ -53,7 +53,7 @@ libpim_a_SOURCES = \ pim_oil.c pim_zlookup.c pim_pim.c pim_tlv.c pim_neighbor.c \ pim_hello.c pim_ifchannel.c pim_join.c pim_assert.c \ pim_msg.c pim_upstream.c pim_rpf.c pim_rand.c pim_macro.c \ - pim_igmp_join.c pim_ssmpingd.c + pim_igmp_join.c pim_ssmpingd.c pim_int.c noinst_HEADERS = \ pimd.h pim_version.h pim_cmd.h pim_signals.h pim_iface.h \ @@ -62,7 +62,7 @@ noinst_HEADERS = \ pim_oil.h pim_zlookup.h pim_pim.h pim_tlv.h pim_neighbor.h \ pim_hello.h pim_ifchannel.h pim_join.h pim_assert.h \ pim_msg.h pim_upstream.h pim_rpf.h pim_rand.h pim_macro.h \ - pim_igmp_join.h pim_ssmpingd.h + pim_igmp_join.h pim_ssmpingd.h pim_int.h pimd_SOURCES = \ pim_main.c $(libpim_a_SOURCES) diff --git a/pimd/pim_assert.c b/pimd/pim_assert.c index 0ce0e5d0..52168d69 100644 --- a/pimd/pim_assert.c +++ b/pimd/pim_assert.c @@ -30,6 +30,7 @@ #include "pim_tlv.h" #include "pim_msg.h" #include "pim_pim.h" +#include "pim_int.h" #include "pim_time.h" #include "pim_iface.h" #include "pim_hello.h" @@ -297,7 +298,7 @@ int pim_assert_recv(struct interface *ifp, Parse assert metric preference */ - msg_metric.metric_preference = ntohl(*(const uint32_t *) curr); + msg_metric.metric_preference = pim_read_uint32_host(curr); msg_metric.rpt_bit_flag = msg_metric.metric_preference & 0x80000000; /* save highest bit */ msg_metric.metric_preference &= ~0x80000000; /* clear highest bit */ @@ -308,7 +309,7 @@ int pim_assert_recv(struct interface *ifp, Parse assert route metric */ - msg_metric.route_metric = ntohl(*(const uint32_t *) curr); + msg_metric.route_metric = pim_read_uint32_host(curr); if (PIM_DEBUG_PIM_TRACE) { char neigh_str[100]; @@ -378,7 +379,7 @@ int pim_assert_metric_match(const struct pim_assert_metric *m1, return m1->ip_address.s_addr == m2->ip_address.s_addr; } -int pim_assert_build_msg(char *pim_msg, int buf_size, +int pim_assert_build_msg(uint8_t *pim_msg, int buf_size, struct interface *ifp, struct in_addr group_addr, struct in_addr source_addr, @@ -386,8 +387,8 @@ int pim_assert_build_msg(char *pim_msg, int buf_size, uint32_t route_metric, uint32_t rpt_bit_flag) { - char *buf_pastend = pim_msg + buf_size; - char *pim_msg_curr; + uint8_t *buf_pastend = pim_msg + buf_size; + uint8_t *pim_msg_curr; int pim_msg_size; int remain; @@ -420,13 +421,13 @@ int pim_assert_build_msg(char *pim_msg, int buf_size, } /* Metric preference */ - *((uint32_t *) pim_msg_curr) = htonl(rpt_bit_flag ? - metric_preference | 0x80000000 : - metric_preference); + pim_write_uint32(pim_msg_curr, rpt_bit_flag ? + metric_preference | 0x80000000 : + metric_preference); pim_msg_curr += 4; /* Route metric */ - *((uint32_t *) pim_msg_curr) = htonl(route_metric); + pim_write_uint32(pim_msg_curr, route_metric); pim_msg_curr += 4; /* diff --git a/pimd/pim_assert.h b/pimd/pim_assert.h index 6a8594ce..feeb91df 100644 --- a/pimd/pim_assert.h +++ b/pimd/pim_assert.h @@ -58,7 +58,7 @@ int pim_assert_metric_better(const struct pim_assert_metric *m1, int pim_assert_metric_match(const struct pim_assert_metric *m1, const struct pim_assert_metric *m2); -int pim_assert_build_msg(char *pim_msg, int buf_size, +int pim_assert_build_msg(uint8_t *pim_msg, int buf_size, struct interface *ifp, struct in_addr group_addr, struct in_addr source_addr, diff --git a/pimd/pim_int.c b/pimd/pim_int.c new file mode 100644 index 00000000..2ff1a116 --- /dev/null +++ b/pimd/pim_int.c @@ -0,0 +1,44 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#include +#include + +#include "pim_int.h" + +uint32_t pim_read_uint32_host(const uint8_t *buf) +{ + uint32_t val; + memcpy(&val, buf, sizeof(val)); + /* val is in netorder */ + val = ntohl(val); + /* val is in hostorder */ + return val; +} + +void pim_write_uint32(uint8_t *buf, uint32_t val_host) +{ + /* val_host is in host order */ + val_host = htonl(val_host); + /* val_host is in netorder */ + memcpy(buf, &val_host, sizeof(val_host)); +} diff --git a/pimd/pim_int.h b/pimd/pim_int.h new file mode 100644 index 00000000..d64b1032 --- /dev/null +++ b/pimd/pim_int.h @@ -0,0 +1,31 @@ +/* + PIM for Quagga + Copyright (C) 2008 Everton da Silva Marques + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + MA 02110-1301 USA + + $QuaggaId: $Format:%an, %ai, %h$ $ +*/ + +#ifndef PIM_INT_H +#define PIM_INT_H + +#include + +uint32_t pim_read_uint32_host(const uint8_t *buf); +void pim_write_uint32(uint8_t *buf, uint32_t val_host); + +#endif /* PIM_INT_H */ diff --git a/pimd/pim_tlv.c b/pimd/pim_tlv.c index fc48c888..16b7e93e 100644 --- a/pimd/pim_tlv.c +++ b/pimd/pim_tlv.c @@ -26,12 +26,13 @@ #include "prefix.h" #include "pimd.h" +#include "pim_int.h" #include "pim_tlv.h" #include "pim_str.h" #include "pim_msg.h" -char *pim_tlv_append_uint16(char *buf, - const char *buf_pastend, +char *pim_tlv_append_uint16(uint8_t *buf, + const uint8_t *buf_pastend, uint16_t option_type, uint16_t option_value) { @@ -54,8 +55,8 @@ char *pim_tlv_append_uint16(char *buf, return buf; } -char *pim_tlv_append_2uint16(char *buf, - const char *buf_pastend, +char *pim_tlv_append_2uint16(uint8_t *buf, + const uint8_t *buf_pastend, uint16_t option_type, uint16_t option_value1, uint16_t option_value2) @@ -81,8 +82,8 @@ char *pim_tlv_append_2uint16(char *buf, return buf; } -char *pim_tlv_append_uint32(char *buf, - const char *buf_pastend, +char *pim_tlv_append_uint32(uint8_t *buf, + const uint8_t *buf_pastend, uint16_t option_type, uint32_t option_value) { @@ -99,7 +100,7 @@ char *pim_tlv_append_uint32(char *buf, buf += 2; *(uint16_t *) buf = htons(option_len); buf += 2; - *(uint32_t *) buf = htonl(option_value); + pim_write_uint32(buf, option_value); buf += option_len; return buf; @@ -107,14 +108,14 @@ char *pim_tlv_append_uint32(char *buf, #define ucast_ipv4_encoding_len (2 + sizeof(struct in_addr)) -char *pim_tlv_append_addrlist_ucast(char *buf, - const char *buf_pastend, +char *pim_tlv_append_addrlist_ucast(uint8_t *buf, + const uint8_t *buf_pastend, struct list *ifconnected) { struct listnode *node; uint16_t option_len = 0; - char *curr; + uint8_t *curr; node = listhead(ifconnected); @@ -323,7 +324,7 @@ int pim_tlv_parse_dr_priority(const char *ifname, struct in_addr src_addr, PIM_OPTION_SET(*hello_options, PIM_OPTION_MASK_DR_PRIORITY); *hello_option_dr_priority = PIM_TLV_GET_DR_PRIORITY(tlv_curr); - + return 0; } diff --git a/pimd/pim_tlv.h b/pimd/pim_tlv.h index 9602cb1c..5ec3dc43 100644 --- a/pimd/pim_tlv.h +++ b/pimd/pim_tlv.h @@ -71,21 +71,21 @@ typedef uint32_t pim_hello_options; #define PIM_TLV_MIN_SIZE (PIM_TLV_TYPE_SIZE + PIM_TLV_LENGTH_SIZE) #define PIM_TLV_OPTION_SIZE(option_len) (PIM_TLV_MIN_SIZE + (option_len)) -char *pim_tlv_append_uint16(char *buf, - const char *buf_pastend, +char *pim_tlv_append_uint16(uint8_t *buf, + const uint8_t *buf_pastend, uint16_t option_type, uint16_t option_value); -char *pim_tlv_append_2uint16(char *buf, - const char *buf_pastend, +char *pim_tlv_append_2uint16(uint8_t *buf, + const uint8_t *buf_pastend, uint16_t option_type, uint16_t option_value1, uint16_t option_value2); -char *pim_tlv_append_uint32(char *buf, - const char *buf_pastend, +char *pim_tlv_append_uint32(uint8_t *buf, + const uint8_t *buf_pastend, uint16_t option_type, uint32_t option_value); -char *pim_tlv_append_addrlist_ucast(char *buf, - const char *buf_pastend, +char *pim_tlv_append_addrlist_ucast(uint8_t *buf, + const uint8_t *buf_pastend, struct list *ifconnected); int pim_tlv_parse_holdtime(const char *ifname, struct in_addr src_addr, From 567f927c39061a0b98caf23381f5eb523f6d5600 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Fri, 19 Feb 2010 19:07:00 -0200 Subject: [PATCH 338/482] [pim] show ip igmp join --- pimd/COMMANDS | 1 + pimd/pim_cmd.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++ pimd/pim_iface.c | 18 ++++++++++-- pimd/pim_igmp.h | 1 + pimd/pim_version.h | 2 +- 5 files changed, 87 insertions(+), 4 deletions(-) diff --git a/pimd/COMMANDS b/pimd/COMMANDS index 8cfa671e..56b15dd5 100644 --- a/pimd/COMMANDS +++ b/pimd/COMMANDS @@ -14,6 +14,7 @@ interface configuration commands: verification commands: show ip igmp interface IGMP interface information + show ip igmp join IGMP static join information show ip igmp parameters IGMP parameters information show ip igmp groups IGMP groups information show ip igmp groups retransmissions IGMP group retransmission diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 23ad11f6..9553361a 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -34,6 +34,7 @@ #include "pim_vty.h" #include "pim_mroute.h" #include "pim_str.h" +#include "pim_igmp.h" #include "pim_igmpv3.h" #include "pim_sock.h" #include "pim_time.h" @@ -455,6 +456,59 @@ static void igmp_show_interfaces(struct vty *vty) } } +static void igmp_show_interface_join(struct vty *vty) +{ + struct listnode *node; + struct interface *ifp; + time_t now; + + now = pim_time_monotonic_sec(); + + vty_out(vty, + "Interface Address Source Group Socket Uptime %s", + VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) { + struct pim_interface *pim_ifp; + struct listnode *join_node; + struct igmp_join *ij; + struct in_addr pri_addr; + char pri_addr_str[100]; + + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + if (!pim_ifp->igmp_join_list) + continue; + + pri_addr = pim_find_primary_addr(ifp); + pim_inet4_dump("", pri_addr, pri_addr_str, sizeof(pri_addr_str)); + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_join_list, join_node, ij)) { + char group_str[100]; + char source_str[100]; + char uptime[10]; + + pim_time_uptime(uptime, sizeof(uptime), now - ij->sock_creation); + pim_inet4_dump("", ij->group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", ij->source_addr, source_str, sizeof(source_str)); + + vty_out(vty, "%-9s %-15s %-15s %-15s %6d %8s%s", + ifp->name, + pri_addr_str, + source_str, + group_str, + ij->sock_fd, + uptime, + VTY_NEWLINE); + } /* for (pim_ifp->igmp_join_list) */ + + } /* for (iflist) */ + +} + static void show_interface_address(struct vty *vty) { struct listnode *ifpnode; @@ -1547,6 +1601,19 @@ DEFUN (show_ip_igmp_interface, return CMD_SUCCESS; } +DEFUN (show_ip_igmp_join, + show_ip_igmp_join_cmd, + "show ip igmp join", + SHOW_STR + IP_STR + IGMP_STR + "IGMP static join information\n") +{ + igmp_show_interface_join(vty); + + return CMD_SUCCESS; +} + DEFUN (show_ip_igmp_groups, show_ip_igmp_groups_cmd, "show ip igmp groups", @@ -4140,6 +4207,7 @@ void pim_cmd_init() install_element (INTERFACE_NODE, &interface_no_ip_pim_ssm_cmd); install_element (VIEW_NODE, &show_ip_igmp_interface_cmd); + install_element (VIEW_NODE, &show_ip_igmp_join_cmd); install_element (VIEW_NODE, &show_ip_igmp_parameters_cmd); install_element (VIEW_NODE, &show_ip_igmp_groups_cmd); install_element (VIEW_NODE, &show_ip_igmp_groups_retransmissions_cmd); @@ -4175,6 +4243,7 @@ void pim_cmd_init() install_element (ENABLE_NODE, &clear_ip_pim_interfaces_cmd); install_element (ENABLE_NODE, &show_ip_igmp_interface_cmd); + install_element (ENABLE_NODE, &show_ip_igmp_join_cmd); install_element (ENABLE_NODE, &show_ip_igmp_parameters_cmd); install_element (ENABLE_NODE, &show_ip_igmp_groups_cmd); install_element (ENABLE_NODE, &show_ip_igmp_groups_retransmissions_cmd); diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index 7806c805..863cc039 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -38,6 +38,7 @@ #include "pim_ifchannel.h" #include "pim_rand.h" #include "pim_sock.h" +#include "pim_time.h" #include "pim_ssmpingd.h" static void pim_if_igmp_join_del_all(struct interface *ifp); @@ -917,9 +918,10 @@ static struct igmp_join *igmp_join_new(struct interface *ifp, return 0; } - ij->sock_fd = join_fd; - ij->group_addr = group_addr; - ij->source_addr = source_addr; + ij->sock_fd = join_fd; + ij->group_addr = group_addr; + ij->source_addr = source_addr; + ij->sock_creation = pim_time_monotonic_sec(); listnode_add(pim_ifp->igmp_join_list, ij); @@ -975,6 +977,16 @@ int pim_if_igmp_join_add(struct interface *ifp, return -4; } + { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); + zlog_info("%s: issued static igmp join for channel (S,G)=(%s,%s) on interface %s", + __PRETTY_FUNCTION__, + source_str, group_str, ifp->name); + } + return 0; } diff --git a/pimd/pim_igmp.h b/pimd/pim_igmp.h index 95e60bf2..d45f223b 100644 --- a/pimd/pim_igmp.h +++ b/pimd/pim_igmp.h @@ -70,6 +70,7 @@ struct igmp_join { struct in_addr group_addr; struct in_addr source_addr; int sock_fd; + time_t sock_creation; }; struct igmp_sock { diff --git a/pimd/pim_version.h b/pimd/pim_version.h index 546ace3b..ef75791e 100644 --- a/pimd/pim_version.h +++ b/pimd/pim_version.h @@ -23,7 +23,7 @@ #ifndef PIM_VERSION_H #define PIM_VERSION_H -#define PIMD_VERSION_STR "0.159" +#define PIMD_VERSION_STR "0.160" const char * const PIMD_VERSION; From 9986fb3a25a0432dbd89aa1e319c89691db05bc0 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Mon, 22 Feb 2010 09:09:09 -0300 Subject: [PATCH 339/482] [pim] TODO T42 Static igmp join fails when loading config at boot time --- pimd/TODO | 27 +++++++++++++++++++++++++++ pimd/pim_iface.c | 6 +++--- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/pimd/TODO b/pimd/TODO index 1dc561dd..0a5617e4 100644 --- a/pimd/TODO +++ b/pimd/TODO @@ -370,4 +370,31 @@ T41 DONE ssmping support show ip ssmpingd +T42 Static igmp join fails when loading config at boot time + + ! Wrong behavior seen at boot time: + ! + 2010/02/22 08:59:00 PIM: igmp_source_forward_start: ignoring request for + looped MFC entry (S,G)=(3.3.3.3,239.3.3.3): igmp_sock=12 oif=eth0 vif_index=2 + + ! Correct behavior seen later: + ! + 2010/02/22 09:03:16 PIM: igmp_source_forward_start: ignoring request for + looped MFC entry (S,G)=(2.2.2.2,239.2.2.2): igmp_sock=17 oif=lo vif_index=1 + + ! To see the wrong message at boot: + ! + debug igmp trace + ! + interface lo + ip igmp + ip igmp join 239.2.2.2 2.2.2.2 + ip igmp join 239.3.3.3 3.3.3.3 + ! + + ! Interfaces indexes: + Interface Address ifi Vif PktsIn PktsOut BytesIn BytesOut + eth0 200.202.112.3 2 2 0 0 0 0 + lo 127.0.0.1 1 1 0 0 0 0 + -x- diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index 863cc039..52d4f0d8 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -982,9 +982,9 @@ int pim_if_igmp_join_add(struct interface *ifp, char source_str[100]; pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); - zlog_info("%s: issued static igmp join for channel (S,G)=(%s,%s) on interface %s", - __PRETTY_FUNCTION__, - source_str, group_str, ifp->name); + zlog_debug("%s: issued static igmp join for channel (S,G)=(%s,%s) on interface %s", + __PRETTY_FUNCTION__, + source_str, group_str, ifp->name); } return 0; From 67faabc18026bf3f8ae6d7ee1df9f2b1729ea06b Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Tue, 23 Feb 2010 12:11:11 -0300 Subject: [PATCH 340/482] [pim] debug mroute --- pimd/pim_cmd.c | 31 +++++++++++++++++++++++++++++++ pimd/pim_cmd.h | 1 + pimd/pim_vty.c | 5 +++++ pimd/pim_zebra.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ pimd/pimd.h | 4 ++++ 5 files changed, 85 insertions(+) diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 9553361a..2b418cd4 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -3131,6 +3131,33 @@ ALIAS (no_debug_igmp_trace, DEBUG_IGMP_STR DEBUG_IGMP_TRACE_STR) +DEFUN (debug_mroute, + debug_mroute_cmd, + "debug mroute", + DEBUG_STR + DEBUG_MROUTE_STR) +{ + PIM_DO_DEBUG_MROUTE; + return CMD_SUCCESS; +} + +DEFUN (no_debug_mroute, + no_debug_mroute_cmd, + "no debug mroute", + NO_STR + DEBUG_STR + DEBUG_MROUTE_STR) +{ + PIM_DONT_DEBUG_MROUTE; + return CMD_SUCCESS; +} + +ALIAS (no_debug_mroute, + undebug_mroute_cmd, + "undebug mroute", + UNDEBUG_STR + DEBUG_MROUTE_STR) + DEFUN (debug_pim, debug_pim_cmd, "debug pim", @@ -4295,6 +4322,8 @@ void pim_cmd_init() install_element (ENABLE_NODE, &debug_igmp_trace_cmd); install_element (ENABLE_NODE, &no_debug_igmp_trace_cmd); install_element (ENABLE_NODE, &undebug_igmp_trace_cmd); + install_element (ENABLE_NODE, &debug_mroute_cmd); + install_element (ENABLE_NODE, &no_debug_mroute_cmd); install_element (ENABLE_NODE, &debug_pim_cmd); install_element (ENABLE_NODE, &no_debug_pim_cmd); install_element (ENABLE_NODE, &undebug_pim_cmd); @@ -4332,6 +4361,8 @@ void pim_cmd_init() install_element (CONFIG_NODE, &debug_igmp_trace_cmd); install_element (CONFIG_NODE, &no_debug_igmp_trace_cmd); install_element (CONFIG_NODE, &undebug_igmp_trace_cmd); + install_element (CONFIG_NODE, &debug_mroute_cmd); + install_element (CONFIG_NODE, &no_debug_mroute_cmd); install_element (CONFIG_NODE, &debug_pim_cmd); install_element (CONFIG_NODE, &no_debug_pim_cmd); install_element (CONFIG_NODE, &undebug_pim_cmd); diff --git a/pimd/pim_cmd.h b/pimd/pim_cmd.h index fd1a62fa..d83cbc09 100644 --- a/pimd/pim_cmd.h +++ b/pimd/pim_cmd.h @@ -38,6 +38,7 @@ #define DEBUG_IGMP_EVENTS_STR "IGMP protocol events\n" #define DEBUG_IGMP_PACKETS_STR "IGMP protocol packets\n" #define DEBUG_IGMP_TRACE_STR "IGMP internal daemon activity\n" +#define DEBUG_MROUTE_STR "PIM interaction with kernel MFC cache\n" #define DEBUG_PIM_STR "PIM protocol activity\n" #define DEBUG_PIM_EVENTS_STR "PIM protocol events\n" #define DEBUG_PIM_PACKETS_STR "PIM protocol packets\n" diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c index 3a1abf13..bcace95c 100644 --- a/pimd/pim_vty.c +++ b/pimd/pim_vty.c @@ -49,6 +49,11 @@ int pim_debug_config_write(struct vty *vty) ++writes; } + if (PIM_DEBUG_MROUTE) { + vty_out(vty, "debug mroute%s", VTY_NEWLINE); + ++writes; + } + if (PIM_DEBUG_PIM_EVENTS) { vty_out(vty, "debug pim events%s", VTY_NEWLINE); ++writes; diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index 3530434d..e18b7ac9 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -754,6 +754,17 @@ static int add_oif(struct channel_oil *channel_oil, pim_ifp = oif->info; + if (PIM_DEBUG_MROUTE) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d", + __FILE__, __PRETTY_FUNCTION__, + source_str, group_str, + proto_mask, oif->name, pim_ifp->mroute_vif_index); + } + if (pim_ifp->mroute_vif_index < 1) { zlog_warn("%s %s: interface %s vif_index=%d < 1", __FILE__, __PRETTY_FUNCTION__, @@ -860,6 +871,17 @@ static int add_oif(struct channel_oil *channel_oil, ++channel_oil->oil_size; channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= proto_mask; + if (PIM_DEBUG_MROUTE) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE", + __FILE__, __PRETTY_FUNCTION__, + source_str, group_str, + proto_mask, oif->name, pim_ifp->mroute_vif_index); + } + return 0; } @@ -878,6 +900,17 @@ static int del_oif(struct channel_oil *channel_oil, zassert(qpim_mroute_oif_highest_vif_index < MAXVIFS); zassert(pim_ifp->mroute_vif_index <= qpim_mroute_oif_highest_vif_index); + if (PIM_DEBUG_MROUTE) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d", + __FILE__, __PRETTY_FUNCTION__, + source_str, group_str, + proto_mask, oif->name, pim_ifp->mroute_vif_index); + } + /* Prevent single protocol from unsubscribing same interface from channel (S,G) multiple times */ if (!(channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask)) { @@ -963,6 +996,17 @@ static int del_oif(struct channel_oil *channel_oil, } } + if (PIM_DEBUG_MROUTE) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE", + __FILE__, __PRETTY_FUNCTION__, + source_str, group_str, + proto_mask, oif->name, pim_ifp->mroute_vif_index); + } + return 0; } diff --git a/pimd/pimd.h b/pimd/pimd.h index 1dcd8654..e11d5bad 100644 --- a/pimd/pimd.h +++ b/pimd/pimd.h @@ -63,6 +63,7 @@ #define PIM_MASK_IGMP_TRACE (1 << 7) #define PIM_MASK_ZEBRA (1 << 8) #define PIM_MASK_SSMPINGD (1 << 9) +#define PIM_MASK_MROUTE (1 << 10) const char *const PIM_ALL_SYSTEMS; const char *const PIM_ALL_ROUTERS; @@ -105,6 +106,7 @@ struct in_addr qpim_ssmpingd_group_addr; #define PIM_DEBUG_IGMP_TRACE (qpim_debugs & PIM_MASK_IGMP_TRACE) #define PIM_DEBUG_ZEBRA (qpim_debugs & PIM_MASK_ZEBRA) #define PIM_DEBUG_SSMPINGD (qpim_debugs & PIM_MASK_SSMPINGD) +#define PIM_DEBUG_MROUTE (qpim_debugs & PIM_MASK_MROUTE) #define PIM_DEBUG_EVENTS (qpim_debugs & (PIM_MASK_PIM_EVENTS | PIM_MASK_IGMP_EVENTS)) #define PIM_DEBUG_PACKETS (qpim_debugs & (PIM_MASK_PIM_PACKETS | PIM_MASK_IGMP_PACKETS)) @@ -120,6 +122,7 @@ struct in_addr qpim_ssmpingd_group_addr; #define PIM_DO_DEBUG_IGMP_TRACE (qpim_debugs |= PIM_MASK_IGMP_TRACE) #define PIM_DO_DEBUG_ZEBRA (qpim_debugs |= PIM_MASK_ZEBRA) #define PIM_DO_DEBUG_SSMPINGD (qpim_debugs |= PIM_MASK_SSMPINGD) +#define PIM_DO_DEBUG_MROUTE (qpim_debugs |= PIM_MASK_MROUTE) #define PIM_DONT_DEBUG_PIM_EVENTS (qpim_debugs &= ~PIM_MASK_PIM_EVENTS) #define PIM_DONT_DEBUG_PIM_PACKETS (qpim_debugs &= ~PIM_MASK_PIM_PACKETS) @@ -131,6 +134,7 @@ struct in_addr qpim_ssmpingd_group_addr; #define PIM_DONT_DEBUG_IGMP_TRACE (qpim_debugs &= ~PIM_MASK_IGMP_TRACE) #define PIM_DONT_DEBUG_ZEBRA (qpim_debugs &= ~PIM_MASK_ZEBRA) #define PIM_DONT_DEBUG_SSMPINGD (qpim_debugs &= ~PIM_MASK_SSMPINGD) +#define PIM_DONT_DEBUG_MROUTE (qpim_debugs &= ~PIM_MASK_MROUTE) void pim_init(void); void pim_terminate(void); From 034bd7fe705b03ea4025478650bd3d690bda923f Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Mon, 1 Mar 2010 17:00:07 -0300 Subject: [PATCH 341/482] [pim] debug mroute --- pimd/COMMANDS | 1 + 1 file changed, 1 insertion(+) diff --git a/pimd/COMMANDS b/pimd/COMMANDS index 56b15dd5..1199368b 100644 --- a/pimd/COMMANDS +++ b/pimd/COMMANDS @@ -49,6 +49,7 @@ debug commands: clear ip igmp interfaces Reset IGMP interfaces clear ip pim interfaces Reset PIM interfaces debug igmp IGMP protocol activity + debug mroute PIM interaction with kernel MFC cache debug pim PIM protocol activity debug pim zebra ZEBRA protocol activity debug ssmpingd ssmpingd activity From f9e05e5f2ae7bc8352a0744d4e4b5105b60e74a4 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Thu, 11 Mar 2010 11:17:33 -0300 Subject: [PATCH 342/482] [pim] Version up to 0.161 --- lib/command.c | 2 +- pimd/pim_main.c | 6 ++++++ pimd/pim_time.c | 38 +++++++++++++++++++++++++++++--------- pimd/pim_version.h | 2 +- 4 files changed, 37 insertions(+), 11 deletions(-) diff --git a/lib/command.c b/lib/command.c index 8870a421..a789b0f4 100644 --- a/lib/command.c +++ b/lib/command.c @@ -105,7 +105,7 @@ static struct cmd_node config_node = }; /* Default motd string. */ -static const char *default_motd = +const char *default_motd = "\r\n\ Hello, this is " QUAGGA_PROGNAME " (version " QUAGGA_VERSION ").\r\n\ " QUAGGA_COPYRIGHT "\r\n\ diff --git a/pimd/pim_main.c b/pimd/pim_main.c index 3cf1869c..2efcbb8c 100644 --- a/pimd/pim_main.c +++ b/pimd/pim_main.c @@ -285,6 +285,12 @@ Hello, this is " QUAGGA_PROGNAME " " QUAGGA_VERSION " " PIMD_PROGNAME " " PIMD_V zlog_notice("PIM_UNEXPECTED_KERNEL_UPCALL: report unexpected kernel upcall"); #endif +#ifdef HAVE_CLOCK_MONOTONIC + zlog_notice("HAVE_CLOCK_MONOTONIC"); +#else + zlog_notice("!HAVE_CLOCK_MONOTONIC"); +#endif + /* Initialize zclient "update" and "lookup" sockets */ diff --git a/pimd/pim_time.c b/pimd/pim_time.c index 63861e5c..a4b1cc46 100644 --- a/pimd/pim_time.c +++ b/pimd/pim_time.c @@ -30,12 +30,32 @@ #include "pim_time.h" -/* - see man clock_gettime - */ -static int pim_gettime(clockid_t clk_id, struct timeval *tv) +static int pim_gettime(enum quagga_clkid clkid, struct timeval *tv) +{ + int result; + + result = quagga_gettime(clkid, tv); + if (result) { + zlog_err("%s: quagga_gettime(clkid=%d) failure: errno=%d: %s", + __PRETTY_FUNCTION__, clkid, + errno, safe_strerror(errno)); + } + + return result; +} + +static int gettime_monotonic(struct timeval *tv) { - return quagga_gettime(clk_id, tv); + int result; + + result = pim_gettime(QUAGGA_CLK_MONOTONIC, tv); + if (result) { + zlog_err("%s: pim_gettime(QUAGGA_CLK_MONOTONIC=%d) failure: errno=%d: %s", + __PRETTY_FUNCTION__, CLOCK_MONOTONIC, + errno, safe_strerror(errno)); + } + + return result; } /* @@ -46,8 +66,8 @@ int64_t pim_time_monotonic_sec() { struct timeval now_tv; - if (pim_gettime(CLOCK_MONOTONIC, &now_tv)) { - zlog_err("%s: gettime(CLOCK_MONOTONIC) failure: errno=%d: %s", + if (gettime_monotonic(&now_tv)) { + zlog_err("%s: gettime_monotonic() failure: errno=%d: %s", __PRETTY_FUNCTION__, errno, safe_strerror(errno)); return -1; @@ -65,8 +85,8 @@ int64_t pim_time_monotonic_dsec() struct timeval now_tv; int64_t now_dsec; - if (pim_gettime(CLOCK_MONOTONIC, &now_tv)) { - zlog_err("%s: gettime(CLOCK_MONOTONIC) failure: errno=%d: %s", + if (gettime_monotonic(&now_tv)) { + zlog_err("%s: gettime_monotonic() failure: errno=%d: %s", __PRETTY_FUNCTION__, errno, safe_strerror(errno)); return -1; diff --git a/pimd/pim_version.h b/pimd/pim_version.h index ef75791e..2734b7ac 100644 --- a/pimd/pim_version.h +++ b/pimd/pim_version.h @@ -23,7 +23,7 @@ #ifndef PIM_VERSION_H #define PIM_VERSION_H -#define PIMD_VERSION_STR "0.160" +#define PIMD_VERSION_STR "0.161" const char * const PIMD_VERSION; From 0ef36d82d87094b32f71be47a73144459f057db9 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Thu, 11 Mar 2010 14:47:42 -0300 Subject: [PATCH 343/482] [pim] Work-around improper monotonic clock --- pimd/Makefile.am | 4 ++++ pimd/pim_main.c | 8 ++++++++ pimd/pim_time.c | 39 +++++++++++++++++++++++++++++++++++---- 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/pimd/Makefile.am b/pimd/Makefile.am index 4aae6378..ef5cffa1 100644 --- a/pimd/Makefile.am +++ b/pimd/Makefile.am @@ -27,6 +27,8 @@ # PIM_REPORT_RECV_IFINDEX_MISMATCH: Report sock/recv ifindex mismatch # PIM_ENFORCE_LOOPFREE_MFC: Refuse adding looping MFC entries # PIM_UNEXPECTED_KERNEL_UPCALL: Report unexpected kernel upcall +# PIM_USE_QUAGGA_GETTIME: Prefer quagga_gettime +# PIM_GETTIME_USE_GETTIMEOFDAY: Work-around improper monotonic clock PIM_DEFS = #PIM_DEFS += -DPIM_DEBUG_BYDEFAULT @@ -37,6 +39,8 @@ PIM_DEFS += -DPIM_MOTD_VERSION PIM_DEFS += -DPIM_USE_QUAGGA_INET_CHECKSUM PIM_DEFS += -DPIM_ENFORCE_LOOPFREE_MFC #PIM_DEFS += -DPIM_UNEXPECTED_KERNEL_UPCALL +#PIM_DEFS += -DPIM_USE_QUAGGA_GETTIME +PIM_DEFS += -DPIM_GETTIME_USE_GETTIMEOFDAY INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" $(PIM_DEFS) diff --git a/pimd/pim_main.c b/pimd/pim_main.c index 2efcbb8c..51e5e360 100644 --- a/pimd/pim_main.c +++ b/pimd/pim_main.c @@ -285,6 +285,14 @@ Hello, this is " QUAGGA_PROGNAME " " QUAGGA_VERSION " " PIMD_PROGNAME " " PIMD_V zlog_notice("PIM_UNEXPECTED_KERNEL_UPCALL: report unexpected kernel upcall"); #endif +#ifdef PIM_FORCE_QUAGGA_REALTIME_STABILISED + zlog_notice("PIM_USE_QUAGGA_GETTIME: using Quagga's quagga_gettime"()); +#endif + +#ifdef PIM_GETTIME_USE_GETTIMEOFDAY + zlog_notice("PIM_GETTIME_USE_GETTIMEOFDAY: work-around improper monotonic clock"); +#endif + #ifdef HAVE_CLOCK_MONOTONIC zlog_notice("HAVE_CLOCK_MONOTONIC"); #else diff --git a/pimd/pim_time.c b/pimd/pim_time.c index a4b1cc46..07719608 100644 --- a/pimd/pim_time.c +++ b/pimd/pim_time.c @@ -30,16 +30,31 @@ #include "pim_time.h" -static int pim_gettime(enum quagga_clkid clkid, struct timeval *tv) +static int pim_gettime(int clk_id, struct timeval *tv) { + struct timespec ts; int result; - result = quagga_gettime(clkid, tv); +#ifdef PIM_USE_QUAGGA_GETTIME + result = quagga_gettime(clk_id, tv); if (result) { - zlog_err("%s: quagga_gettime(clkid=%d) failure: errno=%d: %s", - __PRETTY_FUNCTION__, clkid, + zlog_err("%s: quagga_gettime(clk_id=%d) failure: errno=%d: %s", + __PRETTY_FUNCTION__, clk_id, errno, safe_strerror(errno)); } +#else + result = clock_gettime(clk_id, &ts); + if (result) { + zlog_err("%s: clock_gettime(clk_id=%d) failure: errno=%d: %s", + __PRETTY_FUNCTION__, clk_id, + errno, safe_strerror(errno)); + return result; + } + if (tv) { + tv->tv_sec = ts.tv_sec; + tv->tv_usec = 1000 * ts.tv_nsec; + } +#endif return result; } @@ -48,12 +63,28 @@ static int gettime_monotonic(struct timeval *tv) { int result; +#ifdef PIM_GETTIME_USE_GETTIMEOFDAY + result = gettimeofday(tv, 0); + if (result) { + zlog_err("%s: gettimeofday() failure: errno=%d: %s", + __PRETTY_FUNCTION__, + errno, safe_strerror(errno)); + } +#elif defined(PIM_USE_QUAGGA_GETTIME) result = pim_gettime(QUAGGA_CLK_MONOTONIC, tv); if (result) { zlog_err("%s: pim_gettime(QUAGGA_CLK_MONOTONIC=%d) failure: errno=%d: %s", + __PRETTY_FUNCTION__, QUAGGA_CLK_MONOTONIC, + errno, safe_strerror(errno)); + } +#else + result = pim_gettime(CLOCK_MONOTONIC, tv); + if (result) { + zlog_err("%s: pim_gettime(CLOCK_MONOTONIC=%d) failure: errno=%d: %s", __PRETTY_FUNCTION__, CLOCK_MONOTONIC, errno, safe_strerror(errno)); } +#endif return result; } From ff752d431675caed78e7b460b2d9a4845e5b6a73 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Wed, 17 Mar 2010 10:34:24 -0300 Subject: [PATCH 344/482] [pim] Cosmetic RPF refresh timer display --- pimd/pim_cmd.c | 2 +- pimd/pim_time.c | 8 ++++++++ pimd/pim_time.h | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 2b418cd4..4e6cb89b 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -1081,7 +1081,7 @@ static void show_rpf_refresh_stats(struct vty *vty, time_t now) { char refresh_uptime[10]; - pim_time_uptime(refresh_uptime, sizeof(refresh_uptime), now - qpim_rpf_cache_refresh_last); + pim_time_uptime_begin(refresh_uptime, sizeof(refresh_uptime), now, qpim_rpf_cache_refresh_last); vty_out(vty, "RPF Cache Refresh Delay: %ld msecs%s" diff --git a/pimd/pim_time.c b/pimd/pim_time.c index 07719608..7d4581d9 100644 --- a/pimd/pim_time.c +++ b/pimd/pim_time.c @@ -190,6 +190,14 @@ void pim_time_uptime(char *buf, int buf_size, int64_t uptime_sec) pim_time_hhmmss(buf, buf_size, uptime_sec); } +void pim_time_uptime_begin(char *buf, int buf_size, int64_t now, int64_t begin) +{ + if (begin > 0) + pim_time_uptime(buf, buf_size, now - begin); + else + snprintf(buf, buf_size, "--:--:--"); +} + long pim_time_timer_remain_msec(struct thread *t_timer) { /* FIXME: Actually fetch msec resolution from thread */ diff --git a/pimd/pim_time.h b/pimd/pim_time.h index 379eb6cf..2984d9a8 100644 --- a/pimd/pim_time.h +++ b/pimd/pim_time.h @@ -34,6 +34,7 @@ int pim_time_mmss(char *buf, int buf_size, long sec); void pim_time_timer_to_mmss(char *buf, int buf_size, struct thread *t); void pim_time_timer_to_hhmmss(char *buf, int buf_size, struct thread *t); void pim_time_uptime(char *buf, int buf_size, int64_t uptime_sec); +void pim_time_uptime_begin(char *buf, int buf_size, int64_t now, int64_t begin); long pim_time_timer_remain_msec(struct thread *t_timer); #endif /* PIM_TIME_H */ From fa2e1ee255c6a3be76f74b07cb441c34d4b2583f Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Wed, 17 Mar 2010 10:51:34 -0300 Subject: [PATCH 345/482] [pim] Uniform format for commands "debug pim packet-dump" and "test pim receive dump" --- pimd/pim_util.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pimd/pim_util.c b/pimd/pim_util.c index d97b599c..a7e8234e 100644 --- a/pimd/pim_util.c +++ b/pimd/pim_util.c @@ -141,7 +141,7 @@ void pim_pkt_dump(const char *label, const uint8_t *buf, int size) int i = 0; int j = 0; - for (; i < size; ++i, j += 3) { + for (; i < size; ++i, j += 2) { int left = sizeof(dump_buf) - j; if (left < 4) { if (left > 1) { @@ -149,10 +149,10 @@ void pim_pkt_dump(const char *label, const uint8_t *buf, int size) } break; } - snprintf(dump_buf + j, left, " %02x", buf[i]); + snprintf(dump_buf + j, left, "%02x", buf[i]); } - zlog_debug("%s: pkt dump size=%d:%s", + zlog_debug("%s: pkt dump size=%d: %s", label, size, dump_buf); From 55b12ff4602ae802281f76e4cda681104a6cf89d Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Thu, 15 Apr 2010 15:58:30 -0300 Subject: [PATCH 346/482] [pim] PIM route type. --- lib/route_types.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/route_types.txt b/lib/route_types.txt index cebf01fc..1b856079 100644 --- a/lib/route_types.txt +++ b/lib/route_types.txt @@ -51,6 +51,7 @@ ZEBRA_ROUTE_OSPF, ospf, ospfd, 'O', 1, 0, "OSPF" ZEBRA_ROUTE_OSPF6, ospf6, ospf6d, 'O', 0, 1, "OSPFv6" ZEBRA_ROUTE_ISIS, isis, isisd, 'I', 1, 1, "IS-IS" ZEBRA_ROUTE_BGP, bgp, bgpd, 'B', 1, 1, "BGP" +ZEBRA_ROUTE_PIM, pim, pimd, 'P', 1, 0, "PIM" # HSLS and OLSR both are AFI independent (so: 1, 1), however # we want to disable for them for general Quagga distribution. # This at least makes it trivial for users of these protocols @@ -71,6 +72,7 @@ ZEBRA_ROUTE_OSPF, "Open Shortest Path First (OSPFv2)" ZEBRA_ROUTE_OSPF6, "Open Shortest Path First (IPv6) (OSPFv3)" ZEBRA_ROUTE_ISIS, "Intermediate System to Intermediate System (IS-IS)" ZEBRA_ROUTE_BGP, "Border Gateway Protocol (BGP)" +ZEBRA_ROUTE_PIM, "Protocol Independent Multicast (PIM)" ZEBRA_ROUTE_HSLS, "Hazy-Sighted Link State Protocol (HSLS)" ZEBRA_ROUTE_OLSR, "Optimised Link State Routing (OLSR)" ZEBRA_ROUTE_BABEL, "Babel routing protocol (Babel)" From 6d26c37a21f65bd7b4c8a9a272fafd0a07091c98 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Thu, 15 Apr 2010 16:22:11 -0300 Subject: [PATCH 347/482] [pim] .gitignore --- pimd/.gitignore | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 pimd/.gitignore diff --git a/pimd/.gitignore b/pimd/.gitignore new file mode 100644 index 00000000..7046d69c --- /dev/null +++ b/pimd/.gitignore @@ -0,0 +1,7 @@ +.libs +*.a +*.o +pimd +test_igmpv3_join + + From 05e573de60c0b3f1dd874d306b818f7c0dc12bf7 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Tue, 20 Apr 2010 12:20:46 -0300 Subject: [PATCH 348/482] [pim] "show ip route" renamed to "show ip rib" --- pimd/COMMANDS | 2 +- pimd/pim_cmd.c | 12 ++++++------ pimd/pim_cmd.h | 1 + 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/pimd/COMMANDS b/pimd/COMMANDS index 1199368b..fb09e6c9 100644 --- a/pimd/COMMANDS +++ b/pimd/COMMANDS @@ -41,7 +41,7 @@ verification commands: show ip multicast Multicast global information show ip mroute IP multicast routing table show ip mroute count Route and packet count data - show ip route IP routing table + show ip rib IP unicast routing table show ip ssmpingd ssmpingd operation debug commands: diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 4e6cb89b..60e5a16e 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -2159,12 +2159,12 @@ DEFUN (show_ip_mroute_count, return CMD_SUCCESS; } -DEFUN (show_ip_route, - show_ip_route_cmd, - "show ip route A.B.C.D", +DEFUN (show_ip_rib, + show_ip_rib_cmd, + "show ip rib A.B.C.D", SHOW_STR IP_STR - ROUTE_STR + RIB_STR "Unicast address\n") { struct in_addr addr; @@ -4261,7 +4261,7 @@ void pim_cmd_init() install_element (VIEW_NODE, &show_ip_multicast_cmd); install_element (VIEW_NODE, &show_ip_mroute_cmd); install_element (VIEW_NODE, &show_ip_mroute_count_cmd); - install_element (VIEW_NODE, &show_ip_route_cmd); + install_element (VIEW_NODE, &show_ip_rib_cmd); install_element (VIEW_NODE, &show_ip_ssmpingd_cmd); install_element (VIEW_NODE, &show_debugging_cmd); @@ -4298,7 +4298,7 @@ void pim_cmd_init() install_element (ENABLE_NODE, &show_ip_multicast_cmd); install_element (ENABLE_NODE, &show_ip_mroute_cmd); install_element (ENABLE_NODE, &show_ip_mroute_count_cmd); - install_element (ENABLE_NODE, &show_ip_route_cmd); + install_element (ENABLE_NODE, &show_ip_rib_cmd); install_element (ENABLE_NODE, &show_ip_ssmpingd_cmd); install_element (ENABLE_NODE, &show_debugging_cmd); diff --git a/pimd/pim_cmd.h b/pimd/pim_cmd.h index d83cbc09..391046a4 100644 --- a/pimd/pim_cmd.h +++ b/pimd/pim_cmd.h @@ -51,6 +51,7 @@ #define CLEAR_IP_IGMP_STR "IGMP clear commands\n" #define CLEAR_IP_PIM_STR "PIM clear commands\n" #define MROUTE_STR "IP multicast routing table\n" +#define RIB_STR "IP unicast routing table\n" #define PIM_CMD_NO "no" #define PIM_CMD_IP_MULTICAST_ROUTING "ip multicast-routing" From 4560c44d109a25672bf7ec41adb3c63950c46f3e Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Wed, 5 May 2010 14:32:52 -0300 Subject: [PATCH 349/482] [pim] Reference to troglobit pimd --- pimd/README | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pimd/README b/pimd/README index e42ceda2..f6bd8ec6 100644 --- a/pimd/README +++ b/pimd/README @@ -131,10 +131,17 @@ RELATED WORK igmprt: An IGMPv3-router implementation - http://www.loria.fr/~lahmadi/igmpv3-router.html - pimd: PIMv2-SM daemon + USC pimd: PIMv2-SM daemon - http://netweb.usc.edu/pim/pimd (URL broken in 2008-12-23) - http://packages.debian.org/source/sid/pimd (from Debian) + troglobit pimd: This is the original USC pimd from + http://netweb.usc.edu/pim/. In January 16, 2010 it was revived + with the intention to collect patches floating around in + Debian, Gentoo, Lintrack and other distribution repositories + and to provide a central point of collaboration. + - http://github.com/troglobit/pimd + zpimd: zpimd is not dependent of zebra or any other routing daemon - ftp://robur.slu.se/pub/Routing/Zebra - http://sunsite2.icm.edu.pl/pub/unix/routing/zpimd From 9f4e191bb0c639a21c69b3d139349040283fa941 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Thu, 27 May 2010 10:08:43 -0300 Subject: [PATCH 350/482] [pim] Version up to 0.162 --- pimd/pim_version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pimd/pim_version.h b/pimd/pim_version.h index 2734b7ac..59db6dcf 100644 --- a/pimd/pim_version.h +++ b/pimd/pim_version.h @@ -23,7 +23,7 @@ #ifndef PIM_VERSION_H #define PIM_VERSION_H -#define PIMD_VERSION_STR "0.161" +#define PIMD_VERSION_STR "0.162" const char * const PIMD_VERSION; From d0d7980b7c71c95fd0ddc491b2e60260c604d04b Mon Sep 17 00:00:00 2001 From: User Date: Thu, 5 Aug 2010 13:26:25 -0700 Subject: [PATCH 351/482] [pim] Compile fixes for FreeBSD. --- pimd/pim_igmp_join.c | 6 ++++++ pimd/test_igmpv3_join.c | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/pimd/pim_igmp_join.c b/pimd/pim_igmp_join.c index 7183997c..693a42b7 100644 --- a/pimd/pim_igmp_join.c +++ b/pimd/pim_igmp_join.c @@ -20,10 +20,16 @@ $QuaggaId: $Format:%an, %ai, %h$ $ */ +#include +#include #include #include "pim_igmp_join.h" +#ifndef SOL_IP +#define SOL_IP IPPROTO_IP +#endif + #ifndef MCAST_JOIN_SOURCE_GROUP #define MCAST_JOIN_SOURCE_GROUP 46 struct group_source_req diff --git a/pimd/test_igmpv3_join.c b/pimd/test_igmpv3_join.c index af93ab60..fe64fbc0 100644 --- a/pimd/test_igmpv3_join.c +++ b/pimd/test_igmpv3_join.c @@ -25,9 +25,9 @@ #include #include #include -#include #include #include +#include #include #include "pim_igmp_join.h" From 8281793ebaa1953d679c553f552ba0eee2bab758 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Wed, 18 Aug 2010 10:05:47 -0300 Subject: [PATCH 352/482] [pim] Additional PIM drafts. --- pimd/TODO | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/pimd/TODO b/pimd/TODO index 0a5617e4..3a3330d3 100644 --- a/pimd/TODO +++ b/pimd/TODO @@ -396,5 +396,30 @@ T42 Static igmp join fails when loading config at boot time Interface Address ifi Vif PktsIn PktsOut BytesIn BytesOut eth0 200.202.112.3 2 2 0 0 0 0 lo 127.0.0.1 1 1 0 0 0 0 + +T43 PIM Neighbor Reduction + https://datatracker.ietf.org/doc/draft-wijnands-pim-neighbor-reduction/ + + "In a transit LAN (no directly connected source or receiver), many + of the PIM procedures don't apply. (...) This proposal describes + a procedure to reduce the amount of neighbors established over a + transit LAN." + +T44 Single Stream Multicast Fast Reroute (SMFR) Method + https://datatracker.ietf.org/doc/draft-liu-pim-single-stream-multicast-frr/ + + "This document proposes an IP multicast fast convergence method + based on differentiating primary and backup PIM join." + +T45 RFC5384 - The Join Attribute Format + "This document describes a modification of the Join message that + allows a node to associate attributes with a particular tree." + +T46 PIM Multi-Topology ID (MT-ID) Join-Attribute + http://tools.ietf.org/html/draft-cai-pim-mtid-00 + Depends on T45. + + "This draft introduces a new type of PIM Join Attribute used to + encode the identity of the topology PIM uses for RPF." -x- From b9ef7704a72e3ec52174c2792404b15275ff4681 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Fri, 27 Aug 2010 18:11:10 -0300 Subject: [PATCH 353/482] [pim] bootstrap from tarball prefers autoreconf -i --- pimd/quagga-bootstrap.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pimd/quagga-bootstrap.sh b/pimd/quagga-bootstrap.sh index 1ad9a121..4ec443d9 100755 --- a/pimd/quagga-bootstrap.sh +++ b/pimd/quagga-bootstrap.sh @@ -17,5 +17,7 @@ if [ -f ./bootstrap.sh ]; then ./bootstrap.sh else msg missing ./bootstrap.sh from quagga - autoreconf -i --force + #autoreconf -i --force + #bootstrap from tarball prefers autoreconf -i + autoreconf -i fi From eb383d931c2bb54b5b84a827503f62cbf1070ef5 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Fri, 27 Aug 2010 18:16:33 -0300 Subject: [PATCH 354/482] [pim] Version up to 0.163 --- pimd/pim_version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pimd/pim_version.h b/pimd/pim_version.h index 59db6dcf..22888c53 100644 --- a/pimd/pim_version.h +++ b/pimd/pim_version.h @@ -23,7 +23,7 @@ #ifndef PIM_VERSION_H #define PIM_VERSION_H -#define PIMD_VERSION_STR "0.162" +#define PIMD_VERSION_STR "0.163" const char * const PIMD_VERSION; From 5c6979834655fb7244e23b958582d51bb176ce9a Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Thu, 16 Feb 2012 04:47:56 +0100 Subject: [PATCH 355/482] pimd: fix format strings * pim_igmp.c, * pim_igmpv3.c, * pim_pim.c, * pim_tlv.c: use %zu / %zd for size_t/ssize_t * pim_iface.c, * pim_ifchannel.c, * pim_mroute.c, * pim_neighbor.c, * pim_oil.c, * pim_ssmpingd.c, * pim_upstream.c: %zu for size_t * pim_cmd.c: %zu + a few (long long) casts for int64_t * pim_hello.c: %td for ptrdiff_t --- pimd/pim_cmd.c | 14 +++++++------- pimd/pim_hello.c | 2 +- pimd/pim_iface.c | 4 ++-- pimd/pim_ifchannel.c | 2 +- pimd/pim_igmp.c | 8 ++++---- pimd/pim_igmpv3.c | 12 ++++++------ pimd/pim_mroute.c | 2 +- pimd/pim_neighbor.c | 2 +- pimd/pim_oil.c | 4 ++-- pimd/pim_pim.c | 10 +++++----- pimd/pim_ssmpingd.c | 4 ++-- pimd/pim_tlv.c | 16 ++++++++-------- pimd/pim_upstream.c | 2 +- 13 files changed, 41 insertions(+), 41 deletions(-) diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 60e5a16e..c37e9615 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -1091,8 +1091,8 @@ static void show_rpf_refresh_stats(struct vty *vty, time_t now) "RPF Cache Refresh Last: %s%s", qpim_rpf_cache_refresh_delay_msec, VTY_NEWLINE, pim_time_timer_remain_msec(qpim_rpf_cache_refresher), VTY_NEWLINE, - qpim_rpf_cache_refresh_requests, VTY_NEWLINE, - qpim_rpf_cache_refresh_events, VTY_NEWLINE, + (long long)qpim_rpf_cache_refresh_requests, VTY_NEWLINE, + (long long)qpim_rpf_cache_refresh_events, VTY_NEWLINE, refresh_uptime, VTY_NEWLINE); } @@ -2241,7 +2241,7 @@ static void show_ssmpingd(struct vty *vty) bind_addr_str, ntohs(bind_addr.sin_port), ss_uptime, - ss->requests, + (long long)ss->requests, VTY_NEWLINE); } } @@ -3642,7 +3642,7 @@ DEFUN (test_pim_receive_dump, left = sizeof(buf) - ip_hlen - pim_msg_size; if (left < 1) { - vty_out(vty, "%% Overflow buf_size=%d buf_left=%d at hex array arg %d=%s octet %02x%s", + vty_out(vty, "%% Overflow buf_size=%zu buf_left=%d at hex array arg %d=%s octet %02x%s", sizeof(buf), left, argi, str, octet, VTY_NEWLINE); return CMD_WARNING; } @@ -3653,7 +3653,7 @@ DEFUN (test_pim_receive_dump, ip_msg_len = ip_hlen + pim_msg_size; - vty_out(vty, "Receiving: buf_size=%d ip_msg_size=%d pim_msg_size=%d%s", + vty_out(vty, "Receiving: buf_size=%zu ip_msg_size=%d pim_msg_size=%d%s", sizeof(buf), ip_msg_len, pim_msg_size, VTY_NEWLINE); /* "receive" message */ @@ -3873,7 +3873,7 @@ DEFUN (test_pim_receive_assert, remain = buf_pastend - buf; if (remain < (int) sizeof(struct ip)) { - vty_out(vty, "No room for ip header: buf_size=%d < ip_header_size=%d%s", + vty_out(vty, "No room for ip header: buf_size=%d < ip_header_size=%zu%s", remain, sizeof(struct ip), VTY_NEWLINE); return CMD_WARNING; } @@ -4195,7 +4195,7 @@ DEFUN (test_pim_receive_upcall, result = pim_mroute_msg(-1, (char *) &msg, sizeof(msg)); if (result) { - vty_out(vty, "pim_mroute_msg(len=%d) returned failure: %d%s", + vty_out(vty, "pim_mroute_msg(len=%zu) returned failure: %d%s", sizeof(msg), result, VTY_NEWLINE); return CMD_WARNING; } diff --git a/pimd/pim_hello.c b/pimd/pim_hello.c index 545b3b12..c942de4d 100644 --- a/pimd/pim_hello.c +++ b/pimd/pim_hello.c @@ -197,7 +197,7 @@ int pim_hello_recv(struct interface *ifp, if ((tlv_curr + option_len) > tlv_pastend) { char src_str[100]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); - zlog_warn("%s: long PIM hello TLV type=%d length=%d > left=%d from %s on interface %s", + zlog_warn("%s: long PIM hello TLV type=%d length=%d > left=%td from %s on interface %s", __PRETTY_FUNCTION__, option_type, option_len, tlv_pastend - tlv_curr, src_str, ifp->name); diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index 52d4f0d8..f0c32661 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -80,7 +80,7 @@ struct pim_interface *pim_if_new(struct interface *ifp, int igmp, int pim) pim_ifp = XMALLOC(MTYPE_PIM_INTERFACE, sizeof(*pim_ifp)); if (!pim_ifp) { - zlog_err("PIM XMALLOC(%d) failure", sizeof(*pim_ifp)); + zlog_err("PIM XMALLOC(%zu) failure", sizeof(*pim_ifp)); return 0; } @@ -911,7 +911,7 @@ static struct igmp_join *igmp_join_new(struct interface *ifp, char source_str[100]; pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); - zlog_err("%s: XMALLOC(%d) failure for IGMP group %s source %s on interface %s", + zlog_err("%s: XMALLOC(%zu) failure for IGMP group %s source %s on interface %s", __PRETTY_FUNCTION__, sizeof(*ij), group_str, source_str, ifp->name); close(join_fd); diff --git a/pimd/pim_ifchannel.c b/pimd/pim_ifchannel.c index 7f946cfd..6ceef4ee 100644 --- a/pimd/pim_ifchannel.c +++ b/pimd/pim_ifchannel.c @@ -204,7 +204,7 @@ static struct pim_ifchannel *pim_ifchannel_new(struct interface *ifp, ch = XMALLOC(MTYPE_PIM_IFCHANNEL, sizeof(*ch)); if (!ch) { - zlog_err("%s: PIM XMALLOC(%d) failure", + zlog_err("%s: PIM XMALLOC(%zu) failure", __PRETTY_FUNCTION__, sizeof(*ch)); return 0; } diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c index a0a4aa81..dcb9a9ba 100644 --- a/pimd/pim_igmp.c +++ b/pimd/pim_igmp.c @@ -679,7 +679,7 @@ int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len) char to_str[100]; if (len < sizeof(*ip_hdr)) { - zlog_warn("IGMP packet size=%d shorter than minimum=%d", + zlog_warn("IGMP packet size=%zu shorter than minimum=%zu", len, sizeof(*ip_hdr)); return -1; } @@ -692,7 +692,7 @@ int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len) ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */ if (PIM_DEBUG_IGMP_PACKETS) { - zlog_debug("Recv IP packet from %s to %s on %s: size=%d ip_header_size=%d ip_proto=%d", + zlog_debug("Recv IP packet from %s to %s on %s: size=%zu ip_header_size=%zu ip_proto=%d", from_str, to_str, igmp->interface->name, len, ip_hlen, ip_hdr->ip_p); } @@ -703,12 +703,12 @@ int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len) } if (ip_hlen < PIM_IP_HEADER_MIN_LEN) { - zlog_warn("IP packet header size=%d shorter than minimum=%d", + zlog_warn("IP packet header size=%zu shorter than minimum=%d", ip_hlen, PIM_IP_HEADER_MIN_LEN); return -1; } if (ip_hlen > PIM_IP_HEADER_MAX_LEN) { - zlog_warn("IP packet header size=%d greater than maximum=%d", + zlog_warn("IP packet header size=%zu greater than maximum=%d", ip_hlen, PIM_IP_HEADER_MAX_LEN); return -1; } diff --git a/pimd/pim_igmpv3.c b/pimd/pim_igmpv3.c index 7e76f442..f8926524 100644 --- a/pimd/pim_igmpv3.c +++ b/pimd/pim_igmpv3.c @@ -1151,7 +1151,7 @@ static int group_retransmit_sources(struct igmp_group *group, if (num_sources_tosend1 > query_buf1_max_sources) { char group_str[100]; pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); - zlog_warn("%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%d (max_sources=%d)", + zlog_warn("%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)", __PRETTY_FUNCTION__, group_str, igmp->interface->name, num_sources_tosend1, sizeof(query_buf1), query_buf1_max_sources); } @@ -1193,7 +1193,7 @@ static int group_retransmit_sources(struct igmp_group *group, if (num_sources_tosend2 > query_buf2_max_sources) { char group_str[100]; pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); - zlog_warn("%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%d (max_sources=%d)", + zlog_warn("%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)", __PRETTY_FUNCTION__, group_str, igmp->interface->name, num_sources_tosend2, sizeof(query_buf2), query_buf2_max_sources); } @@ -1622,7 +1622,7 @@ void pim_igmp_send_membership_query(struct igmp_group *group, msg_size = IGMP_V3_SOURCES_OFFSET + (num_sources << 2); if (msg_size > query_buf_size) { - zlog_err("%s %s: unable to send: msg_size=%d larger than query_buf_size=%d", + zlog_err("%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d", __FILE__, __PRETTY_FUNCTION__, msg_size, query_buf_size); return; @@ -1662,7 +1662,7 @@ void pim_igmp_send_membership_query(struct igmp_group *group, char group_str[100]; pim_inet4_dump("", dst_addr, dst_str, sizeof(dst_str)); pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); - zlog_debug("%s: to %s on %s: group=%s sources=%d msg_size=%d s_flag=%x QRV=%u QQI=%u QQIC=%02x checksum=%x", + zlog_debug("%s: to %s on %s: group=%s sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x checksum=%x", __PRETTY_FUNCTION__, dst_str, ifname, group_str, num_sources, msg_size, s_flag, querier_robustness_variable, @@ -1687,13 +1687,13 @@ void pim_igmp_send_membership_query(struct igmp_group *group, pim_inet4_dump("", dst_addr, dst_str, sizeof(dst_str)); pim_inet4_dump("", group_addr, group_str, sizeof(group_str)); if (sent < 0) { - zlog_warn("%s: sendto() failure to %s on %s: group=%s msg_size=%d: errno=%d: %s", + zlog_warn("%s: sendto() failure to %s on %s: group=%s msg_size=%zd: errno=%d: %s", __PRETTY_FUNCTION__, dst_str, ifname, group_str, msg_size, e, safe_strerror(e)); } else { - zlog_warn("%s: sendto() partial to %s on %s: group=%s msg_size=%d: sent=%d", + zlog_warn("%s: sendto() partial to %s on %s: group=%s msg_size=%zd: sent=%zd", __PRETTY_FUNCTION__, dst_str, ifname, group_str, msg_size, sent); diff --git a/pimd/pim_mroute.c b/pimd/pim_mroute.c index c76ba525..d8837362 100644 --- a/pimd/pim_mroute.c +++ b/pimd/pim_mroute.c @@ -201,7 +201,7 @@ static int mroute_read_msg(int fd) int rd; if (((int) sizeof(buf)) < msg_min_size) { - zlog_err("%s: fd=%d: buf size=%d lower than msg_min=%d", + zlog_err("%s: fd=%d: buf size=%zu lower than msg_min=%d", __PRETTY_FUNCTION__, fd, sizeof(buf), msg_min_size); return -1; } diff --git a/pimd/pim_neighbor.c b/pimd/pim_neighbor.c index 9013b50c..0df74221 100644 --- a/pimd/pim_neighbor.c +++ b/pimd/pim_neighbor.c @@ -292,7 +292,7 @@ static struct pim_neighbor *pim_neighbor_new(struct interface *ifp, neigh = XMALLOC(MTYPE_PIM_NEIGHBOR, sizeof(*neigh)); if (!neigh) { - zlog_err("%s: PIM XMALLOC(%d) failure", + zlog_err("%s: PIM XMALLOC(%zu) failure", __PRETTY_FUNCTION__, sizeof(*neigh)); return 0; } diff --git a/pimd/pim_oil.c b/pimd/pim_oil.c index 2c8b056e..1aaece3f 100644 --- a/pimd/pim_oil.c +++ b/pimd/pim_oil.c @@ -69,7 +69,7 @@ static struct channel_oil *channel_oil_new(struct in_addr group_addr, c_oil = XCALLOC(MTYPE_PIM_CHANNEL_OIL, sizeof(*c_oil)); if (!c_oil) { - zlog_err("PIM XCALLOC(%d) failure", sizeof(*c_oil)); + zlog_err("PIM XCALLOC(%zu) failure", sizeof(*c_oil)); return 0; } @@ -91,7 +91,7 @@ static struct channel_oil *pim_add_channel_oil(struct in_addr group_addr, c_oil = channel_oil_new(group_addr, source_addr, input_vif_index); if (!c_oil) { - zlog_warn("PIM XCALLOC(%d) failure", sizeof(*c_oil)); + zlog_warn("PIM XCALLOC(%zu) failure", sizeof(*c_oil)); return 0; } diff --git a/pimd/pim_pim.c b/pimd/pim_pim.c index 9595d2d7..156081ca 100644 --- a/pimd/pim_pim.c +++ b/pimd/pim_pim.c @@ -132,7 +132,7 @@ int pim_pim_packet(struct interface *ifp, char *buf, size_t len) } if (len < sizeof(*ip_hdr)) { - zlog_warn("PIM packet size=%d shorter than minimum=%d", + zlog_warn("PIM packet size=%zu shorter than minimum=%zu", len, sizeof(*ip_hdr)); return -1; } @@ -145,7 +145,7 @@ int pim_pim_packet(struct interface *ifp, char *buf, size_t len) ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */ if (PIM_DEBUG_PIM_PACKETS) { - zlog_debug("Recv IP packet from %s to %s on %s: size=%d ip_header_size=%d ip_proto=%d", + zlog_debug("Recv IP packet from %s to %s on %s: size=%zu ip_header_size=%zu ip_proto=%d", src_str, dst_str, ifp->name, len, ip_hlen, ip_hdr->ip_p); } @@ -156,12 +156,12 @@ int pim_pim_packet(struct interface *ifp, char *buf, size_t len) } if (ip_hlen < PIM_IP_HEADER_MIN_LEN) { - zlog_warn("IP packet header size=%d shorter than minimum=%d", + zlog_warn("IP packet header size=%zu shorter than minimum=%d", ip_hlen, PIM_IP_HEADER_MIN_LEN); return -1; } if (ip_hlen > PIM_IP_HEADER_MAX_LEN) { - zlog_warn("IP packet header size=%d greater than maximum=%d", + zlog_warn("IP packet header size=%zu greater than maximum=%d", ip_hlen, PIM_IP_HEADER_MAX_LEN); return -1; } @@ -480,7 +480,7 @@ int pim_msg_send(int fd, e, safe_strerror(e)); } else { - zlog_warn("%s: sendto() partial to %s on %s: fd=%d msg_size=%d: sent=%d", + zlog_warn("%s: sendto() partial to %s on %s: fd=%d msg_size=%d: sent=%zd", __PRETTY_FUNCTION__, dst_str, ifname, fd, pim_msg_size, sent); diff --git a/pimd/pim_ssmpingd.c b/pimd/pim_ssmpingd.c index 6422cf62..652999d4 100644 --- a/pimd/pim_ssmpingd.c +++ b/pimd/pim_ssmpingd.c @@ -99,7 +99,7 @@ static int ssmpingd_socket(struct in_addr addr, int port, int mttl) if (bind(fd, &sockaddr, sizeof(sockaddr))) { char addr_str[100]; pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); - zlog_warn("%s: bind(fd=%d,addr=%s,port=%d,len=%d) failure: errno=%d: %s", + zlog_warn("%s: bind(fd=%d,addr=%s,port=%d,len=%zu) failure: errno=%d: %s", __PRETTY_FUNCTION__, fd, addr_str, port, sizeof(sockaddr), errno, safe_strerror(errno)); @@ -373,7 +373,7 @@ static struct ssmpingd_sock *ssmpingd_new(struct in_addr source_addr) if (!ss) { char source_str[100]; pim_inet4_dump("", source_addr, source_str, sizeof(source_str)); - zlog_err("%s: XMALLOC(%d) failure for ssmpingd source %s", + zlog_err("%s: XMALLOC(%zu) failure for ssmpingd source %s", __PRETTY_FUNCTION__, sizeof(*ss), source_str); close(sock_fd); diff --git a/pimd/pim_tlv.c b/pimd/pim_tlv.c index 16b7e93e..cb6b6540 100644 --- a/pimd/pim_tlv.c +++ b/pimd/pim_tlv.c @@ -39,7 +39,7 @@ char *pim_tlv_append_uint16(uint8_t *buf, uint16_t option_len = 2; if ((buf + PIM_TLV_OPTION_SIZE(option_len)) > buf_pastend) { - zlog_warn("%s: buffer overflow: left=%d needed=%d", + zlog_warn("%s: buffer overflow: left=%zd needed=%d", __PRETTY_FUNCTION__, buf_pastend - buf, PIM_TLV_OPTION_SIZE(option_len)); return 0; @@ -64,7 +64,7 @@ char *pim_tlv_append_2uint16(uint8_t *buf, uint16_t option_len = 4; if ((buf + PIM_TLV_OPTION_SIZE(option_len)) > buf_pastend) { - zlog_warn("%s: buffer overflow: left=%d needed=%d", + zlog_warn("%s: buffer overflow: left=%zd needed=%d", __PRETTY_FUNCTION__, buf_pastend - buf, PIM_TLV_OPTION_SIZE(option_len)); return 0; @@ -90,7 +90,7 @@ char *pim_tlv_append_uint32(uint8_t *buf, uint16_t option_len = 4; if ((buf + PIM_TLV_OPTION_SIZE(option_len)) > buf_pastend) { - zlog_warn("%s: buffer overflow: left=%d needed=%d", + zlog_warn("%s: buffer overflow: left=%zd needed=%d", __PRETTY_FUNCTION__, buf_pastend - buf, PIM_TLV_OPTION_SIZE(option_len)); return 0; @@ -137,7 +137,7 @@ char *pim_tlv_append_addrlist_ucast(uint8_t *buf, continue; if ((curr + ucast_ipv4_encoding_len) > buf_pastend) { - zlog_warn("%s: buffer overflow: left=%d needed=%d", + zlog_warn("%s: buffer overflow: left=%zd needed=%zu", __PRETTY_FUNCTION__, buf_pastend - curr, ucast_ipv4_encoding_len); return 0; @@ -155,7 +155,7 @@ char *pim_tlv_append_addrlist_ucast(uint8_t *buf, } if (PIM_DEBUG_PIM_TRACE) { - zlog_warn("%s: number of encoded secondary unicast IPv4 addresses: %d", + zlog_warn("%s: number of encoded secondary unicast IPv4 addresses: %zu", __PRETTY_FUNCTION__, option_len / ucast_ipv4_encoding_len); } @@ -398,7 +398,7 @@ int pim_parse_addr_ucast(const char *ifname, struct in_addr src_addr, if ((addr + sizeof(struct in_addr)) > pastend) { char src_str[100]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); - zlog_warn("%s: IPv4 unicast address overflow: left=%d needed=%d from %s on %s", + zlog_warn("%s: IPv4 unicast address overflow: left=%zd needed=%zu from %s on %s", __PRETTY_FUNCTION__, pastend - addr, sizeof(struct in_addr), src_str, ifname); @@ -471,7 +471,7 @@ int pim_parse_addr_group(const char *ifname, struct in_addr src_addr, if ((addr + sizeof(struct in_addr)) > pastend) { char src_str[100]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); - zlog_warn("%s: IPv4 group address overflow: left=%d needed=%d from %s on %s", + zlog_warn("%s: IPv4 group address overflow: left=%zd needed=%zu from %s on %s", __PRETTY_FUNCTION__, pastend - addr, sizeof(struct in_addr), src_str, ifname); @@ -549,7 +549,7 @@ int pim_parse_addr_source(const char *ifname, if ((addr + sizeof(struct in_addr)) > pastend) { char src_str[100]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); - zlog_warn("%s: IPv4 source address overflow: left=%d needed=%d from %s on %s", + zlog_warn("%s: IPv4 source address overflow: left=%zd needed=%zu from %s on %s", __PRETTY_FUNCTION__, pastend - addr, sizeof(struct in_addr), src_str, ifname); diff --git a/pimd/pim_upstream.c b/pimd/pim_upstream.c index b9cf1e52..6702ca13 100644 --- a/pimd/pim_upstream.c +++ b/pimd/pim_upstream.c @@ -355,7 +355,7 @@ static struct pim_upstream *pim_upstream_new(struct in_addr source_addr, up = XMALLOC(MTYPE_PIM_UPSTREAM, sizeof(*up)); if (!up) { - zlog_err("%s: PIM XMALLOC(%d) failure", + zlog_err("%s: PIM XMALLOC(%zu) failure", __PRETTY_FUNCTION__, sizeof(*up)); return 0; } From e269b968fdcd44d7a4043c4e67c3e008f85e7379 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Thu, 16 Feb 2012 04:32:08 +0000 Subject: [PATCH 356/482] pimd: use socklen_t consistently --- pimd/pim_cmd.c | 2 +- pimd/pim_sock.c | 2 +- pimd/pim_sock.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index c37e9615..dd06b070 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -2222,7 +2222,7 @@ static void show_ssmpingd(struct vty *vty) char source_str[100]; char ss_uptime[10]; struct sockaddr_in bind_addr; - int len = sizeof(bind_addr); + socklen_t len = sizeof(bind_addr); char bind_addr_str[100]; pim_inet4_dump("", ss->source_addr, source_str, sizeof(source_str)); diff --git a/pimd/pim_sock.c b/pimd/pim_sock.c index 2e786054..b80c0a22 100644 --- a/pimd/pim_sock.c +++ b/pimd/pim_sock.c @@ -375,7 +375,7 @@ int pim_socket_mcastloop_get(int fd) return loop; } -int pim_socket_getsockname(int fd, struct sockaddr *name, int *namelen) +int pim_socket_getsockname(int fd, struct sockaddr *name, socklen_t *namelen) { if (getsockname(fd, name, namelen)) { int e = errno; diff --git a/pimd/pim_sock.h b/pimd/pim_sock.h index 3f026dcd..29b5aeed 100644 --- a/pimd/pim_sock.h +++ b/pimd/pim_sock.h @@ -52,6 +52,6 @@ int pim_socket_recvfromto(int fd, char *buf, size_t len, int pim_socket_mcastloop_get(int fd); -int pim_socket_getsockname(int fd, struct sockaddr *name, int *namelen); +int pim_socket_getsockname(int fd, struct sockaddr *name, socklen_t *namelen); #endif /* PIM_SOCK_H */ From f8cfeb25e62206aaf940b2aabf6a96c36ad4627d Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Thu, 16 Feb 2012 04:31:08 +0000 Subject: [PATCH 357/482] pimd: fix worst char * <> uint8_t * intermingling --- pimd/pim_assert.c | 6 +-- pimd/pim_assert.h | 2 +- pimd/pim_cmd.c | 22 +++++------ pimd/pim_hello.c | 14 +++---- pimd/pim_hello.h | 4 +- pimd/pim_igmp.c | 4 +- pimd/pim_join.c | 12 +++--- pimd/pim_join.h | 2 +- pimd/pim_msg.c | 20 +++++----- pimd/pim_msg.h | 20 +++++----- pimd/pim_pim.c | 10 ++--- pimd/pim_pim.h | 4 +- pimd/pim_sock.c | 2 +- pimd/pim_sock.h | 2 +- pimd/pim_ssmpingd.c | 4 +- pimd/pim_tlv.c | 90 +++++++++++++++++++++------------------------ pimd/pim_tlv.h | 48 ++++++++++++------------ 17 files changed, 129 insertions(+), 137 deletions(-) diff --git a/pimd/pim_assert.c b/pimd/pim_assert.c index 52168d69..6b062b77 100644 --- a/pimd/pim_assert.c +++ b/pimd/pim_assert.c @@ -236,13 +236,13 @@ static int dispatch_assert(struct interface *ifp, int pim_assert_recv(struct interface *ifp, struct pim_neighbor *neigh, struct in_addr src_addr, - char *buf, int buf_size) + uint8_t *buf, int buf_size) { struct prefix msg_group_addr; struct prefix msg_source_addr; struct pim_assert_metric msg_metric; int offset; - char *curr; + uint8_t *curr; int curr_size; on_trace(__PRETTY_FUNCTION__, ifp, src_addr); @@ -445,7 +445,7 @@ static int pim_assert_do(struct pim_ifchannel *ch, { struct interface *ifp; struct pim_interface *pim_ifp; - char pim_msg[1000]; + uint8_t pim_msg[1000]; int pim_msg_size; ifp = ch->interface; diff --git a/pimd/pim_assert.h b/pimd/pim_assert.h index feeb91df..bd3fb3e2 100644 --- a/pimd/pim_assert.h +++ b/pimd/pim_assert.h @@ -51,7 +51,7 @@ void pim_ifassert_winner_set(struct pim_ifchannel *ch, int pim_assert_recv(struct interface *ifp, struct pim_neighbor *neigh, struct in_addr src_addr, - char *buf, int buf_size); + uint8_t *buf, int buf_size); int pim_assert_metric_better(const struct pim_assert_metric *m1, const struct pim_assert_metric *m2); diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index dd06b070..f046801d 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -3567,8 +3567,8 @@ DEFUN (test_pim_receive_dump, "Neighbor address\n" "Packet dump\n") { - char buf[1000]; - char *pim_msg; + uint8_t buf[1000]; + uint8_t *pim_msg; struct ip *ip_hdr; size_t ip_hlen; /* ip header length in bytes */ int ip_msg_len; @@ -3685,8 +3685,8 @@ DEFUN (test_pim_receive_hello, "Neighbor LAN prune delay T-bit\n" "Neighbor secondary addresses\n") { - char buf[1000]; - char *pim_msg; + uint8_t buf[1000]; + uint8_t *pim_msg; struct ip *ip_hdr; size_t ip_hlen; /* ip header length in bytes */ int ip_msg_len; @@ -3810,9 +3810,9 @@ DEFUN (test_pim_receive_assert, "Assert route metric\n" "Assert RPT bit flag\n") { - char buf[1000]; - char *buf_pastend = buf + sizeof(buf); - char *pim_msg; + uint8_t buf[1000]; + uint8_t *buf_pastend = buf + sizeof(buf); + uint8_t *pim_msg; struct ip *ip_hdr; size_t ip_hlen; /* ip header length in bytes */ int ip_msg_len; @@ -3921,10 +3921,10 @@ static int recv_joinprune(struct vty *vty, const char *argv[], int src_is_join) { - char buf[1000]; - const char *buf_pastend = buf + sizeof(buf); - char *pim_msg; - char *pim_msg_curr; + uint8_t buf[1000]; + const uint8_t *buf_pastend = buf + sizeof(buf); + uint8_t *pim_msg; + uint8_t *pim_msg_curr; int pim_msg_size; struct ip *ip_hdr; size_t ip_hlen; /* ip header length in bytes */ diff --git a/pimd/pim_hello.c b/pimd/pim_hello.c index c942de4d..73c50d4f 100644 --- a/pimd/pim_hello.c +++ b/pimd/pim_hello.c @@ -146,12 +146,12 @@ static void tlv_trace_list(const char *label, const char *tlv_name, int pim_hello_recv(struct interface *ifp, struct in_addr src_addr, - char *tlv_buf, int tlv_buf_size) + uint8_t *tlv_buf, int tlv_buf_size) { struct pim_interface *pim_ifp; struct pim_neighbor *neigh; - char *tlv_curr; - char *tlv_pastend; + uint8_t *tlv_curr; + uint8_t *tlv_pastend; pim_hello_options hello_options = 0; /* bit array recording options found */ uint16_t hello_option_holdtime = 0; uint16_t hello_option_propagation_delay = 0; @@ -422,7 +422,7 @@ int pim_hello_recv(struct interface *ifp, } int pim_hello_build_tlv(const char *ifname, - char *tlv_buf, int tlv_buf_size, + uint8_t *tlv_buf, int tlv_buf_size, uint16_t holdtime, uint32_t dr_priority, uint32_t generation_id, @@ -431,9 +431,9 @@ int pim_hello_build_tlv(const char *ifname, int can_disable_join_suppression, struct list *ifconnected) { - char *curr = tlv_buf; - char *pastend = tlv_buf + tlv_buf_size; - char *tmp; + uint8_t *curr = tlv_buf; + uint8_t *pastend = tlv_buf + tlv_buf_size; + uint8_t *tmp; /* * Append options diff --git a/pimd/pim_hello.h b/pimd/pim_hello.h index 90a11ba6..b5e272d5 100644 --- a/pimd/pim_hello.h +++ b/pimd/pim_hello.h @@ -29,10 +29,10 @@ int pim_hello_recv(struct interface *ifp, struct in_addr src_addr, - char *tlv_buf, int tlv_buf_size); + uint8_t *tlv_buf, int tlv_buf_size); int pim_hello_build_tlv(const char *ifname, - char *tlv_buf, int tlv_buf_size, + uint8_t *tlv_buf, int tlv_buf_size, uint16_t holdtime, uint32_t dr_priority, uint32_t generation_id, diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c index dcb9a9ba..6ad521ca 100644 --- a/pimd/pim_igmp.c +++ b/pimd/pim_igmp.c @@ -930,7 +930,7 @@ static int pim_igmp_read(struct thread *t) struct sockaddr_in to; socklen_t fromlen = sizeof(from); socklen_t tolen = sizeof(to); - char buf[PIM_IGMP_BUFSIZE_READ]; + uint8_t buf[PIM_IGMP_BUFSIZE_READ]; int len; int ifindex = -1; int result = -1; /* defaults to bad */ @@ -995,7 +995,7 @@ static int pim_igmp_read(struct thread *t) } #endif - if (pim_igmp_packet(igmp, buf, len)) { + if (pim_igmp_packet(igmp, (char *)buf, len)) { goto done; } diff --git a/pimd/pim_join.c b/pimd/pim_join.c index aa3aa789..5230bb6c 100644 --- a/pimd/pim_join.c +++ b/pimd/pim_join.c @@ -107,14 +107,14 @@ static void recv_prune(struct interface *ifp, int pim_joinprune_recv(struct interface *ifp, struct pim_neighbor *neigh, struct in_addr src_addr, - char *tlv_buf, int tlv_buf_size) + uint8_t *tlv_buf, int tlv_buf_size) { struct prefix msg_upstream_addr; uint8_t msg_num_groups; uint16_t msg_holdtime; int addr_offset; - char *buf; - char *pastend; + uint8_t *buf; + uint8_t *pastend; int remain; int group; @@ -280,9 +280,9 @@ int pim_joinprune_send(struct interface *ifp, int send_join) { struct pim_interface *pim_ifp; - char pim_msg[1000]; - const char *pastend = pim_msg + sizeof(pim_msg); - char *pim_msg_curr = pim_msg + PIM_MSG_HEADER_LEN; /* room for pim header */ + uint8_t pim_msg[1000]; + const uint8_t *pastend = pim_msg + sizeof(pim_msg); + uint8_t *pim_msg_curr = pim_msg + PIM_MSG_HEADER_LEN; /* room for pim header */ int pim_msg_size; int remain; diff --git a/pimd/pim_join.h b/pimd/pim_join.h index 4d9407be..37ec0f45 100644 --- a/pimd/pim_join.h +++ b/pimd/pim_join.h @@ -32,7 +32,7 @@ int pim_joinprune_recv(struct interface *ifp, struct pim_neighbor *neigh, struct in_addr src_addr, - char *tlv_buf, int tlv_buf_size); + uint8_t *tlv_buf, int tlv_buf_size); int pim_joinprune_send(struct interface *ifp, struct in_addr upstream_addr, diff --git a/pimd/pim_msg.c b/pimd/pim_msg.c index 1105eaca..7eb63e85 100644 --- a/pimd/pim_msg.c +++ b/pimd/pim_msg.c @@ -27,7 +27,7 @@ #include "pim_msg.h" #include "pim_util.h" -void pim_msg_build_header(char *pim_msg, int pim_msg_size, +void pim_msg_build_header(uint8_t *pim_msg, int pim_msg_size, uint8_t pim_msg_type) { uint16_t checksum; @@ -50,9 +50,9 @@ void pim_msg_build_header(char *pim_msg, int pim_msg_size, *(uint16_t *) PIM_MSG_HDR_OFFSET_CHECKSUM(pim_msg) = checksum; } -char *pim_msg_addr_encode_ipv4_ucast(char *buf, - int buf_size, - struct in_addr addr) +uint8_t *pim_msg_addr_encode_ipv4_ucast(uint8_t *buf, + int buf_size, + struct in_addr addr) { const int ENCODED_IPV4_UCAST_SIZE = 6; @@ -67,9 +67,9 @@ char *pim_msg_addr_encode_ipv4_ucast(char *buf, return buf + ENCODED_IPV4_UCAST_SIZE; } -char *pim_msg_addr_encode_ipv4_group(char *buf, - int buf_size, - struct in_addr addr) +uint8_t *pim_msg_addr_encode_ipv4_group(uint8_t *buf, + int buf_size, + struct in_addr addr) { const int ENCODED_IPV4_GROUP_SIZE = 8; @@ -86,9 +86,9 @@ char *pim_msg_addr_encode_ipv4_group(char *buf, return buf + ENCODED_IPV4_GROUP_SIZE; } -char *pim_msg_addr_encode_ipv4_source(char *buf, - int buf_size, - struct in_addr addr) +uint8_t *pim_msg_addr_encode_ipv4_source(uint8_t *buf, + int buf_size, + struct in_addr addr) { const int ENCODED_IPV4_SOURCE_SIZE = 8; diff --git a/pimd/pim_msg.h b/pimd/pim_msg.h index 228d6a85..a884fc84 100644 --- a/pimd/pim_msg.h +++ b/pimd/pim_msg.h @@ -37,16 +37,16 @@ */ #define PIM_MSG_ADDRESS_FAMILY_IPV4 (1) -void pim_msg_build_header(char *pim_msg, int pim_msg_size, +void pim_msg_build_header(uint8_t *pim_msg, int pim_msg_size, uint8_t pim_msg_type); -char *pim_msg_addr_encode_ipv4_ucast(char *buf, - int buf_size, - struct in_addr addr); -char *pim_msg_addr_encode_ipv4_group(char *buf, - int buf_size, - struct in_addr addr); -char *pim_msg_addr_encode_ipv4_source(char *buf, - int buf_size, - struct in_addr addr); +uint8_t *pim_msg_addr_encode_ipv4_ucast(uint8_t *buf, + int buf_size, + struct in_addr addr); +uint8_t *pim_msg_addr_encode_ipv4_group(uint8_t *buf, + int buf_size, + struct in_addr addr); +uint8_t *pim_msg_addr_encode_ipv4_source(uint8_t *buf, + int buf_size, + struct in_addr addr); #endif /* PIM_MSG_H */ diff --git a/pimd/pim_pim.c b/pimd/pim_pim.c index 156081ca..8dd71d66 100644 --- a/pimd/pim_pim.c +++ b/pimd/pim_pim.c @@ -111,13 +111,13 @@ void pim_sock_delete(struct interface *ifp, const char *delete_message) sock_close(ifp); } -int pim_pim_packet(struct interface *ifp, char *buf, size_t len) +int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len) { struct ip *ip_hdr; size_t ip_hlen; /* ip header length in bytes */ char src_str[100]; char dst_str[100]; - char *pim_msg; + uint8_t *pim_msg; int pim_msg_len; uint8_t pim_version; uint8_t pim_type; @@ -253,7 +253,7 @@ static int pim_sock_read(struct thread *t) struct sockaddr_in to; socklen_t fromlen = sizeof(from); socklen_t tolen = sizeof(to); - char buf[PIM_PIM_BUFSIZE_READ]; + uint8_t buf[PIM_PIM_BUFSIZE_READ]; int len; int ifindex = -1; int result = -1; /* defaults to bad */ @@ -437,7 +437,7 @@ void pim_sock_reset(struct interface *ifp) int pim_msg_send(int fd, struct in_addr dst, - char *pim_msg, + uint8_t *pim_msg, int pim_msg_size, const char *ifname) { @@ -494,7 +494,7 @@ int pim_msg_send(int fd, static int hello_send(struct interface *ifp, uint16_t holdtime) { - char pim_msg[PIM_PIM_BUFSIZE_WRITE]; + uint8_t pim_msg[PIM_PIM_BUFSIZE_WRITE]; struct pim_interface *pim_ifp; int pim_tlv_size; int pim_msg_size; diff --git a/pimd/pim_pim.h b/pimd/pim_pim.h index a2c8fa58..6be1a3e6 100644 --- a/pimd/pim_pim.h +++ b/pimd/pim_pim.h @@ -60,11 +60,11 @@ void pim_sock_delete(struct interface *ifp, const char *delete_message); void pim_hello_restart_now(struct interface *ifp); void pim_hello_restart_triggered(struct interface *ifp); -int pim_pim_packet(struct interface *ifp, char *buf, size_t len); +int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len); int pim_msg_send(int fd, struct in_addr dst, - char *pim_msg, + uint8_t *pim_msg, int pim_msg_size, const char *ifname); diff --git a/pimd/pim_sock.c b/pimd/pim_sock.c index b80c0a22..4816c567 100644 --- a/pimd/pim_sock.c +++ b/pimd/pim_sock.c @@ -249,7 +249,7 @@ int pim_socket_join_source(int fd, int ifindex, return 0; } -int pim_socket_recvfromto(int fd, char *buf, size_t len, +int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len, struct sockaddr_in *from, socklen_t *fromlen, struct sockaddr_in *to, socklen_t *tolen, int *ifindex) diff --git a/pimd/pim_sock.h b/pimd/pim_sock.h index 29b5aeed..cfe39ad1 100644 --- a/pimd/pim_sock.h +++ b/pimd/pim_sock.h @@ -45,7 +45,7 @@ int pim_socket_join_source(int fd, int ifindex, struct in_addr group_addr, struct in_addr source_addr, const char *ifname); -int pim_socket_recvfromto(int fd, char *buf, size_t len, +int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len, struct sockaddr_in *from, socklen_t *fromlen, struct sockaddr_in *to, socklen_t *tolen, int *ifindex); diff --git a/pimd/pim_ssmpingd.c b/pimd/pim_ssmpingd.c index 652999d4..fd1eac06 100644 --- a/pimd/pim_ssmpingd.c +++ b/pimd/pim_ssmpingd.c @@ -215,7 +215,7 @@ static void ssmpingd_delete(struct ssmpingd_sock *ss) } static void ssmpingd_sendto(struct ssmpingd_sock *ss, - const char *buf, + const uint8_t *buf, int len, struct sockaddr_in to) { @@ -250,7 +250,7 @@ static int ssmpingd_read_msg(struct ssmpingd_sock *ss) socklen_t fromlen = sizeof(from); socklen_t tolen = sizeof(to); int ifindex = -1; - char buf[1000]; + uint8_t buf[1000]; int len; ++ss->requests; diff --git a/pimd/pim_tlv.c b/pimd/pim_tlv.c index cb6b6540..4ce9a3d2 100644 --- a/pimd/pim_tlv.c +++ b/pimd/pim_tlv.c @@ -31,10 +31,10 @@ #include "pim_str.h" #include "pim_msg.h" -char *pim_tlv_append_uint16(uint8_t *buf, - const uint8_t *buf_pastend, - uint16_t option_type, - uint16_t option_value) +uint8_t *pim_tlv_append_uint16(uint8_t *buf, + const uint8_t *buf_pastend, + uint16_t option_type, + uint16_t option_value) { uint16_t option_len = 2; @@ -55,11 +55,11 @@ char *pim_tlv_append_uint16(uint8_t *buf, return buf; } -char *pim_tlv_append_2uint16(uint8_t *buf, - const uint8_t *buf_pastend, - uint16_t option_type, - uint16_t option_value1, - uint16_t option_value2) +uint8_t *pim_tlv_append_2uint16(uint8_t *buf, + const uint8_t *buf_pastend, + uint16_t option_type, + uint16_t option_value1, + uint16_t option_value2) { uint16_t option_len = 4; @@ -82,10 +82,10 @@ char *pim_tlv_append_2uint16(uint8_t *buf, return buf; } -char *pim_tlv_append_uint32(uint8_t *buf, - const uint8_t *buf_pastend, - uint16_t option_type, - uint32_t option_value) +uint8_t *pim_tlv_append_uint32(uint8_t *buf, + const uint8_t *buf_pastend, + uint16_t option_type, + uint32_t option_value) { uint16_t option_len = 4; @@ -108,9 +108,9 @@ char *pim_tlv_append_uint32(uint8_t *buf, #define ucast_ipv4_encoding_len (2 + sizeof(struct in_addr)) -char *pim_tlv_append_addrlist_ucast(uint8_t *buf, - const uint8_t *buf_pastend, - struct list *ifconnected) +uint8_t *pim_tlv_append_addrlist_ucast(uint8_t *buf, + const uint8_t *buf_pastend, + struct list *ifconnected) { struct listnode *node; uint16_t option_len = 0; @@ -243,7 +243,7 @@ int pim_tlv_parse_holdtime(const char *ifname, struct in_addr src_addr, pim_hello_options *hello_options, uint16_t *hello_option_holdtime, uint16_t option_len, - const char *tlv_curr) + const uint8_t *tlv_curr) { const char *label = "holdtime"; @@ -271,7 +271,7 @@ int pim_tlv_parse_lan_prune_delay(const char *ifname, struct in_addr src_addr, uint16_t *hello_option_propagation_delay, uint16_t *hello_option_override_interval, uint16_t option_len, - const char *tlv_curr) + const uint8_t *tlv_curr) { if (check_tlv_length(__PRETTY_FUNCTION__, "lan_prune_delay", ifname, src_addr, @@ -305,7 +305,7 @@ int pim_tlv_parse_dr_priority(const char *ifname, struct in_addr src_addr, pim_hello_options *hello_options, uint32_t *hello_option_dr_priority, uint16_t option_len, - const char *tlv_curr) + const uint8_t *tlv_curr) { const char *label = "dr_priority"; @@ -332,7 +332,7 @@ int pim_tlv_parse_generation_id(const char *ifname, struct in_addr src_addr, pim_hello_options *hello_options, uint32_t *hello_option_generation_id, uint16_t option_len, - const char *tlv_curr) + const uint8_t *tlv_curr) { const char *label = "generation_id"; @@ -357,12 +357,12 @@ int pim_tlv_parse_generation_id(const char *ifname, struct in_addr src_addr, int pim_parse_addr_ucast(const char *ifname, struct in_addr src_addr, struct prefix *p, - const char *buf, + const uint8_t *buf, int buf_size) { const int ucast_encoding_min_len = 3; /* 1 family + 1 type + 1 addr */ - const char *addr; - const char *pastend; + const uint8_t *addr; + const uint8_t *pastend; int family; int type; @@ -379,10 +379,8 @@ int pim_parse_addr_ucast(const char *ifname, struct in_addr src_addr, addr = buf; pastend = buf + buf_size; - family = *(const uint8_t *) addr; - ++addr; - type = *(const uint8_t *) addr; - ++addr; + family = *addr++; + type = *addr++; switch (family) { case PIM_MSG_ADDRESS_FAMILY_IPV4: @@ -426,12 +424,12 @@ int pim_parse_addr_ucast(const char *ifname, struct in_addr src_addr, int pim_parse_addr_group(const char *ifname, struct in_addr src_addr, struct prefix *p, - const char *buf, + const uint8_t *buf, int buf_size) { const int grp_encoding_min_len = 4; /* 1 family + 1 type + 1 reserved + 1 addr */ - const char *addr; - const char *pastend; + const uint8_t *addr; + const uint8_t *pastend; int family; int type; int mask_len; @@ -449,13 +447,11 @@ int pim_parse_addr_group(const char *ifname, struct in_addr src_addr, addr = buf; pastend = buf + buf_size; - family = *(const uint8_t *) addr; - ++addr; - type = *(const uint8_t *) addr; + family = *addr++; + type = *addr++; ++addr; ++addr; /* skip b_reserved_z fields */ - mask_len = *(const uint8_t *) addr; - ++addr; + mask_len = *addr++; switch (family) { case PIM_MSG_ADDRESS_FAMILY_IPV4: @@ -503,12 +499,12 @@ int pim_parse_addr_source(const char *ifname, struct in_addr src_addr, struct prefix *p, uint8_t *flags, - const char *buf, + const uint8_t *buf, int buf_size) { const int src_encoding_min_len = 4; /* 1 family + 1 type + 1 reserved + 1 addr */ - const char *addr; - const char *pastend; + const uint8_t *addr; + const uint8_t *pastend; int family; int type; int mask_len; @@ -526,14 +522,10 @@ int pim_parse_addr_source(const char *ifname, addr = buf; pastend = buf + buf_size; - family = *(const uint8_t *) addr; - ++addr; - type = *(const uint8_t *) addr; - ++addr; - *flags = *(const uint8_t *) addr; - ++addr; - mask_len = *(const uint8_t *) addr; - ++addr; + family = *addr++; + type = *addr++; + *flags = *addr++; + mask_len = *addr++; switch (family) { case PIM_MSG_ADDRESS_FAMILY_IPV4: @@ -607,10 +599,10 @@ int pim_tlv_parse_addr_list(const char *ifname, struct in_addr src_addr, pim_hello_options *hello_options, struct list **hello_option_addr_list, uint16_t option_len, - const char *tlv_curr) + const uint8_t *tlv_curr) { - const char *addr; - const char *pastend; + const uint8_t *addr; + const uint8_t *pastend; zassert(hello_option_addr_list); diff --git a/pimd/pim_tlv.h b/pimd/pim_tlv.h index 5ec3dc43..b802cf89 100644 --- a/pimd/pim_tlv.h +++ b/pimd/pim_tlv.h @@ -71,63 +71,63 @@ typedef uint32_t pim_hello_options; #define PIM_TLV_MIN_SIZE (PIM_TLV_TYPE_SIZE + PIM_TLV_LENGTH_SIZE) #define PIM_TLV_OPTION_SIZE(option_len) (PIM_TLV_MIN_SIZE + (option_len)) -char *pim_tlv_append_uint16(uint8_t *buf, - const uint8_t *buf_pastend, - uint16_t option_type, - uint16_t option_value); -char *pim_tlv_append_2uint16(uint8_t *buf, - const uint8_t *buf_pastend, - uint16_t option_type, - uint16_t option_value1, - uint16_t option_value2); -char *pim_tlv_append_uint32(uint8_t *buf, - const uint8_t *buf_pastend, - uint16_t option_type, - uint32_t option_value); -char *pim_tlv_append_addrlist_ucast(uint8_t *buf, - const uint8_t *buf_pastend, - struct list *ifconnected); +uint8_t *pim_tlv_append_uint16(uint8_t *buf, + const uint8_t *buf_pastend, + uint16_t option_type, + uint16_t option_value); +uint8_t *pim_tlv_append_2uint16(uint8_t *buf, + const uint8_t *buf_pastend, + uint16_t option_type, + uint16_t option_value1, + uint16_t option_value2); +uint8_t *pim_tlv_append_uint32(uint8_t *buf, + const uint8_t *buf_pastend, + uint16_t option_type, + uint32_t option_value); +uint8_t *pim_tlv_append_addrlist_ucast(uint8_t *buf, + const uint8_t *buf_pastend, + struct list *ifconnected); int pim_tlv_parse_holdtime(const char *ifname, struct in_addr src_addr, pim_hello_options *hello_options, uint16_t *hello_option_holdtime, uint16_t option_len, - const char *tlv_curr); + const uint8_t *tlv_curr); int pim_tlv_parse_lan_prune_delay(const char *ifname, struct in_addr src_addr, pim_hello_options *hello_options, uint16_t *hello_option_propagation_delay, uint16_t *hello_option_override_interval, uint16_t option_len, - const char *tlv_curr); + const uint8_t *tlv_curr); int pim_tlv_parse_dr_priority(const char *ifname, struct in_addr src_addr, pim_hello_options *hello_options, uint32_t *hello_option_dr_priority, uint16_t option_len, - const char *tlv_curr); + const uint8_t *tlv_curr); int pim_tlv_parse_generation_id(const char *ifname, struct in_addr src_addr, pim_hello_options *hello_options, uint32_t *hello_option_generation_id, uint16_t option_len, - const char *tlv_curr); + const uint8_t *tlv_curr); int pim_tlv_parse_addr_list(const char *ifname, struct in_addr src_addr, pim_hello_options *hello_options, struct list **hello_option_addr_list, uint16_t option_len, - const char *tlv_curr); + const uint8_t *tlv_curr); int pim_parse_addr_ucast(const char *ifname, struct in_addr src_addr, struct prefix *p, - const char *buf, + const uint8_t *buf, int buf_size); int pim_parse_addr_group(const char *ifname, struct in_addr src_addr, struct prefix *p, - const char *buf, + const uint8_t *buf, int buf_size); int pim_parse_addr_source(const char *ifname, struct in_addr src_addr, struct prefix *p, uint8_t *flags, - const char *buf, + const uint8_t *buf, int buf_size); #endif /* PIM_TLV_H */ From 105ad8615da1bcb417e1757ef4787cd2cdbb3cbc Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Thu, 16 Feb 2012 04:50:35 +0000 Subject: [PATCH 358/482] pimd: fix wtf code * pim_hello.c, * pim_neighbor.c: print pointers as %p * pim_time.c: comment out unused function * pim_zebra.c: wtf --- pimd/pim_hello.c | 4 ++-- pimd/pim_neighbor.c | 4 ++-- pimd/pim_time.c | 2 ++ pimd/pim_zebra.c | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/pimd/pim_hello.c b/pimd/pim_hello.c index 73c50d4f..94e7c945 100644 --- a/pimd/pim_hello.c +++ b/pimd/pim_hello.c @@ -124,12 +124,12 @@ static void tlv_trace_list(const char *label, const char *tlv_name, if (isset) { char src_str[100]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); - zlog_debug("%s: PIM hello option from %s on interface %s: %s size=%d list=%x", + zlog_debug("%s: PIM hello option from %s on interface %s: %s size=%d list=%p", label, src_str, ifname, tlv_name, addr_list ? ((int) listcount(addr_list)) : -1, - (unsigned) addr_list); + (void *) addr_list); } } diff --git a/pimd/pim_neighbor.c b/pimd/pim_neighbor.c index 0df74221..51ce8c9a 100644 --- a/pimd/pim_neighbor.c +++ b/pimd/pim_neighbor.c @@ -689,8 +689,8 @@ void pim_neighbor_update(struct pim_neighbor *neigh, if (neigh->prefix_list == addr_list) { if (addr_list) { - zlog_err("%s: internal error: trying to replace same prefix list=%u", - __PRETTY_FUNCTION__, (unsigned) addr_list); + zlog_err("%s: internal error: trying to replace same prefix list=%p", + __PRETTY_FUNCTION__, (void *) addr_list); } } else { diff --git a/pimd/pim_time.c b/pimd/pim_time.c index 7d4581d9..b884efc7 100644 --- a/pimd/pim_time.c +++ b/pimd/pim_time.c @@ -30,6 +30,7 @@ #include "pim_time.h" +#if 0 static int pim_gettime(int clk_id, struct timeval *tv) { struct timespec ts; @@ -58,6 +59,7 @@ static int pim_gettime(int clk_id, struct timeval *tv) return result; } +#endif static int gettime_monotonic(struct timeval *tv) { diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index e18b7ac9..74a60a5f 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -548,7 +548,7 @@ static int redist_read_ipv4_route(int command, struct zclient *zclient, } api.distance = CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ? - api.distance = stream_getc(s) : + stream_getc(s) : 0; api.metric = CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? From 3de4ae90586e4a99ab706c5d3726ef1d5aef95c0 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Thu, 13 Feb 2014 14:28:26 -0200 Subject: [PATCH 359/482] Define pim_gettime() when PIM_GETTIME_USE_GETTIMEOFDAY is not available. --- pimd/pim_time.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pimd/pim_time.c b/pimd/pim_time.c index b884efc7..fce30c08 100644 --- a/pimd/pim_time.c +++ b/pimd/pim_time.c @@ -30,7 +30,7 @@ #include "pim_time.h" -#if 0 +#ifndef PIM_GETTIME_USE_GETTIMEOFDAY static int pim_gettime(int clk_id, struct timeval *tv) { struct timespec ts; From a057a066aa258a0f9cf3ae4946ec66adcd2f28f7 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 17 Jan 2012 23:52:58 +0000 Subject: [PATCH 360/482] pimd: extend .gitignore --- pimd/.gitignore | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/pimd/.gitignore b/pimd/.gitignore index 7046d69c..51a2ac87 100644 --- a/pimd/.gitignore +++ b/pimd/.gitignore @@ -1,7 +1,16 @@ -.libs -*.a -*.o +Makefile +Makefile.in +libpim.a pimd test_igmpv3_join - - +tags +TAGS +.deps +*.o +*.lo +*.la +*.libs +.arch-inventory +.arch-ids +*~ +*.loT From c77f01b9c9056bf39a62911218b308cdb11c4718 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Thu, 13 Feb 2014 14:54:43 -0200 Subject: [PATCH 361/482] PIM_ZCLIENT_DEBUG enables zclient_socket() / zclient_socket_un() debug --- pimd/pim_zlookup.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pimd/pim_zlookup.c b/pimd/pim_zlookup.c index 98548e79..112ff261 100644 --- a/pimd/pim_zlookup.c +++ b/pimd/pim_zlookup.c @@ -51,6 +51,8 @@ static int zclient_lookup_connect(struct thread *t) return 0; } +#ifdef PIM_ZCLIENT_DEBUG + #ifdef HAVE_TCP_ZEBRA zlog_debug("%s: FIXME blocking connect: zclient_socket()", __PRETTY_FUNCTION__); @@ -77,6 +79,17 @@ static int zclient_lookup_connect(struct thread *t) } #endif /* HAVE_TCP_ZEBRA */ +#else + + zlog_debug("%s: FIXME blocking connect: zclient_socket_connect()", + __PRETTY_FUNCTION__); + if (zclient_socket_connect(zlookup) < 0) { + zlog_warn("%s: failure connecting zclient socket", + __PRETTY_FUNCTION__); + } + +#endif /* PIM_ZCLIENT_DEBUG */ + zassert(!zlookup->t_connect); if (zlookup->sock < 0) { /* Since last connect failed, retry within 10 secs */ From 3defeb3465a1f6e7a50d0f2b3d292686bb1449b1 Mon Sep 17 00:00:00 2001 From: Klemen Sladic Date: Fri, 7 Feb 2014 16:23:44 +1300 Subject: [PATCH 362/482] fix address assigment --- pimd/pim_cmd.c | 2 +- pimd/pim_igmpv3.c | 3 ++- pimd/pim_msg.c | 6 +++--- pimd/pim_tlv.c | 9 +++++---- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index f046801d..d2f9eb89 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -3515,7 +3515,7 @@ DEFUN (test_igmp_receive_report, *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = 0; /* for computing checksum */ *(uint16_t *) (igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET) = htons(1); /* one group record */ *(uint8_t *) (group_record + IGMP_V3_GROUP_RECORD_TYPE_OFFSET) = record_type; - *(struct in_addr *)(group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET) = grp_addr; + memcpy(group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET, &grp_addr, sizeof(struct in_addr)); /* Scan LINE sources */ sources = (struct in_addr *) (group_record + IGMP_V3_GROUP_RECORD_SOURCE_OFFSET); diff --git a/pimd/pim_igmpv3.c b/pimd/pim_igmpv3.c index f8926524..4be52d60 100644 --- a/pimd/pim_igmpv3.c +++ b/pimd/pim_igmpv3.c @@ -1649,7 +1649,8 @@ void pim_igmp_send_membership_query(struct igmp_group *group, query_buf[0] = PIM_IGMP_MEMBERSHIP_QUERY; query_buf[1] = max_resp_code; *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = 0; /* for computing checksum */ - *(struct in_addr *)(query_buf + 4) = group_addr; + memcpy(query_buf+4, &group_addr, sizeof(struct in_addr)); + query_buf[8] = (s_flag << 3) | querier_robustness_variable; query_buf[9] = qqic; *(uint16_t *)(query_buf + IGMP_V3_NUMSOURCES_OFFSET) = htons(num_sources); diff --git a/pimd/pim_msg.c b/pimd/pim_msg.c index 7eb63e85..79ae33ac 100644 --- a/pimd/pim_msg.c +++ b/pimd/pim_msg.c @@ -62,7 +62,7 @@ uint8_t *pim_msg_addr_encode_ipv4_ucast(uint8_t *buf, buf[0] = PIM_MSG_ADDRESS_FAMILY_IPV4; /* addr family */ buf[1] = '\0'; /* native encoding */ - *(struct in_addr *)(buf + 2) = addr; + memcpy(buf+2, &addr, sizeof(struct in_addr)); return buf + ENCODED_IPV4_UCAST_SIZE; } @@ -81,7 +81,7 @@ uint8_t *pim_msg_addr_encode_ipv4_group(uint8_t *buf, buf[1] = '\0'; /* native encoding */ buf[2] = '\0'; /* reserved */ buf[3] = 32; /* mask len */ - *(struct in_addr *)(buf + 4) = addr; + memcpy(buf+4, &addr, sizeof(struct in_addr)); return buf + ENCODED_IPV4_GROUP_SIZE; } @@ -100,7 +100,7 @@ uint8_t *pim_msg_addr_encode_ipv4_source(uint8_t *buf, buf[1] = '\0'; /* native encoding */ buf[2] = 4; /* reserved = 0 | S bit = 1 | W bit = 0 | R bit = 0 */ buf[3] = 32; /* mask len */ - *(struct in_addr *)(buf + 4) = addr; + memcpy(buf+4, &addr, sizeof(struct in_addr)); return buf + ENCODED_IPV4_SOURCE_SIZE; } diff --git a/pimd/pim_tlv.c b/pimd/pim_tlv.c index 4ce9a3d2..962c2b87 100644 --- a/pimd/pim_tlv.c +++ b/pimd/pim_tlv.c @@ -148,7 +148,7 @@ uint8_t *pim_tlv_append_addrlist_ucast(uint8_t *buf, ++curr; *(uint8_t *) curr = 0; /* ucast IPv4 native encoding type (RFC 4601: 4.9.1) */ ++curr; - *(struct in_addr *) curr = p->u.prefix4; + memcpy(curr, &p->u.prefix4, sizeof(struct in_addr)); curr += sizeof(struct in_addr); option_len += ucast_ipv4_encoding_len; @@ -404,7 +404,8 @@ int pim_parse_addr_ucast(const char *ifname, struct in_addr src_addr, } p->family = AF_INET; /* notice: AF_INET != PIM_MSG_ADDRESS_FAMILY_IPV4 */ - p->u.prefix4 = *(const struct in_addr *) addr; + memcpy(&p->u.prefix4, addr, sizeof(struct in_addr)); + addr += sizeof(struct in_addr); break; @@ -475,7 +476,7 @@ int pim_parse_addr_group(const char *ifname, struct in_addr src_addr, } p->family = AF_INET; /* notice: AF_INET != PIM_MSG_ADDRESS_FAMILY_IPV4 */ - p->u.prefix4 = *(const struct in_addr *) addr; + memcpy(&p->u.prefix4, addr, sizeof(struct in_addr)); p->prefixlen = mask_len; addr += sizeof(struct in_addr); @@ -549,7 +550,7 @@ int pim_parse_addr_source(const char *ifname, } p->family = AF_INET; /* notice: AF_INET != PIM_MSG_ADDRESS_FAMILY_IPV4 */ - p->u.prefix4 = *(const struct in_addr *) addr; + memcpy(&p->u.prefix4, addr, sizeof(struct in_addr)); p->prefixlen = mask_len; /* From d4595869045498d830be34403217822f77446ae0 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Thu, 13 Feb 2014 19:50:30 -0200 Subject: [PATCH 363/482] Parsing fixes. --- pimd/pim_igmp.c | 20 ++++++++++++++------ pimd/pim_join.c | 15 +++++++++++++++ pimd/pim_tlv.c | 12 +++++++----- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c index 6ad521ca..03cf3cd1 100644 --- a/pimd/pim_igmp.c +++ b/pimd/pim_igmp.c @@ -288,7 +288,8 @@ static int recv_igmp_query(struct igmp_sock *igmp, int query_version, uint16_t checksum; int i; - group_addr = *(struct in_addr *)(igmp_msg + 4); + //group_addr = *(struct in_addr *)(igmp_msg + 4); + memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr)); ifp = igmp->interface; pim_ifp = ifp->info; @@ -421,8 +422,12 @@ static int recv_igmp_query(struct igmp_sock *igmp, int query_version, /* Scan sources in query and lower their timers to LMQT */ struct in_addr *sources = (struct in_addr *)(igmp_msg + IGMP_V3_SOURCES_OFFSET); for (i = 0; i < recv_num_sources; ++i) { - struct in_addr src_addr = sources[i]; - struct igmp_source *src = igmp_find_source_by_addr(group, src_addr); + //struct in_addr src_addr = sources[i]; + //struct igmp_source *src = igmp_find_source_by_addr(group, src_addr); + struct in_addr src_addr; + struct igmp_source *src; + memcpy(&src_addr, sources + i, sizeof(struct in_addr)); + src = igmp_find_source_by_addr(group, src_addr); if (src) { igmp_source_timer_lower_to_lmqt(src); } @@ -506,7 +511,8 @@ static int igmp_v3_report(struct igmp_sock *igmp, rec_auxdatalen = group_record[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET]; rec_num_sources = ntohs(* (uint16_t *) (group_record + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET)); - rec_group = *(struct in_addr *)(group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET); + //rec_group = *(struct in_addr *)(group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET); + memcpy(&rec_group, group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET, sizeof(struct in_addr)); if (PIM_DEBUG_IGMP_PACKETS) { zlog_debug("Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%s", @@ -599,7 +605,8 @@ static int igmp_v2_report(struct igmp_sock *igmp, __FILE__, __PRETTY_FUNCTION__); } - group_addr = *(struct in_addr *)(igmp_msg + 4); + //group_addr = *(struct in_addr *)(igmp_msg + 4); + memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr)); /* non-existant group is created as INCLUDE {empty} */ group = igmp_add_group_by_addr(igmp, group_addr, ifp->name); @@ -655,7 +662,8 @@ static int igmp_v1_report(struct igmp_sock *igmp, __FILE__, __PRETTY_FUNCTION__); } - group_addr = *(struct in_addr *)(igmp_msg + 4); + //group_addr = *(struct in_addr *)(igmp_msg + 4); + memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr)); /* non-existant group is created as INCLUDE {empty} */ group = igmp_add_group_by_addr(igmp, group_addr, ifp->name); diff --git a/pimd/pim_join.c b/pimd/pim_join.c index 5230bb6c..9d8e0012 100644 --- a/pimd/pim_join.c +++ b/pimd/pim_join.c @@ -129,6 +129,11 @@ int pim_joinprune_recv(struct interface *ifp, addr_offset = pim_parse_addr_ucast(ifp->name, src_addr, &msg_upstream_addr, buf, pastend - buf); +#if 0 + zlog_warn("%s: pim_parse_addr_ucast addr_offset=%d", + __PRETTY_FUNCTION__, + addr_offset); +#endif if (addr_offset < 1) { char src_str[100]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); @@ -194,6 +199,11 @@ int pim_joinprune_recv(struct interface *ifp, addr_offset = pim_parse_addr_group(ifp->name, src_addr, &msg_group_addr, buf, pastend - buf); +#if 0 + zlog_warn("%s: pim_parse_addr_group addr_offset=%d", + __PRETTY_FUNCTION__, + addr_offset); +#endif if (addr_offset < 1) { return -5; } @@ -236,6 +246,11 @@ int pim_joinprune_recv(struct interface *ifp, &msg_source_addr, &msg_source_flags, buf, pastend - buf); +#if 0 + zlog_warn("%s: pim_parse_addr_source addr_offset=%d", + __PRETTY_FUNCTION__, + addr_offset); +#endif if (addr_offset < 1) { return -7; } diff --git a/pimd/pim_tlv.c b/pimd/pim_tlv.c index 962c2b87..95ee5ab0 100644 --- a/pimd/pim_tlv.c +++ b/pimd/pim_tlv.c @@ -450,7 +450,7 @@ int pim_parse_addr_group(const char *ifname, struct in_addr src_addr, family = *addr++; type = *addr++; - ++addr; + //++addr; ++addr; /* skip b_reserved_z fields */ mask_len = *addr++; @@ -533,9 +533,10 @@ int pim_parse_addr_source(const char *ifname, if (type) { char src_str[100]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); - zlog_warn("%s: unknown source address encoding type=%d from %s on %s", + zlog_warn("%s: unknown source address encoding type=%d from %s on %s: %02x%02x%02x%02x%02x%02x%02x%02x", __PRETTY_FUNCTION__, - type, src_str, ifname); + type, src_str, ifname, + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); return -2; } @@ -578,9 +579,10 @@ int pim_parse_addr_source(const char *ifname, { char src_str[100]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); - zlog_warn("%s: unknown source address encoding family=%d from %s on %s", + zlog_warn("%s: unknown source address encoding family=%d from %s on %s: %02x%02x%02x%02x%02x%02x%02x%02x", __PRETTY_FUNCTION__, - family, src_str, ifname); + family, src_str, ifname, + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); return -5; } } From 777fe1f2b63c76d0df5c136dbd335bd2718785fb Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Fri, 14 Feb 2014 14:16:07 -0200 Subject: [PATCH 364/482] Run DR election when hello packet is received. --- pimd/pim_cmd.c | 5 +++-- pimd/pim_iface.c | 2 +- pimd/pim_iface.h | 1 + pimd/pim_neighbor.c | 9 +++++---- pimd/pim_pim.c | 18 +++++++++++++----- 5 files changed, 23 insertions(+), 12 deletions(-) diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index d2f9eb89..c89f2b5a 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -563,7 +563,7 @@ static void pim_show_dr(struct vty *vty) "NonPri: Number of neighbors missing DR Priority hello option%s%s", VTY_NEWLINE, VTY_NEWLINE); - vty_out(vty, "Interface Address DR Uptime Elections NonPri%s", VTY_NEWLINE); + vty_out(vty, "Interface Address DR Uptime Elections Changes NonPri%s", VTY_NEWLINE); for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) { struct pim_interface *pim_ifp; @@ -587,12 +587,13 @@ static void pim_show_dr(struct vty *vty) pim_inet4_dump("", pim_ifp->pim_dr_addr, dr_str, sizeof(dr_str)); - vty_out(vty, "%-9s %-15s %-15s %8s %9d %6d%s", + vty_out(vty, "%-9s %-15s %-15s %8s %9d %7d %6d%s", ifp->name, inet_ntoa(ifaddr), dr_str, dr_uptime, pim_ifp->pim_dr_election_count, + pim_ifp->pim_dr_election_changes, pim_ifp->pim_dr_num_nondrpri_neighbors, VTY_NEWLINE); } diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index f0c32661..72626eca 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -226,7 +226,7 @@ static void pim_addr_change(struct interface *ifp) pim_ifp = ifp->info; zassert(pim_ifp); - pim_if_dr_election(ifp); /* Done TODO T30 */ + pim_if_dr_election(ifp); /* router's own DR Priority (addr) changes -- Done TODO T30 */ pim_if_update_join_desired(pim_ifp); /* depends on DR */ pim_if_update_could_assert(ifp); /* depends on DR */ pim_if_update_my_assert_metric(ifp); /* depends on could_assert */ diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h index 0a702c27..6a2f7c95 100644 --- a/pimd/pim_iface.h +++ b/pimd/pim_iface.h @@ -87,6 +87,7 @@ struct pim_interface { /* DR Election */ int64_t pim_dr_election_last; /* timestamp */ int pim_dr_election_count; + int pim_dr_election_changes; struct in_addr pim_dr_addr; uint32_t pim_dr_priority; /* config */ int pim_dr_num_nondrpri_neighbors; /* neighbors without dr_pri */ diff --git a/pimd/pim_neighbor.c b/pimd/pim_neighbor.c index 51ce8c9a..7d7b43a6 100644 --- a/pimd/pim_neighbor.c +++ b/pimd/pim_neighbor.c @@ -132,7 +132,8 @@ void pim_if_dr_election(struct interface *ifp) zlog_info("%s: DR was %s now is %s on interface %s", __PRETTY_FUNCTION__, dr_old_str, dr_new_str, ifp->name); - + + ++pim_ifp->pim_dr_election_changes; pim_if_update_join_desired(pim_ifp); pim_if_update_could_assert(ifp); pim_if_update_assert_tracking_desired(ifp); @@ -191,7 +192,7 @@ static void update_dr_priority(struct pim_neighbor *neigh, PIM Hello message is received, when a neighbor times out, or when a router's own DR Priority changes. */ - pim_if_dr_election(neigh->interface); + pim_if_dr_election(neigh->interface); // router's own DR Priority changes } } @@ -226,7 +227,7 @@ static int on_neighbor_timer(struct thread *t) PIM Hello message is received, when a neighbor times out, or when a router's own DR Priority changes. */ - pim_if_dr_election(ifp); + pim_if_dr_election(ifp); // neighbor times out return 0; } @@ -347,7 +348,7 @@ static struct pim_neighbor *pim_neighbor_new(struct interface *ifp, PIM Hello message is received, when a neighbor times out, or when a router's own DR Priority changes. */ - pim_if_dr_election(neigh->interface); + pim_if_dr_election(neigh->interface); // new neighbor -- should not trigger dr election... /* RFC 4601: 4.3.1. Sending Hello Messages diff --git a/pimd/pim_pim.c b/pimd/pim_pim.c index 8dd71d66..4fda26ec 100644 --- a/pimd/pim_pim.c +++ b/pimd/pim_pim.c @@ -208,10 +208,14 @@ int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len) } if (pim_type == PIM_MSG_TYPE_HELLO) { - return pim_hello_recv(ifp, - ip_hdr->ip_src, - pim_msg + PIM_MSG_HEADER_LEN, - pim_msg_len - PIM_MSG_HEADER_LEN); + int result = pim_hello_recv(ifp, + ip_hdr->ip_src, + pim_msg + PIM_MSG_HEADER_LEN, + pim_msg_len - PIM_MSG_HEADER_LEN); + if (!result) { + pim_if_dr_election(ifp); /* PIM Hello message is received */ + } + return result; } neigh = pim_neighbor_find(ifp, ip_hdr->ip_src); @@ -324,7 +328,10 @@ static int pim_sock_read(struct thread *t) } #endif - if (pim_pim_packet(ifp, buf, len)) { + int fail = pim_pim_packet(ifp, buf, len); + if (fail) { + zlog_warn("%s: pim_pim_packet() return=%d", + __PRETTY_FUNCTION__, fail); goto done; } @@ -429,6 +436,7 @@ void pim_sock_reset(struct interface *ifp) /* DR Election */ pim_ifp->pim_dr_election_last = 0; /* timestamp */ pim_ifp->pim_dr_election_count = 0; + pim_ifp->pim_dr_election_changes = 0; pim_ifp->pim_dr_num_nondrpri_neighbors = 0; /* neighbors without dr_pri */ pim_ifp->pim_dr_addr = pim_ifp->primary_address; From f24200d77a98c8091d23638096faf366e7c6c953 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Fri, 14 Feb 2014 16:40:34 -0200 Subject: [PATCH 365/482] C18 MFC never recovers from removal of static route to source --- pimd/CAVEATS | 24 +++++++++ pimd/COMMANDS | 2 + pimd/pim_cmd.c | 128 ++++++++++++++++++++++++++++++++-------------- pimd/pim_mroute.c | 6 +++ pimd/pim_zebra.c | 7 ++- pimd/pim_zebra.h | 2 + pimd/pimd.c | 6 +++ pimd/pimd.h | 6 +++ 8 files changed, 141 insertions(+), 40 deletions(-) diff --git a/pimd/CAVEATS b/pimd/CAVEATS index c436d552..05a439b8 100644 --- a/pimd/CAVEATS +++ b/pimd/CAVEATS @@ -134,4 +134,28 @@ C16 AMT Draft (mboned-auto-multicast) is not supported. C17 SNMP / RFC 5060 (PIM MIB) is not supported. +C18 MFC never recovers from removal of static route to source + + # route add -host 1.2.3.4 gw 192.168.56.10 + Before removal: + quagga-pimd-router# sh ip mroute + Source Group Proto Input iVifI Output oVifI TTL Uptime + 1.2.3.4 232.1.2.3 I eth1 3 eth0 2 1 00:00:36 + + # route del -host 1.2.3.4 gw 192.168.56.10 + After removal: sh ip mroute --> empty output + + # route add -host 1.2.3.4 gw 192.168.56.10 + After the route is restored: sh ip mroute --> never recovers (empty output) + + At this point, "no ip pim ssm" on the upstream interface (eth0) crashes pimd: + + 2014/02/14 16:30:14 PIM: ifmembership_set: (S,G)=(1.2.3.4,232.1.2.3) membership now is NOINFO on interface eth0 + 2014/02/14 16:30:14 PIM: pim_ifchannel_update_assert_tracking_desired: AssertTrackingDesired(1.2.3.4,232.1.2.3,eth0) changed from 1 to 0 + 2014/02/14 16:30:14 PIM: pim_zebra.c del_oif: nonexistent protocol mask 2 removed OIF eth0 (vif_index=2, min_ttl=0) from channel (S,G)=(1.2.3.4,232.1.2.3) + 2014/02/14 16:30:14 PIM: pim_ifchannel_update_could_assert: CouldAssert(1.2.3.4,232.1.2.3,eth0) changed from 1 to 0 + 2014/02/14 16:30:14 PIM: pim_ifchannel_update_my_assert_metric: my_assert_metric(1.2.3.4,232.1.2.3,eth0) changed from 0,0,0,10.0.2.15 to 1,4294967295,4294967295,0.0.0.0 + 2014/02/14 16:30:14 PIM: pim_zebra.c del_oif: nonexistent protocol mask 1 removed OIF eth0 (vif_index=2, min_ttl=0) from channel (S,G)=(1.2.3.4,232.1.2.3) + 2014/02/14 16:30:14 PIM: Assertion `!IGMP_SOURCE_TEST_FORWARDING(source->source_flags)' failed in file pim_igmpv3.c, line 412, function igmp_source_delete + -x- diff --git a/pimd/COMMANDS b/pimd/COMMANDS index fb09e6c9..6210bb43 100644 --- a/pimd/COMMANDS +++ b/pimd/COMMANDS @@ -47,7 +47,9 @@ verification commands: debug commands: clear ip interfaces Reset interfaces clear ip igmp interfaces Reset IGMP interfaces + clear ip mroute Reset multicast routes clear ip pim interfaces Reset PIM interfaces + clear ip pim oil Rescan PIM OIL (output interface list) debug igmp IGMP protocol activity debug mroute PIM interaction with kernel MFC cache debug pim PIM protocol activity diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index c89f2b5a..25a567fe 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -49,6 +49,7 @@ #include "pim_rpf.h" #include "pim_macro.h" #include "pim_ssmpingd.h" +#include "pim_zebra.h" static struct cmd_node pim_global_node = { PIM_NODE, @@ -1097,6 +1098,25 @@ static void show_rpf_refresh_stats(struct vty *vty, time_t now) refresh_uptime, VTY_NEWLINE); } +static void show_scan_oil_stats(struct vty *vty, time_t now) +{ + char uptime_scan_oil[10]; + char uptime_mroute_add[10]; + char uptime_mroute_del[10]; + + pim_time_uptime_begin(uptime_scan_oil, sizeof(uptime_scan_oil), now, qpim_scan_oil_last); + pim_time_uptime_begin(uptime_mroute_add, sizeof(uptime_mroute_add), now, qpim_mroute_add_last); + pim_time_uptime_begin(uptime_mroute_del, sizeof(uptime_mroute_del), now, qpim_mroute_del_last); + + vty_out(vty, + "Scan OIL - Last: %s Events: %lld%s" + "MFC Add - Last: %s Events: %lld%s" + "MFC Del - Last: %s Events: %lld%s", + uptime_scan_oil, (long long) qpim_scan_oil_events, VTY_NEWLINE, + uptime_mroute_add, (long long) qpim_mroute_add_events, VTY_NEWLINE, + uptime_mroute_del, (long long) qpim_mroute_del_events, VTY_NEWLINE); +} + static void pim_show_rpf(struct vty *vty) { struct listnode *up_node; @@ -1576,6 +1596,57 @@ DEFUN (clear_ip_igmp_interfaces, return CMD_SUCCESS; } +static void mroute_add_all() +{ + struct listnode *node; + struct channel_oil *c_oil; + + for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) { + if (pim_mroute_add(&c_oil->oil)) { + /* just log warning */ + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + zlog_warn("%s %s: (S,G)=(%s,%s) failure writing MFC", + __FILE__, __PRETTY_FUNCTION__, + source_str, group_str); + } + } +} + +static void mroute_del_all() +{ + struct listnode *node; + struct channel_oil *c_oil; + + for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) { + if (pim_mroute_del(&c_oil->oil)) { + /* just log warning */ + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); + pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); + zlog_warn("%s %s: (S,G)=(%s,%s) failure clearing MFC", + __FILE__, __PRETTY_FUNCTION__, + source_str, group_str); + } + } +} + +DEFUN (clear_ip_mroute, + clear_ip_mroute_cmd, + "clear ip mroute", + CLEAR_STR + IP_STR + "Reset multicast routes\n") +{ + mroute_del_all(); + mroute_add_all(); + + return CMD_SUCCESS; +} + DEFUN (clear_ip_pim_interfaces, clear_ip_pim_interfaces_cmd, "clear ip pim interfaces", @@ -1589,6 +1660,19 @@ DEFUN (clear_ip_pim_interfaces, return CMD_SUCCESS; } +DEFUN (clear_ip_pim_oil, + clear_ip_pim_oil_cmd, + "clear ip pim oil", + CLEAR_STR + IP_STR + CLEAR_IP_PIM_STR + "Rescan PIM OIL (output interface list)\n") +{ + pim_scan_oil(); + + return CMD_SUCCESS; +} + DEFUN (show_ip_igmp_interface, show_ip_igmp_interface_cmd, "show ip igmp interface", @@ -2026,6 +2110,10 @@ DEFUN (show_ip_multicast, show_rpf_refresh_stats(vty, now); + vty_out(vty, "%s", VTY_NEWLINE); + + show_scan_oil_stats(vty, now); + show_multicast_interfaces(vty); return CMD_SUCCESS; @@ -2258,44 +2346,6 @@ DEFUN (show_ip_ssmpingd, return CMD_SUCCESS; } -static void mroute_add_all() -{ - struct listnode *node; - struct channel_oil *c_oil; - - for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) { - if (pim_mroute_add(&c_oil->oil)) { - /* just log warning */ - char source_str[100]; - char group_str[100]; - pim_inet4_dump("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); - pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); - zlog_warn("%s %s: (S,G)=(%s,%s) failure writing MFC", - __FILE__, __PRETTY_FUNCTION__, - source_str, group_str); - } - } -} - -static void mroute_del_all() -{ - struct listnode *node; - struct channel_oil *c_oil; - - for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) { - if (pim_mroute_del(&c_oil->oil)) { - /* just log warning */ - char source_str[100]; - char group_str[100]; - pim_inet4_dump("", c_oil->oil.mfcc_origin, source_str, sizeof(source_str)); - pim_inet4_dump("", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str)); - zlog_warn("%s %s: (S,G)=(%s,%s) failure clearing MFC", - __FILE__, __PRETTY_FUNCTION__, - source_str, group_str); - } - } -} - DEFUN (ip_multicast_routing, ip_multicast_routing_cmd, PIM_CMD_IP_MULTICAST_ROUTING, @@ -4268,7 +4318,9 @@ void pim_cmd_init() install_element (ENABLE_NODE, &clear_ip_interfaces_cmd); install_element (ENABLE_NODE, &clear_ip_igmp_interfaces_cmd); + install_element (ENABLE_NODE, &clear_ip_mroute_cmd); install_element (ENABLE_NODE, &clear_ip_pim_interfaces_cmd); + install_element (ENABLE_NODE, &clear_ip_pim_oil_cmd); install_element (ENABLE_NODE, &show_ip_igmp_interface_cmd); install_element (ENABLE_NODE, &show_ip_igmp_join_cmd); diff --git a/pimd/pim_mroute.c b/pimd/pim_mroute.c index d8837362..fa460e28 100644 --- a/pimd/pim_mroute.c +++ b/pimd/pim_mroute.c @@ -399,6 +399,9 @@ int pim_mroute_add(struct mfcctl *mc) { int err; + qpim_mroute_add_last = pim_time_monotonic_sec(); + ++qpim_mroute_add_events; + if (PIM_MROUTE_IS_DISABLED) { zlog_warn("%s: global multicast is disabled", __PRETTY_FUNCTION__); @@ -424,6 +427,9 @@ int pim_mroute_del(struct mfcctl *mc) { int err; + qpim_mroute_del_last = pim_time_monotonic_sec(); + ++qpim_mroute_del_events; + if (PIM_MROUTE_IS_DISABLED) { zlog_warn("%s: global multicast is disabled", __PRETTY_FUNCTION__); diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index 74a60a5f..820b0efc 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -353,12 +353,15 @@ static void scan_upstream_rpf_cache() } -static void scan_oil() +void pim_scan_oil() { struct listnode *node; struct listnode *nextnode; struct channel_oil *c_oil; + qpim_scan_oil_last = pim_time_monotonic_sec(); + ++qpim_scan_oil_events; + for (ALL_LIST_ELEMENTS(qpim_channel_oil_list, node, nextnode, c_oil)) { int old_vif_index; int input_iface_vif_index = fib_lookup_if_vif_index(c_oil->oil.mfcc_origin); @@ -445,7 +448,7 @@ static int on_rpf_cache_refresh(struct thread *t) scan_upstream_rpf_cache(); /* update kernel multicast forwarding cache (MFC) */ - scan_oil(); + pim_scan_oil(); qpim_rpf_cache_refresh_last = pim_time_monotonic_sec(); ++qpim_rpf_cache_refresh_events; diff --git a/pimd/pim_zebra.h b/pimd/pim_zebra.h index e0c9bdcb..474e7a2e 100644 --- a/pimd/pim_zebra.h +++ b/pimd/pim_zebra.h @@ -28,6 +28,8 @@ void pim_zebra_init(void); +void pim_scan_oil(void); + void igmp_anysource_forward_start(struct igmp_group *group); void igmp_anysource_forward_stop(struct igmp_group *group); diff --git a/pimd/pimd.c b/pimd/pimd.c index 71b74e97..49ddc212 100644 --- a/pimd/pimd.c +++ b/pimd/pimd.c @@ -61,6 +61,12 @@ int64_t qpim_rpf_cache_refresh_last = 0; struct in_addr qpim_inaddr_any; struct list *qpim_ssmpingd_list = 0; struct in_addr qpim_ssmpingd_group_addr; +int64_t qpim_scan_oil_events = 0; +int64_t qpim_scan_oil_last = 0; +int64_t qpim_mroute_add_events = 0; +int64_t qpim_mroute_add_last = 0; +int64_t qpim_mroute_del_events = 0; +int64_t qpim_mroute_del_last = 0; static void pim_free() { diff --git a/pimd/pimd.h b/pimd/pimd.h index e11d5bad..09900888 100644 --- a/pimd/pimd.h +++ b/pimd/pimd.h @@ -90,6 +90,12 @@ int64_t qpim_rpf_cache_refresh_last; struct in_addr qpim_inaddr_any; struct list *qpim_ssmpingd_list; /* list of struct ssmpingd_sock */ struct in_addr qpim_ssmpingd_group_addr; +int64_t qpim_scan_oil_events; +int64_t qpim_scan_oil_last; +int64_t qpim_mroute_add_events; +int64_t qpim_mroute_add_last; +int64_t qpim_mroute_del_events; +int64_t qpim_mroute_del_last; #define PIM_JP_HOLDTIME (qpim_t_periodic * 7 / 2) From d1a87ee63042225d4b3bf6b4655cd7e0e2b64828 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Fri, 14 Feb 2014 16:51:05 -0200 Subject: [PATCH 366/482] Fix pim join uptime display. --- pimd/pim_cmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 25a567fe..eaf6afe6 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -728,7 +728,7 @@ static void pim_show_join(struct vty *vty) pim_inet4_dump("", ch->group_addr, ch_grp_str, sizeof(ch_grp_str)); - pim_time_uptime(uptime, sizeof(uptime), now - ch->ifjoin_creation); + pim_time_uptime_begin(uptime, sizeof(uptime), now, ch->ifjoin_creation); pim_time_timer_to_mmss(expire, sizeof(expire), ch->t_ifjoin_expiry_timer); pim_time_timer_to_mmss(prune, sizeof(prune), From 1f7a2b44687a49e4dab649ce369b5735aa2dd20f Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Tue, 1 Apr 2014 18:38:50 -0300 Subject: [PATCH 367/482] github repository. --- doc/pimd.8 | 4 ++-- pimd/README | 4 ++-- pimd/github-git-clone.sh | 27 +++++++++++++++++++++++++++ pimd/pimd.h | 2 +- 4 files changed, 32 insertions(+), 5 deletions(-) create mode 100755 pimd/github-git-clone.sh diff --git a/doc/pimd.8 b/doc/pimd.8 index b26b1128..3995cfe4 100644 --- a/doc/pimd.8 +++ b/doc/pimd.8 @@ -100,9 +100,9 @@ production use. .B pimd eats bugs for breakfast. If you have food for the maintainers try -.BI http://savannah.nongnu.org/projects/qpimd +.BI https://github.com/udhos/qpimd .SH AUTHORS See -.BI http://savannah.nongnu.org/projects/qpimd +.BI https://github.com/udhos/qpimd for an accurate list of authors. diff --git a/pimd/README b/pimd/README index f6bd8ec6..1e3f72c8 100644 --- a/pimd/README +++ b/pimd/README @@ -38,7 +38,7 @@ HOME SITE qpimd lives at: - http://savannah.nongnu.org/projects/qpimd + https://github.com/udhos/qpimd PLATFORMS @@ -124,7 +124,7 @@ SUPPORT Please post comments, questions, patches, bug reports at the support site: - http://savannah.nongnu.org/projects/qpimd + https://github.com/udhos/qpimd RELATED WORK diff --git a/pimd/github-git-clone.sh b/pimd/github-git-clone.sh new file mode 100755 index 00000000..ea359b03 --- /dev/null +++ b/pimd/github-git-clone.sh @@ -0,0 +1,27 @@ +#! /bin/bash +# +# Github Developer Git Checkout +# +# Delete remote branch qpimd: git push origin :qpimd +# (git push origin :refs/heads/branch_to_delete) +# Delete remote tag v0.139: git push origin :v0.139 +# (git push origin :refs/tags/tag_to_delete) +# Create remote-tracking branch: git checkout -b pim0.142 origin/pim0.142 +# Rename branch qpimd to pim: git branch -m qpimd pim +# Commit changes: git commit -a +# Send changes: git push --all +# +# Recipe to re-sync with Quagga repository: +# git clone https://github.com/udhos/qpimd +# cd qpimd +# git checkout master +# git pull git clone http://git.sv.gnu.org/r/quagga.git master +# git checkout -b pim origin/pim +# git rebase master pim +# # Test, then push back into Github repository: +# git push origin :pim ;# delete remote branch pim +# git push --all +# +# $QuaggaId: $Format:%an, %ai, %h$ $ + +git clone https://github.com/udhos/qpimd diff --git a/pimd/pimd.h b/pimd/pimd.h index 09900888..68b11994 100644 --- a/pimd/pimd.h +++ b/pimd/pimd.h @@ -31,7 +31,7 @@ #define PIMD_PROGNAME "pimd" #define PIMD_DEFAULT_CONFIG "pimd.conf" #define PIMD_VTY_PORT 2611 -#define PIMD_BUG_ADDRESS "http://savannah.nongnu.org/projects/qpimd" +#define PIMD_BUG_ADDRESS "https://github.com/udhos/qpimd" #define PIM_IP_HEADER_MIN_LEN (20) #define PIM_IP_HEADER_MAX_LEN (60) From 069a0208ec10fca53676ec187f958037108363a6 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Wed, 2 Apr 2014 11:04:52 -0300 Subject: [PATCH 368/482] Fixed recipe to re-sync with Quagga repository --- pimd/github-git-clone.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pimd/github-git-clone.sh b/pimd/github-git-clone.sh index ea359b03..ae2362a7 100755 --- a/pimd/github-git-clone.sh +++ b/pimd/github-git-clone.sh @@ -12,10 +12,10 @@ # Send changes: git push --all # # Recipe to re-sync with Quagga repository: -# git clone https://github.com/udhos/qpimd -# cd qpimd +# git clone https://github.com/udhos/qpimd quagga +# cd quagga # git checkout master -# git pull git clone http://git.sv.gnu.org/r/quagga.git master +# git pull http://git.sv.gnu.org/r/quagga.git master # git checkout -b pim origin/pim # git rebase master pim # # Test, then push back into Github repository: From 60353ab4b0857d3f416e315fd81568d9e60205c4 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Wed, 2 Apr 2014 11:46:45 -0300 Subject: [PATCH 369/482] C19 Provision to prevent group mode clash --- pimd/CAVEATS | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pimd/CAVEATS b/pimd/CAVEATS index 05a439b8..b1f1a039 100644 --- a/pimd/CAVEATS +++ b/pimd/CAVEATS @@ -158,4 +158,20 @@ C18 MFC never recovers from removal of static route to source 2014/02/14 16:30:14 PIM: pim_zebra.c del_oif: nonexistent protocol mask 1 removed OIF eth0 (vif_index=2, min_ttl=0) from channel (S,G)=(1.2.3.4,232.1.2.3) 2014/02/14 16:30:14 PIM: Assertion `!IGMP_SOURCE_TEST_FORWARDING(source->source_flags)' failed in file pim_igmpv3.c, line 412, function igmp_source_delete +C19 Provision to prevent group mode clash + + Beware group mode clash. A host/application issuing IGMPv2 + any-source joins for a group will disrupt SSM multicast for that + group. + + For instance, support for source-specific static igmp WILL FAIL if + there is host/application issuing IGMPv2 any-source joins for the + same group. + + The reason is the IGMPv2 any-source join forces qpimd to switch + the group mode to ASM (any-source multicast); however, qpimd is + unable to program ASM groups into the kernel; multicast won't + flow. There could be some provision to prevent such a behavior, + but currently there is none. + -x- From 829198800217fe321faa397d21e2d8f99fcaf0dd Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Wed, 25 Jun 2014 13:25:17 -0300 Subject: [PATCH 370/482] pimd: Rename script with recipe for git cloning --- pimd/{github-git-clone.sh => git-clone-github.sh} | 0 pimd/{savannah-git-clone.sh => git-clone-savannah.sh} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename pimd/{github-git-clone.sh => git-clone-github.sh} (100%) rename pimd/{savannah-git-clone.sh => git-clone-savannah.sh} (100%) diff --git a/pimd/github-git-clone.sh b/pimd/git-clone-github.sh similarity index 100% rename from pimd/github-git-clone.sh rename to pimd/git-clone-github.sh diff --git a/pimd/savannah-git-clone.sh b/pimd/git-clone-savannah.sh similarity index 100% rename from pimd/savannah-git-clone.sh rename to pimd/git-clone-savannah.sh From ff57d3653efe4a1428147204b54cacf3651c4dd0 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Wed, 25 Jun 2014 15:54:03 -0300 Subject: [PATCH 371/482] pimd: Fixes to build against current quagga. --- configure.ac | 1 + doc/Makefile.am | 2 ++ lib/zclient.c | 2 +- lib/zclient.h | 1 + lib/zebra.h | 16 ++-------------- pimd/Makefile.am | 6 +++++- pimd/pim_main.c | 1 - pimd/pim_signals.c | 5 +++-- 8 files changed, 15 insertions(+), 19 deletions(-) diff --git a/configure.ac b/configure.ac index 7696bd70..6632e543 100755 --- a/configure.ac +++ b/configure.ac @@ -1369,6 +1369,7 @@ case "${enable_pimd}" in "no" ) PIMD="";; * ) ;; esac +AM_CONDITIONAL(PIMD, test "x$PIMD" = "xpimd") # XXX Perhaps auto-enable on Solaris, but that's messy for cross builds. case "${enable_solaris}" in diff --git a/doc/Makefile.am b/doc/Makefile.am index 8869c818..dfc5e402 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -61,6 +61,8 @@ quagga_TEXINFOS = appendix.texi babeld.texi basic.texi bgpd.texi filter.texi \ .dia.png: $(DIATOPNG) "$@" $< +man_MANS = + if PIMD man_MANS += pimd.8 endif diff --git a/lib/zclient.c b/lib/zclient.c index 3b5477e9..709d9b98 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -148,7 +148,7 @@ zclient_reset (struct zclient *zclient) #ifdef HAVE_TCP_ZEBRA /* Make socket to zebra daemon. Return zebra socket. */ -static int +int zclient_socket(void) { int sock; diff --git a/lib/zclient.h b/lib/zclient.h index a660bbf1..b0b76444 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -132,6 +132,7 @@ extern void zclient_stop (struct zclient *); extern void zclient_reset (struct zclient *); extern void zclient_free (struct zclient *); +extern int zclient_socket(void); extern int zclient_socket_connect (struct zclient *); extern void zclient_serv_path_set (char *path); diff --git a/lib/zebra.h b/lib/zebra.h index 67d714cf..57e591ff 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -435,20 +435,8 @@ struct in_pktinfo */ #define ZEBRA_HEADER_MARKER 255 -/* Zebra route's types. */ -#define ZEBRA_ROUTE_SYSTEM 0 -#define ZEBRA_ROUTE_KERNEL 1 -#define ZEBRA_ROUTE_CONNECT 2 -#define ZEBRA_ROUTE_STATIC 3 -#define ZEBRA_ROUTE_RIP 4 -#define ZEBRA_ROUTE_RIPNG 5 -#define ZEBRA_ROUTE_OSPF 6 -#define ZEBRA_ROUTE_OSPF6 7 -#define ZEBRA_ROUTE_ISIS 8 -#define ZEBRA_ROUTE_BGP 9 -#define ZEBRA_ROUTE_HSLS 10 -#define ZEBRA_ROUTE_PIM 11 -#define ZEBRA_ROUTE_MAX 12 +/* Zebra route's types are defined in route_types.h */ +#include "route_types.h" /* Note: whenever a new route-type or zserv-command is added the * corresponding {command,route}_types[] table in lib/log.c MUST be diff --git a/pimd/Makefile.am b/pimd/Makefile.am index ef5cffa1..70c5096c 100644 --- a/pimd/Makefile.am +++ b/pimd/Makefile.am @@ -45,7 +45,11 @@ PIM_DEFS += -DPIM_GETTIME_USE_GETTIMEOFDAY INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" $(PIM_DEFS) INSTALL_SDATA=@INSTALL@ -m 600 -LIBS = @LIBS@ +LIBS = @LIBS@ + +AM_CFLAGS = $(PICFLAGS) +AM_LDFLAGS = $(PILDFLAGS) + noinst_LIBRARIES = libpim.a sbin_PROGRAMS = pimd bin_PROGRAMS = test_igmpv3_join diff --git a/pimd/pim_main.c b/pimd/pim_main.c index 51e5e360..64d7787d 100644 --- a/pimd/pim_main.c +++ b/pimd/pim_main.c @@ -201,7 +201,6 @@ int main(int argc, char** argv, char** envp) { memory_init(); access_list_init(); pim_init(); - sort_node(); /* * reset zlog default, then will obey configuration file diff --git a/pimd/pim_signals.c b/pimd/pim_signals.c index 1b146f6f..d1350b08 100644 --- a/pimd/pim_signals.c +++ b/pimd/pim_signals.c @@ -24,6 +24,7 @@ #include #include "sigevent.h" +#include "memory.h" #include "log.h" #include "pim_signals.h" @@ -58,7 +59,7 @@ static void pim_sigusr1() zlog_rotate (NULL); } -struct quagga_signal_t pimd_signals[] = +static struct quagga_signal_t pimd_signals[] = { { .signal = SIGHUP, @@ -80,6 +81,6 @@ struct quagga_signal_t pimd_signals[] = void pim_signals_init() { - signal_init(master, Q_SIGC(pimd_signals), pimd_signals); + signal_init(master, array_size(pimd_signals), pimd_signals); } From 82e6c730e624e86c00042b64fc32170630b2c18c Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Wed, 25 Jun 2014 17:28:04 -0300 Subject: [PATCH 372/482] pimd: Reset DR uptime only on actual change --- pimd/pim_neighbor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pimd/pim_neighbor.c b/pimd/pim_neighbor.c index 7d7b43a6..66d895e9 100644 --- a/pimd/pim_neighbor.c +++ b/pimd/pim_neighbor.c @@ -111,7 +111,6 @@ void pim_if_dr_election(struct interface *ifp) struct pim_interface *pim_ifp = ifp->info; struct in_addr old_dr_addr; - pim_ifp->pim_dr_election_last = pim_time_monotonic_sec(); /* timestamp */ ++pim_ifp->pim_dr_election_count; old_dr_addr = pim_ifp->pim_dr_addr; @@ -133,6 +132,7 @@ void pim_if_dr_election(struct interface *ifp) __PRETTY_FUNCTION__, dr_old_str, dr_new_str, ifp->name); + pim_ifp->pim_dr_election_last = pim_time_monotonic_sec(); /* timestamp */ ++pim_ifp->pim_dr_election_changes; pim_if_update_join_desired(pim_ifp); pim_if_update_could_assert(ifp); From 04c833a1e0fbf063f49ff0b9f837b07456580374 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Mon, 22 Sep 2014 19:35:24 -0300 Subject: [PATCH 373/482] pimd: Query mrib (SAFI_MULTICAST). --- pimd/pim_zlookup.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pimd/pim_zlookup.c b/pimd/pim_zlookup.c index 112ff261..1492f7c3 100644 --- a/pimd/pim_zlookup.c +++ b/pimd/pim_zlookup.c @@ -230,7 +230,7 @@ static int zclient_read_nexthop(struct zclient *zlookup, } command = stream_getw(s); - if (command != ZEBRA_IPV4_NEXTHOP_LOOKUP_V2) { + if (command != ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB) { zlog_err("%s: socket %d command mismatch: %d", __func__, zlookup->sock, command); return -5; @@ -369,7 +369,7 @@ static int zclient_lookup_nexthop_once(struct zclient *zlookup, s = zlookup->obuf; stream_reset(s); - zclient_create_header(s, ZEBRA_IPV4_NEXTHOP_LOOKUP_V2); + zclient_create_header(s, ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB); stream_put_in_addr(s, &addr); stream_putw_at(s, 0, stream_get_endp(s)); From 5c55a496fae5ab089c5009bc4c03084fdeb51f55 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Wed, 2 Jul 2014 12:12:16 -0300 Subject: [PATCH 374/482] pimd: Cosmetic fix for dr uptime display. --- pimd/pim_cmd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index eaf6afe6..90ca07c0 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -582,8 +582,8 @@ static void pim_show_dr(struct vty *vty) ifaddr = pim_ifp->primary_address; - pim_time_uptime(dr_uptime, sizeof(dr_uptime), - now - pim_ifp->pim_dr_election_last); + pim_time_uptime_begin(dr_uptime, sizeof(dr_uptime), + now, pim_ifp->pim_dr_election_last); pim_inet4_dump("", pim_ifp->pim_dr_addr, dr_str, sizeof(dr_str)); From f62a19cc281276607b164c99e672d536a012514d Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Thu, 3 Jul 2014 14:53:44 -0300 Subject: [PATCH 375/482] zebra: Export zclient_socket_un(). --- lib/zclient.c | 2 +- lib/zclient.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/zclient.c b/lib/zclient.c index 709d9b98..c3a4905f 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -184,7 +184,7 @@ zclient_socket(void) /* For sockaddr_un. */ #include -static int +int zclient_socket_un (const char *path) { int ret; diff --git a/lib/zclient.h b/lib/zclient.h index b0b76444..6a5e6268 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -133,6 +133,7 @@ extern void zclient_reset (struct zclient *); extern void zclient_free (struct zclient *); extern int zclient_socket(void); +extern int zclient_socket_un (const char *path); extern int zclient_socket_connect (struct zclient *); extern void zclient_serv_path_set (char *path); From 2f1df6a58b9f0a278cbdee9fc57f964eef1c7bf7 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Thu, 3 Jul 2014 15:37:52 -0300 Subject: [PATCH 376/482] pimd: Version up to 0.164 --- pimd/pim_version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pimd/pim_version.h b/pimd/pim_version.h index 22888c53..73711367 100644 --- a/pimd/pim_version.h +++ b/pimd/pim_version.h @@ -23,7 +23,7 @@ #ifndef PIM_VERSION_H #define PIM_VERSION_H -#define PIMD_VERSION_STR "0.163" +#define PIMD_VERSION_STR "0.164" const char * const PIMD_VERSION; From 6f40e6b74effb7447854f690a2a4fa9339d8a967 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Fri, 11 Jul 2014 14:16:45 -0300 Subject: [PATCH 377/482] pimd: Fix compiler warnings. --- pimd/pim_cmd.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 90ca07c0..9363e3c9 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -1006,7 +1006,6 @@ static void pim_show_join_desired(struct vty *vty) struct interface *ifp; struct pim_interface *pim_ifp; struct pim_ifchannel *ch; - struct in_addr me_ifaddr; char src_str[100]; char grp_str[100]; @@ -1020,8 +1019,6 @@ static void pim_show_join_desired(struct vty *vty) if (!pim_ifp) continue; - me_ifaddr = pim_ifp->primary_address; - /* scan per-interface (S,G) state */ for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, chnode, ch)) { struct pim_upstream *up = ch->upstream; @@ -1249,9 +1246,6 @@ static void igmp_show_group_retransmission(struct vty *vty) { struct listnode *ifnode; struct interface *ifp; - time_t now; - - now = pim_time_monotonic_sec(); vty_out(vty, "Interface Address Group RetTimer Counter RetSrcs%s", VTY_NEWLINE); @@ -1446,9 +1440,6 @@ static void igmp_show_source_retransmission(struct vty *vty) { struct listnode *ifnode; struct interface *ifp; - time_t now; - - now = pim_time_monotonic_sec(); vty_out(vty, "Interface Address Group Source Counter%s", VTY_NEWLINE); From 6c1d36a462388039f92b875196c4d1c92ae69644 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Thu, 18 Sep 2014 14:15:04 -0300 Subject: [PATCH 378/482] pimd: Clean-up. --- pimd/COMMANDS | 11 +++++++++++ pimd/pim_igmpv3.c | 15 --------------- pimd/pim_upstream.c | 3 --- 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/pimd/COMMANDS b/pimd/COMMANDS index 6210bb43..425ac822 100644 --- a/pimd/COMMANDS +++ b/pimd/COMMANDS @@ -1,10 +1,15 @@ # $QuaggaId: $Format:%an, %ai, %h$ $ global configuration commands: + pimd: ip multicast-routing Enable IP multicast forwarding ip ssmpingd Enable ssmpingd operation + zebra: + ip mroute Configure static unicast route into MRIB for multicast RPF lookup + interface configuration commands: + pimd: ip igmp Enable IGMP operation ip igmp join IGMP join multicast group ip igmp query-interval <1-1800> IGMP host query interval @@ -13,6 +18,7 @@ interface configuration commands: ip pim ssm Enable PIM SSM operation verification commands: + pimd: show ip igmp interface IGMP interface information show ip igmp join IGMP static join information show ip igmp parameters IGMP parameters information @@ -44,7 +50,11 @@ verification commands: show ip rib IP unicast routing table show ip ssmpingd ssmpingd operation + zebra: + show ip rpf Display RPF information for multicast source + debug commands: + pimd: clear ip interfaces Reset interfaces clear ip igmp interfaces Reset IGMP interfaces clear ip mroute Reset multicast routes @@ -65,6 +75,7 @@ debug commands: test pim receive upcall Test reception of kernel upcall statistics commands: + pimd: show memory pim PIM memory statistics -x- diff --git a/pimd/pim_igmpv3.c b/pimd/pim_igmpv3.c index 4be52d60..3da1dd1e 100644 --- a/pimd/pim_igmpv3.c +++ b/pimd/pim_igmpv3.c @@ -529,12 +529,9 @@ static void allow(struct igmp_sock *igmp, struct in_addr from, int num_sources, struct in_addr *sources) { struct interface *ifp = igmp->interface; - struct pim_interface *pim_ifp; struct igmp_group *group; int i; - pim_ifp = ifp->info; - /* non-existant group is created as INCLUDE {empty} */ group = igmp_add_group_by_addr(igmp, group_addr, ifp->name); if (!group) { @@ -673,14 +670,11 @@ void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from, int num_sources, struct in_addr *sources) { struct interface *ifp = igmp->interface; - struct pim_interface *pim_ifp; struct igmp_group *group; on_trace(__PRETTY_FUNCTION__, ifp, from, group_addr, num_sources, sources); - pim_ifp = ifp->info; - /* non-existant group is created as INCLUDE {empty} */ group = igmp_add_group_by_addr(igmp, group_addr, ifp->name); if (!group) { @@ -800,14 +794,11 @@ void igmpv3_report_toin(struct igmp_sock *igmp, struct in_addr from, int num_sources, struct in_addr *sources) { struct interface *ifp = igmp->interface; - struct pim_interface *pim_ifp; struct igmp_group *group; on_trace(__PRETTY_FUNCTION__, ifp, from, group_addr, num_sources, sources); - pim_ifp = ifp->info; - /* non-existant group is created as INCLUDE {empty} */ group = igmp_add_group_by_addr(igmp, group_addr, ifp->name); if (!group) { @@ -955,14 +946,11 @@ void igmpv3_report_toex(struct igmp_sock *igmp, struct in_addr from, int num_sources, struct in_addr *sources) { struct interface *ifp = igmp->interface; - struct pim_interface *pim_ifp; struct igmp_group *group; on_trace(__PRETTY_FUNCTION__, ifp, from, group_addr, num_sources, sources); - pim_ifp = ifp->info; - /* non-existant group is created as INCLUDE {empty} */ group = igmp_add_group_by_addr(igmp, group_addr, ifp->name); if (!group) { @@ -1480,14 +1468,11 @@ void igmpv3_report_block(struct igmp_sock *igmp, struct in_addr from, int num_sources, struct in_addr *sources) { struct interface *ifp = igmp->interface; - struct pim_interface *pim_ifp; struct igmp_group *group; on_trace(__PRETTY_FUNCTION__, ifp, from, group_addr, num_sources, sources); - pim_ifp = ifp->info; - /* non-existant group is created as INCLUDE {empty} */ group = igmp_add_group_by_addr(igmp, group_addr, ifp->name); if (!group) { diff --git a/pimd/pim_upstream.c b/pimd/pim_upstream.c index 6702ca13..d02f9154 100644 --- a/pimd/pim_upstream.c +++ b/pimd/pim_upstream.c @@ -256,7 +256,6 @@ static void forward_on(struct pim_upstream *up) struct interface *ifp; struct pim_interface *pim_ifp; struct pim_ifchannel *ch; - struct in_addr ifaddr; /* scan all interfaces */ for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) { @@ -264,8 +263,6 @@ static void forward_on(struct pim_upstream *up) if (!pim_ifp) continue; - ifaddr = pim_ifp->primary_address; - /* scan per-interface (S,G) state */ for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) { From e4f2a2bef879d9e7f5c4c80994c44772b09beb59 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Wed, 16 Jul 2014 12:19:40 -0300 Subject: [PATCH 379/482] pimd: Report first route's metric/distance for recursive RPF lookup. --- pimd/pim_zlookup.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/pimd/pim_zlookup.c b/pimd/pim_zlookup.c index 1492f7c3..be0499e3 100644 --- a/pimd/pim_zlookup.c +++ b/pimd/pim_zlookup.c @@ -402,6 +402,8 @@ int zclient_lookup_nexthop(struct zclient *zlookup, int max_lookup) { int lookup; + uint32_t route_metric = 0xFFFFFFFF; + uint8_t protocol_distance = 0xFF; for (lookup = 0; lookup < max_lookup; ++lookup) { int num_ifindex; @@ -418,6 +420,12 @@ int zclient_lookup_nexthop(struct zclient *zlookup, lookup, max_lookup, addr_str); return -1; } + + if (lookup < 1) { + /* this is the non-recursive lookup - save original metric/distance */ + route_metric = nexthop_tab[0].route_metric; + protocol_distance = nexthop_tab[0].protocol_distance; + } /* FIXME: Non-recursive nexthop ensured only for first ifindex. @@ -433,12 +441,18 @@ int zclient_lookup_nexthop(struct zclient *zlookup, /* Report non-recursive success after first lookup */ char addr_str[100]; pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); - zlog_info("%s %s: lookup=%d/%d: found non-recursive ifindex=%d for address %s", + zlog_info("%s %s: lookup=%d/%d: found non-recursive ifindex=%d for address %s dist=%d met=%d", __FILE__, __PRETTY_FUNCTION__, - lookup, max_lookup, first_ifindex, addr_str); + lookup, max_lookup, first_ifindex, addr_str, + nexthop_tab[0].protocol_distance, + nexthop_tab[0].route_metric); /* use last address as nexthop address */ nexthop_tab[0].nexthop_addr = addr; + + /* report original route metric/distance */ + nexthop_tab[0].route_metric = route_metric; + nexthop_tab[0].protocol_distance = protocol_distance; } return num_ifindex; @@ -449,9 +463,11 @@ int zclient_lookup_nexthop(struct zclient *zlookup, char nexthop_str[100]; pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); pim_inet4_dump("", nexthop_addr, nexthop_str, sizeof(nexthop_str)); - zlog_warn("%s %s: lookup=%d/%d: zebra returned recursive nexthop %s for address %s", + zlog_warn("%s %s: lookup=%d/%d: zebra returned recursive nexthop %s for address %s dist=%d met=%d", __FILE__, __PRETTY_FUNCTION__, - lookup, max_lookup, nexthop_str, addr_str); + lookup, max_lookup, nexthop_str, addr_str, + nexthop_tab[0].protocol_distance, + nexthop_tab[0].route_metric); } addr = nexthop_addr; /* use nexthop addr for recursive lookup */ From f6e16b9f3592a3213e94f44b71caa311e0270cd9 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Wed, 16 Jul 2014 14:13:47 -0300 Subject: [PATCH 380/482] pimd: Document MRIB support. --- pimd/CAVEATS | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pimd/CAVEATS b/pimd/CAVEATS index b1f1a039..ba6426dd 100644 --- a/pimd/CAVEATS +++ b/pimd/CAVEATS @@ -82,9 +82,9 @@ C11 SSM Mapping is not supported is changed, or when the DNS mapping changes, the router will leave the current sources associated with the joined groups. -C12 MRIB for incongruent unicast/multicast topologies is not supported. - RPF mechanism currently just looks up the information in the - unicast routing table. +C12 FIXED MRIB for incongruent unicast/multicast topologies is not + supported. RPF mechanism currently just looks up the information + in the unicast routing table. See also: RFC5110: 2.2.3. Issue: Overlapping Unicast/Multicast Topology From 306c99eae67a7966a3994be1007f03665eb6682b Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Wed, 16 Jul 2014 15:51:37 -0300 Subject: [PATCH 381/482] pimd: FIXED C14 T32 Detection of interface primary address changes may fail. --- pimd/CAVEATS | 4 ++-- pimd/TODO | 4 ++-- pimd/pim_iface.c | 37 ++++++++++++++++++++++++++++++++----- pimd/pim_zebra.c | 38 +++++++++++++++++++++++++++++++------- 4 files changed, 67 insertions(+), 16 deletions(-) diff --git a/pimd/CAVEATS b/pimd/CAVEATS index ba6426dd..7e2820b3 100644 --- a/pimd/CAVEATS +++ b/pimd/CAVEATS @@ -109,8 +109,8 @@ C13 Can't detect change of primary address before the actual change. See also pim_sock_delete(). -C14 Detection of interface primary address changes may fail when there - are multiple addresses. +C14 FIXED Detection of interface primary address changes may fail when + there are multiple addresses. See also TODO T32. C15 Changes in interface secondary address list are not immediately diff --git a/pimd/TODO b/pimd/TODO index 3a3330d3..80835e46 100644 --- a/pimd/TODO +++ b/pimd/TODO @@ -267,8 +267,8 @@ T31 If an interface changes one of its secondary IP addresses, a Hello See also CAVEAT C15. See also RFC 4601: 4.3.1. Sending Hello Messages -T32 Detection of interface primary address changes may fail when there - are multiple addresses. +T32 FIXED Detection of interface primary address changes may fail when + there are multiple addresses. See also CAVEAT C14. pim_find_primary_addr() should return interface primary address diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index 72626eca..3dee7519 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -286,6 +286,7 @@ static void detect_primary_address_change(struct interface *ifp, { struct pim_interface *pim_ifp; struct in_addr new_prim_addr; + int changed; pim_ifp = ifp->info; if (!pim_ifp) @@ -293,17 +294,20 @@ static void detect_primary_address_change(struct interface *ifp, new_prim_addr = pim_find_primary_addr(ifp); + changed = new_prim_addr.s_addr != pim_ifp->primary_address.s_addr; + if (PIM_DEBUG_ZEBRA) { char new_prim_str[100]; char old_prim_str[100]; pim_inet4_dump("", new_prim_addr, new_prim_str, sizeof(new_prim_str)); pim_inet4_dump("", pim_ifp->primary_address, old_prim_str, sizeof(old_prim_str)); - zlog_debug("%s: old primary addr %s, new primary addr %s on interface %s", + zlog_debug("%s: old=%s new=%s on interface %s: %s", __PRETTY_FUNCTION__, - old_prim_str, new_prim_str, ifp->name); + old_prim_str, new_prim_str, ifp->name, + changed ? "changed" : "unchanged"); } - if (new_prim_addr.s_addr != pim_ifp->primary_address.s_addr) { + if (changed) { struct in_addr old_addr = pim_ifp->primary_address; pim_ifp->primary_address = new_prim_addr; @@ -328,6 +332,16 @@ void pim_if_addr_add(struct connected *ifc) if (!if_is_operative(ifp)) return; + if (PIM_DEBUG_ZEBRA) { + char buf[BUFSIZ]; + prefix2str(ifc->address, buf, BUFSIZ); + zlog_debug("%s: %s connected IP address %s %s", + __PRETTY_FUNCTION__, + ifp->name, buf, + CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY) ? + "secondary" : "primary"); + } + ifaddr = ifc->address->u.prefix4; detect_primary_address_change(ifp, __PRETTY_FUNCTION__); @@ -437,6 +451,16 @@ void pim_if_addr_del(struct connected *ifc) ifp = ifc->ifp; zassert(ifp); + if (PIM_DEBUG_ZEBRA) { + char buf[BUFSIZ]; + prefix2str(ifc->address, buf, BUFSIZ); + zlog_debug("%s: %s disconnected IP address %s %s", + __PRETTY_FUNCTION__, + ifp->name, buf, + CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY) ? + "secondary" : "primary"); + } + detect_primary_address_change(ifp, __PRETTY_FUNCTION__); pim_if_addr_del_igmp(ifc); @@ -523,7 +547,7 @@ void pim_if_addr_del_all_pim(struct interface *ifp) } } -static struct in_addr find_first_addr(struct interface *ifp) +static struct in_addr find_first_nonsec_addr(struct interface *ifp) { struct connected *ifc; struct listnode *node; @@ -541,6 +565,9 @@ static struct in_addr find_first_addr(struct interface *ifp) continue; } + if (CHECK_FLAG (ifc->flags, ZEBRA_IFA_SECONDARY)) + continue; + return p->u.prefix4; } @@ -551,7 +578,7 @@ static struct in_addr find_first_addr(struct interface *ifp) struct in_addr pim_find_primary_addr(struct interface *ifp) { - return find_first_addr(ifp); + return find_first_nonsec_addr(ifp); } /* diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index 820b0efc..f0aa84f0 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -203,10 +203,12 @@ static void dump_if_address(struct interface *ifp) if (p->family != AF_INET) continue; - zlog_debug("%s %s: interface %s address %s", + zlog_debug("%s %s: interface %s address %s %s", __FILE__, __PRETTY_FUNCTION__, ifp->name, - inet_ntoa(p->u.prefix4)); + inet_ntoa(p->u.prefix4), + CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY) ? + "secondary" : "primary"); } } #endif @@ -238,15 +240,36 @@ static int pim_zebra_if_address_add(int command, struct zclient *zclient, if (PIM_DEBUG_ZEBRA) { char buf[BUFSIZ]; prefix2str(p, buf, BUFSIZ); - zlog_debug("%s: %s connected IP address %s flags %u", + zlog_debug("%s: %s connected IP address %s flags %u %s", __PRETTY_FUNCTION__, - c->ifp->name, buf, c->flags); + c->ifp->name, buf, c->flags, + CHECK_FLAG(c->flags, ZEBRA_IFA_SECONDARY) ? "secondary" : "primary"); #ifdef PIM_DEBUG_IFADDR_DUMP dump_if_address(c->ifp); #endif } + if (!CHECK_FLAG(c->flags, ZEBRA_IFA_SECONDARY)) { + /* trying to add primary address */ + + struct in_addr primary_addr = pim_find_primary_addr(c->ifp); + if (primary_addr.s_addr != p->u.prefix4.s_addr) { + /* but we had a primary address already */ + + char buf[BUFSIZ]; + char old[100]; + + prefix2str(p, buf, BUFSIZ); + pim_inet4_dump("", primary_addr, old, sizeof(old)); + + zlog_warn("%s: %s primary addr old=%s: forcing secondary flag on new=%s", + __PRETTY_FUNCTION__, + c->ifp->name, old, buf); + SET_FLAG(c->flags, ZEBRA_IFA_SECONDARY); + } + } + pim_if_addr_add(c); return 0; @@ -279,15 +302,16 @@ static int pim_zebra_if_address_del(int command, struct zclient *client, if (PIM_DEBUG_ZEBRA) { char buf[BUFSIZ]; prefix2str(p, buf, BUFSIZ); - zlog_debug("%s: %s disconnected IP address %s flags %u", + zlog_debug("%s: %s disconnected IP address %s flags %u %s", __PRETTY_FUNCTION__, - c->ifp->name, buf, c->flags); + c->ifp->name, buf, c->flags, + CHECK_FLAG(c->flags, ZEBRA_IFA_SECONDARY) ? "secondary" : "primary"); #ifdef PIM_DEBUG_IFADDR_DUMP dump_if_address(c->ifp); #endif } - + pim_if_addr_del(c); return 0; From 075ac8ddbd6aa782619565a97ec474a90f1b6f4c Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Wed, 24 Sep 2014 15:18:37 -0300 Subject: [PATCH 382/482] pimd: Prevent interfaces' addresses duplication when zebra connection is restored. --- pimd/pim_iface.c | 2 +- pimd/pim_zebra.c | 20 +++++++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index 3dee7519..8ceecf85 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -565,7 +565,7 @@ static struct in_addr find_first_nonsec_addr(struct interface *ifp) continue; } - if (CHECK_FLAG (ifc->flags, ZEBRA_IFA_SECONDARY)) + if (CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY)) continue; return p->u.prefix4; diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index f0aa84f0..6bef43e9 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -24,6 +24,7 @@ #include "zebra/rib.h" +#include "if.h" #include "log.h" #include "prefix.h" #include "zclient.h" @@ -50,15 +51,32 @@ static int del_oif(struct channel_oil *channel_oil, struct interface *oif, uint32_t proto_mask); +static void reset_iface_addresses() { + struct listnode *ifnode; + struct interface *ifp; + + zlog_warn("%s %s: resetting all interface addresses", + __FILE__, __PRETTY_FUNCTION__); + + for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) { + if_connected_reset(ifp); + } +} + /* Router-id update message from zebra. */ static int pim_router_id_update_zebra(int command, struct zclient *zclient, zebra_size_t length) { struct prefix router_id; - /* FIXME: actually use router_id for anything ? */ zebra_router_id_update_read(zclient->ibuf, &router_id); + zlog_info("zebra router id update"); + + /* Prevent interfaces' addresses duplication when zebra connection + is restored */ + reset_iface_addresses(); + return 0; } From d96ab32b6fce23907f0c5f35760a2b7410724d95 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Mon, 22 Sep 2014 17:59:13 -0300 Subject: [PATCH 383/482] pimd: Update configure recipe. --- pimd/quagga-configure.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pimd/quagga-configure.sh b/pimd/quagga-configure.sh index 45d7be08..f351cdc9 100755 --- a/pimd/quagga-configure.sh +++ b/pimd/quagga-configure.sh @@ -7,4 +7,4 @@ # # $QuaggaId: $Format:%an, %ai, %h$ $ -./configure --disable-bgpd --disable-ripd --disable-ripngd --disable-ospfd --disable-ospf6d --disable-watchquagga --disable-bgp-announce --disable-ospfapi --disable-ospfclient --disable-rtadv --disable-irdp --enable-pimd --enable-tcp-zebra --enable-ipv6 --enable-vtysh +./configure --disable-babeld --disable-bgpd --disable-ripd --disable-ripngd --disable-ospfd --disable-ospf6d --disable-watchquagga --disable-bgp-announce --disable-ospfapi --disable-ospfclient --disable-rtadv --disable-irdp --enable-pimd --enable-tcp-zebra --enable-ipv6 --enable-vtysh From b24c7fed49a861562bfb713db70ed1db2b89ff42 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Mon, 22 Sep 2014 18:00:15 -0300 Subject: [PATCH 384/482] zebra_rib: Revert debug hooks. --- zebra/zebra_rib.c | 95 +++++------------------------------------------ 1 file changed, 10 insertions(+), 85 deletions(-) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index d6ec9147..c6dbdec5 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -496,17 +496,6 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, break; } - if (IS_ZEBRA_DEBUG_RIB) { - char buf[INET6_ADDRSTRLEN]; - inet_ntop(rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); - zlog_debug("%s: %s/%d: nexthop match %p: %s type=%d sel=%d int=%d", - __func__, buf, rn->p.prefixlen, match, - match ? zebra_route_string(match->type) : "", - match ? match->type : -1, - match ? CHECK_FLAG(match->flags, ZEBRA_FLAG_SELECTED) : -1, - match ? CHECK_FLAG(match->flags, ZEBRA_FLAG_INTERNAL) : -1); - } - /* If there is no selected route or matched route is EGP, go up tree. */ if (! match @@ -537,6 +526,11 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, } else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL) || match->type == ZEBRA_ROUTE_KERNEL) + /* + || match->type == ZEBRA_ROUTE_KERNEL + This prevents zebra from marking recursive static route as inactive. + See pimd/TODO T26. + */ { resolved = 0; for (newhop = match->nexthop; newhop; newhop = newhop->next) @@ -1054,43 +1048,6 @@ rib_match_ipv6 (struct in6_addr *addr) } #endif /* HAVE_IPV6 */ -static void nexthop_dump(struct nexthop *nexthop, - char *type_str_buf, - int type_str_buf_size, - char *addr_str_buf, - int addr_str_buf_size, - char *via_str_buf, - int via_str_buf_size) -{ - switch (nexthop->type) { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - snprintf(type_str_buf, type_str_buf_size, "ipv4"); - snprintf(addr_str_buf, addr_str_buf_size, "%s", inet_ntoa(nexthop->gate.ipv4)); - snprintf(via_str_buf, via_str_buf_size, "%s", nexthop->ifindex ? ifindex2ifname(nexthop->ifindex) : ""); - break; - case NEXTHOP_TYPE_IFINDEX: - snprintf(type_str_buf, type_str_buf_size, "connected"); - snprintf(addr_str_buf, addr_str_buf_size, ""); - snprintf(via_str_buf, via_str_buf_size, "%s", ifindex2ifname(nexthop->ifindex)); - break; - case NEXTHOP_TYPE_IFNAME: - snprintf(type_str_buf, type_str_buf_size, "connected"); - snprintf(addr_str_buf, addr_str_buf_size, ""); - snprintf(via_str_buf, via_str_buf_size, "%s", nexthop->ifname); - break; - case NEXTHOP_TYPE_BLACKHOLE: - snprintf(type_str_buf, type_str_buf_size, "blackhole"); - snprintf(addr_str_buf, addr_str_buf_size, ""); - snprintf(via_str_buf, via_str_buf_size, "Null0"); - break; - default: - snprintf(type_str_buf, type_str_buf_size, "unknown"); - snprintf(addr_str_buf, addr_str_buf_size, ""); - snprintf(via_str_buf, via_str_buf_size, ""); - } -} - #define RIB_SYSTEM_ROUTE(R) \ ((R)->type == ZEBRA_ROUTE_KERNEL || (R)->type == ZEBRA_ROUTE_CONNECT) @@ -1145,27 +1102,10 @@ nexthop_active_check (struct route_node *rn, struct rib *rib, case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV4_IFINDEX: family = AFI_IP; - { - int nh_active = nexthop_active_ipv4 (rib, nexthop, set, rn); - if (IS_ZEBRA_DEBUG_RIB) { - char type_str_buf[100]; - char addr_str_buf[100]; - char via_str_buf[100]; - nexthop_dump(nexthop, - type_str_buf, sizeof(type_str_buf), - addr_str_buf, sizeof(addr_str_buf), - via_str_buf, sizeof(via_str_buf)); - zlog_debug("%s: rib %p nexthop %p type=%d %s %s via %s ifindex=%d nexthop_active_ipv4=%d", - __func__, rib, nexthop, - nexthop->type, type_str_buf, - addr_str_buf, via_str_buf, nexthop->ifindex, - nh_active); - } - if (nh_active) - SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); - else - UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); - } + if (nexthop_active_ipv4 (rib, nexthop, set, rn)) + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + else + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); break; #ifdef HAVE_IPV6 case NEXTHOP_TYPE_IPV6: @@ -1256,23 +1196,8 @@ nexthop_active_update (struct route_node *rn, struct rib *rib, int set) { prev_active = CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); prev_index = nexthop->ifindex; - new_active = nexthop_active_check (rn, rib, nexthop, set); - if (new_active) + if ((new_active = nexthop_active_check (rn, rib, nexthop, set))) rib->nexthop_active_num++; - if (IS_ZEBRA_DEBUG_RIB) { - char type_str_buf[100]; - char addr_str_buf[100]; - char via_str_buf[100]; - nexthop_dump(nexthop, - type_str_buf, sizeof(type_str_buf), - addr_str_buf, sizeof(addr_str_buf), - via_str_buf, sizeof(via_str_buf)); - zlog_debug("%s: rib %p nexthop %p type=%d %s %s via %s ifindex=%d act=%d total_act=%d", - __func__, rib, nexthop, - nexthop->type, type_str_buf, - addr_str_buf, via_str_buf, nexthop->ifindex, - new_active, rib->nexthop_active_num); - } if (prev_active != new_active || prev_index != nexthop->ifindex) SET_FLAG (rib->flags, ZEBRA_FLAG_CHANGED); From 2a0ecf21d6c0918da92033fbe4741ede63f108f2 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Mon, 22 Sep 2014 18:18:26 -0300 Subject: [PATCH 385/482] pimd: Withstand zclient connection restablishment. --- pimd/pim_zebra.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index 6bef43e9..e080c04f 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -51,16 +51,19 @@ static int del_oif(struct channel_oil *channel_oil, struct interface *oif, uint32_t proto_mask); -static void reset_iface_addresses() { +static void zclient_broken(struct zclient *zclient) +{ struct listnode *ifnode; struct interface *ifp; - zlog_warn("%s %s: resetting all interface addresses", + zlog_warn("%s %s: broken zclient connection", __FILE__, __PRETTY_FUNCTION__); for (ALL_LIST_ELEMENTS_RO(iflist, ifnode, ifp)) { - if_connected_reset(ifp); + pim_if_addr_del_all(ifp); } + + /* upon return, zclient will discard connected addresses */ } /* Router-id update message from zebra. */ @@ -71,12 +74,6 @@ static int pim_router_id_update_zebra(int command, struct zclient *zclient, zebra_router_id_update_read(zclient->ibuf, &router_id); - zlog_info("zebra router id update"); - - /* Prevent interfaces' addresses duplication when zebra connection - is restored */ - reset_iface_addresses(); - return 0; } @@ -330,7 +327,7 @@ static int pim_zebra_if_address_del(int command, struct zclient *client, #endif } - pim_if_addr_del(c); + pim_if_addr_del(c, 0); return 0; } @@ -659,6 +656,7 @@ void pim_zebra_init() /* Socket for receiving updates from Zebra daemon */ zclient = zclient_new(); + zclient->router_id_update = pim_router_id_update; zclient->router_id_update = pim_router_id_update_zebra; zclient->interface_add = pim_zebra_if_add; zclient->interface_delete = pim_zebra_if_del; From b240297cfea37f7a6608cac9fd2ae4848fe88e3f Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Mon, 22 Sep 2014 18:29:29 -0300 Subject: [PATCH 386/482] pimd: Detection of interface primary address changes. --- pimd/pim_iface.c | 14 +++++++++----- pimd/pim_iface.h | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index 8ceecf85..fdbb79be 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -282,6 +282,7 @@ static void on_primary_address_change(struct interface *ifp, } static void detect_primary_address_change(struct interface *ifp, + int force_prim_as_any, const char *caller) { struct pim_interface *pim_ifp; @@ -292,7 +293,10 @@ static void detect_primary_address_change(struct interface *ifp, if (!pim_ifp) return; - new_prim_addr = pim_find_primary_addr(ifp); + if (force_prim_as_any) + new_prim_addr = qpim_inaddr_any; + else + new_prim_addr = pim_find_primary_addr(ifp); changed = new_prim_addr.s_addr != pim_ifp->primary_address.s_addr; @@ -344,7 +348,7 @@ void pim_if_addr_add(struct connected *ifc) ifaddr = ifc->address->u.prefix4; - detect_primary_address_change(ifp, __PRETTY_FUNCTION__); + detect_primary_address_change(ifp, 0, __PRETTY_FUNCTION__); if (PIM_IF_TEST_IGMP(pim_ifp->options)) { struct igmp_sock *igmp; @@ -443,7 +447,7 @@ static void pim_if_addr_del_pim(struct connected *ifc) pim_sock_delete(ifc->ifp, "last address has been removed from interface"); } -void pim_if_addr_del(struct connected *ifc) +void pim_if_addr_del(struct connected *ifc, int force_prim_as_any) { struct interface *ifp; @@ -461,7 +465,7 @@ void pim_if_addr_del(struct connected *ifc) "secondary" : "primary"); } - detect_primary_address_change(ifp, __PRETTY_FUNCTION__); + detect_primary_address_change(ifp, force_prim_as_any, __PRETTY_FUNCTION__); pim_if_addr_del_igmp(ifc); pim_if_addr_del_pim(ifc); @@ -503,7 +507,7 @@ void pim_if_addr_del_all(struct interface *ifp) if (p->family != AF_INET) continue; - pim_if_addr_del(ifc); + pim_if_addr_del(ifc, 1 /* force_prim_as_any=true */); } } diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h index 6a2f7c95..4b06b9ff 100644 --- a/pimd/pim_iface.h +++ b/pimd/pim_iface.h @@ -113,7 +113,7 @@ void pim_if_init(void); struct pim_interface *pim_if_new(struct interface *ifp, int igmp, int pim); void pim_if_delete(struct interface *ifp); void pim_if_addr_add(struct connected *ifc); -void pim_if_addr_del(struct connected *ifc); +void pim_if_addr_del(struct connected *ifc, int force_prim_as_any); void pim_if_addr_add_all(struct interface *ifp); void pim_if_addr_del_all(struct interface *ifp); void pim_if_addr_del_all_igmp(struct interface *ifp); From d96f9ffaeac6ebb5b897c318b82fcb37c3ee81c2 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Tue, 22 Jul 2014 14:24:49 -0300 Subject: [PATCH 387/482] pimd: Recipe for building without vtysh. --- pimd/quagga-build-no-vtysh.sh | 10 ++++++++++ pimd/quagga-configure-no-vtysh.sh | 10 ++++++++++ pimd/quagga-configure.sh | 2 +- 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100755 pimd/quagga-build-no-vtysh.sh create mode 100755 pimd/quagga-configure-no-vtysh.sh diff --git a/pimd/quagga-build-no-vtysh.sh b/pimd/quagga-build-no-vtysh.sh new file mode 100755 index 00000000..22e084e7 --- /dev/null +++ b/pimd/quagga-build-no-vtysh.sh @@ -0,0 +1,10 @@ +#! /bin/bash +# +# Build minimum Quagga needed for pimd. +# +# Run from quagga's top dir as: +# ./pimd/quagga-build.sh +# +# $QuaggaId: $Format:%an, %ai, %h$ $ + +./pimd/quagga-memtypes.sh && ./pimd/quagga-bootstrap.sh && ./pimd/quagga-configure-no-vtysh.sh && make diff --git a/pimd/quagga-configure-no-vtysh.sh b/pimd/quagga-configure-no-vtysh.sh new file mode 100755 index 00000000..72caefb4 --- /dev/null +++ b/pimd/quagga-configure-no-vtysh.sh @@ -0,0 +1,10 @@ +#! /bin/bash +# +# Configure for minimum Quagga build needed for pimd. +# +# Run from quagga's top dir as: +# . pimd/quagga-configure.sh +# +# $QuaggaId: $Format:%an, %ai, %h$ $ + +./configure --disable-babeld --disable-bgpd --disable-ripd --disable-ripngd --disable-ospfd --disable-ospf6d --disable-watchquagga --disable-bgp-announce --disable-ospfapi --disable-ospfclient --disable-rtadv --disable-irdp --enable-pimd --enable-tcp-zebra --enable-ipv6 diff --git a/pimd/quagga-configure.sh b/pimd/quagga-configure.sh index f351cdc9..72329eb9 100755 --- a/pimd/quagga-configure.sh +++ b/pimd/quagga-configure.sh @@ -7,4 +7,4 @@ # # $QuaggaId: $Format:%an, %ai, %h$ $ -./configure --disable-babeld --disable-bgpd --disable-ripd --disable-ripngd --disable-ospfd --disable-ospf6d --disable-watchquagga --disable-bgp-announce --disable-ospfapi --disable-ospfclient --disable-rtadv --disable-irdp --enable-pimd --enable-tcp-zebra --enable-ipv6 --enable-vtysh +tail -1 ./pimd/quagga-configure-no-vtysh.sh --enable-vtysh From ee61109c045f3c9960ec52912ebdbb8998ac63a1 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Tue, 22 Jul 2014 14:27:54 -0300 Subject: [PATCH 388/482] pimd: Fix comment --- pimd/quagga-build-no-vtysh.sh | 2 +- pimd/quagga-configure-no-vtysh.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pimd/quagga-build-no-vtysh.sh b/pimd/quagga-build-no-vtysh.sh index 22e084e7..7136f670 100755 --- a/pimd/quagga-build-no-vtysh.sh +++ b/pimd/quagga-build-no-vtysh.sh @@ -3,7 +3,7 @@ # Build minimum Quagga needed for pimd. # # Run from quagga's top dir as: -# ./pimd/quagga-build.sh +# ./pimd/quagga-build-no-vtysh.sh # # $QuaggaId: $Format:%an, %ai, %h$ $ diff --git a/pimd/quagga-configure-no-vtysh.sh b/pimd/quagga-configure-no-vtysh.sh index 72caefb4..b3052dce 100755 --- a/pimd/quagga-configure-no-vtysh.sh +++ b/pimd/quagga-configure-no-vtysh.sh @@ -3,7 +3,7 @@ # Configure for minimum Quagga build needed for pimd. # # Run from quagga's top dir as: -# . pimd/quagga-configure.sh +# ./pimd/quagga-configure-no-vtysh.sh # # $QuaggaId: $Format:%an, %ai, %h$ $ From 3456a80f5f8e6e44c30453bd92eabf5faf7ab25b Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Tue, 22 Jul 2014 14:52:57 -0300 Subject: [PATCH 389/482] pimd: clear zclient-update: Reset zclient update connection to zebra daemon --- pimd/COMMANDS | 1 + pimd/pim_cmd.c | 13 +++++++++++++ pimd/pim_zebra.c | 37 ++++++++++++++++++------------------- pimd/pimd.c | 1 + pimd/pimd.h | 1 + 5 files changed, 34 insertions(+), 19 deletions(-) diff --git a/pimd/COMMANDS b/pimd/COMMANDS index 425ac822..2dedea06 100644 --- a/pimd/COMMANDS +++ b/pimd/COMMANDS @@ -60,6 +60,7 @@ debug commands: clear ip mroute Reset multicast routes clear ip pim interfaces Reset PIM interfaces clear ip pim oil Rescan PIM OIL (output interface list) + clear zclient-update Reset zclient update connection to zebra daemon debug igmp IGMP protocol activity debug mroute PIM interaction with kernel MFC cache debug pim PIM protocol activity diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 9363e3c9..a49264a3 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -27,6 +27,7 @@ #include "command.h" #include "if.h" #include "prefix.h" +#include "zclient.h" #include "pimd.h" #include "pim_cmd.h" @@ -1562,6 +1563,17 @@ DEFUN (pim_interface, return CMD_SUCCESS; } +DEFUN (clear_zclient_update, + clear_zclient_update_cmd, + "clear zclient-update", + CLEAR_STR + "Reset zclient update connection to zebra daemon\n") +{ + zclient_reset(qpim_zclient_update); + + return CMD_SUCCESS; +} + DEFUN (clear_ip_interfaces, clear_ip_interfaces_cmd, "clear ip interfaces", @@ -4312,6 +4324,7 @@ void pim_cmd_init() install_element (ENABLE_NODE, &clear_ip_mroute_cmd); install_element (ENABLE_NODE, &clear_ip_pim_interfaces_cmd); install_element (ENABLE_NODE, &clear_ip_pim_oil_cmd); + install_element (ENABLE_NODE, &clear_zclient_update_cmd); install_element (ENABLE_NODE, &show_ip_igmp_interface_cmd); install_element (ENABLE_NODE, &show_ip_igmp_join_cmd); diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index e080c04f..44046dbb 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -644,7 +644,6 @@ static int redist_read_ipv4_route(int command, struct zclient *zclient, void pim_zebra_init() { - struct zclient *zclient; int i; #ifdef HAVE_TCP_ZEBRA @@ -654,35 +653,35 @@ void pim_zebra_init() #endif /* Socket for receiving updates from Zebra daemon */ - zclient = zclient_new(); - - zclient->router_id_update = pim_router_id_update; - zclient->router_id_update = pim_router_id_update_zebra; - zclient->interface_add = pim_zebra_if_add; - zclient->interface_delete = pim_zebra_if_del; - zclient->interface_up = pim_zebra_if_state_up; - zclient->interface_down = pim_zebra_if_state_down; - zclient->interface_address_add = pim_zebra_if_address_add; - zclient->interface_address_delete = pim_zebra_if_address_del; - zclient->ipv4_route_add = redist_read_ipv4_route; - zclient->ipv4_route_delete = redist_read_ipv4_route; - - zclient_init(zclient, ZEBRA_ROUTE_PIM); + qpim_zclient_update = zclient_new(); + + qpim_zclient_update->zclient_broken = zclient_broken; + qpim_zclient_update->router_id_update = pim_router_id_update_zebra; + qpim_zclient_update->interface_add = pim_zebra_if_add; + qpim_zclient_update->interface_delete = pim_zebra_if_del; + qpim_zclient_update->interface_up = pim_zebra_if_state_up; + qpim_zclient_update->interface_down = pim_zebra_if_state_down; + qpim_zclient_update->interface_address_add = pim_zebra_if_address_add; + qpim_zclient_update->interface_address_delete = pim_zebra_if_address_del; + qpim_zclient_update->ipv4_route_add = redist_read_ipv4_route; + qpim_zclient_update->ipv4_route_delete = redist_read_ipv4_route; + + zclient_init(qpim_zclient_update, ZEBRA_ROUTE_PIM); zlog_info("zclient_init cleared redistribution request"); - zassert(zclient->redist_default == ZEBRA_ROUTE_PIM); + zassert(qpim_zclient_update->redist_default == ZEBRA_ROUTE_PIM); /* Request all redistribution */ for (i = 0; i < ZEBRA_ROUTE_MAX; i++) { - if (i == zclient->redist_default) + if (i == qpim_zclient_update->redist_default) continue; - zclient->redist[i] = 1; + qpim_zclient_update->redist[i] = 1; zlog_info("%s: requesting redistribution for %s (%i)", __PRETTY_FUNCTION__, zebra_route_string(i), i); } /* Request default information */ - zclient->default_information = 1; + qpim_zclient_update->default_information = 1; zlog_info("%s: requesting default information redistribution", __PRETTY_FUNCTION__); diff --git a/pimd/pimd.c b/pimd/pimd.c index 49ddc212..855defcc 100644 --- a/pimd/pimd.c +++ b/pimd/pimd.c @@ -51,6 +51,7 @@ int qpim_mroute_oif_highest_vif_index = -1; struct list *qpim_channel_oil_list = 0; int qpim_t_periodic = PIM_DEFAULT_T_PERIODIC; /* Period between Join/Prune Messages */ struct list *qpim_upstream_list = 0; +struct zclient *qpim_zclient_update = 0; struct zclient *qpim_zclient_lookup = 0; struct pim_assert_metric qpim_infinite_assert_metric; long qpim_rpf_cache_refresh_delay_msec = 10000; diff --git a/pimd/pimd.h b/pimd/pimd.h index 68b11994..b0a1b648 100644 --- a/pimd/pimd.h +++ b/pimd/pimd.h @@ -80,6 +80,7 @@ struct list *qpim_channel_oil_list; /* list of struct channel_oil * struct in_addr qpim_all_pim_routers_addr; int qpim_t_periodic; /* Period between Join/Prune Messages */ struct list *qpim_upstream_list; /* list of struct pim_upstream */ +struct zclient *qpim_zclient_update; struct zclient *qpim_zclient_lookup; struct pim_assert_metric qpim_infinite_assert_metric; long qpim_rpf_cache_refresh_delay_msec; From df838e2abd9727109cba559c6429ee4da82f863c Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Fri, 25 Jul 2014 15:38:00 -0300 Subject: [PATCH 390/482] pimd: Better assert state transition message. --- pimd/pim_assert.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pimd/pim_assert.c b/pimd/pim_assert.c index 6b062b77..b742223c 100644 --- a/pimd/pim_assert.c +++ b/pimd/pim_assert.c @@ -66,17 +66,19 @@ void pim_ifassert_winner_set(struct pim_ifchannel *ch, ch->interface->name); } - { + if (winner_changed) { char src_str[100]; char grp_str[100]; + char was_str[100]; char winner_str[100]; pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", ch->ifassert_winner, was_str, sizeof(was_str)); pim_inet4_dump("", winner, winner_str, sizeof(winner_str)); - zlog_info("%s: (S,G)=(%s,%s) assert winner now is %s on interface %s", + zlog_info("%s: (S,G)=(%s,%s) assert winner changed from %s to %s on interface %s", __PRETTY_FUNCTION__, src_str, grp_str, - winner_str, ch->interface->name); + was_str, winner_str, ch->interface->name); } ch->ifassert_state = new_state; From cf800dd0f9f2ae58efedc6af4e39ae6c0d99f873 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Tue, 19 Aug 2014 12:01:52 -0300 Subject: [PATCH 391/482] pimd: Version up. --- pimd/pim_version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pimd/pim_version.h b/pimd/pim_version.h index 73711367..68ab7693 100644 --- a/pimd/pim_version.h +++ b/pimd/pim_version.h @@ -23,7 +23,7 @@ #ifndef PIM_VERSION_H #define PIM_VERSION_H -#define PIMD_VERSION_STR "0.164" +#define PIMD_VERSION_STR "0.165" const char * const PIMD_VERSION; From 9830ce2ef715a79c691866b83526d1025e47082d Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Tue, 19 Aug 2014 12:16:11 -0300 Subject: [PATCH 392/482] pimd: Why ssm. --- pimd/WHY_SSM | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 pimd/WHY_SSM diff --git a/pimd/WHY_SSM b/pimd/WHY_SSM new file mode 100644 index 00000000..a8469719 --- /dev/null +++ b/pimd/WHY_SSM @@ -0,0 +1,24 @@ +WHY SSM + +Benefis of PIM SSM over PIM SM +------------------------------ + +- SSM consumes minimum link bandwidth +- SSM simplifies multicast address management (specially important for + inter-domain multicast) +- SSM does not suffer instabilities from traffic-driven SPT switchover +- SSM does not use RP. Some RP issues: + - RP is possible point of failure + - RP demands redundancy management + - RP may require PIM dense mode support for RP election + - RP is possible performance bottleneck + - RP may demand lots of extra management + +PIM-SSM drawbacks +----------------- + +- SSM requires IGMPv3 support on receivers +- SSM may be memory intensive when managing (S,G) states for + many-to-many multicast distribution + +--EOF-- From 7cb0d4a384b4964cc53b61549ffdef37fb0d76c5 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Tue, 19 Aug 2014 13:44:37 -0300 Subject: [PATCH 393/482] pimd: Cisco Documentation for SSM Benefits --- pimd/WHY_SSM | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/pimd/WHY_SSM b/pimd/WHY_SSM index a8469719..6ff8f134 100644 --- a/pimd/WHY_SSM +++ b/pimd/WHY_SSM @@ -6,19 +6,32 @@ Benefis of PIM SSM over PIM SM - SSM consumes minimum link bandwidth - SSM simplifies multicast address management (specially important for inter-domain multicast) + - SSM (S,G) channels easily provide unique per-application addressing + - SSM does not require MSDP between PIM domains - SSM does not suffer instabilities from traffic-driven SPT switchover +- SSM is not suscetible to DoS attack from unwanted sources - SSM does not use RP. Some RP issues: - RP is possible point of failure - RP demands redundancy management - RP may require PIM dense mode support for RP election - RP is possible performance bottleneck - RP may demand lots of extra management +- SSM can be deployed in an existing PIM SM network (only the last hop + routers need to support IGMPv3) +- SSM is easier to deploy and maintain PIM-SSM drawbacks ----------------- -- SSM requires IGMPv3 support on receivers +- SSM requires IGMPv3 support on both receivers and last-hop routers - SSM may be memory intensive when managing (S,G) states for many-to-many multicast distribution +- SSM will keep (S,G) state as long as there are subscriptions from + receivers, even if the source is not actually sending traffic + +Cisco Documentation for SSM Benefits +------------------------------------ + +http://www.cisco.com/c/en/us/td/docs/ios/12_2/ip/configuration/guide/fipr_c/1cfssm.html#wp1000969 --EOF-- From 1f298949bea9e58623eb81f245491dcdb1df8c59 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Thu, 21 Aug 2014 15:47:28 -0300 Subject: [PATCH 394/482] pimd: -z command-line switch to specify zebra socket path. --- doc/pimd.8 | 17 +++++++++++++++++ lib/zclient.c | 7 ++++++- lib/zclient.h | 1 + pimd/pim_main.c | 10 ++++++++-- pimd/pim_zebra.c | 7 +++++-- pimd/pim_zebra.h | 2 +- pimd/pim_zlookup.c | 23 +++++++++++++---------- 7 files changed, 51 insertions(+), 16 deletions(-) diff --git a/doc/pimd.8 b/doc/pimd.8 index 3995cfe4..0dd170a2 100644 --- a/doc/pimd.8 +++ b/doc/pimd.8 @@ -12,6 +12,9 @@ pimd \- a PIM routing for use with Quagga Routing Suite. .B \-i .I pid-file ] [ +.B \-z +.I path +] [ .B \-P .I port-number ] [ @@ -52,6 +55,10 @@ When pimd starts its process identifier is written to \fB\fIpid-file\fR. The init system uses the recorded PID to stop or restart pimd. The likely default is \fB\fI/var/run/pimd.pid\fR. .TP +\fB\-z\fR, \fB\-\-socket \fR\fIpath\fR +Specify the socket path for contacting the zebra daemon. +The likely default is \fB\fI/var/run/zserv.api\fR. +.TP \fB\-P\fR, \fB\-\-vty_port \fR\fIport-number\fR Specify the port that the pimd VTY will listen on. This defaults to 2611, as specified in \fB\fI/etc/services\fR. @@ -80,6 +87,16 @@ The default location of the .B pimd config file. .TP +.BI /var/run/pimd.pid +The default location of the +.B pimd +pid file. +.TP +.BI /var/run/zserv.api +The default location of the +.B zebra +unix socket file. +.TP .BI $(PWD)/pimd.log If the .B pimd diff --git a/lib/zclient.c b/lib/zclient.c index c3a4905f..93614360 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -229,7 +229,7 @@ zclient_socket_connect (struct zclient *zclient) #ifdef HAVE_TCP_ZEBRA zclient->sock = zclient_socket (); #else - zclient->sock = zclient_socket_un (zclient_serv_path ? zclient_serv_path : ZEBRA_SERV_PATH); + zclient->sock = zclient_socket_un (zclient_serv_path_get()); #endif return zclient->sock; } @@ -1053,6 +1053,11 @@ zclient_event (enum event event, struct zclient *zclient) } } +const char *const zclient_serv_path_get() +{ + return zclient_serv_path ? zclient_serv_path : ZEBRA_SERV_PATH; +} + void zclient_serv_path_set (char *path) { diff --git a/lib/zclient.h b/lib/zclient.h index 6a5e6268..c7d4d227 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -136,6 +136,7 @@ extern int zclient_socket(void); extern int zclient_socket_un (const char *path); extern int zclient_socket_connect (struct zclient *); extern void zclient_serv_path_set (char *path); +extern const char *const zclient_serv_path_get (void); /* Send redistribute command to zebra daemon. Do not update zclient state. */ extern int zebra_redistribute_send (int command, struct zclient *, int type); diff --git a/pimd/pim_main.c b/pimd/pim_main.c index 64d7787d..b314df27 100644 --- a/pimd/pim_main.c +++ b/pimd/pim_main.c @@ -98,6 +98,7 @@ Daemon which manages PIM.\n\n\ -d, --daemon Run in daemon mode\n\ -f, --config_file Set configuration file name\n\ -i, --pid_file Set process identifier file name\n\ +-z, --socket Set path of zebra socket\n\ -A, --vty_addr Set vty's bind address\n\ -P, --vty_port Set vty's port number\n\ -v, --version Print program version\n\ @@ -125,6 +126,7 @@ int main(int argc, char** argv, char** envp) { int vty_port = -1; int daemon_mode = 0; char *config_file = NULL; + char *zebra_sock_path = NULL; struct thread thread; umask(0027); @@ -138,7 +140,7 @@ int main(int argc, char** argv, char** envp) { while (1) { int opt; - opt = getopt_long (argc, argv, "df:i:A:P:vZh", longopts, 0); + opt = getopt_long (argc, argv, "df:i:z:A:P:vZh", longopts, 0); if (opt == EOF) break; @@ -155,6 +157,9 @@ int main(int argc, char** argv, char** envp) { case 'i': pid_file = optarg; break; + case 'z': + zebra_sock_path = optarg; + break; case 'A': vty_addr = optarg; break; @@ -298,10 +303,11 @@ Hello, this is " QUAGGA_PROGNAME " " QUAGGA_VERSION " " PIMD_PROGNAME " " PIMD_V zlog_notice("!HAVE_CLOCK_MONOTONIC"); #endif + /* Initialize zclient "update" and "lookup" sockets */ - pim_zebra_init(); + pim_zebra_init(zebra_sock_path); while (thread_fetch(master, &thread)) thread_call(&thread); diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index 44046dbb..321e3171 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -642,14 +642,17 @@ static int redist_read_ipv4_route(int command, struct zclient *zclient, return 0; } -void pim_zebra_init() +void pim_zebra_init(char *zebra_sock_path) { int i; + if (zebra_sock_path) + zclient_serv_path_set(zebra_sock_path); + #ifdef HAVE_TCP_ZEBRA zlog_notice("zclient update contacting ZEBRA daemon at socket TCP %s,%d", "127.0.0.1", ZEBRA_PORT); #else - zlog_notice("zclient update contacting ZEBRA daemon at socket UNIX %s", ZEBRA_SERV_PATH); + zlog_notice("zclient update contacting ZEBRA daemon at socket UNIX %s", zclient_serv_path_get()); #endif /* Socket for receiving updates from Zebra daemon */ diff --git a/pimd/pim_zebra.h b/pimd/pim_zebra.h index 474e7a2e..d624c866 100644 --- a/pimd/pim_zebra.h +++ b/pimd/pim_zebra.h @@ -26,7 +26,7 @@ #include "pim_igmp.h" #include "pim_ifchannel.h" -void pim_zebra_init(void); +void pim_zebra_init(char *zebra_sock_path); void pim_scan_oil(void); diff --git a/pimd/pim_zlookup.c b/pimd/pim_zlookup.c index be0499e3..ed47e673 100644 --- a/pimd/pim_zlookup.c +++ b/pimd/pim_zlookup.c @@ -66,16 +66,19 @@ static int zclient_lookup_connect(struct thread *t) __PRETTY_FUNCTION__, "127.0.0.1", ZEBRA_PORT); } #else - zlog_debug("%s: FIXME blocking connect: zclient_socket_un()", - __PRETTY_FUNCTION__); - zlookup->sock = zclient_socket_un(ZEBRA_SERV_PATH); - if (zlookup->sock < 0) { - zlog_warn("%s: failure connecting UNIX socket %s", - __PRETTY_FUNCTION__, ZEBRA_SERV_PATH); - } - else if (zclient_debug) { - zlog_notice("%s: connected UNIX socket %s", - __PRETTY_FUNCTION__, ZEBRA_SERV_PATH); + { + const char *const path = zclient_serv_path_get(); + zlog_debug("%s: FIXME blocking connect: zclient_socket_un()", + __PRETTY_FUNCTION__); + zlookup->sock = zclient_socket_un(path); + if (zlookup->sock < 0) { + zlog_warn("%s: failure connecting UNIX socket %s", + __PRETTY_FUNCTION__, path); + } + else if (zclient_debug) { + zlog_notice("%s: connected UNIX socket %s", + __PRETTY_FUNCTION__, path); + } } #endif /* HAVE_TCP_ZEBRA */ From 275e24d0ec67f79ae4c5977ea419e1659f9c40ac Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Fri, 22 Aug 2014 11:12:23 -0300 Subject: [PATCH 395/482] pimd: Fix interface "no ip igmp" should not disrupt PIM. Plus docs updates. --- pimd/DEBUG | 18 +++++++++++++++--- pimd/pim_cmd.c | 2 +- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/pimd/DEBUG b/pimd/DEBUG index 119c46f5..8d64475f 100644 --- a/pimd/DEBUG +++ b/pimd/DEBUG @@ -8,7 +8,7 @@ DEBUG HINTS - Check the multicast packets are not being dropped due to fragmentation problems. - - Two easy options to test IGMPv3 joins from the receiver host: + - Three easy options to test IGMPv3 joins from the receiver host: 1) Configure pimd on the receiver host with "ip igmp join": @@ -16,15 +16,27 @@ DEBUG HINTS ip pim ssm ip igmp join 239.1.1.1 1.1.1.1 - 2) Use the test_igmpv3_join command-line utility: + 2) Use test_igmpv3_join command-line utility (provided with qpimd): test_igmpv3_join eth0 239.1.1.1 1.1.1.1 + 3) User the Stig Venaas' ssmping utility: + + ssmping -I eth0 1.1.1.1 + + To see multicast responses with ssmping, you will need run on + the host 1.1.1.1 either: + a) Stig Venaas' ssmpingd command-line daemon + OR + b) qpimd built-in ssmpingd service: + conf t + ip ssmpingd 1.1.1.1 + - The following command generates a 100-kbps multicast stream for channel 1.1.1.1,239.1.1.1 with TTL 10 and 1000-byte payload per UDP packet (to avoid fragmentation): - nepim -b 1.1.1.1 -c 239.1.1.1 -T 10 -W 1000 -r 100k -a 1d + nepim -M -b 1.1.1.1 -c 239.1.1.1 -T 10 -W 1000 -r 100k -a 1d - Remotely you can receive that stream by running: diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index a49264a3..8b519778 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -2481,7 +2481,7 @@ DEFUN (interface_no_ip_igmp, pim_if_membership_clear(ifp); - pim_if_addr_del_all(ifp); + pim_if_addr_del_all_igmp(ifp); if (!PIM_IF_TEST_PIM(pim_ifp->options)) { pim_if_delete(ifp); From 3edadebed3f29383cc8d5825a9ef2fed5431235d Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Fri, 22 Aug 2014 14:29:31 -0300 Subject: [PATCH 396/482] pimd: Clarifications on debug hints. --- pimd/DEBUG | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/pimd/DEBUG b/pimd/DEBUG index 8d64475f..72fb8264 100644 --- a/pimd/DEBUG +++ b/pimd/DEBUG @@ -32,17 +32,30 @@ DEBUG HINTS conf t ip ssmpingd 1.1.1.1 - - The following command generates a 100-kbps multicast stream for - channel 1.1.1.1,239.1.1.1 with TTL 10 and 1000-byte payload per UDP - packet (to avoid fragmentation): + - Using nepim to generate multicast stream from 1.1.1.1 to 239.1.1.1: + + Notices: - nepim -M -b 1.1.1.1 -c 239.1.1.1 -T 10 -W 1000 -r 100k -a 1d + a) The host unicast address 1.1.1.1 must be reachable from the + receiver. - - Remotely you can receive that stream by running: + b) nepim tool requires the receiver must be started *before* the + sender. - nepim -j 1.1.1.1+239.1.1.1@eth0 + First: Start a receiver for that stream by running: + + nepim -q -6 -j 1.1.1.1+239.1.1.1@eth0 (Remember of enabling both "ip pim ssm" and "ip igmp" under eth0.) + Second: Start the sender at host 1.1.1.1. + + The following command generates a 100-kbps multicast stream for + channel 1.1.1.1,239.1.1.1 with TTL 10 and 1000-byte payload per UDP + packet (to avoid fragmentation): + + nepim -6 -M -b 1.1.1.1 -c 239.1.1.1 -T 10 -W 1000 -r 100k -a 1d + + SAMPLE DEBUG COMMANDS From bb61be2e5acd272d4e1467406bc13e5b56b8ef66 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Fri, 22 Aug 2014 15:40:02 -0300 Subject: [PATCH 397/482] pimd: Replace assert with warning. --- pimd/pim_igmpv3.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/pimd/pim_igmpv3.c b/pimd/pim_igmpv3.c index 3da1dd1e..24db40ba 100644 --- a/pimd/pim_igmpv3.c +++ b/pimd/pim_igmpv3.c @@ -408,8 +408,19 @@ void igmp_source_delete(struct igmp_source *source) source_timer_off(group, source); igmp_source_forward_stop(source); - /* make sure forwarding is disabled */ - zassert(!IGMP_SOURCE_TEST_FORWARDING(source->source_flags)); + /* sanity check that forwarding has been disabled */ + if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) { + char group_str[100]; + char source_str[100]; + pim_inet4_dump("", group->group_addr, group_str, sizeof(group_str)); + pim_inet4_dump("", source->source_addr, source_str, sizeof(source_str)); + zlog_warn("%s: forwarding=ON(!) IGMP source %s for group %s from socket %d interface %s", + __PRETTY_FUNCTION__, + source_str, group_str, + group->group_igmp_sock->fd, + group->group_igmp_sock->interface->name); + /* warning only */ + } source_channel_oil_detach(source); From c1b228c5cfd589b3fee5c0cbe1564f38df57f7f6 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Wed, 27 Aug 2014 15:27:26 -0300 Subject: [PATCH 398/482] pimd: Reduce informative mandatory logging. --- pimd/pim_assert.c | 56 +++++++++++++++++++++++--------------------- pimd/pim_ifchannel.c | 56 ++++++++++++++++++++++---------------------- pimd/pim_neighbor.c | 17 ++++++++------ pimd/pim_rpf.c | 54 +++++++++++++++++++++++------------------- pimd/pim_zebra.c | 20 ++++++++++------ 5 files changed, 110 insertions(+), 93 deletions(-) diff --git a/pimd/pim_assert.c b/pimd/pim_assert.c index b742223c..ad21e08e 100644 --- a/pimd/pim_assert.c +++ b/pimd/pim_assert.c @@ -53,33 +53,35 @@ void pim_ifassert_winner_set(struct pim_ifchannel *ch, int metric_changed = !pim_assert_metric_match(&ch->ifassert_winner_metric, &winner_metric); - if (ch->ifassert_state != new_state) { - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); - zlog_info("%s: (S,G)=(%s,%s) assert state changed from %s to %s on interface %s", - __PRETTY_FUNCTION__, - src_str, grp_str, - pim_ifchannel_ifassert_name(ch->ifassert_state), - pim_ifchannel_ifassert_name(new_state), - ch->interface->name); - } + if (PIM_DEBUG_PIM_EVENTS) { + if (ch->ifassert_state != new_state) { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + zlog_debug("%s: (S,G)=(%s,%s) assert state changed from %s to %s on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, + pim_ifchannel_ifassert_name(ch->ifassert_state), + pim_ifchannel_ifassert_name(new_state), + ch->interface->name); + } - if (winner_changed) { - char src_str[100]; - char grp_str[100]; - char was_str[100]; - char winner_str[100]; - pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); - pim_inet4_dump("", ch->ifassert_winner, was_str, sizeof(was_str)); - pim_inet4_dump("", winner, winner_str, sizeof(winner_str)); - zlog_info("%s: (S,G)=(%s,%s) assert winner changed from %s to %s on interface %s", - __PRETTY_FUNCTION__, - src_str, grp_str, - was_str, winner_str, ch->interface->name); - } + if (winner_changed) { + char src_str[100]; + char grp_str[100]; + char was_str[100]; + char winner_str[100]; + pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", ch->ifassert_winner, was_str, sizeof(was_str)); + pim_inet4_dump("", winner, winner_str, sizeof(winner_str)); + zlog_debug("%s: (S,G)=(%s,%s) assert winner changed from %s to %s on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, + was_str, winner_str, ch->interface->name); + } + } /* PIM_DEBUG_PIM_EVENTS */ ch->ifassert_state = new_state; ch->ifassert_winner = winner; @@ -655,7 +657,7 @@ int assert_action_a1(struct pim_ifchannel *ch) char grp_str[100]; pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); - zlog_warn("%s: (S,G)=(%s,%s) multicast no enabled on interface %s", + zlog_warn("%s: (S,G)=(%s,%s) multicast not enabled on interface %s", __PRETTY_FUNCTION__, src_str, grp_str, ifp->name); return -1; /* must return since pim_ifp is used below */ diff --git a/pimd/pim_ifchannel.c b/pimd/pim_ifchannel.c index 6ceef4ee..e253a0ea 100644 --- a/pimd/pim_ifchannel.c +++ b/pimd/pim_ifchannel.c @@ -286,16 +286,16 @@ static void ifmembership_set(struct pim_ifchannel *ch, if (ch->local_ifmembership == membership) return; - { + /* if (PIM_DEBUG_PIM_EVENTS) */ { char src_str[100]; char grp_str[100]; pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); - zlog_info("%s: (S,G)=(%s,%s) membership now is %s on interface %s", - __PRETTY_FUNCTION__, - src_str, grp_str, - membership == PIM_IFMEMBERSHIP_INCLUDE ? "INCLUDE" : "NOINFO", - ch->interface->name); + zlog_debug("%s: (S,G)=(%s,%s) membership now is %s on interface %s", + __PRETTY_FUNCTION__, + src_str, grp_str, + membership == PIM_IFMEMBERSHIP_INCLUDE ? "INCLUDE" : "NOINFO", + ch->interface->name); } ch->local_ifmembership = membership; @@ -787,15 +787,15 @@ void pim_ifchannel_update_could_assert(struct pim_ifchannel *ch) if (new_couldassert == old_couldassert) return; - { + if (PIM_DEBUG_PIM_EVENTS) { char src_str[100]; char grp_str[100]; pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); - zlog_info("%s: CouldAssert(%s,%s,%s) changed from %d to %d", - __PRETTY_FUNCTION__, - src_str, grp_str, ch->interface->name, - old_couldassert, new_couldassert); + zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d", + __PRETTY_FUNCTION__, + src_str, grp_str, ch->interface->name, + old_couldassert, new_couldassert); } if (new_couldassert) { @@ -829,7 +829,7 @@ void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel *ch) if (pim_assert_metric_match(&my_metric_new, &ch->ifassert_my_metric)) return; - { + if (PIM_DEBUG_PIM_EVENTS) { char src_str[100]; char grp_str[100]; char old_addr_str[100]; @@ -838,17 +838,17 @@ void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel *ch) pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); pim_inet4_dump("", ch->ifassert_my_metric.ip_address, old_addr_str, sizeof(old_addr_str)); pim_inet4_dump("", my_metric_new.ip_address, new_addr_str, sizeof(new_addr_str)); - zlog_info("%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s", - __PRETTY_FUNCTION__, - src_str, grp_str, ch->interface->name, - ch->ifassert_my_metric.rpt_bit_flag, - ch->ifassert_my_metric.metric_preference, - ch->ifassert_my_metric.route_metric, - old_addr_str, - my_metric_new.rpt_bit_flag, - my_metric_new.metric_preference, - my_metric_new.route_metric, - new_addr_str); + zlog_debug("%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s", + __PRETTY_FUNCTION__, + src_str, grp_str, ch->interface->name, + ch->ifassert_my_metric.rpt_bit_flag, + ch->ifassert_my_metric.metric_preference, + ch->ifassert_my_metric.route_metric, + old_addr_str, + my_metric_new.rpt_bit_flag, + my_metric_new.metric_preference, + my_metric_new.route_metric, + new_addr_str); } ch->ifassert_my_metric = my_metric_new; @@ -867,15 +867,15 @@ void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch) if (new_atd == old_atd) return; - { + if (PIM_DEBUG_PIM_EVENTS) { char src_str[100]; char grp_str[100]; pim_inet4_dump("", ch->source_addr, src_str, sizeof(src_str)); pim_inet4_dump("", ch->group_addr, grp_str, sizeof(grp_str)); - zlog_info("%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d", - __PRETTY_FUNCTION__, - src_str, grp_str, ch->interface->name, - old_atd, new_atd); + zlog_debug("%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d", + __PRETTY_FUNCTION__, + src_str, grp_str, ch->interface->name, + old_atd, new_atd); } if (new_atd) { diff --git a/pimd/pim_neighbor.c b/pimd/pim_neighbor.c index 66d895e9..9404cec1 100644 --- a/pimd/pim_neighbor.c +++ b/pimd/pim_neighbor.c @@ -124,13 +124,16 @@ void pim_if_dr_election(struct interface *ifp) /* DR changed ? */ if (old_dr_addr.s_addr != pim_ifp->pim_dr_addr.s_addr) { - char dr_old_str[100]; - char dr_new_str[100]; - pim_inet4_dump("", old_dr_addr, dr_old_str, sizeof(dr_old_str)); - pim_inet4_dump("", pim_ifp->pim_dr_addr, dr_new_str, sizeof(dr_new_str)); - zlog_info("%s: DR was %s now is %s on interface %s", - __PRETTY_FUNCTION__, - dr_old_str, dr_new_str, ifp->name); + + /* if (PIM_DEBUG_PIM_EVENTS) */ { + char dr_old_str[100]; + char dr_new_str[100]; + pim_inet4_dump("", old_dr_addr, dr_old_str, sizeof(dr_old_str)); + pim_inet4_dump("", pim_ifp->pim_dr_addr, dr_new_str, sizeof(dr_new_str)); + zlog_debug("%s: DR was %s now is %s on interface %s", + __PRETTY_FUNCTION__, + dr_old_str, dr_new_str, ifp->name); + } pim_ifp->pim_dr_election_last = pim_time_monotonic_sec(); /* timestamp */ ++pim_ifp->pim_dr_election_changes; diff --git a/pimd/pim_rpf.c b/pimd/pim_rpf.c index 7fdeecfd..dedc60a5 100644 --- a/pimd/pim_rpf.c +++ b/pimd/pim_rpf.c @@ -150,20 +150,23 @@ enum pim_rpf_result pim_rpf_update(struct pim_upstream *up, /* detect change in pim_nexthop */ if (nexthop_mismatch(&rpf->source_nexthop, &save_nexthop)) { - char src_str[100]; - char grp_str[100]; - char nhaddr_str[100]; - pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); - pim_inet4_dump("", rpf->source_nexthop.mrib_nexthop_addr, nhaddr_str, sizeof(nhaddr_str)); - zlog_warn("%s %s: (S,G)=(%s,%s) source nexthop now is: interface=%s address=%s pref=%d metric=%d", - __FILE__, __PRETTY_FUNCTION__, - src_str, grp_str, - rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "", - nhaddr_str, - rpf->source_nexthop.mrib_metric_preference, - rpf->source_nexthop.mrib_route_metric); - /* warning only */ + + /* if (PIM_DEBUG_PIM_EVENTS) */ { + char src_str[100]; + char grp_str[100]; + char nhaddr_str[100]; + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + pim_inet4_dump("", rpf->source_nexthop.mrib_nexthop_addr, nhaddr_str, sizeof(nhaddr_str)); + zlog_debug("%s %s: (S,G)=(%s,%s) source nexthop now is: interface=%s address=%s pref=%d metric=%d", + __FILE__, __PRETTY_FUNCTION__, + src_str, grp_str, + rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "", + nhaddr_str, + rpf->source_nexthop.mrib_metric_preference, + rpf->source_nexthop.mrib_route_metric); + /* warning only */ + } pim_upstream_update_join_desired(up); pim_upstream_update_could_assert(up); @@ -172,16 +175,19 @@ enum pim_rpf_result pim_rpf_update(struct pim_upstream *up, /* detect change in RPF_interface(S) */ if (save_nexthop.interface != rpf->source_nexthop.interface) { - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); - zlog_warn("%s %s: (S,G)=(%s,%s) RPF_interface(S) changed from %s to %s", - __FILE__, __PRETTY_FUNCTION__, - src_str, grp_str, - save_nexthop.interface ? save_nexthop.interface->name : "", - rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : ""); - /* warning only */ + + /* if (PIM_DEBUG_PIM_EVENTS) */ { + char src_str[100]; + char grp_str[100]; + pim_inet4_dump("", up->source_addr, src_str, sizeof(src_str)); + pim_inet4_dump("", up->group_addr, grp_str, sizeof(grp_str)); + zlog_debug("%s %s: (S,G)=(%s,%s) RPF_interface(S) changed from %s to %s", + __FILE__, __PRETTY_FUNCTION__, + src_str, grp_str, + save_nexthop.interface ? save_nexthop.interface->name : "", + rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : ""); + /* warning only */ + } pim_upstream_rpf_interface_changed(up, save_nexthop.interface); } diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index 321e3171..4cef422e 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -670,7 +670,9 @@ void pim_zebra_init(char *zebra_sock_path) qpim_zclient_update->ipv4_route_delete = redist_read_ipv4_route; zclient_init(qpim_zclient_update, ZEBRA_ROUTE_PIM); - zlog_info("zclient_init cleared redistribution request"); + if (PIM_DEBUG_PIM_TRACE) { + zlog_info("zclient_init cleared redistribution request"); + } zassert(qpim_zclient_update->redist_default == ZEBRA_ROUTE_PIM); @@ -679,18 +681,22 @@ void pim_zebra_init(char *zebra_sock_path) if (i == qpim_zclient_update->redist_default) continue; qpim_zclient_update->redist[i] = 1; - zlog_info("%s: requesting redistribution for %s (%i)", - __PRETTY_FUNCTION__, zebra_route_string(i), i); + if (PIM_DEBUG_PIM_TRACE) { + zlog_debug("%s: requesting redistribution for %s (%i)", + __PRETTY_FUNCTION__, zebra_route_string(i), i); + } } /* Request default information */ qpim_zclient_update->default_information = 1; - zlog_info("%s: requesting default information redistribution", - __PRETTY_FUNCTION__); - - zlog_notice("%s: zclient update socket initialized", + if (PIM_DEBUG_PIM_TRACE) { + zlog_info("%s: requesting default information redistribution", __PRETTY_FUNCTION__); + zlog_notice("%s: zclient update socket initialized", + __PRETTY_FUNCTION__); + } + zassert(!qpim_zclient_lookup); qpim_zclient_lookup = zclient_lookup_new(); zassert(qpim_zclient_lookup); From 8852dba7737d85f9ff37c38358a4c92006c9a92e Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Thu, 28 Aug 2014 16:02:11 -0300 Subject: [PATCH 399/482] pimd: React as secondary address change for any address change --- pimd/CAVEATS | 1 + pimd/TODO | 1 + pimd/pim_iface.c | 72 +++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 64 insertions(+), 10 deletions(-) diff --git a/pimd/CAVEATS b/pimd/CAVEATS index 7e2820b3..9f07bda6 100644 --- a/pimd/CAVEATS +++ b/pimd/CAVEATS @@ -115,6 +115,7 @@ C14 FIXED Detection of interface primary address changes may fail when C15 Changes in interface secondary address list are not immediately detected. + See also detect_secondary_address_change See also TODO T31. C16 AMT Draft (mboned-auto-multicast) is not supported. diff --git a/pimd/TODO b/pimd/TODO index 80835e46..2308573b 100644 --- a/pimd/TODO +++ b/pimd/TODO @@ -264,6 +264,7 @@ T30 DONE Run interface DR election when primary address changes T31 If an interface changes one of its secondary IP addresses, a Hello message with an updated Address_List option and a non-zero HoldTime should be sent immediately. + See also detect_secondary_address_change See also CAVEAT C15. See also RFC 4601: 4.3.1. Sending Hello Messages diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index fdbb79be..770bd4e0 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -273,17 +273,20 @@ static void on_primary_address_change(struct interface *ifp, } pim_ifp = ifp->info; + if (!pim_ifp) { + return; + } - if (pim_ifp) { - if (PIM_IF_TEST_PIM(pim_ifp->options)) { - pim_addr_change(ifp); - } + if (!PIM_IF_TEST_PIM(pim_ifp->options)) { + return; } + + pim_addr_change(ifp); } -static void detect_primary_address_change(struct interface *ifp, - int force_prim_as_any, - const char *caller) +static int detect_primary_address_change(struct interface *ifp, + int force_prim_as_any, + const char *caller) { struct pim_interface *pim_ifp; struct in_addr new_prim_addr; @@ -291,7 +294,7 @@ static void detect_primary_address_change(struct interface *ifp, pim_ifp = ifp->info; if (!pim_ifp) - return; + return 0; if (force_prim_as_any) new_prim_addr = qpim_inaddr_any; @@ -317,6 +320,55 @@ static void detect_primary_address_change(struct interface *ifp, on_primary_address_change(ifp, caller, old_addr, new_prim_addr); } + + return changed; +} + +static void detect_secondary_address_change(struct interface *ifp, + const char *caller) +{ + struct pim_interface *pim_ifp; + int changed; + + pim_ifp = ifp->info; + if (!pim_ifp) + return; + + changed = 1; /* true */ + zlog_debug("FIXME T31 C15 %s: on interface %s: acting on any addr change", + __PRETTY_FUNCTION__, ifp->name); + + if (PIM_DEBUG_ZEBRA) { + zlog_debug("%s: on interface %s: %s", + __PRETTY_FUNCTION__, + ifp->name, changed ? "changed" : "unchanged"); + } + + if (!changed) { + return; + } + + if (!PIM_IF_TEST_PIM(pim_ifp->options)) { + return; + } + + pim_addr_change(ifp); +} + +static void detect_address_change(struct interface *ifp, + int force_prim_as_any, + const char *caller) +{ + int prim_changed; + + prim_changed = detect_primary_address_change(ifp, force_prim_as_any, caller); + if (prim_changed) { + /* no need to detect secondary change because + the reaction would be the same */ + return; + } + + detect_secondary_address_change(ifp, caller); } void pim_if_addr_add(struct connected *ifc) @@ -348,7 +400,7 @@ void pim_if_addr_add(struct connected *ifc) ifaddr = ifc->address->u.prefix4; - detect_primary_address_change(ifp, 0, __PRETTY_FUNCTION__); + detect_address_change(ifp, 0, __PRETTY_FUNCTION__); if (PIM_IF_TEST_IGMP(pim_ifp->options)) { struct igmp_sock *igmp; @@ -465,7 +517,7 @@ void pim_if_addr_del(struct connected *ifc, int force_prim_as_any) "secondary" : "primary"); } - detect_primary_address_change(ifp, force_prim_as_any, __PRETTY_FUNCTION__); + detect_address_change(ifp, force_prim_as_any, __PRETTY_FUNCTION__); pim_if_addr_del_igmp(ifc); pim_if_addr_del_pim(ifc); From f80f8aa34b88c4e97654bca62b43605a3d029d92 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Fri, 29 Aug 2014 14:55:30 -0300 Subject: [PATCH 400/482] pimd: Troubleshooting script. --- pimd/TROUBLESHOOTING | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 pimd/TROUBLESHOOTING diff --git a/pimd/TROUBLESHOOTING b/pimd/TROUBLESHOOTING new file mode 100644 index 00000000..7d1f52de --- /dev/null +++ b/pimd/TROUBLESHOOTING @@ -0,0 +1,33 @@ +TROUBLESHOOTING + +# Check kernel mcast cache +# On Linux: +ip mroute show + +! qpimd on last-hop router +! . attached to mcast receiver +! . runnning both PIM-SSM and IGMPv3 +! +show ip mroute (kernel mcast programming is correct?) +show ip pim upstream (we joined our upstream?) +show ip pim neighbor (upstream is neighbor?) +show ip pim interface (pim enabled on interfaces?) +show ip multicast (multicast enabled at all?) +show ip rib SRC (unicast route towards source?) + +show ip igmp sources (receiver joined on interface?) +show ip igmp interface (igmp enabled on receiver interface?) + +! qpimd on intermmediate routers +! . may be attached to mcast source +! . runnning only PIM-SSM, not IGMPv3 +! +show ip mroute (kernel mcast programming is correct?) +show ip pim upstream (we joined our upstream?) +show ip pim join (downstream joined us?) +show ip pim neighbor (downstream is neighbor?) +show ip pim interface (pim enabled on interfaces?) +show ip multicast (multicast enabled at all?) +show ip rib SRC (unicast route towards source?) + +--EOF-- From 629e30bb436ce2231c47a38e1a5c5ac1c72beefa Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Fri, 29 Aug 2014 16:10:08 -0300 Subject: [PATCH 401/482] pimd: Version up. --- pimd/pim_version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pimd/pim_version.h b/pimd/pim_version.h index 68ab7693..ef9f370c 100644 --- a/pimd/pim_version.h +++ b/pimd/pim_version.h @@ -23,7 +23,7 @@ #ifndef PIM_VERSION_H #define PIM_VERSION_H -#define PIMD_VERSION_STR "0.165" +#define PIMD_VERSION_STR "0.166" const char * const PIMD_VERSION; From 93911267a3105931fbaee62dabf7cc444466a6c2 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Thu, 18 Sep 2014 11:10:58 -0300 Subject: [PATCH 402/482] pimd: Remove unused pim checksum in favour of quagga's version. --- pimd/Makefile.am | 2 -- pimd/pim_cmd.c | 2 +- pimd/pim_igmp.c | 4 ++-- pimd/pim_igmpv3.c | 2 +- pimd/pim_main.c | 4 ---- pimd/pim_msg.c | 2 +- pimd/pim_pim.c | 2 +- pimd/pim_util.c | 37 ------------------------------------- pimd/pim_util.h | 6 ------ 9 files changed, 6 insertions(+), 55 deletions(-) diff --git a/pimd/Makefile.am b/pimd/Makefile.am index 70c5096c..9345460d 100644 --- a/pimd/Makefile.am +++ b/pimd/Makefile.am @@ -22,7 +22,6 @@ # PIM_DEBUG_BYDEFAULT: Automatically enables all pimd "debug ..." commands # PIM_ZCLIENT_DEBUG: Support for internal ZEBRA client debugging # PIM_MOTD_VERSION: Includes pimd version in default MOTD -# PIM_USE_QUAGGA_INET_CHECKSUM: Prefer Quagga inet checksum # PIM_CHECK_RECV_IFINDEX_SANITY: Compare socket ifindex with recv ifindex # PIM_REPORT_RECV_IFINDEX_MISMATCH: Report sock/recv ifindex mismatch # PIM_ENFORCE_LOOPFREE_MFC: Refuse adding looping MFC entries @@ -36,7 +35,6 @@ PIM_DEFS += -DPIM_CHECK_RECV_IFINDEX_SANITY #PIM_DEFS += -DPIM_REPORT_RECV_IFINDEX_MISMATCH PIM_DEFS += -DPIM_ZCLIENT_DEBUG PIM_DEFS += -DPIM_MOTD_VERSION -PIM_DEFS += -DPIM_USE_QUAGGA_INET_CHECKSUM PIM_DEFS += -DPIM_ENFORCE_LOOPFREE_MFC #PIM_DEFS += -DPIM_UNEXPECTED_KERNEL_UPCALL #PIM_DEFS += -DPIM_USE_QUAGGA_GETTIME diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 8b519778..eaf209ce 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -3590,7 +3590,7 @@ DEFUN (test_igmp_receive_report, igmp_msg_len = IGMP_V3_MSG_MIN_SIZE + (num_sources << 4); /* v3 report for one single group record */ /* compute checksum */ - *(uint16_t *)(igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = pim_inet_checksum(igmp_msg, igmp_msg_len); + *(uint16_t *)(igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = in_cksum(igmp_msg, igmp_msg_len); /* "receive" message */ diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c index 03cf3cd1..a54463fa 100644 --- a/pimd/pim_igmp.c +++ b/pimd/pim_igmp.c @@ -299,7 +299,7 @@ static int recv_igmp_query(struct igmp_sock *igmp, int query_version, /* for computing checksum */ *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = 0; - checksum = pim_inet_checksum(igmp_msg, igmp_msg_len); + checksum = in_cksum(igmp_msg, igmp_msg_len); if (checksum != recv_checksum) { zlog_warn("Recv IGMP query v%d from %s on %s: checksum mismatch: received=%x computed=%x", query_version, from_str, ifp->name, recv_checksum, checksum); @@ -470,7 +470,7 @@ static int igmp_v3_report(struct igmp_sock *igmp, /* for computing checksum */ *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = 0; - checksum = pim_inet_checksum(igmp_msg, igmp_msg_len); + checksum = in_cksum(igmp_msg, igmp_msg_len); if (checksum != recv_checksum) { zlog_warn("Recv IGMP report v3 from %s on %s: checksum mismatch: received=%x computed=%x", from_str, ifp->name, recv_checksum, checksum); diff --git a/pimd/pim_igmpv3.c b/pimd/pim_igmpv3.c index 24db40ba..b95adb6f 100644 --- a/pimd/pim_igmpv3.c +++ b/pimd/pim_igmpv3.c @@ -1651,7 +1651,7 @@ void pim_igmp_send_membership_query(struct igmp_group *group, query_buf[9] = qqic; *(uint16_t *)(query_buf + IGMP_V3_NUMSOURCES_OFFSET) = htons(num_sources); - checksum = pim_inet_checksum(query_buf, msg_size); + checksum = in_cksum(query_buf, msg_size); *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = checksum; if (PIM_DEBUG_IGMP_PACKETS) { diff --git a/pimd/pim_main.c b/pimd/pim_main.c index b314df27..768ac08c 100644 --- a/pimd/pim_main.c +++ b/pimd/pim_main.c @@ -281,10 +281,6 @@ Hello, this is " QUAGGA_PROGNAME " " QUAGGA_VERSION " " PIMD_PROGNAME " " PIMD_V #endif #endif -#ifdef PIM_USE_QUAGGA_INET_CHECKSUM - zlog_notice("PIM_USE_QUAGGA_INET_CHECKSUM: using Quagga's builtin checksum"); -#endif - #ifdef PIM_UNEXPECTED_KERNEL_UPCALL zlog_notice("PIM_UNEXPECTED_KERNEL_UPCALL: report unexpected kernel upcall"); #endif diff --git a/pimd/pim_msg.c b/pimd/pim_msg.c index 79ae33ac..8ead7ce6 100644 --- a/pimd/pim_msg.c +++ b/pimd/pim_msg.c @@ -46,7 +46,7 @@ void pim_msg_build_header(uint8_t *pim_msg, int pim_msg_size, */ *(uint16_t *) PIM_MSG_HDR_OFFSET_CHECKSUM(pim_msg) = 0; - checksum = pim_inet_checksum(pim_msg, pim_msg_size); + checksum = in_cksum(pim_msg, pim_msg_size); *(uint16_t *) PIM_MSG_HDR_OFFSET_CHECKSUM(pim_msg) = checksum; } diff --git a/pimd/pim_pim.c b/pimd/pim_pim.c index 4fda26ec..fb6c3acb 100644 --- a/pimd/pim_pim.c +++ b/pimd/pim_pim.c @@ -194,7 +194,7 @@ int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len) /* for computing checksum */ *(uint16_t *) PIM_MSG_HDR_OFFSET_CHECKSUM(pim_msg) = 0; - checksum = pim_inet_checksum(pim_msg, pim_msg_len); + checksum = in_cksum(pim_msg, pim_msg_len); if (checksum != pim_checksum) { zlog_warn("Ignoring PIM pkt from %s with invalid checksum: received=%x calculated=%x", ifp->name, pim_checksum, checksum); diff --git a/pimd/pim_util.c b/pimd/pim_util.c index a7e8234e..fdfed2bf 100644 --- a/pimd/pim_util.c +++ b/pimd/pim_util.c @@ -98,43 +98,6 @@ uint16_t igmp_msg_decode8to16(uint8_t code) return value; } -#ifndef PIM_USE_QUAGGA_INET_CHECKSUM -/* - RFC 3376: 4.1.2. Checksum - - The Checksum is the 16-bit one's complement of the one's complement - sum of the whole IGMP message (the entire IP payload). For - computing the checksum, the Checksum field is set to zero. When - receiving packets, the checksum MUST be verified before processing a - packet. [RFC-1071] -*/ -uint16_t pim_inet_checksum(const char *buf, int size) -{ - const uint16_t *ptr; - uint32_t sum; - uint16_t checksum; - - ptr = (const uint16_t *) buf; - sum = 0; - while (size > 1) { - sum += *ptr; - ++ptr; - size -= 2; - } - - /* Add left-over byte, if any */ - if (size > 0) - sum += (uint16_t) *(const uint8_t *) ptr; - - /* Fold 32-bit sum to 16 bits */ - sum = (sum & 0xffff) + (sum >> 16); - - checksum = ~sum; - - return checksum; -} -#endif /* PIM_USE_QUAGGA_INET_CHECKSUM */ - void pim_pkt_dump(const char *label, const uint8_t *buf, int size) { char dump_buf[1000]; diff --git a/pimd/pim_util.h b/pimd/pim_util.h index 6f5bf223..a8613e2b 100644 --- a/pimd/pim_util.h +++ b/pimd/pim_util.h @@ -32,12 +32,6 @@ uint8_t igmp_msg_encode16to8(uint16_t value); uint16_t igmp_msg_decode8to16(uint8_t code); -#ifdef PIM_USE_QUAGGA_INET_CHECKSUM -#define pim_inet_checksum(buf,size) in_cksum(buf,size) -#else -uint16_t pim_inet_checksum(const char *buf, int size); -#endif /* PIM_USE_QUAGGA_INET_CHECKSUM */ - void pim_pkt_dump(const char *label, const uint8_t *buf, int size); #endif /* PIM_UTIL_H */ From 3d62667ab0e8e7ee6e17e883b144e25ee84c4545 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Thu, 18 Sep 2014 11:24:36 -0300 Subject: [PATCH 403/482] pimd: Remove reference to external doc. --- pimd/WHY_SSM | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pimd/WHY_SSM b/pimd/WHY_SSM index 6ff8f134..2e8c966f 100644 --- a/pimd/WHY_SSM +++ b/pimd/WHY_SSM @@ -29,9 +29,4 @@ PIM-SSM drawbacks - SSM will keep (S,G) state as long as there are subscriptions from receivers, even if the source is not actually sending traffic -Cisco Documentation for SSM Benefits ------------------------------------- - -http://www.cisco.com/c/en/us/td/docs/ios/12_2/ip/configuration/guide/fipr_c/1cfssm.html#wp1000969 - --EOF-- From 74b4fad93e89df358441b1b3b23282aaca8c80b8 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Thu, 18 Sep 2014 12:06:53 -0300 Subject: [PATCH 404/482] pimd: Remove motd tweaking. --- lib/command.c | 2 +- pimd/Makefile.am | 2 -- pimd/pim_main.c | 13 ------------- 3 files changed, 1 insertion(+), 16 deletions(-) diff --git a/lib/command.c b/lib/command.c index a789b0f4..8870a421 100644 --- a/lib/command.c +++ b/lib/command.c @@ -105,7 +105,7 @@ static struct cmd_node config_node = }; /* Default motd string. */ -const char *default_motd = +static const char *default_motd = "\r\n\ Hello, this is " QUAGGA_PROGNAME " (version " QUAGGA_VERSION ").\r\n\ " QUAGGA_COPYRIGHT "\r\n\ diff --git a/pimd/Makefile.am b/pimd/Makefile.am index 9345460d..3b8a0e17 100644 --- a/pimd/Makefile.am +++ b/pimd/Makefile.am @@ -21,7 +21,6 @@ # PIM_DEBUG_BYDEFAULT: Automatically enables all pimd "debug ..." commands # PIM_ZCLIENT_DEBUG: Support for internal ZEBRA client debugging -# PIM_MOTD_VERSION: Includes pimd version in default MOTD # PIM_CHECK_RECV_IFINDEX_SANITY: Compare socket ifindex with recv ifindex # PIM_REPORT_RECV_IFINDEX_MISMATCH: Report sock/recv ifindex mismatch # PIM_ENFORCE_LOOPFREE_MFC: Refuse adding looping MFC entries @@ -34,7 +33,6 @@ PIM_DEFS = PIM_DEFS += -DPIM_CHECK_RECV_IFINDEX_SANITY #PIM_DEFS += -DPIM_REPORT_RECV_IFINDEX_MISMATCH PIM_DEFS += -DPIM_ZCLIENT_DEBUG -PIM_DEFS += -DPIM_MOTD_VERSION PIM_DEFS += -DPIM_ENFORCE_LOOPFREE_MFC #PIM_DEFS += -DPIM_UNEXPECTED_KERNEL_UPCALL #PIM_DEFS += -DPIM_USE_QUAGGA_GETTIME diff --git a/pimd/pim_main.c b/pimd/pim_main.c index 768ac08c..68cfe218 100644 --- a/pimd/pim_main.c +++ b/pimd/pim_main.c @@ -46,7 +46,6 @@ extern int zclient_debug; #endif extern struct host host; -extern const char *default_motd; char config_default[] = SYSCONFDIR PIMD_DEFAULT_CONFIG; @@ -246,18 +245,6 @@ int main(int argc, char** argv, char** envp) { zlog_notice("Quagga %s " PIMD_PROGNAME " %s starting, VTY interface at port TCP %d", QUAGGA_VERSION, PIMD_VERSION, vty_port); -#ifdef PIM_MOTD_VERSION - /* Tweak default MOTD to include pimd version */ - zlog_notice("PIM_MOTD_VERSION: adding pimd version to default MOTD"); - if (host.motd == default_motd) { - host.motd = - "\r\n\ -Hello, this is " QUAGGA_PROGNAME " " QUAGGA_VERSION " " PIMD_PROGNAME " " PIMD_VERSION_STR "\r\n\ -" QUAGGA_COPYRIGHT "\r\n\ -\r\n"; - } -#endif - #ifdef PIM_DEBUG_BYDEFAULT zlog_notice("PIM_DEBUG_BYDEFAULT: Enabling all debug commands"); PIM_DO_DEBUG_PIM_EVENTS; From a089db4a0678cc1bbbf003bbda2561c03760badc Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Thu, 18 Sep 2014 12:08:05 -0300 Subject: [PATCH 405/482] pimd: Fix log about PIM_USE_QUAGGA_GETTIME. --- pimd/pim_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pimd/pim_main.c b/pimd/pim_main.c index 68cfe218..0ae4ae9c 100644 --- a/pimd/pim_main.c +++ b/pimd/pim_main.c @@ -272,7 +272,7 @@ int main(int argc, char** argv, char** envp) { zlog_notice("PIM_UNEXPECTED_KERNEL_UPCALL: report unexpected kernel upcall"); #endif -#ifdef PIM_FORCE_QUAGGA_REALTIME_STABILISED +#ifdef PIM_USE_QUAGGA_GETTIME zlog_notice("PIM_USE_QUAGGA_GETTIME: using Quagga's quagga_gettime"()); #endif From 4d330a2719fd684739a16c6aa3be6632bc3745a2 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Thu, 18 Sep 2014 12:15:55 -0300 Subject: [PATCH 406/482] pimd: Remove conflict marker. --- vtysh/vtysh.c | 1 - 1 file changed, 1 deletion(-) diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index a5e29dc9..984f8d39 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -60,7 +60,6 @@ struct vtysh_client { .fd = -1, .name = "isisd", .flag = VTYSH_ISISD, .path = ISIS_VTYSH_PATH}, { .fd = -1, .name = "babeld", .flag = VTYSH_BABELD, .path = BABEL_VTYSH_PATH}, { .fd = -1, .name = "pimd", .flag = VTYSH_PIMD, .path = PIM_VTYSH_PATH}, ->>>>>>> [pim] pim commands added to vtysh }; From 679fab42343381f609527166f48dbf9ba19f3aab Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Thu, 18 Sep 2014 14:54:07 -0300 Subject: [PATCH 407/482] pimd: Simplify gettime-related code. --- pimd/Makefile.am | 4 ---- pimd/pim_main.c | 8 -------- pimd/pim_time.c | 47 ----------------------------------------------- 3 files changed, 59 deletions(-) diff --git a/pimd/Makefile.am b/pimd/Makefile.am index 3b8a0e17..7173a231 100644 --- a/pimd/Makefile.am +++ b/pimd/Makefile.am @@ -25,8 +25,6 @@ # PIM_REPORT_RECV_IFINDEX_MISMATCH: Report sock/recv ifindex mismatch # PIM_ENFORCE_LOOPFREE_MFC: Refuse adding looping MFC entries # PIM_UNEXPECTED_KERNEL_UPCALL: Report unexpected kernel upcall -# PIM_USE_QUAGGA_GETTIME: Prefer quagga_gettime -# PIM_GETTIME_USE_GETTIMEOFDAY: Work-around improper monotonic clock PIM_DEFS = #PIM_DEFS += -DPIM_DEBUG_BYDEFAULT @@ -35,8 +33,6 @@ PIM_DEFS += -DPIM_CHECK_RECV_IFINDEX_SANITY PIM_DEFS += -DPIM_ZCLIENT_DEBUG PIM_DEFS += -DPIM_ENFORCE_LOOPFREE_MFC #PIM_DEFS += -DPIM_UNEXPECTED_KERNEL_UPCALL -#PIM_DEFS += -DPIM_USE_QUAGGA_GETTIME -PIM_DEFS += -DPIM_GETTIME_USE_GETTIMEOFDAY INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" $(PIM_DEFS) diff --git a/pimd/pim_main.c b/pimd/pim_main.c index 0ae4ae9c..c646356a 100644 --- a/pimd/pim_main.c +++ b/pimd/pim_main.c @@ -272,14 +272,6 @@ int main(int argc, char** argv, char** envp) { zlog_notice("PIM_UNEXPECTED_KERNEL_UPCALL: report unexpected kernel upcall"); #endif -#ifdef PIM_USE_QUAGGA_GETTIME - zlog_notice("PIM_USE_QUAGGA_GETTIME: using Quagga's quagga_gettime"()); -#endif - -#ifdef PIM_GETTIME_USE_GETTIMEOFDAY - zlog_notice("PIM_GETTIME_USE_GETTIMEOFDAY: work-around improper monotonic clock"); -#endif - #ifdef HAVE_CLOCK_MONOTONIC zlog_notice("HAVE_CLOCK_MONOTONIC"); #else diff --git a/pimd/pim_time.c b/pimd/pim_time.c index fce30c08..097b470b 100644 --- a/pimd/pim_time.c +++ b/pimd/pim_time.c @@ -30,63 +30,16 @@ #include "pim_time.h" -#ifndef PIM_GETTIME_USE_GETTIMEOFDAY -static int pim_gettime(int clk_id, struct timeval *tv) -{ - struct timespec ts; - int result; - -#ifdef PIM_USE_QUAGGA_GETTIME - result = quagga_gettime(clk_id, tv); - if (result) { - zlog_err("%s: quagga_gettime(clk_id=%d) failure: errno=%d: %s", - __PRETTY_FUNCTION__, clk_id, - errno, safe_strerror(errno)); - } -#else - result = clock_gettime(clk_id, &ts); - if (result) { - zlog_err("%s: clock_gettime(clk_id=%d) failure: errno=%d: %s", - __PRETTY_FUNCTION__, clk_id, - errno, safe_strerror(errno)); - return result; - } - if (tv) { - tv->tv_sec = ts.tv_sec; - tv->tv_usec = 1000 * ts.tv_nsec; - } -#endif - - return result; -} -#endif - static int gettime_monotonic(struct timeval *tv) { int result; -#ifdef PIM_GETTIME_USE_GETTIMEOFDAY result = gettimeofday(tv, 0); if (result) { zlog_err("%s: gettimeofday() failure: errno=%d: %s", __PRETTY_FUNCTION__, errno, safe_strerror(errno)); } -#elif defined(PIM_USE_QUAGGA_GETTIME) - result = pim_gettime(QUAGGA_CLK_MONOTONIC, tv); - if (result) { - zlog_err("%s: pim_gettime(QUAGGA_CLK_MONOTONIC=%d) failure: errno=%d: %s", - __PRETTY_FUNCTION__, QUAGGA_CLK_MONOTONIC, - errno, safe_strerror(errno)); - } -#else - result = pim_gettime(CLOCK_MONOTONIC, tv); - if (result) { - zlog_err("%s: pim_gettime(CLOCK_MONOTONIC=%d) failure: errno=%d: %s", - __PRETTY_FUNCTION__, CLOCK_MONOTONIC, - errno, safe_strerror(errno)); - } -#endif return result; } From 96b6dfe98793549aca6a7cc77eaf0957b1168ed2 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Mon, 22 Sep 2014 15:47:52 -0300 Subject: [PATCH 408/482] pim: Remove connected addresses on loss of zebra connection. --- pimd/pim_zebra.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index 4cef422e..b9b4dab1 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -63,7 +63,9 @@ static void zclient_broken(struct zclient *zclient) pim_if_addr_del_all(ifp); } - /* upon return, zclient will discard connected addresses */ + /* discard connected addresses because zclient lib will reassign + them upon reconnection */ + if_connected_reset_all(); } /* Router-id update message from zebra. */ From bbb8a18bb5570ff59cf71b46793465828af1fcf3 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Tue, 23 Sep 2014 14:05:55 -0300 Subject: [PATCH 409/482] zebra_rib: Revert work-around for zebra marking recursive static route as inactive. --- zebra/zebra_rib.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index c6dbdec5..effe2338 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -524,13 +524,7 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, return 1; } - else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL) || - match->type == ZEBRA_ROUTE_KERNEL) - /* - || match->type == ZEBRA_ROUTE_KERNEL - This prevents zebra from marking recursive static route as inactive. - See pimd/TODO T26. - */ + else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL)) { resolved = 0; for (newhop = match->nexthop; newhop; newhop = newhop->next) From ea537be5278398cd8c32f8046e4789e613420916 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Tue, 23 Sep 2014 14:30:10 -0300 Subject: [PATCH 410/482] pimd: Remove debuggging for zclient TCP/UNIX sockets. --- pimd/pim_zlookup.c | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/pimd/pim_zlookup.c b/pimd/pim_zlookup.c index ed47e673..7433f1b8 100644 --- a/pimd/pim_zlookup.c +++ b/pimd/pim_zlookup.c @@ -51,48 +51,11 @@ static int zclient_lookup_connect(struct thread *t) return 0; } -#ifdef PIM_ZCLIENT_DEBUG - -#ifdef HAVE_TCP_ZEBRA - zlog_debug("%s: FIXME blocking connect: zclient_socket()", - __PRETTY_FUNCTION__); - zlookup->sock = zclient_socket(); - if (zlookup->sock < 0) { - zlog_warn("%s: failure connecting TCP socket %s,%d", - __PRETTY_FUNCTION__, "127.0.0.1", ZEBRA_PORT); - } - else if (zclient_debug) { - zlog_notice("%s: connected TCP socket %s,%d", - __PRETTY_FUNCTION__, "127.0.0.1", ZEBRA_PORT); - } -#else - { - const char *const path = zclient_serv_path_get(); - zlog_debug("%s: FIXME blocking connect: zclient_socket_un()", - __PRETTY_FUNCTION__); - zlookup->sock = zclient_socket_un(path); - if (zlookup->sock < 0) { - zlog_warn("%s: failure connecting UNIX socket %s", - __PRETTY_FUNCTION__, path); - } - else if (zclient_debug) { - zlog_notice("%s: connected UNIX socket %s", - __PRETTY_FUNCTION__, path); - } - } -#endif /* HAVE_TCP_ZEBRA */ - -#else - - zlog_debug("%s: FIXME blocking connect: zclient_socket_connect()", - __PRETTY_FUNCTION__); if (zclient_socket_connect(zlookup) < 0) { zlog_warn("%s: failure connecting zclient socket", __PRETTY_FUNCTION__); } -#endif /* PIM_ZCLIENT_DEBUG */ - zassert(!zlookup->t_connect); if (zlookup->sock < 0) { /* Since last connect failed, retry within 10 secs */ From 1a9352a7487531578a0db9ca86c2647f8e304ca4 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Tue, 23 Sep 2014 14:33:34 -0300 Subject: [PATCH 411/482] zclient: Revert lib export of zclient_socket()/zclient_socket_un(). --- lib/zclient.c | 4 ++-- lib/zclient.h | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/zclient.c b/lib/zclient.c index 93614360..41ecbb61 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -148,7 +148,7 @@ zclient_reset (struct zclient *zclient) #ifdef HAVE_TCP_ZEBRA /* Make socket to zebra daemon. Return zebra socket. */ -int +static int zclient_socket(void) { int sock; @@ -184,7 +184,7 @@ zclient_socket(void) /* For sockaddr_un. */ #include -int +static int zclient_socket_un (const char *path) { int ret; diff --git a/lib/zclient.h b/lib/zclient.h index c7d4d227..d0c5450b 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -132,8 +132,6 @@ extern void zclient_stop (struct zclient *); extern void zclient_reset (struct zclient *); extern void zclient_free (struct zclient *); -extern int zclient_socket(void); -extern int zclient_socket_un (const char *path); extern int zclient_socket_connect (struct zclient *); extern void zclient_serv_path_set (char *path); extern const char *const zclient_serv_path_get (void); From 8f4a59aaffd90ac820601fdf69accc8f6c953a1c Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Tue, 23 Sep 2014 15:51:11 -0300 Subject: [PATCH 412/482] pimd: Revert accidental removal of show_memory_isis_cmd(). --- lib/memory.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/memory.c b/lib/memory.c index 28bbdc11..3b87fcc2 100644 --- a/lib/memory.c +++ b/lib/memory.c @@ -569,6 +569,7 @@ memory_init (void) install_element (ENABLE_NODE, &show_memory_bgp_cmd); install_element (ENABLE_NODE, &show_memory_ospf_cmd); install_element (ENABLE_NODE, &show_memory_ospf6_cmd); + install_element (ENABLE_NODE, &show_memory_isis_cmd); install_element (ENABLE_NODE, &show_memory_pim_cmd); } From 05a49cea19d861ceec67ce6402264d353bb3b290 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Tue, 23 Sep 2014 16:14:27 -0300 Subject: [PATCH 413/482] zebra: mrib: Remove non-standard copyright line. --- lib/zebra.h | 1 - zebra/zserv.c | 1 - 2 files changed, 2 deletions(-) diff --git a/lib/zebra.h b/lib/zebra.h index 57e591ff..a4e02148 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -1,6 +1,5 @@ /* Zebra common header. Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Kunihiro Ishiguro - Portions Copyright (c) 2008 Everton da Silva Marques This file is part of GNU Zebra. diff --git a/zebra/zserv.c b/zebra/zserv.c index 261c49a3..afd722a1 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -1,6 +1,5 @@ /* Zebra daemon server routine. * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro - * Portions Copyright (c) 2008 Everton da Silva Marques * * This file is part of GNU Zebra. * From d8410a0242ab055b96708c5b33358916330bc85a Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Tue, 23 Sep 2014 16:14:49 -0300 Subject: [PATCH 414/482] pimd: Remove non-standard copyright line. --- lib/command.h | 1 - lib/log.c | 1 - lib/log.h | 1 - lib/memory.c | 1 - lib/memtypes.c | 2 -- lib/thread.h | 1 - 6 files changed, 7 deletions(-) diff --git a/lib/command.h b/lib/command.h index 5156dbf1..8eb0cbd9 100644 --- a/lib/command.h +++ b/lib/command.h @@ -1,7 +1,6 @@ /* * Zebra configuration command interface routine * Copyright (C) 1997, 98 Kunihiro Ishiguro - * Portions Copyright (c) 2008 Everton da Silva Marques * * This file is part of GNU Zebra. * diff --git a/lib/log.c b/lib/log.c index abfd35bc..f02e4c73 100644 --- a/lib/log.c +++ b/lib/log.c @@ -1,7 +1,6 @@ /* * Logging of zebra * Copyright (C) 1997, 1998, 1999 Kunihiro Ishiguro - * Portions Copyright (c) 2008 Everton da Silva Marques * * This file is part of GNU Zebra. * diff --git a/lib/log.h b/lib/log.h index d9f1ecaa..77cd53bc 100644 --- a/lib/log.h +++ b/lib/log.h @@ -1,7 +1,6 @@ /* * Zebra logging funcions. * Copyright (C) 1997, 1998, 1999 Kunihiro Ishiguro - * Portions Copyright (c) 2008 Everton da Silva Marques * * This file is part of GNU Zebra. * diff --git a/lib/memory.c b/lib/memory.c index 3b87fcc2..84daeeef 100644 --- a/lib/memory.c +++ b/lib/memory.c @@ -1,7 +1,6 @@ /* * Memory management routine * Copyright (C) 1998 Kunihiro Ishiguro - * Portions Copyright (c) 2008 Everton da Silva Marques * * This file is part of GNU Zebra. * diff --git a/lib/memtypes.c b/lib/memtypes.c index 4ed1e895..1a0c11fe 100644 --- a/lib/memtypes.c +++ b/lib/memtypes.c @@ -1,6 +1,4 @@ /* - * Portions Copyright (c) 2008 Everton da Silva Marques - * * Memory type definitions. This file is parsed by memtypes.awk to extract * MTYPE_ and memory_list_.. information in order to autogenerate * memtypes.h. diff --git a/lib/thread.h b/lib/thread.h index 4856dec7..43ffbf60 100644 --- a/lib/thread.h +++ b/lib/thread.h @@ -1,6 +1,5 @@ /* Thread management routine header. * Copyright (C) 1998 Kunihiro Ishiguro - * Portions Copyright (c) 2008 Everton da Silva Marques * * This file is part of GNU Zebra. * From 05c6dcdf8cd8e50681de76cc787f46389a7b9238 Mon Sep 17 00:00:00 2001 From: Savannah SR#108542 Date: Thu, 25 Sep 2014 14:52:18 -0300 Subject: [PATCH 415/482] pimd: Fix invalid memory read when receiving a V1 or V2 query. https://savannah.nongnu.org/support/index.php?108542 --- pimd/pim_igmp.c | 44 ++++++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c index a54463fa..4fd3edcb 100644 --- a/pimd/pim_igmp.c +++ b/pimd/pim_igmp.c @@ -280,9 +280,9 @@ static int recv_igmp_query(struct igmp_sock *igmp, int query_version, { struct interface *ifp; struct pim_interface *pim_ifp; - uint8_t resv_s_qrv; - uint8_t s_flag; - uint8_t qrv; + uint8_t resv_s_qrv = 0; + uint8_t s_flag = 0; + uint8_t qrv = 0; struct in_addr group_addr; uint16_t recv_checksum; uint16_t checksum; @@ -336,18 +336,20 @@ static int recv_igmp_query(struct igmp_sock *igmp, int query_version, pim_igmp_other_querier_timer_on(igmp); } - /* - RFC 3376: 4.1.6. QRV (Querier's Robustness Variable) + if (query_version == 3) { + /* + RFC 3376: 4.1.6. QRV (Querier's Robustness Variable) - Routers adopt the QRV value from the most recently received Query - as their own [Robustness Variable] value, unless that most - recently received QRV was zero, in which case the receivers use - the default [Robustness Variable] value specified in section 8.1 - or a statically configured value. - */ - resv_s_qrv = igmp_msg[8]; - qrv = 7 & resv_s_qrv; - igmp->querier_robustness_variable = qrv ? qrv : pim_ifp->igmp_default_robustness_variable; + Routers adopt the QRV value from the most recently received Query + as their own [Robustness Variable] value, unless that most + recently received QRV was zero, in which case the receivers use + the default [Robustness Variable] value specified in section 8.1 + or a statically configured value. + */ + resv_s_qrv = igmp_msg[8]; + qrv = 7 & resv_s_qrv; + igmp->querier_robustness_variable = qrv ? qrv : pim_ifp->igmp_default_robustness_variable; + } /* RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code) @@ -357,7 +359,7 @@ static int recv_igmp_query(struct igmp_sock *igmp, int query_version, Interval] value, unless that most recently received QQI was zero, in which case the receiving routers use the default. */ - if (igmp->t_other_querier_timer) { + if (igmp->t_other_querier_timer && query_version == 3) { /* other querier present */ uint8_t qqic; uint16_t qqi; @@ -386,7 +388,17 @@ static int recv_igmp_query(struct igmp_sock *igmp, int query_version, General queries don't trigger timer update. */ - s_flag = (1 << 3) & resv_s_qrv; + if (query_version == 3) { + s_flag = (1 << 3) & resv_s_qrv; + } + else { + /* Neither V1 nor V2 have this field. Pimd should really go into + * a compatibility mode here and run as V2 (or V1) but it doesn't + * so for now, lets just set the flag to suppress these timer updates. + */ + s_flag = 1; + } + if (!s_flag) { /* s_flag is clear */ From ecc1fb93419e0f9d9297f0417bee0b697ce24dec Mon Sep 17 00:00:00 2001 From: Savannah SR#108542 Date: Thu, 25 Sep 2014 14:41:43 -0300 Subject: [PATCH 416/482] pimd: Fix igmp_source_forward_stop called when IGMP forwarding flag is not set in oif_flags. https://savannah.nongnu.org/support/index.php?108542 --- pimd/pim_zebra.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index b9b4dab1..ff62bec8 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -1192,11 +1192,25 @@ void igmp_source_forward_stop(struct igmp_source *source) group = source->source_group; - if (del_oif(source->source_channel_oil, + /* + It appears that in certain circumstances that + igmp_source_forward_stop is called when IGMP forwarding + was not enabled in oif_flags for this outgoing interface. + Possibly because of multiple calls. When that happens, we + enter the below if statement and this function returns early + which in turn triggers the calling function to assert. + Making the call to del_oif and ignoring the return code + fixes the issue without ill effect, similar to + pim_forward_stop below. + */ + /*if (del_oif(source->source_channel_oil, group->group_igmp_sock->interface, PIM_OIF_FLAG_PROTO_IGMP)) { return; - } + }*/ + del_oif(source->source_channel_oil, + group->group_igmp_sock->interface, + PIM_OIF_FLAG_PROTO_IGMP); /* Feed IGMPv3-gathered local membership information into PIM From 6ab3e2f5759db58469ceb1702df1bc3d18f7a952 Mon Sep 17 00:00:00 2001 From: Savannah SR#108542 Date: Thu, 25 Sep 2014 16:59:38 -0300 Subject: [PATCH 417/482] pimd: Fix attempted out of bounds read when deleteing an interface. https://savannah.nongnu.org/support/index.php?108542 --- pimd/pim_zebra.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index ff62bec8..fd525ae6 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -113,8 +113,14 @@ static int pim_zebra_if_del(int command, struct zclient *zclient, /* zebra api adds/dels interfaces using the same call interface_add_read below, see comments in lib/zclient.c + + comments in lib/zclient.c seem to indicate that calling + zebra_interface_add_read is the correct call, but that + results in an attemted out of bounds read which causes + pimd to assert. Other clients use zebra_interface_state_read + and it appears to work just fine. */ - ifp = zebra_interface_add_read(zclient->ibuf); + ifp = zebra_interface_state_read(zclient->ibuf); if (!ifp) return 0; @@ -138,7 +144,7 @@ static int pim_zebra_if_state_up(int command, struct zclient *zclient, /* zebra api notifies interface up/down events by using the same call - interface_add_read below, see comments in lib/zclient.c + zebra_interface_state_read below, see comments in lib/zclient.c */ ifp = zebra_interface_state_read(zclient->ibuf); if (!ifp) @@ -170,7 +176,7 @@ static int pim_zebra_if_state_down(int command, struct zclient *zclient, /* zebra api notifies interface up/down events by using the same call - interface_add_read below, see comments in lib/zclient.c + zebra_interface_state_read below, see comments in lib/zclient.c */ ifp = zebra_interface_state_read(zclient->ibuf); if (!ifp) From e324ddc5c73b2e2fb1c450a5fe927aa336e568e6 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Mon, 29 Sep 2014 17:59:02 -0300 Subject: [PATCH 418/482] pimd: sh ip multicast: Display zclient sockets. --- pimd/pim_cmd.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index eaf209ce..cc15a09c 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -2093,6 +2093,22 @@ DEFUN (show_ip_multicast, VTY_NEWLINE); } + vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, "Zclient update socket: "); + if (qpim_zclient_update) { + vty_out(vty, "%d%s", qpim_zclient_update->sock, VTY_NEWLINE); + } + else { + vty_out(vty, "%s", VTY_NEWLINE); + } + vty_out(vty, "Zclient lookup socket: "); + if (qpim_zclient_lookup) { + vty_out(vty, "%d%s", qpim_zclient_lookup->sock, VTY_NEWLINE); + } + else { + vty_out(vty, "%s", VTY_NEWLINE); + } + vty_out(vty, "%s", VTY_NEWLINE); vty_out(vty, "Current highest VifIndex: %d%s", qpim_mroute_oif_highest_vif_index, From 8150beed9a4f50a72696a65c1f40889ab65ad7ff Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Mon, 29 Sep 2014 17:58:30 -0300 Subject: [PATCH 419/482] pimd: Explicitly restart zclient update connection. --- pimd/pim_zebra.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index fd525ae6..c4a6f7a8 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -66,6 +66,8 @@ static void zclient_broken(struct zclient *zclient) /* discard connected addresses because zclient lib will reassign them upon reconnection */ if_connected_reset_all(); + + zclient_init(qpim_zclient_update, ZEBRA_ROUTE_PIM); /* reconnect */ } /* Router-id update message from zebra. */ @@ -677,11 +679,12 @@ void pim_zebra_init(char *zebra_sock_path) qpim_zclient_update->ipv4_route_add = redist_read_ipv4_route; qpim_zclient_update->ipv4_route_delete = redist_read_ipv4_route; - zclient_init(qpim_zclient_update, ZEBRA_ROUTE_PIM); if (PIM_DEBUG_PIM_TRACE) { zlog_info("zclient_init cleared redistribution request"); } + zclient_init(qpim_zclient_update, ZEBRA_ROUTE_PIM); + zassert(qpim_zclient_update->redist_default == ZEBRA_ROUTE_PIM); /* Request all redistribution */ From a59f21b1a58f121aac466710f32b557a4c75061d Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Tue, 30 Sep 2014 17:23:56 -0300 Subject: [PATCH 420/482] pimd: Update lookup zclient counter for connection failures. --- pimd/pim_zlookup.c | 43 +++++++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/pimd/pim_zlookup.c b/pimd/pim_zlookup.c index 7433f1b8..2e71dc4e 100644 --- a/pimd/pim_zlookup.c +++ b/pimd/pim_zlookup.c @@ -52,8 +52,12 @@ static int zclient_lookup_connect(struct thread *t) } if (zclient_socket_connect(zlookup) < 0) { - zlog_warn("%s: failure connecting zclient socket", - __PRETTY_FUNCTION__); + ++zlookup->fail; + zlog_warn("%s: failure connecting zclient socket: failures=%d", + __PRETTY_FUNCTION__, zlookup->fail); + } + else { + zlookup->fail = 0; /* reset counter on connection */ } zassert(!zlookup->t_connect); @@ -101,6 +105,19 @@ static void zclient_lookup_reconnect(struct zclient *zlookup) zclient_lookup_sched_now(zlookup); } +static void zclient_lookup_failed(struct zclient *zlookup) +{ + if (zlookup->sock >= 0) { + if (close(zlookup->sock)) { + zlog_warn("%s: closing fd=%d: errno=%d %s", __func__, zlookup->sock, + errno, safe_strerror(errno)); + } + zlookup->sock = -1; + } + + zclient_lookup_reconnect(zlookup); +} + struct zclient *zclient_lookup_new() { struct zclient *zlookup; @@ -159,9 +176,7 @@ static int zclient_read_nexthop(struct zclient *zlookup, if (nbytes < 2) { zlog_err("%s %s: failure reading zclient lookup socket: nbytes=%d", __FILE__, __PRETTY_FUNCTION__, nbytes); - close(zlookup->sock); - zlookup->sock = -1; - zclient_lookup_reconnect(zlookup); + zclient_lookup_failed(zlookup); return -1; } length = stream_getw(s); @@ -171,9 +186,7 @@ static int zclient_read_nexthop(struct zclient *zlookup, if (len < MIN_LEN) { zlog_err("%s %s: failure reading zclient lookup socket: len=%d < MIN_LEN=%d", __FILE__, __PRETTY_FUNCTION__, len, MIN_LEN); - close(zlookup->sock); - zlookup->sock = -1; - zclient_lookup_reconnect(zlookup); + zclient_lookup_failed(zlookup); return -2; } @@ -181,9 +194,7 @@ static int zclient_read_nexthop(struct zclient *zlookup, if (nbytes < (length - 2)) { zlog_err("%s %s: failure reading zclient lookup socket: nbytes=%d < len=%d", __FILE__, __PRETTY_FUNCTION__, nbytes, len); - close(zlookup->sock); - zlookup->sock = -1; - zclient_lookup_reconnect(zlookup); + zclient_lookup_failed(zlookup); return -3; } marker = stream_getc(s); @@ -329,7 +340,7 @@ static int zclient_lookup_nexthop_once(struct zclient *zlookup, if (zlookup->sock < 0) { zlog_err("%s %s: zclient lookup socket is not connected", __FILE__, __PRETTY_FUNCTION__); - zclient_lookup_reconnect(zlookup); + zclient_lookup_failed(zlookup); return -1; } @@ -343,17 +354,13 @@ static int zclient_lookup_nexthop_once(struct zclient *zlookup, if (ret < 0) { zlog_err("%s %s: writen() failure writing to zclient lookup socket", __FILE__, __PRETTY_FUNCTION__); - close(zlookup->sock); - zlookup->sock = -1; - zclient_lookup_reconnect(zlookup); + zclient_lookup_failed(zlookup); return -2; } if (ret == 0) { zlog_err("%s %s: connection closed on zclient lookup socket", __FILE__, __PRETTY_FUNCTION__); - close(zlookup->sock); - zlookup->sock = -1; - zclient_lookup_reconnect(zlookup); + zclient_lookup_failed(zlookup); return -3; } From 199f85ade39f751dd493fe011107736c9b168953 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Tue, 30 Sep 2014 16:50:40 -0300 Subject: [PATCH 421/482] pimd: Revert: Explicitly restart zclient update connection. --- pimd/pim_zebra.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index c4a6f7a8..68aee687 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -66,8 +66,6 @@ static void zclient_broken(struct zclient *zclient) /* discard connected addresses because zclient lib will reassign them upon reconnection */ if_connected_reset_all(); - - zclient_init(qpim_zclient_update, ZEBRA_ROUTE_PIM); /* reconnect */ } /* Router-id update message from zebra. */ From ddc6659dd0f05b304ef579dcee6ac803e1a4b6d2 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Tue, 30 Sep 2014 16:49:36 -0300 Subject: [PATCH 422/482] pimd: sh ip multicast: Display zclient socket fail counter. --- pimd/pim_cmd.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index cc15a09c..076b7baa 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -2096,14 +2096,16 @@ DEFUN (show_ip_multicast, vty_out(vty, "%s", VTY_NEWLINE); vty_out(vty, "Zclient update socket: "); if (qpim_zclient_update) { - vty_out(vty, "%d%s", qpim_zclient_update->sock, VTY_NEWLINE); + vty_out(vty, "%d failures=%d%s", qpim_zclient_update->sock, + qpim_zclient_update->fail, VTY_NEWLINE); } else { vty_out(vty, "%s", VTY_NEWLINE); } vty_out(vty, "Zclient lookup socket: "); if (qpim_zclient_lookup) { - vty_out(vty, "%d%s", qpim_zclient_lookup->sock, VTY_NEWLINE); + vty_out(vty, "%d failures=%d%s", qpim_zclient_lookup->sock, + qpim_zclient_lookup->fail, VTY_NEWLINE); } else { vty_out(vty, "%s", VTY_NEWLINE); From 24e3a9b5ff17553d20a2f9e4ce2a61b5012cd0f6 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Tue, 30 Sep 2014 19:14:19 -0300 Subject: [PATCH 423/482] pimd: Report del_oif() failure within igmp_source_forward_stop(). --- pimd/pim_igmpv3.c | 4 ++++ pimd/pim_zebra.c | 29 +++++++++++++++++++---------- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/pimd/pim_igmpv3.c b/pimd/pim_igmpv3.c index b95adb6f..3baddbfa 100644 --- a/pimd/pim_igmpv3.c +++ b/pimd/pim_igmpv3.c @@ -388,6 +388,10 @@ static void source_channel_oil_detach(struct igmp_source *source) } } +/* + igmp_source_delete: stop fowarding, and delete the source + igmp_source_forward_stop: stop fowarding, but keep the source +*/ void igmp_source_delete(struct igmp_source *source) { struct igmp_group *group; diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index 68aee687..fbc7c16e 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -1073,6 +1073,7 @@ static int del_oif(struct channel_oil *channel_oil, void igmp_source_forward_start(struct igmp_source *source) { struct igmp_group *group; + int result; if (PIM_DEBUG_IGMP_TRACE) { char source_str[100]; @@ -1158,9 +1159,12 @@ void igmp_source_forward_start(struct igmp_source *source) } } - if (add_oif(source->source_channel_oil, - group->group_igmp_sock->interface, - PIM_OIF_FLAG_PROTO_IGMP)) { + result = add_oif(source->source_channel_oil, + group->group_igmp_sock->interface, + PIM_OIF_FLAG_PROTO_IGMP); + if (result) { + zlog_warn("%s: add_oif() failed with return=%d", + __func__, result); return; } @@ -1174,9 +1178,14 @@ void igmp_source_forward_start(struct igmp_source *source) IGMP_SOURCE_DO_FORWARDING(source->source_flags); } +/* + igmp_source_forward_stop: stop fowarding, but keep the source + igmp_source_delete: stop fowarding, and delete the source + */ void igmp_source_forward_stop(struct igmp_source *source) { struct igmp_group *group; + int result; if (PIM_DEBUG_IGMP_TRACE) { char source_str[100]; @@ -1210,14 +1219,14 @@ void igmp_source_forward_stop(struct igmp_source *source) fixes the issue without ill effect, similar to pim_forward_stop below. */ - /*if (del_oif(source->source_channel_oil, - group->group_igmp_sock->interface, - PIM_OIF_FLAG_PROTO_IGMP)) { + result = del_oif(source->source_channel_oil, + group->group_igmp_sock->interface, + PIM_OIF_FLAG_PROTO_IGMP); + if (result) { + zlog_warn("%s: del_oif() failed with return=%d", + __func__, result); return; - }*/ - del_oif(source->source_channel_oil, - group->group_igmp_sock->interface, - PIM_OIF_FLAG_PROTO_IGMP); + } /* Feed IGMPv3-gathered local membership information into PIM From 21d1e26dcb4dc290fd0fe05618cbc96c67f85ffe Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Wed, 1 Oct 2014 18:34:04 -0300 Subject: [PATCH 424/482] pimd: show ip pim lan-prune-delay: Cosmetic. --- pimd/pim_cmd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 076b7baa..eae7dc31 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -831,7 +831,7 @@ static void pim_show_lan_prune_delay(struct vty *vty) "T=t_bit LPD=lan_prune_delay_hello_option%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); - vty_out(vty, "Interface Address PrDly OvInt NoDly HiDly HiInt T Neighbor LPD PrDly OvInt T%s", VTY_NEWLINE); + vty_out(vty, "Interface Address PrDly OvInt NoDly HiDly HiInt T | Neighbor LPD PrDly OvInt T%s", VTY_NEWLINE); for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) { struct pim_interface *pim_ifp; @@ -855,7 +855,7 @@ static void pim_show_lan_prune_delay(struct vty *vty) pim_inet4_dump("", neigh->source_addr, neigh_src_str, sizeof(neigh_src_str)); - vty_out(vty, "%-9s %-15s %5u %5u %5u %5u %5u %1u %-15s %-3s %5u %5u %1u%s", + vty_out(vty, "%-9s %-15s %5u %5u %5u %5u %5u %1u | %-15s %-3s %5u %5u %1u%s", ifp->name, inet_ntoa(ifaddr), pim_ifp->pim_propagation_delay_msec, From ed14fa00758a156b108854bb35bc5077654f080d Mon Sep 17 00:00:00 2001 From: "Balaji.G" Date: Wed, 8 Oct 2014 01:11:31 -0300 Subject: [PATCH 425/482] pimd: Addition of Hello & Join-Prune message debug commands Separate "debug pim packets hello and Join-Prune" added to enable hello and Join-Prune debugs specifically --- pimd/pim_cmd.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++-- pimd/pim_cmd.h | 2 ++ pimd/pim_hello.c | 4 ++-- pimd/pim_pim.c | 2 +- pimd/pimd.h | 8 +++++++ 5 files changed, 68 insertions(+), 5 deletions(-) diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index eae7dc31..6176fe55 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -3302,7 +3302,30 @@ DEFUN (debug_pim_packets, DEBUG_PIM_STR DEBUG_PIM_PACKETS_STR) { - PIM_DO_DEBUG_PIM_PACKETS; + PIM_DO_DEBUG_PIM_PACKETS; + vty_out (vty, "PIM Packet debugging is on %s", VTY_NEWLINE); + return CMD_SUCCESS; +} + +DEFUN (debug_pim_packets_filter, + debug_pim_packets_filter_cmd, + "debug pim packets (hello|joins)", + DEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_PACKETS_STR + DEBUG_PIM_HELLO_PACKETS_STR + DEBUG_PIM_J_P_PACKETS_STR) +{ + if (strncmp(argv[0],"h",1) == 0) + { + PIM_DO_DEBUG_PIM_HELLO; + vty_out (vty, "PIM Hello debugging is on %s", VTY_NEWLINE); + } + else if (strncmp(argv[0],"j",1) == 0) + { + PIM_DO_DEBUG_PIM_J_P; + vty_out (vty, "PIM Join/Prune debugging is on %s", VTY_NEWLINE); + } return CMD_SUCCESS; } @@ -3312,12 +3335,38 @@ DEFUN (no_debug_pim_packets, NO_STR DEBUG_STR DEBUG_PIM_STR - DEBUG_PIM_PACKETS_STR) + DEBUG_PIM_PACKETS_STR + DEBUG_PIM_HELLO_PACKETS_STR + DEBUG_PIM_J_P_PACKETS_STR) { PIM_DONT_DEBUG_PIM_PACKETS; + vty_out (vty, "PIM Packet debugging is off %s", VTY_NEWLINE); return CMD_SUCCESS; } +DEFUN (no_debug_pim_packets_filter, + no_debug_pim_packets_filter_cmd, + "no debug pim packets (hello|joins)", + NO_STR + DEBUG_STR + DEBUG_PIM_STR + DEBUG_PIM_PACKETS_STR + DEBUG_PIM_HELLO_PACKETS_STR + DEBUG_PIM_J_P_PACKETS_STR) +{ + if (strncmp(argv[0],"h",1) == 0) + { + PIM_DONT_DEBUG_PIM_HELLO; + vty_out (vty, "PIM Hello debugging is off %s", VTY_NEWLINE); + } + else if (strncmp(argv[0],"j",1) == 0) + { + PIM_DONT_DEBUG_PIM_J_P; + vty_out (vty, "PIM Join/Prune debugging is off %s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} + ALIAS (no_debug_pim_packets, undebug_pim_packets_cmd, "undebug pim packets", @@ -4406,7 +4455,9 @@ void pim_cmd_init() install_element (ENABLE_NODE, &no_debug_pim_events_cmd); install_element (ENABLE_NODE, &undebug_pim_events_cmd); install_element (ENABLE_NODE, &debug_pim_packets_cmd); + install_element (ENABLE_NODE, &debug_pim_packets_filter_cmd); install_element (ENABLE_NODE, &no_debug_pim_packets_cmd); + install_element (ENABLE_NODE, &no_debug_pim_packets_filter_cmd); install_element (ENABLE_NODE, &undebug_pim_packets_cmd); install_element (ENABLE_NODE, &debug_pim_packetdump_send_cmd); install_element (ENABLE_NODE, &no_debug_pim_packetdump_send_cmd); @@ -4445,7 +4496,9 @@ void pim_cmd_init() install_element (CONFIG_NODE, &no_debug_pim_events_cmd); install_element (CONFIG_NODE, &undebug_pim_events_cmd); install_element (CONFIG_NODE, &debug_pim_packets_cmd); + install_element (CONFIG_NODE, &debug_pim_packets_filter_cmd); install_element (CONFIG_NODE, &no_debug_pim_packets_cmd); + install_element (CONFIG_NODE, &no_debug_pim_packets_filter_cmd); install_element (CONFIG_NODE, &undebug_pim_packets_cmd); install_element (CONFIG_NODE, &debug_pim_trace_cmd); install_element (CONFIG_NODE, &no_debug_pim_trace_cmd); diff --git a/pimd/pim_cmd.h b/pimd/pim_cmd.h index 391046a4..c5037400 100644 --- a/pimd/pim_cmd.h +++ b/pimd/pim_cmd.h @@ -42,6 +42,8 @@ #define DEBUG_PIM_STR "PIM protocol activity\n" #define DEBUG_PIM_EVENTS_STR "PIM protocol events\n" #define DEBUG_PIM_PACKETS_STR "PIM protocol packets\n" +#define DEBUG_PIM_HELLO_PACKETS_STR "PIM Hello protocol packets\n" +#define DEBUG_PIM_J_P_PACKETS_STR "PIM Join/Prune protocol packets\n" #define DEBUG_PIM_PACKETDUMP_STR "PIM packet dump\n" #define DEBUG_PIM_PACKETDUMP_SEND_STR "Dump sent packets\n" #define DEBUG_PIM_PACKETDUMP_RECV_STR "Dump received packets\n" diff --git a/pimd/pim_hello.c b/pimd/pim_hello.c index 94e7c945..12857831 100644 --- a/pimd/pim_hello.c +++ b/pimd/pim_hello.c @@ -204,7 +204,7 @@ int pim_hello_recv(struct interface *ifp, FREE_ADDR_LIST_THEN_RETURN(-2); } - if (PIM_DEBUG_PIM_TRACE) { + if (PIM_DEBUG_PIM_TRACE || PIM_DEBUG_PIM_HELLO) { char src_str[100]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_debug("%s: parse left_size=%d: PIM hello TLV type=%d length=%d from %s on %s", @@ -262,7 +262,7 @@ int pim_hello_recv(struct interface *ifp, } break; case PIM_MSG_OPTION_TYPE_DM_STATE_REFRESH: - if (PIM_DEBUG_PIM_TRACE) { + if (PIM_DEBUG_PIM_TRACE || PIM_DEBUG_PIM_HELLO) { char src_str[100]; pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); zlog_debug("%s: ignoring PIM hello dense-mode state refresh TLV option type=%d length=%d from %s on interface %s", diff --git a/pimd/pim_pim.c b/pimd/pim_pim.c index fb6c3acb..04823c21 100644 --- a/pimd/pim_pim.c +++ b/pimd/pim_pim.c @@ -509,7 +509,7 @@ static int hello_send(struct interface *ifp, pim_ifp = ifp->info; - if (PIM_DEBUG_PIM_PACKETS) { + if (PIM_DEBUG_PIM_PACKETS || PIM_DEBUG_PIM_HELLO) { char dst_str[100]; pim_inet4_dump("", qpim_all_pim_routers_addr, dst_str, sizeof(dst_str)); zlog_debug("%s: to %s on %s: holdt=%u prop_d=%u overr_i=%u dis_join_supp=%d dr_prio=%u gen_id=%08x addrs=%d", diff --git a/pimd/pimd.h b/pimd/pimd.h index b0a1b648..22a29220 100644 --- a/pimd/pimd.h +++ b/pimd/pimd.h @@ -64,6 +64,8 @@ #define PIM_MASK_ZEBRA (1 << 8) #define PIM_MASK_SSMPINGD (1 << 9) #define PIM_MASK_MROUTE (1 << 10) +#define PIM_MASK_PIM_HELLO (1 << 11) +#define PIM_MASK_PIM_J_P (1 << 12) const char *const PIM_ALL_SYSTEMS; const char *const PIM_ALL_ROUTERS; @@ -114,6 +116,8 @@ int64_t qpim_mroute_del_last; #define PIM_DEBUG_ZEBRA (qpim_debugs & PIM_MASK_ZEBRA) #define PIM_DEBUG_SSMPINGD (qpim_debugs & PIM_MASK_SSMPINGD) #define PIM_DEBUG_MROUTE (qpim_debugs & PIM_MASK_MROUTE) +#define PIM_DEBUG_PIM_HELLO (qpim_debugs & PIM_MASK_PIM_HELLO) +#define PIM_DEBUG_PIM_J_P (qpim_debugs & PIM_MASK_PIM_J_P) #define PIM_DEBUG_EVENTS (qpim_debugs & (PIM_MASK_PIM_EVENTS | PIM_MASK_IGMP_EVENTS)) #define PIM_DEBUG_PACKETS (qpim_debugs & (PIM_MASK_PIM_PACKETS | PIM_MASK_IGMP_PACKETS)) @@ -130,6 +134,8 @@ int64_t qpim_mroute_del_last; #define PIM_DO_DEBUG_ZEBRA (qpim_debugs |= PIM_MASK_ZEBRA) #define PIM_DO_DEBUG_SSMPINGD (qpim_debugs |= PIM_MASK_SSMPINGD) #define PIM_DO_DEBUG_MROUTE (qpim_debugs |= PIM_MASK_MROUTE) +#define PIM_DO_DEBUG_PIM_HELLO (qpim_debugs |= PIM_MASK_PIM_HELLO) +#define PIM_DO_DEBUG_PIM_J_P (qpim_debugs |= PIM_MASK_PIM_J_P) #define PIM_DONT_DEBUG_PIM_EVENTS (qpim_debugs &= ~PIM_MASK_PIM_EVENTS) #define PIM_DONT_DEBUG_PIM_PACKETS (qpim_debugs &= ~PIM_MASK_PIM_PACKETS) @@ -142,6 +148,8 @@ int64_t qpim_mroute_del_last; #define PIM_DONT_DEBUG_ZEBRA (qpim_debugs &= ~PIM_MASK_ZEBRA) #define PIM_DONT_DEBUG_SSMPINGD (qpim_debugs &= ~PIM_MASK_SSMPINGD) #define PIM_DONT_DEBUG_MROUTE (qpim_debugs &= ~PIM_MASK_MROUTE) +#define PIM_DONT_DEBUG_PIM_HELLO (qpim_debugs &= ~PIM_MASK_PIM_HELLO) +#define PIM_DONT_DEBUG_PIM_J_P (qpim_debugs &= ~PIM_MASK_PIM_J_P) void pim_init(void); void pim_terminate(void); From d632689579bbcbfb5f38c3faf05ad675e002c059 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Mon, 19 Jan 2015 16:50:24 -0200 Subject: [PATCH 426/482] pimd: Fix configuration file reading upon startup Without the fix, qpimd issues this error message: pim_if_add_vif: ifindex=0 < 1 on interface swp1 It happens because in pim_main.c:main() we are initializing zebra with pim_zebra_init() after we read in the configuration with vty_read_config(). See also: https://github.com/udhos/qpimd/issues/3 --- pimd/pim_main.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/pimd/pim_main.c b/pimd/pim_main.c index c646356a..b57f8811 100644 --- a/pimd/pim_main.c +++ b/pimd/pim_main.c @@ -216,6 +216,11 @@ int main(int argc, char** argv, char** envp) { zlog_default->maxlvl[ZLOG_DEST_STDOUT] = ZLOG_DISABLED; #endif + /* + Initialize zclient "update" and "lookup" sockets + */ + pim_zebra_init(zebra_sock_path); + zlog_notice("Loading configuration - begin"); /* Get configuration file. */ @@ -278,12 +283,6 @@ int main(int argc, char** argv, char** envp) { zlog_notice("!HAVE_CLOCK_MONOTONIC"); #endif - - /* - Initialize zclient "update" and "lookup" sockets - */ - pim_zebra_init(zebra_sock_path); - while (thread_fetch(master, &thread)) thread_call(&thread); From 85385f7eeee14d529065db7b863478c3ba455dd4 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Mon, 19 Jan 2015 18:25:45 -0200 Subject: [PATCH 427/482] pimd: Log ifindex found for an interface when zebra lib reports a new connected address. --- pimd/pim_iface.c | 12 ++++++------ pimd/pim_pim.c | 4 ++-- pimd/pim_zebra.c | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index 770bd4e0..ecf9ef6b 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -388,12 +388,12 @@ void pim_if_addr_add(struct connected *ifc) if (!if_is_operative(ifp)) return; - if (PIM_DEBUG_ZEBRA) { + /* if (PIM_DEBUG_ZEBRA) */ { char buf[BUFSIZ]; prefix2str(ifc->address, buf, BUFSIZ); - zlog_debug("%s: %s connected IP address %s %s", + zlog_debug("%s: %s ifindex=%d connected IP address %s %s", __PRETTY_FUNCTION__, - ifp->name, buf, + ifp->name, ifp->ifindex, buf, CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY) ? "secondary" : "primary"); } @@ -507,12 +507,12 @@ void pim_if_addr_del(struct connected *ifc, int force_prim_as_any) ifp = ifc->ifp; zassert(ifp); - if (PIM_DEBUG_ZEBRA) { + /* if (PIM_DEBUG_ZEBRA) */ { char buf[BUFSIZ]; prefix2str(ifc->address, buf, BUFSIZ); - zlog_debug("%s: %s disconnected IP address %s %s", + zlog_debug("%s: %s ifindex=%d disconnected IP address %s %s", __PRETTY_FUNCTION__, - ifp->name, buf, + ifp->name, ifp->ifindex, buf, CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY) ? "secondary" : "primary"); } diff --git a/pimd/pim_pim.c b/pimd/pim_pim.c index 04823c21..f6f4c953 100644 --- a/pimd/pim_pim.c +++ b/pimd/pim_pim.c @@ -725,8 +725,8 @@ int pim_sock_add(struct interface *ifp) pim_ifp->pim_generation_id = pim_rand() & (int64_t) 0xFFFFFFFF; - zlog_info("PIM INTERFACE UP: on interface %s", - ifp->name); + zlog_info("PIM INTERFACE UP: on interface %s ifindex=%d", + ifp->name, ifp->ifindex); /* * Start receiving PIM messages diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index fbc7c16e..f1840245 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -150,7 +150,7 @@ static int pim_zebra_if_state_up(int command, struct zclient *zclient, if (!ifp) return 0; - zlog_info("INTERFACE UP: %s", ifp->name); + zlog_info("INTERFACE UP: %s ifindex=%d", ifp->name, ifp->ifindex); if (PIM_DEBUG_ZEBRA) { zlog_debug("%s: %s index %d flags %ld metric %d mtu %d operative %d", @@ -182,7 +182,7 @@ static int pim_zebra_if_state_down(int command, struct zclient *zclient, if (!ifp) return 0; - zlog_info("INTERFACE DOWN: %s", ifp->name); + zlog_info("INTERFACE DOWN: %s ifindex=%d", ifp->name, ifp->ifindex); if (PIM_DEBUG_ZEBRA) { zlog_debug("%s: %s index %d flags %ld metric %d mtu %d operative %d", From 1b47764f50cc074ba795d2b4477a3bc2372a8d9b Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Wed, 4 Feb 2015 06:15:34 +0100 Subject: [PATCH 428/482] redhat: revert non-pim changes in .spec No idea what weird Fedora magic this does... if it's needed, it can be pushed separately from pimd. Signed-off-by: David Lamparter --- redhat/quagga.spec.in | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/redhat/quagga.spec.in b/redhat/quagga.spec.in index 35d691ba..75835bbe 100644 --- a/redhat/quagga.spec.in +++ b/redhat/quagga.spec.in @@ -48,16 +48,16 @@ %define quagga_buildreqs %{quagga_buildreqs} patch libcap-devel # FC4 and 5 split texi2html out of tetex package. -%if "%dist" == "fc4" || "%dist" == "fc5" +%if "%dist" != "fc2" || "%dist" != "fc3" %define quagga_buildreqs %{quagga_buildreqs} texi2html %endif # pam_stack is deprecated in FC5 # default to pam_stack, default should be changed later. -%if "%dist" == "fc5" -%define quagga_pam_source quagga.pam -%else +%if "%dist" == "fc4" || "%dist" == "fc3" %define quagga_pam_source quagga.pam.stack +%else +%define quagga_pam_source quagga.pam %endif ############################################################################ From b3c6afe9fdc8c65d71abde3a2f26525b87189297 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Wed, 4 Feb 2015 06:32:28 +0100 Subject: [PATCH 429/482] Revert "pimd: Revert: Explicitly restart zclient update connection." This reverts commit 199f85ade39f751dd493fe011107736c9b168953. This depends on the zebra reconnect changes, which we're not picking up at this point. Signed-off-by: David Lamparter --- pimd/pim_zebra.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index f1840245..63d029b2 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -66,6 +66,8 @@ static void zclient_broken(struct zclient *zclient) /* discard connected addresses because zclient lib will reassign them upon reconnection */ if_connected_reset_all(); + + zclient_init(qpim_zclient_update, ZEBRA_ROUTE_PIM); /* reconnect */ } /* Router-id update message from zebra. */ From a2805de2b25383695f38a3ebbefe75e26a5e9aba Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Wed, 4 Feb 2015 06:33:26 +0100 Subject: [PATCH 430/482] Revert "pimd: Explicitly restart zclient update connection." This reverts commit 8150beed9a4f50a72696a65c1f40889ab65ad7ff. This depends on the zebra reconnect changes, which we're not picking up at this point. Signed-off-by: David Lamparter --- pimd/pim_zebra.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index 63d029b2..8d015737 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -66,8 +66,6 @@ static void zclient_broken(struct zclient *zclient) /* discard connected addresses because zclient lib will reassign them upon reconnection */ if_connected_reset_all(); - - zclient_init(qpim_zclient_update, ZEBRA_ROUTE_PIM); /* reconnect */ } /* Router-id update message from zebra. */ @@ -679,12 +677,11 @@ void pim_zebra_init(char *zebra_sock_path) qpim_zclient_update->ipv4_route_add = redist_read_ipv4_route; qpim_zclient_update->ipv4_route_delete = redist_read_ipv4_route; + zclient_init(qpim_zclient_update, ZEBRA_ROUTE_PIM); if (PIM_DEBUG_PIM_TRACE) { zlog_info("zclient_init cleared redistribution request"); } - zclient_init(qpim_zclient_update, ZEBRA_ROUTE_PIM); - zassert(qpim_zclient_update->redist_default == ZEBRA_ROUTE_PIM); /* Request all redistribution */ From 5d5af78a1b9310c0c2290f81ee8abc12d5376500 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Wed, 4 Feb 2015 06:33:59 +0100 Subject: [PATCH 431/482] Revert "pim: Remove connected addresses on loss of zebra connection." This reverts commit 96b6dfe98793549aca6a7cc77eaf0957b1168ed2. This depends on the zebra reconnect changes, which we're not picking up at this point. Signed-off-by: David Lamparter --- pimd/pim_zebra.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index 8d015737..43c80c8b 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -63,9 +63,7 @@ static void zclient_broken(struct zclient *zclient) pim_if_addr_del_all(ifp); } - /* discard connected addresses because zclient lib will reassign - them upon reconnection */ - if_connected_reset_all(); + /* upon return, zclient will discard connected addresses */ } /* Router-id update message from zebra. */ From bb7feff0af6c8519df45a4f40f06cdd819fe70d0 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Wed, 4 Feb 2015 06:40:25 +0100 Subject: [PATCH 432/482] Revert "pimd: clear zclient-update: Reset zclient update connection to zebra daemon" This reverts commit 3456a80f5f8e6e44c30453bd92eabf5faf7ab25b. Conflicts: pimd/pim_zebra.c This depends on the zebra reconnect changes, which we're not picking up at this point. This revert is partial, only bumping out the reconnect-related changes. Signed-off-by: David Lamparter --- pimd/COMMANDS | 1 - pimd/pim_cmd.c | 12 ------------ pimd/pim_zebra.c | 1 - 3 files changed, 14 deletions(-) diff --git a/pimd/COMMANDS b/pimd/COMMANDS index 2dedea06..425ac822 100644 --- a/pimd/COMMANDS +++ b/pimd/COMMANDS @@ -60,7 +60,6 @@ debug commands: clear ip mroute Reset multicast routes clear ip pim interfaces Reset PIM interfaces clear ip pim oil Rescan PIM OIL (output interface list) - clear zclient-update Reset zclient update connection to zebra daemon debug igmp IGMP protocol activity debug mroute PIM interaction with kernel MFC cache debug pim PIM protocol activity diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 6176fe55..6b2ac664 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -1563,17 +1563,6 @@ DEFUN (pim_interface, return CMD_SUCCESS; } -DEFUN (clear_zclient_update, - clear_zclient_update_cmd, - "clear zclient-update", - CLEAR_STR - "Reset zclient update connection to zebra daemon\n") -{ - zclient_reset(qpim_zclient_update); - - return CMD_SUCCESS; -} - DEFUN (clear_ip_interfaces, clear_ip_interfaces_cmd, "clear ip interfaces", @@ -4391,7 +4380,6 @@ void pim_cmd_init() install_element (ENABLE_NODE, &clear_ip_mroute_cmd); install_element (ENABLE_NODE, &clear_ip_pim_interfaces_cmd); install_element (ENABLE_NODE, &clear_ip_pim_oil_cmd); - install_element (ENABLE_NODE, &clear_zclient_update_cmd); install_element (ENABLE_NODE, &show_ip_igmp_interface_cmd); install_element (ENABLE_NODE, &show_ip_igmp_join_cmd); diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index 43c80c8b..6f241d59 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -664,7 +664,6 @@ void pim_zebra_init(char *zebra_sock_path) /* Socket for receiving updates from Zebra daemon */ qpim_zclient_update = zclient_new(); - qpim_zclient_update->zclient_broken = zclient_broken; qpim_zclient_update->router_id_update = pim_router_id_update_zebra; qpim_zclient_update->interface_add = pim_zebra_if_add; qpim_zclient_update->interface_delete = pim_zebra_if_del; From 60b815eb26c4e94e07524a508433e6f66a6d6183 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Wed, 4 Feb 2015 06:53:46 +0100 Subject: [PATCH 433/482] vtysh: add missing pimd define Signed-off-by: David Lamparter --- vtysh/vtysh.h | 1 + 1 file changed, 1 insertion(+) diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h index 5d513c8d..1681a71a 100644 --- a/vtysh/vtysh.h +++ b/vtysh/vtysh.h @@ -30,6 +30,7 @@ #define VTYSH_BGPD 0x20 #define VTYSH_ISISD 0x40 #define VTYSH_BABELD 0x80 +#define VTYSH_PIMD 0x100 #define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_BABELD|VTYSH_PIMD #define VTYSH_RMAP VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_BABELD #define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_BABELD|VTYSH_PIMD From 7d924b422ffdeb37027ac979c6a62d845499fab8 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Wed, 4 Feb 2015 07:00:06 +0100 Subject: [PATCH 434/482] doc: list pimd.8 in EXTRA_DIST Signed-off-by: David Lamparter --- doc/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/Makefile.am b/doc/Makefile.am index dfc5e402..bb7e87a1 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -109,7 +109,7 @@ endif EXTRA_DIST = BGP-TypeCode draft-zebra-00.ms draft-zebra-00.txt \ bgpd.8 isisd.8 ospf6d.8 ospfclient.8 ospfd.8 ripd.8 \ - ripngd.8 vtysh.1 watchquagga.8 zebra.8 \ + ripngd.8 pimd.8 vtysh.1 watchquagga.8 zebra.8 \ mpls/ChangeLog.opaque.txt mpls/cli_summary.txt \ mpls/opaque_lsa.txt mpls/ospfd.conf \ $(figures_sources) $(figures_png) $(figures_txt) From 143637198e333f0c822766b38f9d8cfe75c04e21 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Wed, 4 Feb 2015 07:26:03 +0100 Subject: [PATCH 435/482] tests: fix tests for 1a211cb (missing well-known) Fix tests/aspathtest.c again, this time by including a NEXT_HOP attribute (which is out of correct order with AS_PATH, but that doesn't matter here.) This satisfies bgp_attr_check(), which after 1a211cb refuses updates without nexthop attribute. Fixes: 1a211cb ("one more fix for tightening of check for missing well-known attributes") Cc: Paul Jakma Signed-off-by: David Lamparter --- tests/aspath_test.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/aspath_test.c b/tests/aspath_test.c index 71a31022..0aa3e47e 100644 --- a/tests/aspath_test.c +++ b/tests/aspath_test.c @@ -457,8 +457,11 @@ static struct test_segment { BGP_ATTR_FLAG_TRANS, \ BGP_ATTR_ORIGIN, \ 1, \ - BGP_ORIGIN_EGP -#define COMMON_ATTR_SIZE 4 + BGP_ORIGIN_EGP, \ + BGP_ATTR_FLAG_TRANS, \ + BGP_ATTR_NEXT_HOP, \ + 4, 192, 0, 2, 0 +#define COMMON_ATTR_SIZE 11 /* */ static struct aspath_tests { From cb4fc59c8a0f9df81109d38acbeaab5627e361f5 Mon Sep 17 00:00:00 2001 From: Milan Kocian Date: Mon, 1 Dec 2014 12:48:25 +0000 Subject: [PATCH 436/482] bgpd: fix negative values in output Negative value in output of ecommunities (and as numbers) seems odd :-). This patch fixes it. And add minor formating modification, better for big as numbers. Signed-off-by: Milan Kocian Signed-off-by: David Lamparter --- bgpd/bgp_ecommunity.c | 6 +++--- bgpd/bgp_vty.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index 8a326a8b..482e76b7 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -688,7 +688,7 @@ ecommunity_ecom2str (struct ecommunity *ecom, int format) eas.val = (*pnt++ << 8); eas.val |= (*pnt++); - len = sprintf( str_buf + str_pnt, "%s%u:%d", prefix, + len = sprintf( str_buf + str_pnt, "%s%u:%u", prefix, eas.as, eas.val ); str_pnt += len; first = 0; @@ -703,7 +703,7 @@ ecommunity_ecom2str (struct ecommunity *ecom, int format) eas.val |= (*pnt++ << 8); eas.val |= (*pnt++); - len = sprintf (str_buf + str_pnt, "%s%u:%d", prefix, + len = sprintf (str_buf + str_pnt, "%s%u:%u", prefix, eas.as, eas.val); str_pnt += len; first = 0; @@ -715,7 +715,7 @@ ecommunity_ecom2str (struct ecommunity *ecom, int format) eip.val = (*pnt++ << 8); eip.val |= (*pnt++); - len = sprintf (str_buf + str_pnt, "%s%s:%d", prefix, + len = sprintf (str_buf + str_pnt, "%s%s:%u", prefix, inet_ntoa (eip.ip), eip.val); str_pnt += len; first = 0; diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index ca44774a..e6a36605 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -6937,7 +6937,7 @@ bgp_show_summary (struct vty *vty, struct bgp *bgp, int afi, int safi) int len; /* Header string for each address family. */ - static char header[] = "Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd"; + static char header[] = "Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd"; for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) { @@ -8302,7 +8302,7 @@ bgp_write_rsclient_summary (struct vty *vty, struct peer *rsclient, vty_out (vty, "4 "); - vty_out (vty, "%11d ", rsclient->as); + vty_out (vty, "%10u ", rsclient->as); rmname = ROUTE_MAP_EXPORT_NAME(&rsclient->filter[afi][safi]); if ( rmname && strlen (rmname) > 13 ) @@ -8347,7 +8347,7 @@ bgp_show_rsclient_summary (struct vty *vty, struct bgp *bgp, int count = 0; /* Header string for each address family. */ - static char header[] = "Neighbor V AS Export-Policy Import-Policy Up/Down State"; + static char header[] = "Neighbor V AS Export-Policy Import-Policy Up/Down State"; for (ALL_LIST_ELEMENTS (bgp->rsclient, node, nnode, peer)) { From 86ce951e349fd08d1ba2c66f5f6d07756689422a Mon Sep 17 00:00:00 2001 From: Lu Feng Date: Thu, 8 Jan 2015 01:39:18 +0000 Subject: [PATCH 437/482] ospfd: set O-bit in the option of all DD packets If opaque-capability is enabled, we must set the O-bit in the option field of all DD packets. Changing the option field of DD packets may cause the peer to reset the state back to ExStart. Signed-off-by: Feng Lu Signed-off-by: David Lamparter --- ospfd/ospf_packet.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index 36aa8958..98b1af3b 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -3143,22 +3143,7 @@ ospf_make_db_desc (struct ospf_interface *oi, struct ospf_neighbor *nbr, options = OPTIONS (oi); #ifdef HAVE_OPAQUE_LSA if (CHECK_FLAG (oi->ospf->config, OSPF_OPAQUE_CAPABLE)) - { - if (IS_SET_DD_I (nbr->dd_flags) - || CHECK_FLAG (nbr->options, OSPF_OPTION_O)) - /* - * Set O-bit in the outgoing DD packet for capablity negotiation, - * if one of following case is applicable. - * - * 1) WaitTimer expiration event triggered the neighbor state to - * change to Exstart, but no (valid) DD packet has received - * from the neighbor yet. - * - * 2) At least one DD packet with O-bit on has received from the - * neighbor. - */ - SET_FLAG (options, OSPF_OPTION_O); - } + SET_FLAG (options, OSPF_OPTION_O); #endif /* HAVE_OPAQUE_LSA */ stream_putc (s, options); From 92cff4f7cd7e805e6689e73e63029aaccd145eca Mon Sep 17 00:00:00 2001 From: Lu Feng Date: Thu, 8 Jan 2015 01:21:02 +0000 Subject: [PATCH 438/482] isisd: fix crash on changing the circuit type of a passive interface Signed-off-by: Feng Lu Signed-off-by: David Lamparter --- isisd/isis_events.c | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/isisd/isis_events.c b/isisd/isis_events.c index 3887b7c5..96d5762d 100644 --- a/isisd/isis_events.c +++ b/isisd/isis_events.c @@ -280,26 +280,29 @@ isis_event_circuit_type_change (struct isis_circuit *circuit, int newtype) return; } - switch (circuit->is_type) + if (! circuit->is_passive) { - case IS_LEVEL_1: - if (newtype == IS_LEVEL_2) - circuit_resign_level (circuit, 1); - circuit_commence_level (circuit, 2); - break; - case IS_LEVEL_1_AND_2: - if (newtype == IS_LEVEL_1) - circuit_resign_level (circuit, 2); - else - circuit_resign_level (circuit, 1); - break; - case IS_LEVEL_2: - if (newtype == IS_LEVEL_1) - circuit_resign_level (circuit, 2); - circuit_commence_level (circuit, 1); - break; - default: - break; + switch (circuit->is_type) + { + case IS_LEVEL_1: + if (newtype == IS_LEVEL_2) + circuit_resign_level (circuit, 1); + circuit_commence_level (circuit, 2); + break; + case IS_LEVEL_1_AND_2: + if (newtype == IS_LEVEL_1) + circuit_resign_level (circuit, 2); + else + circuit_resign_level (circuit, 1); + break; + case IS_LEVEL_2: + if (newtype == IS_LEVEL_1) + circuit_resign_level (circuit, 2); + circuit_commence_level (circuit, 1); + break; + default: + break; + } } circuit->is_type = newtype; From 3c28aaf437d8d473adb89c5e74574a61a9ea7cc6 Mon Sep 17 00:00:00 2001 From: Amritha Nambiar Date: Wed, 28 Jan 2015 18:09:30 +0000 Subject: [PATCH 439/482] isisd: match adjacency with source of hellos isis_pdu.c: match adjacency with source of hellos, check for source ID on receiving hello If an adjacency exists, check the adjacency is with the same router as the source of the hellos. In case a mismatch is detected, bring down the adjacency and let the next hellos trigger creating the new adjacency. Signed-off-by: Amritha Nambiar Signed-off-by: David Lamparter --- isisd/isis_pdu.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c index 8d8a5e00..166dd7c0 100644 --- a/isisd/isis_pdu.c +++ b/isisd/isis_pdu.c @@ -566,6 +566,17 @@ process_p2p_hello (struct isis_circuit *circuit) * the circuit */ adj = circuit->u.p2p.neighbor; + /* If an adjacency exists, check it is with the source of the hello + * packets */ + if (adj) + { + if (memcmp(hdr->source_id, adj->sysid, ISIS_SYS_ID_LEN)) + { + zlog_debug("hello source and adjacency do not match, set adj down\n"); + isis_adj_state_change (adj, ISIS_ADJ_DOWN, "adj do not exist"); + return 0; + } + } if (!adj || adj->level != hdr->circuit_t) { if (!adj) From 9481374d4ff7cfbc6274954bff8b0e4c52578911 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Thu, 24 Apr 2014 20:22:53 +0200 Subject: [PATCH 440/482] zebra: factor out rib debug logs Introduces a logging function that takes a struct route_node * argument, and prefixes log output with that node's prefix. While this removes some duplication, it will also later be useful for srcdest route nodes. Behaviour before and after the patch should be exactly identical. Signed-off-by: David Lamparter --- zebra/zebra_rib.c | 114 +++++++++++++++++++++------------------------- 1 file changed, 51 insertions(+), 63 deletions(-) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 245011eb..4ee1a84c 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -75,6 +75,37 @@ static const struct /* Vector for routing table. */ static vector vrf_vector; +static void +_rnode_zlog(const char *_func, struct route_node *rn, int priority, + const char *msgfmt, ...) +{ + char buf[INET6_ADDRSTRLEN + 4], *bptr; + char msgbuf[512]; + va_list ap; + + va_start(ap, msgfmt); + vsnprintf(msgbuf, sizeof(msgbuf), msgfmt, ap); + va_end(ap); + + if (rn) + { + inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); + bptr = buf + strlen(buf); + snprintf(bptr, buf + sizeof(buf) - bptr, "/%d", rn->p.prefixlen); + } + else + { + snprintf(buf, sizeof(buf), "{(route_node *) NULL}"); + } + + zlog (NULL, priority, "%s: %s: %s", _func, buf, msgbuf); +} + +#define rnode_debug(node, ...) \ + _rnode_zlog(__func__, node, LOG_DEBUG, __VA_ARGS__) +#define rnode_info(node, ...) \ + _rnode_zlog(__func__, node, LOG_INFO, __VA_ARGS__) + /* * vrf_table_create */ @@ -1209,7 +1240,6 @@ int rib_gc_dest (struct route_node *rn) { rib_dest_t *dest; - char buf[INET6_ADDRSTRLEN]; dest = rib_dest_from_rnode (rn); if (!dest) @@ -1219,11 +1249,7 @@ rib_gc_dest (struct route_node *rn) return 0; if (IS_ZEBRA_DEBUG_RIB) - { - inet_ntop (rn->p.family, &rn->p.u.prefix, buf, sizeof (buf)); - zlog_debug ("%s: %s/%d: removing dest from table", __func__, - buf, rn->p.prefixlen); - } + rnode_debug (rn, "removing dest from table"); dest->rnode = NULL; XFREE (MTYPE_RIB_DEST, dest); @@ -1248,16 +1274,12 @@ rib_process (struct route_node *rn) int installed = 0; struct nexthop *nexthop = NULL, *tnexthop; int recursing; - char buf[INET6_ADDRSTRLEN]; rib_table_info_t *info; assert (rn); info = rn->table->info; - if (IS_ZEBRA_DEBUG_RIB || IS_ZEBRA_DEBUG_RIB_Q) - inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); - RNODE_FOREACH_RIB_SAFE (rn, rib, next) { /* Currently installed rib. */ @@ -1275,8 +1297,7 @@ rib_process (struct route_node *rn) if (rib != fib) { if (IS_ZEBRA_DEBUG_RIB) - zlog_debug ("%s: %s/%d: rn %p, removing rib %p", __func__, - buf, rn->p.prefixlen, rn, rib); + rnode_debug (rn, "rn %p, removing rib %p", rn, rib); rib_unlink (rn, rib); } else @@ -1350,8 +1371,8 @@ rib_process (struct route_node *rn) if (select && select == fib) { if (IS_ZEBRA_DEBUG_RIB) - zlog_debug ("%s: %s/%d: Updating existing route, select %p, fib %p", - __func__, buf, rn->p.prefixlen, select, fib); + rnode_debug (rn, "Updating existing route, select %p, fib %p", + select, fib); if (CHECK_FLAG (select->flags, ZEBRA_FLAG_CHANGED)) { zfpm_trigger_update (rn, "updating existing route"); @@ -1395,8 +1416,7 @@ rib_process (struct route_node *rn) if (fib) { if (IS_ZEBRA_DEBUG_RIB) - zlog_debug ("%s: %s/%d: Removing existing route, fib %p", __func__, - buf, rn->p.prefixlen, fib); + rnode_debug (rn, "Removing existing route, fib %p", fib); zfpm_trigger_update (rn, "removing existing route"); @@ -1416,8 +1436,7 @@ rib_process (struct route_node *rn) if (select) { if (IS_ZEBRA_DEBUG_RIB) - zlog_debug ("%s: %s/%d: Adding route, select %p", __func__, buf, - rn->p.prefixlen, select); + rnode_debug (rn, "Adding route, select %p", select); zfpm_trigger_update (rn, "new route selected"); @@ -1434,14 +1453,13 @@ rib_process (struct route_node *rn) if (del) { if (IS_ZEBRA_DEBUG_RIB) - zlog_debug ("%s: %s/%d: Deleting fib %p, rn %p", __func__, buf, - rn->p.prefixlen, del, rn); + rnode_debug (rn, "Deleting fib %p, rn %p", del, rn); rib_unlink (rn, del); } end: if (IS_ZEBRA_DEBUG_RIB_Q) - zlog_debug ("%s: %s/%d: rn %p dequeued", __func__, buf, rn->p.prefixlen, rn); + rnode_debug (rn, "rn %p dequeued", rn); /* * Check if the dest can be deleted now. @@ -1525,10 +1543,6 @@ static void rib_meta_queue_add (struct meta_queue *mq, struct route_node *rn) { 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); RNODE_FOREACH_RIB (rn, rib) { @@ -1539,8 +1553,8 @@ rib_meta_queue_add (struct meta_queue *mq, struct route_node *rn) 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); + rnode_debug (rn, "rn %p is already queued in sub-queue %u", + rn, qindex); continue; } @@ -1550,8 +1564,8 @@ rib_meta_queue_add (struct meta_queue *mq, struct route_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); + rnode_debug (rn, "queued rn %p into sub-queue %u", + rn, qindex); } } @@ -1559,11 +1573,7 @@ rib_meta_queue_add (struct meta_queue *mq, struct route_node *rn) static void rib_queue_add (struct zebra_t *zebra, struct route_node *rn) { - char buf[INET_ADDRSTRLEN]; assert (zebra && rn); - - if (IS_ZEBRA_DEBUG_RIB_Q) - inet_ntop (AF_INET, &rn->p.u.prefix, buf, INET_ADDRSTRLEN); /* Pointless to queue a route_node with no RIB entries to add or remove */ if (!rnode_to_ribs (rn)) @@ -1575,7 +1585,7 @@ rib_queue_add (struct zebra_t *zebra, struct route_node *rn) } if (IS_ZEBRA_DEBUG_RIB_Q) - zlog_info ("%s: %s/%d: work queue added", __func__, buf, rn->p.prefixlen); + rnode_info (rn, "work queue added"); assert (zebra); @@ -1599,7 +1609,7 @@ rib_queue_add (struct zebra_t *zebra, struct route_node *rn) 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); + rnode_debug (rn, "rn %p queued", rn); return; } @@ -1696,25 +1706,17 @@ rib_link (struct route_node *rn, struct rib *rib) { struct rib *head; rib_dest_t *dest; - char buf[INET6_ADDRSTRLEN]; assert (rib && rn); if (IS_ZEBRA_DEBUG_RIB) - { - inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); - zlog_debug ("%s: %s/%d: rn %p, rib %p", __func__, - buf, rn->p.prefixlen, rn, rib); - } + rnode_debug (rn, "rn %p, rib %p", rn, rib); dest = rib_dest_from_rnode (rn); if (!dest) { if (IS_ZEBRA_DEBUG_RIB) - { - zlog_debug ("%s: %s/%d: adding dest to table", __func__, - buf, rn->p.prefixlen); - } + rnode_debug (rn, "adding dest to table"); dest = XCALLOC (MTYPE_RIB_DEST, sizeof (rib_dest_t)); route_lock_node (rn); /* rn route table reference */ @@ -1741,12 +1743,8 @@ rib_addnode (struct route_node *rn, struct rib *rib) if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) { if (IS_ZEBRA_DEBUG_RIB) - { - char buf[INET6_ADDRSTRLEN]; - inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); - zlog_debug ("%s: %s/%d: rn %p, un-removed rib %p", - __func__, buf, rn->p.prefixlen, rn, rib); - } + rnode_debug (rn, "rn %p, un-removed rib %p", rn, rib); + UNSET_FLAG (rib->status, RIB_ENTRY_REMOVED); return; } @@ -1765,17 +1763,12 @@ rib_addnode (struct route_node *rn, struct rib *rib) static void rib_unlink (struct route_node *rn, struct rib *rib) { - char buf[INET6_ADDRSTRLEN]; rib_dest_t *dest; assert (rn && rib); if (IS_ZEBRA_DEBUG_RIB) - { - inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); - zlog_debug ("%s: %s/%d: rn %p, rib %p", - __func__, buf, rn->p.prefixlen, rn, rib); - } + rnode_debug (rn, "rn %p, rib %p", rn, rib); dest = rib_dest_from_rnode (rn); @@ -1799,12 +1792,7 @@ static void rib_delnode (struct route_node *rn, struct rib *rib) { if (IS_ZEBRA_DEBUG_RIB) - { - char buf[INET6_ADDRSTRLEN]; - inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); - zlog_debug ("%s: %s/%d: rn %p, rib %p, removing", __func__, - buf, rn->p.prefixlen, rn, rib); - } + rnode_debug (rn, "rn %p, rib %p, removing", rn, rib); SET_FLAG (rib->status, RIB_ENTRY_REMOVED); rib_queue_add (&zebrad, rn); } From ab2ba612320e011abbb1011823b66afc35859081 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Thu, 22 Jan 2015 19:02:13 +0100 Subject: [PATCH 441/482] zebra: identify MRIB on debug messages since the same code handles both URIB and MRIB, the debug messages can get rather confusing if the RIB isn't identified. Mark the MRIB in debug messages so we can distinguish that. Signed-off-by: David Lamparter --- zebra/zebra_rib.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 4ee1a84c..8354513c 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -89,9 +89,12 @@ _rnode_zlog(const char *_func, struct route_node *rn, int priority, if (rn) { + rib_table_info_t *info = rn->table->info; + inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN); bptr = buf + strlen(buf); - snprintf(bptr, buf + sizeof(buf) - bptr, "/%d", rn->p.prefixlen); + snprintf(bptr, buf + sizeof(buf) - bptr, "/%d%s", rn->p.prefixlen, + info->safi == SAFI_MULTICAST ? " (MRIB)" : ""); } else { From 3dea1780c98ab3717c9c61f401b66a9c08a23661 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Mon, 22 Sep 2014 19:35:51 -0300 Subject: [PATCH 442/482] zebra: add rib_match_ipv4_safi() This is the same as rib_lookup_ipv4(), without the SAFI hardcoded. Cc: Balaji G Cc: Everton Marques Signed-off-by: David Lamparter --- zebra/rib.h | 1 + zebra/zebra_rib.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/zebra/rib.h b/zebra/rib.h index d3a83c68..13011e26 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -419,6 +419,7 @@ extern int rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p, u_int32_t, safi_t safi); extern struct rib *rib_match_ipv4 (struct in_addr); +extern struct rib *rib_match_ipv4_safi (struct in_addr addr, safi_t safi); extern struct rib *rib_lookup_ipv4 (struct prefix_ipv4 *); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 8354513c..5b9b00ed 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -781,6 +781,63 @@ rib_match_ipv4 (struct in_addr addr) return NULL; } +struct rib * +rib_match_ipv4_safi (struct in_addr addr, safi_t safi) +{ + struct route_table *table; + struct route_node *rn; + struct rib *match; + struct nexthop *newhop, *tnewhop; + int recursing; + + /* Lookup table. */ + table = vrf_table (AFI_IP, safi, 0); + if (! table) + return 0; + + rn = route_node_match_ipv4 (table, &addr); + + while (rn) + { + route_unlock_node (rn); + + /* Pick up selected route. */ + RNODE_FOREACH_RIB (rn, match) + { + if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED)) + continue; + if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED)) + break; + } + + /* If there is no selected route or matched route is EGP, go up + tree. */ + if (! match + || match->type == ZEBRA_ROUTE_BGP) + { + do { + rn = rn->parent; + } while (rn && rn->info == NULL); + if (rn) + route_lock_node (rn); + } + else + { + if (match->type == ZEBRA_ROUTE_CONNECT) + /* Directly point connected route. */ + return match; + else + { + for (ALL_NEXTHOPS_RO(match->nexthop, newhop, tnewhop, recursing)) + if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB)) + return match; + return NULL; + } + } + } + return NULL; +} + struct rib * rib_lookup_ipv4 (struct prefix_ipv4 *p) { From f9b9234bae058a7d152c51c318997c459f54e59d Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Fri, 21 Nov 2014 15:57:45 -0800 Subject: [PATCH 443/482] zebra: point rib_match_ipv4() to ._safi() Since rib_match_ipv4() is just rib_match_ipv4_safi() for SAFI_UNICAST, the former can be removed and pointed to the latter instead. Cc: Balaji G Cc: Everton Marques Signed-off-by: David Lamparter --- zebra/zebra_rib.c | 59 +---------------------------------------------- 1 file changed, 1 insertion(+), 58 deletions(-) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 5b9b00ed..469c10b2 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -721,64 +721,7 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, struct rib * rib_match_ipv4 (struct in_addr addr) { - struct prefix_ipv4 p; - struct route_table *table; - struct route_node *rn; - struct rib *match; - struct nexthop *newhop, *tnewhop; - int recursing; - - /* Lookup table. */ - table = vrf_table (AFI_IP, SAFI_UNICAST, 0); - if (! table) - return 0; - - memset (&p, 0, sizeof (struct prefix_ipv4)); - p.family = AF_INET; - p.prefixlen = IPV4_MAX_PREFIXLEN; - p.prefix = addr; - - rn = route_node_match (table, (struct prefix *) &p); - - while (rn) - { - route_unlock_node (rn); - - /* Pick up selected route. */ - RNODE_FOREACH_RIB (rn, match) - { - if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED)) - continue; - if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED)) - break; - } - - /* If there is no selected route or matched route is EGP, go up - tree. */ - if (! match - || match->type == ZEBRA_ROUTE_BGP) - { - do { - rn = rn->parent; - } while (rn && rn->info == NULL); - if (rn) - route_lock_node (rn); - } - else - { - if (match->type == ZEBRA_ROUTE_CONNECT) - /* Directly point connected route. */ - return match; - else - { - for (ALL_NEXTHOPS_RO(match->nexthop, newhop, tnewhop, recursing)) - if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB)) - return match; - return NULL; - } - } - } - return NULL; + return rib_match_ipv4_safi (addr, SAFI_UNICAST); } struct rib * From 4e5275befee4acd91edd835a0b037cc2161ff834 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Tue, 1 Jul 2014 15:15:52 -0300 Subject: [PATCH 444/482] zebra: add ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB This adds a new zapi call "ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB" performing a Multicast RPF lookup for a given source. Details of the lookup behaviour are left to the zebra side of things. Note: this is non-reactive, as in, only delivers a snapshot of the state at a particular point in time. There's no push notification of changes happening to the RIB. This combines the following 3 original patches: - zebra: add zsend_ipv4_nexthop_lookup_mrib() - zserv: Query mrib (SAFI_MULTICAST). - zebra: Cleanups to zebra_rib. Cc: Everton Marques Cc: Balaji G Signed-off-by: David Lamparter --- lib/zebra.h | 3 +- zebra/zserv.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 1 deletion(-) diff --git a/lib/zebra.h b/lib/zebra.h index b289a19e..a4e02148 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -425,7 +425,8 @@ struct in_pktinfo #define ZEBRA_ROUTER_ID_DELETE 21 #define ZEBRA_ROUTER_ID_UPDATE 22 #define ZEBRA_HELLO 23 -#define ZEBRA_MESSAGE_MAX 24 +#define ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB 24 +#define ZEBRA_MESSAGE_MAX 25 /* Marker value used in new Zserv, in the byte location corresponding * the command value in the old zserv header. To allow old and new diff --git a/zebra/zserv.c b/zebra/zserv.c index ca17c2c6..89eb266a 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -599,6 +599,89 @@ zsend_ipv4_nexthop_lookup (struct zserv *client, struct in_addr addr) return zebra_server_send_message(client); } +/* + Modified version of zsend_ipv4_nexthop_lookup(): + Query unicast rib if nexthop is not found on mrib. + Returns both route metric and protocol distance. +*/ +static int +zsend_ipv4_nexthop_lookup_mrib (struct zserv *client, struct in_addr addr) +{ + struct stream *s; + struct rib *rib; + unsigned long nump; + u_char num; + struct nexthop *nexthop; + + /* Lookup nexthop. */ + rib = rib_match_ipv4_safi (addr, SAFI_MULTICAST); + + if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: %s mrib entry found.", __func__, rib ? "Matching" : "No matching"); + + if (!rib) { + /* Retry lookup with unicast rib */ + rib = rib_match_ipv4_safi (addr, SAFI_UNICAST); + if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: %s rib entry found.", __func__, rib ? "Matching" : "No matching"); + } + + /* Get output stream. */ + s = client->obuf; + stream_reset (s); + + /* Fill in result. */ + zserv_create_header (s, ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB); + stream_put_in_addr (s, &addr); + + if (rib) + { + stream_putc (s, rib->distance); + stream_putl (s, rib->metric); + num = 0; + nump = stream_get_endp(s); /* remember position for nexthop_num */ + stream_putc (s, 0); /* reserve room for nexthop_num */ + /* Only non-recursive routes are elegible to resolve the nexthop we + * are looking up. Therefore, we will just iterate over the top + * chain of nexthops. */ + for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) + { + stream_putc (s, nexthop->type); + switch (nexthop->type) + { + case ZEBRA_NEXTHOP_IPV4: + stream_put_in_addr (s, &nexthop->gate.ipv4); + break; + case ZEBRA_NEXTHOP_IPV4_IFINDEX: + stream_put_in_addr (s, &nexthop->gate.ipv4); + stream_putl (s, nexthop->ifindex); + break; + case ZEBRA_NEXTHOP_IFINDEX: + case ZEBRA_NEXTHOP_IFNAME: + stream_putl (s, nexthop->ifindex); + break; + default: + /* do nothing */ + break; + } + num++; + } + + stream_putc_at (s, nump, num); /* store nexthop_num */ + } + else + { + stream_putc (s, 0); /* distance */ + stream_putl (s, 0); /* metric */ + stream_putc (s, 0); /* nexthop_num */ + } + + stream_putw_at (s, 0, stream_get_endp (s)); + + return zebra_server_send_message(client); +} + static int zsend_ipv4_import_lookup (struct zserv *client, struct prefix_ipv4 *p) { @@ -920,6 +1003,16 @@ zread_ipv4_nexthop_lookup (struct zserv *client, u_short length) return zsend_ipv4_nexthop_lookup (client, addr); } +/* MRIB Nexthop lookup for IPv4. */ +static int +zread_ipv4_nexthop_lookup_mrib (struct zserv *client, u_short length) +{ + struct in_addr addr; + + addr.s_addr = stream_get_ipv4 (client->ibuf); + return zsend_ipv4_nexthop_lookup_mrib (client, addr); +} + /* Nexthop lookup for IPv4. */ static int zread_ipv4_import_lookup (struct zserv *client, u_short length) @@ -1352,6 +1445,9 @@ zebra_client_read (struct thread *thread) case ZEBRA_IPV4_NEXTHOP_LOOKUP: zread_ipv4_nexthop_lookup (client, length); break; + case ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB: + zread_ipv4_nexthop_lookup_mrib (client, length); + break; #ifdef HAVE_IPV6 case ZEBRA_IPV6_NEXTHOP_LOOKUP: zread_ipv6_nexthop_lookup (client, length); From 83d711234a22a2e7996905667468b0276e5b2c57 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Fri, 19 Sep 2014 16:39:34 -0300 Subject: [PATCH 445/482] zebra: mrib: Include BGP routes in RPF lookups The rib_match_ipv4() function was previously used only for iBGP recursive nexthop lookups, which ignore eBGP routes. This is not desirable for PIM RPF lookups, which may well use an eBGP route. Cc: Everton Marques Cc: Balaji G Signed-off-by: David Lamparter --- zebra/rib.h | 2 +- zebra/zebra_rib.c | 7 +++---- zebra/zserv.c | 5 +++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/zebra/rib.h b/zebra/rib.h index 13011e26..aef71500 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -419,7 +419,7 @@ extern int rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p, u_int32_t, safi_t safi); extern struct rib *rib_match_ipv4 (struct in_addr); -extern struct rib *rib_match_ipv4_safi (struct in_addr addr, safi_t safi); +extern struct rib *rib_match_ipv4_safi (struct in_addr addr, safi_t safi, int skip_bgp); extern struct rib *rib_lookup_ipv4 (struct prefix_ipv4 *); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 469c10b2..f4a91554 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -721,11 +721,11 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, struct rib * rib_match_ipv4 (struct in_addr addr) { - return rib_match_ipv4_safi (addr, SAFI_UNICAST); + return rib_match_ipv4_safi (addr, SAFI_UNICAST, 1); } struct rib * -rib_match_ipv4_safi (struct in_addr addr, safi_t safi) +rib_match_ipv4_safi (struct in_addr addr, safi_t safi, int skip_bgp) { struct route_table *table; struct route_node *rn; @@ -755,8 +755,7 @@ rib_match_ipv4_safi (struct in_addr addr, safi_t safi) /* If there is no selected route or matched route is EGP, go up tree. */ - if (! match - || match->type == ZEBRA_ROUTE_BGP) + if (!match || (skip_bgp && (match->type == ZEBRA_ROUTE_BGP))) { do { rn = rn->parent; diff --git a/zebra/zserv.c b/zebra/zserv.c index 89eb266a..1b693159 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -612,16 +612,17 @@ zsend_ipv4_nexthop_lookup_mrib (struct zserv *client, struct in_addr addr) unsigned long nump; u_char num; struct nexthop *nexthop; + int skip_bgp = 0; /* bool */ /* Lookup nexthop. */ - rib = rib_match_ipv4_safi (addr, SAFI_MULTICAST); + rib = rib_match_ipv4_safi (addr, SAFI_MULTICAST, skip_bgp); if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV) zlog_debug("%s: %s mrib entry found.", __func__, rib ? "Matching" : "No matching"); if (!rib) { /* Retry lookup with unicast rib */ - rib = rib_match_ipv4_safi (addr, SAFI_UNICAST); + rib = rib_match_ipv4_safi (addr, SAFI_UNICAST, skip_bgp); if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV) zlog_debug("%s: %s rib entry found.", __func__, rib ? "Matching" : "No matching"); } From f598cf7ecc8dd72dca08e97eb766e5ccaabe3424 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sat, 22 Nov 2014 14:44:20 -0800 Subject: [PATCH 446/482] zebra: kill rib_match_ipv4() Since this function is internal to zebra, there is no reason to keep this one-line indirect wrapper to rib_match_ipv4_safi() around. Cc: Everton Marques Cc: Balaji G Signed-off-by: David Lamparter --- zebra/rib.h | 1 - zebra/zebra_rib.c | 6 ------ zebra/zserv.c | 4 ++-- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/zebra/rib.h b/zebra/rib.h index aef71500..d40b17e8 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -418,7 +418,6 @@ extern int rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p, struct in_addr *gate, unsigned int ifindex, u_int32_t, safi_t safi); -extern struct rib *rib_match_ipv4 (struct in_addr); extern struct rib *rib_match_ipv4_safi (struct in_addr addr, safi_t safi, int skip_bgp); extern struct rib *rib_lookup_ipv4 (struct prefix_ipv4 *); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index f4a91554..08ce9645 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -718,12 +718,6 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, } #endif /* HAVE_IPV6 */ -struct rib * -rib_match_ipv4 (struct in_addr addr) -{ - return rib_match_ipv4_safi (addr, SAFI_UNICAST, 1); -} - struct rib * rib_match_ipv4_safi (struct in_addr addr, safi_t safi, int skip_bgp) { diff --git a/zebra/zserv.c b/zebra/zserv.c index 1b693159..e9236dec 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -539,8 +539,8 @@ zsend_ipv4_nexthop_lookup (struct zserv *client, struct in_addr addr) u_char num; struct nexthop *nexthop; - /* Lookup nexthop. */ - rib = rib_match_ipv4 (addr); + /* Lookup nexthop - eBGP excluded */ + rib = rib_match_ipv4_safi (addr, SAFI_UNICAST, 1); /* Get output stream. */ s = client->obuf; From 33d86db3df7052da33990b47ad5a171dad6df691 Mon Sep 17 00:00:00 2001 From: Everton Marques Date: Mon, 14 Jul 2014 11:19:00 -0300 Subject: [PATCH 447/482] zebra: mrib: static route support With the MRIB being independent from the Unicast RIB, there's currently now way to add static routes to the MRIB. Address that by adding a separate set of commands for MRIB static routes. Combines these original patches: - zebra: mrib: ip mroute command to add unicast route to MRIB for multicast RPF. - zebra: mrib: no ip mroute: Fix removal of static multicast RPF route. - zebra: mrib: remove unused static_add/delete_ipv4 - zebra: Cleanups to zebra_rib. - pimd: Merge pim-only branch. Cc: Everton Marques Cc: Balaji G Signed-off-by: David Lamparter --- zebra/rib.h | 10 +++--- zebra/zebra_rib.c | 30 ++++++++-------- zebra/zebra_vty.c | 87 +++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 91 insertions(+), 36 deletions(-) diff --git a/zebra/rib.h b/zebra/rib.h index d40b17e8..5eedfde6 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -430,12 +430,12 @@ extern void rib_init (void); extern unsigned long rib_score_proto (u_char proto); extern int -static_add_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, - u_char flags, u_char distance, u_int32_t vrf_id); - +static_add_ipv4_safi (safi_t safi, struct prefix *p, struct in_addr *gate, + const char *ifname, u_char flags, u_char distance, + u_int32_t vrf_id); extern int -static_delete_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, - u_char distance, u_int32_t vrf_id); +static_delete_ipv4_safi (safi_t safi, struct prefix *p, struct in_addr *gate, + const char *ifname, u_char distance, u_int32_t vrf_id); #ifdef HAVE_IPV6 extern int diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 08ce9645..a07036e5 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -2276,14 +2276,14 @@ rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p, /* Install static route into rib. */ static void -static_install_ipv4 (struct prefix *p, struct static_ipv4 *si) +static_install_ipv4 (safi_t safi, struct prefix *p, struct static_ipv4 *si) { struct rib *rib; struct route_node *rn; struct route_table *table; /* Lookup table. */ - table = vrf_table (AFI_IP, SAFI_UNICAST, 0); + table = vrf_table (AFI_IP, safi, 0); if (! table) return; @@ -2368,7 +2368,7 @@ static_ipv4_nexthop_same (struct nexthop *nexthop, struct static_ipv4 *si) /* Uninstall static route from RIB. */ static void -static_uninstall_ipv4 (struct prefix *p, struct static_ipv4 *si) +static_uninstall_ipv4 (safi_t safi, struct prefix *p, struct static_ipv4 *si) { struct route_node *rn; struct rib *rib; @@ -2376,7 +2376,7 @@ static_uninstall_ipv4 (struct prefix *p, struct static_ipv4 *si) struct route_table *table; /* Lookup table. */ - table = vrf_table (AFI_IP, SAFI_UNICAST, 0); + table = vrf_table (AFI_IP, safi, 0); if (! table) return; @@ -2427,10 +2427,10 @@ static_uninstall_ipv4 (struct prefix *p, struct static_ipv4 *si) route_unlock_node (rn); } -/* Add static route into static route configuration. */ int -static_add_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, - u_char flags, u_char distance, u_int32_t vrf_id) +static_add_ipv4_safi (safi_t safi, struct prefix *p, struct in_addr *gate, + const char *ifname, u_char flags, u_char distance, + u_int32_t vrf_id) { u_char type = 0; struct route_node *rn; @@ -2441,7 +2441,7 @@ static_add_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, struct route_table *stable; /* Lookup table. */ - stable = vrf_static_table (AFI_IP, SAFI_UNICAST, vrf_id); + stable = vrf_static_table (AFI_IP, safi, vrf_id); if (! stable) return -1; @@ -2475,7 +2475,7 @@ static_add_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, /* Distance changed. */ if (update) - static_delete_ipv4 (p, gate, ifname, update->distance, vrf_id); + static_delete_ipv4_safi (safi, p, gate, ifname, update->distance, vrf_id); /* Make new static route structure. */ si = XCALLOC (MTYPE_STATIC_IPV4, sizeof (struct static_ipv4)); @@ -2517,15 +2517,14 @@ static_add_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, si->next = cp; /* Install into rib. */ - static_install_ipv4 (p, si); + static_install_ipv4 (safi, p, si); return 1; } -/* Delete static route from static route configuration. */ int -static_delete_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, - u_char distance, u_int32_t vrf_id) +static_delete_ipv4_safi (safi_t safi, struct prefix *p, struct in_addr *gate, + const char *ifname, u_char distance, u_int32_t vrf_id) { u_char type = 0; struct route_node *rn; @@ -2533,7 +2532,7 @@ static_delete_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, struct route_table *stable; /* Lookup table. */ - stable = vrf_static_table (AFI_IP, SAFI_UNICAST, vrf_id); + stable = vrf_static_table (AFI_IP, safi, vrf_id); if (! stable) return -1; @@ -2565,7 +2564,7 @@ static_delete_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, } /* Install into rib. */ - static_uninstall_ipv4 (p, si); + static_uninstall_ipv4 (safi, p, si); /* Unlink static route from linked list. */ if (si->prev) @@ -2586,7 +2585,6 @@ static_delete_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, return 1; } - #ifdef HAVE_IPV6 static int rib_bogus_ipv6 (int type, struct prefix_ipv6 *p, diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 9d6c1ddd..6802eceb 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -30,11 +30,14 @@ #include "zebra/zserv.h" -/* General fucntion for static route. */ +static int do_show_ip_route(struct vty *vty, safi_t safi); + +/* General function for static route. */ static int -zebra_static_ipv4 (struct vty *vty, int add_cmd, const char *dest_str, - const char *mask_str, const char *gate_str, - const char *flag_str, const char *distance_str) +zebra_static_ipv4_safi (struct vty *vty, safi_t safi, int add_cmd, + const char *dest_str, const char *mask_str, + const char *gate_str, const char *flag_str, + const char *distance_str) { int ret; u_char distance; @@ -81,9 +84,9 @@ zebra_static_ipv4 (struct vty *vty, int add_cmd, const char *dest_str, return CMD_WARNING; } if (add_cmd) - static_add_ipv4 (&p, NULL, NULL, ZEBRA_FLAG_BLACKHOLE, distance, 0); + static_add_ipv4_safi (safi, &p, NULL, NULL, ZEBRA_FLAG_BLACKHOLE, distance, 0); else - static_delete_ipv4 (&p, NULL, NULL, distance, 0); + static_delete_ipv4_safi (safi, &p, NULL, NULL, distance, 0); return CMD_SUCCESS; } @@ -107,9 +110,9 @@ zebra_static_ipv4 (struct vty *vty, int add_cmd, const char *dest_str, if (gate_str == NULL) { if (add_cmd) - static_add_ipv4 (&p, NULL, NULL, flag, distance, 0); + static_add_ipv4_safi (safi, &p, NULL, NULL, flag, distance, 0); else - static_delete_ipv4 (&p, NULL, NULL, distance, 0); + static_delete_ipv4_safi (safi, &p, NULL, NULL, distance, 0); return CMD_SUCCESS; } @@ -123,13 +126,58 @@ zebra_static_ipv4 (struct vty *vty, int add_cmd, const char *dest_str, ifname = gate_str; if (add_cmd) - static_add_ipv4 (&p, ifname ? NULL : &gate, ifname, flag, distance, 0); + static_add_ipv4_safi (safi, &p, ifname ? NULL : &gate, ifname, flag, distance, 0); else - static_delete_ipv4 (&p, ifname ? NULL : &gate, ifname, distance, 0); + static_delete_ipv4_safi (safi, &p, ifname ? NULL : &gate, ifname, distance, 0); return CMD_SUCCESS; } +static int +zebra_static_ipv4 (struct vty *vty, int add_cmd, const char *dest_str, + const char *mask_str, const char *gate_str, + const char *flag_str, const char *distance_str) +{ + return zebra_static_ipv4_safi(vty, SAFI_UNICAST, add_cmd, dest_str, mask_str, gate_str, flag_str, distance_str); +} + +/* Static unicast routes for multicast RPF lookup. */ +DEFUN (ip_mroute, + ip_mroute_cmd, + "ip mroute A.B.C.D/M (A.B.C.D|INTERFACE) [<1-255>]", + IP_STR + "Configure static unicast route into MRIB for multicast RPF lookup\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "Nexthop address\n" + "Nexthop interface name\n" + "Distance\n") +{ + return zebra_static_ipv4_safi(vty, SAFI_MULTICAST, 1, argv[0], NULL, argv[1], NULL, argv[2]); +} + +DEFUN (no_ip_mroute, + no_ip_mroute_cmd, + "no ip mroute A.B.C.D/M (A.B.C.D|INTERFACE) [<1-255>]", + IP_STR + "Configure static unicast route into MRIB for multicast RPF lookup\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "Nexthop address\n" + "Nexthop interface name\n" + "Distance\n") +{ + return zebra_static_ipv4_safi(vty, SAFI_MULTICAST, 0, argv[0], NULL, argv[1], NULL, argv[2]); +} + +DEFUN (show_ip_rpf, + show_ip_rpf_cmd, + "show ip rpf", + SHOW_STR + IP_STR + "Display RPF information for multicast source\n") +{ + return do_show_ip_route(vty, SAFI_MULTICAST); +} + /* Static route configuration. */ DEFUN (ip_route, ip_route_cmd, @@ -788,12 +836,16 @@ DEFUN (show_ip_route, IP_STR "IP routing table\n") { + return do_show_ip_route(vty, SAFI_UNICAST); +} + +static int do_show_ip_route(struct vty *vty, safi_t safi) { struct route_table *table; struct route_node *rn; struct rib *rib; int first = 1; - table = vrf_table (AFI_IP, SAFI_UNICAST, 0); + table = vrf_table (AFI_IP, safi, 0); if (! table) return CMD_SUCCESS; @@ -1195,7 +1247,7 @@ DEFUN (show_ip_route_summary_prefix, /* Write IPv4 static route configuration. */ static int -static_config_ipv4 (struct vty *vty) +static_config_ipv4 (struct vty *vty, safi_t safi, const char *cmd) { struct route_node *rn; struct static_ipv4 *si; @@ -1205,14 +1257,14 @@ static_config_ipv4 (struct vty *vty) write = 0; /* Lookup table. */ - stable = vrf_static_table (AFI_IP, SAFI_UNICAST, 0); + stable = vrf_static_table (AFI_IP, safi, 0); if (! stable) return -1; for (rn = route_top (stable); rn; rn = route_next (rn)) for (si = rn->info; si; si = si->next) { - vty_out (vty, "ip route %s/%d", inet_ntoa (rn->p.u.prefix4), + vty_out (vty, "%s %s/%d", cmd, inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen); switch (si->type) @@ -2146,7 +2198,8 @@ zebra_ip_config (struct vty *vty) { int write = 0; - write += static_config_ipv4 (vty); + write += static_config_ipv4 (vty, SAFI_UNICAST, "ip route"); + write += static_config_ipv4 (vty, SAFI_MULTICAST, "ip mroute"); #ifdef HAVE_IPV6 write += static_config_ipv6 (vty); #endif /* HAVE_IPV6 */ @@ -2185,6 +2238,8 @@ zebra_vty_init (void) install_node (&ip_node, zebra_ip_config); install_node (&protocol_node, config_write_protocol); + install_element (CONFIG_NODE, &ip_mroute_cmd); + install_element (CONFIG_NODE, &no_ip_mroute_cmd); install_element (CONFIG_NODE, &ip_protocol_cmd); install_element (CONFIG_NODE, &no_ip_protocol_cmd); install_element (VIEW_NODE, &show_ip_protocol_cmd); @@ -2233,6 +2288,8 @@ zebra_vty_init (void) install_element (VIEW_NODE, &show_ip_mroute_cmd); install_element (ENABLE_NODE, &show_ip_mroute_cmd); + install_element (VIEW_NODE, &show_ip_rpf_cmd); + install_element (ENABLE_NODE, &show_ip_rpf_cmd); #ifdef HAVE_IPV6 install_element (CONFIG_NODE, &ipv6_route_cmd); From a76681b66746a8b1cbaea7032044b93958473aa1 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Thu, 22 Jan 2015 19:03:53 +0100 Subject: [PATCH 448/482] zebra: fix optional distance on static mrib route Unfortunately, the quagga CLI parser doesn't support [<1-255>]. Fix by working around with an alias. Replaces the following commits: - zebra: mrib: [no] ip mroute - require distance. - zebra: mrib: [no] ip mroute - make distance optional. (Rewritten as alias) Cc: Everton Marques Cc: Balaji G Signed-off-by: David Lamparter --- zebra/zebra_vty.c | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 6802eceb..69245a54 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -142,9 +142,9 @@ zebra_static_ipv4 (struct vty *vty, int add_cmd, const char *dest_str, } /* Static unicast routes for multicast RPF lookup. */ -DEFUN (ip_mroute, - ip_mroute_cmd, - "ip mroute A.B.C.D/M (A.B.C.D|INTERFACE) [<1-255>]", +DEFUN (ip_mroute_dist, + ip_mroute_dist_cmd, + "ip mroute A.B.C.D/M (A.B.C.D|INTERFACE) <1-255>", IP_STR "Configure static unicast route into MRIB for multicast RPF lookup\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" @@ -152,12 +152,22 @@ DEFUN (ip_mroute, "Nexthop interface name\n" "Distance\n") { - return zebra_static_ipv4_safi(vty, SAFI_MULTICAST, 1, argv[0], NULL, argv[1], NULL, argv[2]); + return zebra_static_ipv4_safi(vty, SAFI_MULTICAST, 1, argv[0], NULL, argv[1], + NULL, argc > 2 ? argv[2] : NULL); } -DEFUN (no_ip_mroute, - no_ip_mroute_cmd, - "no ip mroute A.B.C.D/M (A.B.C.D|INTERFACE) [<1-255>]", +ALIAS (ip_mroute_dist, + ip_mroute_cmd, + "ip mroute A.B.C.D/M (A.B.C.D|INTERFACE)", + IP_STR + "Configure static unicast route into MRIB for multicast RPF lookup\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "Nexthop address\n" + "Nexthop interface name\n") + +DEFUN (no_ip_mroute_dist, + no_ip_mroute_dist_cmd, + "no ip mroute A.B.C.D/M (A.B.C.D|INTERFACE) <1-255>", IP_STR "Configure static unicast route into MRIB for multicast RPF lookup\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" @@ -165,9 +175,20 @@ DEFUN (no_ip_mroute, "Nexthop interface name\n" "Distance\n") { - return zebra_static_ipv4_safi(vty, SAFI_MULTICAST, 0, argv[0], NULL, argv[1], NULL, argv[2]); + return zebra_static_ipv4_safi(vty, SAFI_MULTICAST, 0, argv[0], NULL, argv[1], + NULL, argc > 2 ? argv[2] : NULL); } +ALIAS (no_ip_mroute_dist, + no_ip_mroute_cmd, + "no ip mroute A.B.C.D/M (A.B.C.D|INTERFACE)", + NO_STR + IP_STR + "Configure static unicast route into MRIB for multicast RPF lookup\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "Nexthop address\n" + "Nexthop interface name\n") + DEFUN (show_ip_rpf, show_ip_rpf_cmd, "show ip rpf", @@ -2239,7 +2260,9 @@ zebra_vty_init (void) install_node (&protocol_node, config_write_protocol); install_element (CONFIG_NODE, &ip_mroute_cmd); + install_element (CONFIG_NODE, &ip_mroute_dist_cmd); install_element (CONFIG_NODE, &no_ip_mroute_cmd); + install_element (CONFIG_NODE, &no_ip_mroute_dist_cmd); install_element (CONFIG_NODE, &ip_protocol_cmd); install_element (CONFIG_NODE, &no_ip_protocol_cmd); install_element (VIEW_NODE, &show_ip_protocol_cmd); From 7ce9e6a3e8f0318656c1e619f48f3935e41638f1 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Mon, 12 Jan 2015 07:05:06 +0100 Subject: [PATCH 449/482] zebra: dummy kernel "install" multicast routes This is a followup to 9511633 ("zebra: MBGP routes should not be installed in the kernel"), which was correct in disabling MRIB routes being installed in the kernel, yet broke the MRIB since now routes were never marked as active. Hence, push down the check into the kernel install functions, so that the routes are still marked active. At the same time, the FPM calls get a check each since otherwise we'd bump the FPM interface on MRIB updates. Fixes: 9511633 ("zebra: MBGP routes should not be installed in the kernel") Cc: Everton Marques Cc: Balaji G Signed-off-by: David Lamparter --- zebra/zebra_rib.c | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index a07036e5..b4ea2424 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -1124,8 +1124,16 @@ rib_install_kernel (struct route_node *rn, struct rib *rib) { int ret = 0; struct nexthop *nexthop, *tnexthop; + rib_table_info_t *info = rn->table->info; int recursing; + if (info->safi != SAFI_UNICAST) + { + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); + return; + } + /* * Make sure we update the FPM any time we send new information to * the kernel. @@ -1157,8 +1165,16 @@ rib_uninstall_kernel (struct route_node *rn, struct rib *rib) { int ret = 0; struct nexthop *nexthop, *tnexthop; + rib_table_info_t *info = rn->table->info; int recursing; + if (info->safi != SAFI_UNICAST) + { + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); + return ret; + } + /* * Make sure we update the FPM any time we send new information to * the kernel. @@ -1187,9 +1203,12 @@ rib_uninstall_kernel (struct route_node *rn, struct rib *rib) static void rib_uninstall (struct route_node *rn, struct rib *rib) { + rib_table_info_t *info = rn->table->info; + if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) { - zfpm_trigger_update (rn, "rib_uninstall"); + if (info->safi == SAFI_UNICAST) + zfpm_trigger_update (rn, "rib_uninstall"); redistribute_delete (&rn->p, rib); if (! RIB_SYSTEM_ROUTE (rib)) @@ -1306,9 +1325,6 @@ rib_process (struct route_node *rn) if (! nexthop_active_update (rn, rib, 0)) continue; - if (info->safi == SAFI_MULTICAST) - continue; - /* Infinit distance. */ if (rib->distance == DISTANCE_INFINITY) continue; @@ -1371,7 +1387,8 @@ rib_process (struct route_node *rn) select, fib); if (CHECK_FLAG (select->flags, ZEBRA_FLAG_CHANGED)) { - zfpm_trigger_update (rn, "updating existing route"); + if (info->safi == SAFI_UNICAST) + zfpm_trigger_update (rn, "updating existing route"); redistribute_delete (&rn->p, select); if (! RIB_SYSTEM_ROUTE (select)) @@ -1414,7 +1431,8 @@ rib_process (struct route_node *rn) if (IS_ZEBRA_DEBUG_RIB) rnode_debug (rn, "Removing existing route, fib %p", fib); - zfpm_trigger_update (rn, "removing existing route"); + if (info->safi == SAFI_UNICAST) + zfpm_trigger_update (rn, "removing existing route"); redistribute_delete (&rn->p, fib); if (! RIB_SYSTEM_ROUTE (fib)) @@ -1434,7 +1452,8 @@ rib_process (struct route_node *rn) if (IS_ZEBRA_DEBUG_RIB) rnode_debug (rn, "Adding route, select %p", select); - zfpm_trigger_update (rn, "new route selected"); + if (info->safi == SAFI_UNICAST) + zfpm_trigger_update (rn, "new route selected"); /* Set real nexthop. */ nexthop_active_update (rn, select, 1); @@ -3267,6 +3286,7 @@ static void rib_close_table (struct route_table *table) { struct route_node *rn; + rib_table_info_t *info = table->info; struct rib *rib; if (table) @@ -3276,7 +3296,8 @@ rib_close_table (struct route_table *table) if (!CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) continue; - zfpm_trigger_update (rn, NULL); + if (info->safi == SAFI_UNICAST) + zfpm_trigger_update (rn, NULL); if (! RIB_SYSTEM_ROUTE (rib)) rib_uninstall_kernel (rn, rib); From 24480d426046e46fbcec098be1147650d6d3ff50 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Thu, 22 Jan 2015 19:09:36 +0100 Subject: [PATCH 450/482] zebra: return route_node from rib_match_ipv4_safi The multicast code needs to know the route_node in addition to the rib entry in order to perform distance or prefix-length comparisons. Add it as optional "out" pointer parameter. Cc: Everton Marques Cc: Balaji G Signed-off-by: David Lamparter --- zebra/rib.h | 3 ++- zebra/zebra_rib.c | 21 ++++++++++++++------- zebra/zserv.c | 4 ++-- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/zebra/rib.h b/zebra/rib.h index 5eedfde6..347fadb0 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -418,7 +418,8 @@ extern int rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p, struct in_addr *gate, unsigned int ifindex, u_int32_t, safi_t safi); -extern struct rib *rib_match_ipv4_safi (struct in_addr addr, safi_t safi, int skip_bgp); +extern struct rib *rib_match_ipv4_safi (struct in_addr addr, safi_t safi, + int skip_bgp, struct route_node **rn_out); extern struct rib *rib_lookup_ipv4 (struct prefix_ipv4 *); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index b4ea2424..abef90fb 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -719,7 +719,8 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, #endif /* HAVE_IPV6 */ struct rib * -rib_match_ipv4_safi (struct in_addr addr, safi_t safi, int skip_bgp) +rib_match_ipv4_safi (struct in_addr addr, safi_t safi, int skip_bgp, + struct route_node **rn_out) { struct route_table *table; struct route_node *rn; @@ -759,16 +760,22 @@ rib_match_ipv4_safi (struct in_addr addr, safi_t safi, int skip_bgp) } else { - if (match->type == ZEBRA_ROUTE_CONNECT) - /* Directly point connected route. */ - return match; - else + if (match->type != ZEBRA_ROUTE_CONNECT) { + int found = 0; for (ALL_NEXTHOPS_RO(match->nexthop, newhop, tnewhop, recursing)) if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB)) - return match; - return NULL; + { + found = 1; + break; + } + if (!found) + return NULL; } + + if (rn_out) + *rn_out = rn; + return match; } } return NULL; diff --git a/zebra/zserv.c b/zebra/zserv.c index e9236dec..e678f3a3 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -540,7 +540,7 @@ zsend_ipv4_nexthop_lookup (struct zserv *client, struct in_addr addr) struct nexthop *nexthop; /* Lookup nexthop - eBGP excluded */ - rib = rib_match_ipv4_safi (addr, SAFI_UNICAST, 1); + rib = rib_match_ipv4_safi (addr, SAFI_UNICAST, 1, NULL); /* Get output stream. */ s = client->obuf; @@ -615,7 +615,7 @@ zsend_ipv4_nexthop_lookup_mrib (struct zserv *client, struct in_addr addr) int skip_bgp = 0; /* bool */ /* Lookup nexthop. */ - rib = rib_match_ipv4_safi (addr, SAFI_MULTICAST, skip_bgp); + rib = rib_match_ipv4_safi (addr, SAFI_MULTICAST, skip_bgp, NULL); if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV) zlog_debug("%s: %s mrib entry found.", __func__, rib ? "Matching" : "No matching"); From bd0781296703cf2eddebced34258a1897a03b535 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 6 Jan 2015 19:53:24 +0100 Subject: [PATCH 451/482] zebra: make MRIB lookup behaviour switchable depending on the usage scenario (and availability of multitopology IGP protocols, which is currently zero in Quagga), different approaches of Multicast RPF lookups are useful. Reference behaviours from commercial vendors are urib-only/mrib-only (Juniper, depending on inet.2 availability) and lowest-distance (Cisco). As we are currently without MT IGP support, mrib-first seems the most useful default for Quagga. Cc: Everton Marques Cc: Balaji G Signed-off-by: David Lamparter --- zebra/rib.h | 17 +++++++++++ zebra/zebra_rib.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++ zebra/zebra_vty.c | 76 ++++++++++++++++++++++++++++++++++++++++++++--- zebra/zserv.c | 22 ++++---------- 4 files changed, 169 insertions(+), 21 deletions(-) diff --git a/zebra/rib.h b/zebra/rib.h index 347fadb0..94a74194 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -373,6 +373,21 @@ typedef struct rib_tables_iter_t_ rib_tables_iter_state_t state; } rib_tables_iter_t; +/* RPF lookup behaviour */ +enum multicast_mode +{ + MCAST_NO_CONFIG = 0, /* MIX_MRIB_FIRST, but no show in config write */ + MCAST_MRIB_ONLY, /* MRIB only */ + MCAST_URIB_ONLY, /* URIB only */ + MCAST_MIX_MRIB_FIRST, /* MRIB, if nothing at all then URIB */ + MCAST_MIX_DISTANCE, /* MRIB & URIB, lower distance wins */ + MCAST_MIX_PFXLEN, /* MRIB & URIB, longer prefix wins */ + /* on equal value, MRIB wins for last 2 */ +}; + +extern void multicast_mode_ipv4_set (enum multicast_mode mode); +extern enum multicast_mode multicast_mode_ipv4_get (void); + extern const char *nexthop_type_to_str (enum nexthop_types_t nh_type); extern struct nexthop *nexthop_ifindex_add (struct rib *, unsigned int); extern struct nexthop *nexthop_ifname_add (struct rib *, char *); @@ -420,6 +435,8 @@ extern int rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p, extern struct rib *rib_match_ipv4_safi (struct in_addr addr, safi_t safi, int skip_bgp, struct route_node **rn_out); +extern struct rib *rib_match_ipv4_multicast (struct in_addr addr, + struct route_node **rn_out); extern struct rib *rib_lookup_ipv4 (struct prefix_ipv4 *); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index abef90fb..effe2338 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -75,6 +75,9 @@ static const struct /* Vector for routing table. */ static vector vrf_vector; +/* RPF lookup behaviour */ +static enum multicast_mode ipv4_multicast_mode = MCAST_NO_CONFIG; + static void _rnode_zlog(const char *_func, struct route_node *rn, int priority, const char *msgfmt, ...) @@ -781,6 +784,78 @@ rib_match_ipv4_safi (struct in_addr addr, safi_t safi, int skip_bgp, return NULL; } +struct rib * +rib_match_ipv4_multicast (struct in_addr addr, struct route_node **rn_out) +{ + struct rib *rib = NULL, *mrib = NULL, *urib = NULL; + struct route_node *m_rn = NULL, *u_rn = NULL; + int skip_bgp = 0; /* bool */ + + switch (ipv4_multicast_mode) + { + case MCAST_MRIB_ONLY: + return rib_match_ipv4_safi (addr, SAFI_MULTICAST, skip_bgp, rn_out); + case MCAST_URIB_ONLY: + return rib_match_ipv4_safi (addr, SAFI_UNICAST, skip_bgp, rn_out); + case MCAST_NO_CONFIG: + case MCAST_MIX_MRIB_FIRST: + rib = mrib = rib_match_ipv4_safi (addr, SAFI_MULTICAST, skip_bgp, &m_rn); + if (!mrib) + rib = urib = rib_match_ipv4_safi (addr, SAFI_UNICAST, skip_bgp, &u_rn); + break; + case MCAST_MIX_DISTANCE: + mrib = rib_match_ipv4_safi (addr, SAFI_MULTICAST, skip_bgp, &m_rn); + urib = rib_match_ipv4_safi (addr, SAFI_UNICAST, skip_bgp, &u_rn); + if (mrib && urib) + rib = urib->distance < mrib->distance ? urib : mrib; + else if (mrib) + rib = mrib; + else if (urib) + rib = urib; + break; + case MCAST_MIX_PFXLEN: + mrib = rib_match_ipv4_safi (addr, SAFI_MULTICAST, skip_bgp, &m_rn); + urib = rib_match_ipv4_safi (addr, SAFI_UNICAST, skip_bgp, &u_rn); + if (mrib && urib) + rib = u_rn->p.prefixlen > m_rn->p.prefixlen ? urib : mrib; + else if (mrib) + rib = mrib; + else if (urib) + rib = urib; + break; + } + + if (rn_out) + *rn_out = (rib == mrib) ? m_rn : u_rn; + + if (IS_ZEBRA_DEBUG_RIB) + { + char buf[BUFSIZ]; + inet_ntop (AF_INET, &addr, buf, BUFSIZ); + + zlog_debug("%s: %s: found %s, using %s", + __func__, buf, + mrib ? (urib ? "MRIB+URIB" : "MRIB") : + urib ? "URIB" : "nothing", + rib == urib ? "URIB" : rib == mrib ? "MRIB" : "none"); + } + return rib; +} + +void +multicast_mode_ipv4_set (enum multicast_mode mode) +{ + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug("%s: multicast lookup mode set (%d)", __func__, mode); + ipv4_multicast_mode = mode; +} + +enum multicast_mode +multicast_mode_ipv4_get (void) +{ + return ipv4_multicast_mode; +} + struct rib * rib_lookup_ipv4 (struct prefix_ipv4 *p) { diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 69245a54..f00e35ef 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -189,6 +189,62 @@ ALIAS (no_ip_mroute_dist, "Nexthop address\n" "Nexthop interface name\n") +DEFUN (ip_multicast_mode, + ip_multicast_mode_cmd, + "ip multicast rpf-lookup-mode (urib-only|mrib-only|mrib-then-urib|lower-distance|longer-prefix)", + IP_STR + "Multicast options\n" + "RPF lookup behavior\n" + "Lookup in unicast RIB only\n" + "Lookup in multicast RIB only\n" + "Try multicast RIB first, fall back to unicast RIB\n" + "Lookup both, use entry with lower distance\n" + "Lookup both, use entry with longer prefix\n") +{ + if (!strncmp (argv[0], "u", 1)) + multicast_mode_ipv4_set (MCAST_URIB_ONLY); + else if (!strncmp (argv[0], "mrib-o", 6)) + multicast_mode_ipv4_set (MCAST_MRIB_ONLY); + else if (!strncmp (argv[0], "mrib-t", 6)) + multicast_mode_ipv4_set (MCAST_MIX_MRIB_FIRST); + else if (!strncmp (argv[0], "low", 3)) + multicast_mode_ipv4_set (MCAST_MIX_DISTANCE); + else if (!strncmp (argv[0], "lon", 3)) + multicast_mode_ipv4_set (MCAST_MIX_PFXLEN); + else + { + vty_out (vty, "Invalid mode specified%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN (no_ip_multicast_mode, + no_ip_multicast_mode_cmd, + "no ip multicast rpf-lookup-mode (urib-only|mrib-only|mrib-then-urib|lower-distance|longer-prefix)", + NO_STR + IP_STR + "Multicast options\n" + "RPF lookup behavior\n" + "Lookup in unicast RIB only\n" + "Lookup in multicast RIB only\n" + "Try multicast RIB first, fall back to unicast RIB\n" + "Lookup both, use entry with lower distance\n" + "Lookup both, use entry with longer prefix\n") +{ + multicast_mode_ipv4_set (MCAST_NO_CONFIG); + return CMD_SUCCESS; +} + +ALIAS (no_ip_multicast_mode, + no_ip_multicast_mode_noarg_cmd, + "no ip multicast rpf-lookup-mode", + NO_STR + IP_STR + "Multicast options\n" + "RPF lookup behavior\n") + DEFUN (show_ip_rpf, show_ip_rpf_cmd, "show ip rpf", @@ -2228,10 +2284,19 @@ zebra_ip_config (struct vty *vty) return write; } -/* ip protocol configuration write function */ -static int config_write_protocol(struct vty *vty) -{ +static int config_write_vty(struct vty *vty) +{ int i; + enum multicast_mode ipv4_multicast_mode = multicast_mode_ipv4_get (); + + if (ipv4_multicast_mode != MCAST_NO_CONFIG) + vty_out (vty, "ip multicast rpf-lookup-mode %s%s", + ipv4_multicast_mode == MCAST_URIB_ONLY ? "urib-only" : + ipv4_multicast_mode == MCAST_MRIB_ONLY ? "mrib-only" : + ipv4_multicast_mode == MCAST_MIX_MRIB_FIRST ? "mrib-then-urib" : + ipv4_multicast_mode == MCAST_MIX_DISTANCE ? "lower-distance" : + "longer-prefix", + VTY_NEWLINE); for (i=0;iobuf; @@ -1009,9 +995,11 @@ static int zread_ipv4_nexthop_lookup_mrib (struct zserv *client, u_short length) { struct in_addr addr; + struct rib *rib; addr.s_addr = stream_get_ipv4 (client->ibuf); - return zsend_ipv4_nexthop_lookup_mrib (client, addr); + rib = rib_match_ipv4_multicast (addr, NULL); + return zsend_ipv4_nexthop_lookup_mrib (client, addr, rib); } /* Nexthop lookup for IPv4. */ From 3b02fe84aae567c56ef63e74cdb0dc63c66e2968 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Thu, 22 Jan 2015 19:12:35 +0100 Subject: [PATCH 452/482] zebra: add "show ip rpf" to get result of RPF lookup Checking what route exactly a RPF lookup for a given source uses is essential for an administrator to debug multicast routing issues. This command provides exactly that, using the multicst RPF lookup function and printing out its result to the CLI. Cc: Everton Marques Cc: Balaji G Signed-off-by: David Lamparter --- zebra/zebra_vty.c | 52 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index f00e35ef..29c01c3c 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -31,6 +31,8 @@ #include "zebra/zserv.h" static int do_show_ip_route(struct vty *vty, safi_t safi); +static void vty_show_ip_route_detail (struct vty *vty, struct route_node *rn, + int mcast); /* General function for static route. */ static int @@ -255,6 +257,36 @@ DEFUN (show_ip_rpf, return do_show_ip_route(vty, SAFI_MULTICAST); } +DEFUN (show_ip_rpf_addr, + show_ip_rpf_addr_cmd, + "show ip rpf A.B.C.D", + SHOW_STR + IP_STR + "Display RPF information for multicast source\n" + "IP multicast source address (e.g. 10.0.0.0)\n") +{ + struct in_addr addr; + struct route_node *rn; + struct rib *rib; + int ret; + + ret = inet_aton (argv[0], &addr); + if (ret == 0) + { + vty_out (vty, "%% Malformed address%s", VTY_NEWLINE); + return CMD_WARNING; + } + + rib = rib_match_ipv4_multicast (addr, &rn); + + if (rib) + vty_show_ip_route_detail (vty, rn, 1); + else + vty_out (vty, "%% No match for RPF lookup%s", VTY_NEWLINE); + + return CMD_SUCCESS; +} + /* Static route configuration. */ DEFUN (ip_route, ip_route_cmd, @@ -655,7 +687,7 @@ DEFUN (no_ip_protocol, /* New RIB. Detailed information for IPv4 route. */ static void -vty_show_ip_route_detail (struct vty *vty, struct route_node *rn) +vty_show_ip_route_detail (struct vty *vty, struct route_node *rn, int mcast) { struct rib *rib; struct nexthop *nexthop, *tnexthop; @@ -663,8 +695,16 @@ vty_show_ip_route_detail (struct vty *vty, struct route_node *rn) RNODE_FOREACH_RIB (rn, rib) { - vty_out (vty, "Routing entry for %s/%d%s", - inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen, + const char *mcast_info; + if (mcast) + { + rib_table_info_t *info = rn->table->info; + mcast_info = (info->safi == SAFI_MULTICAST) + ? " using Multicast RIB" + : " using Unicast RIB"; + } + vty_out (vty, "Routing entry for %s/%d%s%s", + inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen, mcast_info, VTY_NEWLINE); vty_out (vty, " Known via \"%s\"", zebra_route_string (rib->type)); vty_out (vty, ", distance %u, metric %u", rib->distance, rib->metric); @@ -1092,7 +1132,7 @@ DEFUN (show_ip_route_addr, return CMD_WARNING; } - vty_show_ip_route_detail (vty, rn); + vty_show_ip_route_detail (vty, rn, 0); route_unlock_node (rn); @@ -1132,7 +1172,7 @@ DEFUN (show_ip_route_prefix, return CMD_WARNING; } - vty_show_ip_route_detail (vty, rn); + vty_show_ip_route_detail (vty, rn, 0); route_unlock_node (rn); @@ -2381,6 +2421,8 @@ zebra_vty_init (void) install_element (VIEW_NODE, &show_ip_rpf_cmd); install_element (ENABLE_NODE, &show_ip_rpf_cmd); + install_element (VIEW_NODE, &show_ip_rpf_addr_cmd); + install_element (ENABLE_NODE, &show_ip_rpf_addr_cmd); #ifdef HAVE_IPV6 install_element (CONFIG_NODE, &ipv6_route_cmd); From 863f20c326758c8a97e0a7a41c87355b66c4012d Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 27 Jan 2015 20:24:15 +0100 Subject: [PATCH 453/482] zebra: mark multicast commands experimental depending on feedback from actually having these commands in a released version, we may want to adjust them. Thus, mark them as experimental so users are aware of this. Cc: Everton Marques Cc: Balaji G Signed-off-by: David Lamparter --- lib/vty.h | 8 ++++++++ zebra/zebra_vty.c | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/lib/vty.h b/lib/vty.h index 4d6048c9..f31f4b5d 100644 --- a/lib/vty.h +++ b/lib/vty.h @@ -217,6 +217,14 @@ do { } \ } while (0) +#define VTY_WARN_EXPERIMENTAL() \ +do { \ + vty_out (vty, "%% WARNING: this command is experimental. Both its name and" \ + " parameters may%s%% change in a future version of Quagga," \ + " possibly breaking your configuration!%s", \ + VTY_NEWLINE, VTY_NEWLINE); \ +} while (0) + /* Exported variables */ extern char integrate_default[]; diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 29c01c3c..598b40de 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -154,6 +154,7 @@ DEFUN (ip_mroute_dist, "Nexthop interface name\n" "Distance\n") { + VTY_WARN_EXPERIMENTAL(); return zebra_static_ipv4_safi(vty, SAFI_MULTICAST, 1, argv[0], NULL, argv[1], NULL, argc > 2 ? argv[2] : NULL); } @@ -177,6 +178,7 @@ DEFUN (no_ip_mroute_dist, "Nexthop interface name\n" "Distance\n") { + VTY_WARN_EXPERIMENTAL(); return zebra_static_ipv4_safi(vty, SAFI_MULTICAST, 0, argv[0], NULL, argv[1], NULL, argc > 2 ? argv[2] : NULL); } @@ -203,6 +205,8 @@ DEFUN (ip_multicast_mode, "Lookup both, use entry with lower distance\n" "Lookup both, use entry with longer prefix\n") { + VTY_WARN_EXPERIMENTAL(); + if (!strncmp (argv[0], "u", 1)) multicast_mode_ipv4_set (MCAST_URIB_ONLY); else if (!strncmp (argv[0], "mrib-o", 6)) @@ -254,6 +258,7 @@ DEFUN (show_ip_rpf, IP_STR "Display RPF information for multicast source\n") { + VTY_WARN_EXPERIMENTAL(); return do_show_ip_route(vty, SAFI_MULTICAST); } @@ -270,6 +275,8 @@ DEFUN (show_ip_rpf_addr, struct rib *rib; int ret; + VTY_WARN_EXPERIMENTAL(); + ret = inet_aton (argv[0], &addr); if (ret == 0) { From 3a27aae7e479b2fa09cc9f27c439b9dfdb383364 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Fri, 30 Jan 2015 01:44:25 +0100 Subject: [PATCH 454/482] doc: zebra multicast RIB commands Signed-off-by: David Lamparter --- doc/main.texi | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/doc/main.texi b/doc/main.texi index a6bf0d1c..810866aa 100644 --- a/doc/main.texi +++ b/doc/main.texi @@ -10,6 +10,7 @@ different routing protocols. * Invoking zebra:: Running the program * Interface Commands:: Commands for zebra interfaces * Static Route Commands:: Commands for adding static routes +* Multicast RIB Commands:: Commands for controlling MRIB behavior * zebra Route Filtering:: Commands for zebra route filtering * zebra FIB push interface:: Interface to optional FPM component * zebra Terminal Mode Commands:: Commands for zebra's VTY @@ -185,6 +186,81 @@ and later). After setting @var{tableno} with this command, static routes defined after this are added to the specified table. @end deffn +@node Multicast RIB Commands +@section Multicast RIB Commands + +The Multicast RIB provides a separate table of unicast destinations which +is used for Multicast Reverse Path Forwarding decisions. It is used with +a multicast source's IP address, hence contains not multicast group +addresses but unicast addresses. + +This table is fully separate from the default unicast table. However, +RPF lookup can include the unicast table. + +WARNING: RPF lookup results are non-responsive in this version of Quagga, +i.e. multicast routing does not actively react to changes in underlying +unicast topology! + +@deffn Command {ip multicast rpf-lookup-mode @var{mode}} {} +@deffnx Command {no ip multicast rpf-lookup-mode [@var{mode}]} {} + +@var{mode} sets the method used to perform RPF lookups. Supported modes: + +@table @samp +@item urib-only +Performs the lookup on the Unicast RIB. The Multicast RIB is never used. +@item mrib-only +Performs the lookup on the Multicast RIB. The Unicast RIB is never used. +@item mrib-then-urib +Tries to perform the lookup on the Multicast RIB. If any route is found, +that route is used. Otherwise, the Unicast RIB is tried. +@item lower-distance +Performs a lookup on the Multicast RIB and Unicast RIB each. The result +with the lower administrative distance is used; if they're equal, the +Multicast RIB takes precedence. +@item longer-prefix +Performs a lookup on the Multicast RIB and Unicast RIB each. The result +with the longer prefix length is used; if they're equal, the +Multicast RIB takes precedence. +@end table + +WARNING: Unreachable routes do not receive special treatment and do not +cause fallback to a second lookup. +@end deffn + +@deffn Command {show ip rpf @var{addr}} {} + +Performs a Multicast RPF lookup, as configured with +@command{ip multicast rpf-lookup-mode @var{mode}}. @var{addr} specifies +the multicast source address to look up. + +@example +> show ip rpf 192.0.2.1 +Routing entry for 192.0.2.0/24 using Unicast RIB + Known via "kernel", distance 0, metric 0, best + * 198.51.100.1, via eth0 +@end example + +Indicates that a multicast source lookup for 192.0.2.1 would use an +Unicast RIB entry for 192.0.2.0/24 with a gateway of 198.51.100.1. +@end deffn + +@deffn Command {show ip rpf} {} + +Prints the entire Multicast RIB. Note that this is independent of the +configured RPF lookup mode, the Multicast RIB may be printed yet not +used at all. +@end deffn + +@deffn Command {ip mroute @var{prefix} @var{nexthop} [@var{distance}]} {} +@deffnx Command {no ip mroute @var{prefix} @var{nexthop} [@var{distance}]} {} + +Adds a static route entry to the Multicast RIB. This performs exactly as +the @command{ip route} command, except that it inserts the route in the +Multicast RIB instead of the Unicast RIB. +@end deffn + + @node zebra Route Filtering @section zebra Route Filtering Zebra supports @command{prefix-list} and @command{route-map} to match From 7397217e9dbc7384951ea146c0f9ca5784f6561e Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Mon, 2 Feb 2015 03:00:22 +0100 Subject: [PATCH 455/482] doc: explain rpf lookup default mode Reported-by: Alexis Rosen Signed-off-by: David Lamparter --- doc/main.texi | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/main.texi b/doc/main.texi index 810866aa..4c11d244 100644 --- a/doc/main.texi +++ b/doc/main.texi @@ -224,6 +224,11 @@ with the longer prefix length is used; if they're equal, the Multicast RIB takes precedence. @end table +The @code{mrib-then-urib} setting is the default behavior if nothing is +configured. If this is the desired behavior, it should be explicitly +configured to make the configuration immune against possible changes in +what the default behavior is. + WARNING: Unreachable routes do not receive special treatment and do not cause fallback to a second lookup. @end deffn From 77ef0ace6b178601a0649ecf88c12c8203c9e077 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Wed, 4 Feb 2015 06:24:41 +0100 Subject: [PATCH 456/482] build: enable pimd in test script Signed-off-by: David Lamparter --- buildtest.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildtest.sh b/buildtest.sh index de638f56..4859bf4a 100755 --- a/buildtest.sh +++ b/buildtest.sh @@ -4,7 +4,7 @@ # builds some git commit of Quagga in some different configurations # usage: buildtest.sh [commit [configurations...]] -basecfg="--prefix=/usr --enable-user=quagga --enable-group=quagga --enable-vty-group=quagga --enable-configfile-mask=0660 --enable-logfile-mask=0640 --enable-vtysh --sysconfdir=/etc/quagga --enable-exampledir=/etc/quagga/samples --localstatedir=/var/run/quagga --libdir=/usr/lib64/quagga --enable-ipv6 --enable-ripngd --enable-ospf6d --enable-rtadv --disable-static --enable-isisd --enable-multipath=0 --enable-babeld" +basecfg="--prefix=/usr --enable-user=quagga --enable-group=quagga --enable-vty-group=quagga --enable-configfile-mask=0660 --enable-logfile-mask=0640 --enable-vtysh --sysconfdir=/etc/quagga --enable-exampledir=/etc/quagga/samples --localstatedir=/var/run/quagga --libdir=/usr/lib64/quagga --enable-ipv6 --enable-ripngd --enable-ospf6d --enable-rtadv --disable-static --enable-isisd --enable-multipath=0 --enable-babeld --enable-pimd" configs_base="gcc|$basecfg" From 369b973e42f2b4f00a02e3ca8a1c6f1b252cf4ae Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 10 Feb 2015 11:00:30 +0100 Subject: [PATCH 457/482] build: Quagga 0.99.24-rc1 this is not a full release version, so neither release notes nor documentation are updated yet. Also, signing the tag with my private GPG key instead of the Quagga one. Signed-off-by: David Lamparter --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 6632e543..a01c9a7b 100755 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,7 @@ ## AC_PREREQ(2.53) -AC_INIT(Quagga, 0.99.23, [https://bugzilla.quagga.net]) +AC_INIT(Quagga, 0.99.24-rc1, [https://bugzilla.quagga.net]) AC_CONFIG_SRCDIR(lib/zebra.h) AC_CONFIG_MACRO_DIR([m4]) From 79f74962d20fa2c90df5a57335fc3b5e19bfeccf Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sat, 28 Jun 2014 20:22:55 +0200 Subject: [PATCH 458/482] build: remove bogus/deprecated inet_* tests These actually break configure on FreeBSD very subtly, because inet_aton and __inet_aton are both detected, and then later other tests get warnings about HAVE_INET_ATON being defined twice. That said, they're incorrect to begin with since they detect alternative functions but there is nothing in place to actually use these alternates. Signed-off-by: David Lamparter Acked-by: Greg Troxel Acked-by: Feng Lu Acked-by: Paul Jakma --- configure.ac | 7 ------- 1 file changed, 7 deletions(-) diff --git a/configure.ac b/configure.ac index a01c9a7b..73cbda9c 100755 --- a/configure.ac +++ b/configure.ac @@ -1406,13 +1406,6 @@ AC_CHECK_LIB(c, inet_pton, [AC_DEFINE(HAVE_INET_PTON,,inet_pton)]) AC_CHECK_LIB(crypt, crypt) AC_CHECK_LIB(resolv, res_init) -dnl --------------------------------------------------- -dnl BSD/OS 4.1 define inet_XtoY function as __inet_XtoY -dnl --------------------------------------------------- -AC_CHECK_FUNC(__inet_ntop, AC_DEFINE(HAVE_INET_NTOP,,__inet_ntop)) -AC_CHECK_FUNC(__inet_pton, AC_DEFINE(HAVE_INET_PTON,,__inet_pton)) -AC_CHECK_FUNC(__inet_aton, AC_DEFINE(HAVE_INET_ATON,,__inet_aton)) - dnl --------------------------- dnl check system has PCRE regexp dnl --------------------------- From 2e5ca49758543cde69d98f4a6a7b39486e88311d Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sat, 28 Jun 2014 20:54:31 +0200 Subject: [PATCH 459/482] build: remove Linux non-netlink config This path is deprecated, completely untested, likely broken and will not be maintained. Kill it with fire. Signed-off-by: David Lamparter Acked-by: Greg Troxel Acked-by: Feng Lu Acked-by: Paul Jakma --- configure.ac | 50 +-------- zebra/Makefile.am | 7 +- zebra/if_proc.c | 248 -------------------------------------------- zebra/rtread_proc.c | 175 ------------------------------- 4 files changed, 8 insertions(+), 472 deletions(-) delete mode 100644 zebra/if_proc.c delete mode 100644 zebra/rtread_proc.c diff --git a/configure.ac b/configure.ac index 73cbda9c..f46673c4 100755 --- a/configure.ac +++ b/configure.ac @@ -226,10 +226,6 @@ AC_ARG_ENABLE(solaris, [ --enable-solaris build solaris]) AC_ARG_ENABLE(bgp-announce, [ --disable-bgp-announce, turn off BGP route announcement]) -AC_ARG_ENABLE(netlink, -[ --enable-netlink force to use Linux netlink interface]) -AC_ARG_ENABLE(broken-aliases, -[ --enable-broken-aliases enable aliases as distinct interfaces for Linux 2.2.X]) AC_ARG_ENABLE(snmp, [ --enable-snmp=ARG enable SNMP support (smux or agentx)]) AC_ARG_WITH(libpam, @@ -308,15 +304,6 @@ if test "${enable_fpm}" = "yes"; then AC_DEFINE(HAVE_FPM,,Forwarding Plane Manager support) fi -if test "${enable_broken_aliases}" = "yes"; then - if test "${enable_netlink}" = "yes" - then - AC_MSG_FAILURE([Sorry you can not use netlink with broken aliases]) - fi - AC_DEFINE(HAVE_BROKEN_ALIASES,,Broken Alias) - enable_netlink=no -fi - if test "${enable_tcp_zebra}" = "yes"; then AC_DEFINE(HAVE_TCP_ZEBRA,,Use TCP for zebra communication) fi @@ -814,21 +801,10 @@ dnl Determine routing get and set method dnl ------------------------------------ AC_MSG_CHECKING(zebra between kernel interface method) if test x"$opsys" = x"gnu-linux"; then - if test "${enable_netlink}" = "yes";then - AC_MSG_RESULT(netlink) - RT_METHOD=rt_netlink.o - AC_DEFINE(HAVE_NETLINK,,netlink) - netlink=yes - elif test "${enable_netlink}" = "no"; then - AC_MSG_RESULT(ioctl) - RT_METHOD=rt_ioctl.o - netlink=no - else - AC_MSG_RESULT(netlink) - RT_METHOD=rt_netlink.o - AC_DEFINE(HAVE_NETLINK,,netlink) - netlink=yes - fi + AC_MSG_RESULT(netlink) + RT_METHOD=rt_netlink.o + AC_DEFINE(HAVE_NETLINK,,netlink) + netlink=yes elif test x"$opsys" = x"sol2-6";then AC_MSG_RESULT(Route socket) KERNEL_METHOD="kernel_socket.o" @@ -941,12 +917,11 @@ AC_CACHE_CHECK([route read method], [quagga_cv_rtread_method], [if test "x$netlink" = xyes; then quagga_cv_rtread_method="netlink" else -for quagga_cv_rtread_method in /proc/net/route /dev/ip /dev/null; +for quagga_cv_rtread_method in /dev/ip /dev/null; do test x`ls $quagga_cv_rtread_method 2>/dev/null` = x"$quagga_cv_rtread_method" && break done case $quagga_cv_rtread_method in - "/proc/net/route") quagga_cv_rtread_method="proc";; "/dev/ip") case "$host" in *-freebsd*) quagga_cv_rtread_method="sysctl";; @@ -1063,21 +1038,6 @@ if test $ac_cv_have_decl_TCP_MD5SIG = no; then AC_CHECK_DECLS([TCP_MD5SIG], [], [], MD5_INCLUDES)]) fi -dnl ----------------------- -dnl check proc file system. -dnl ----------------------- -if test "$netlink" != yes; then - if test -r /proc/net/dev; then - AC_DEFINE(HAVE_PROC_NET_DEV,,/proc/net/dev) - IF_PROC=if_proc.o - fi - if test -r /proc/net/if_inet6; then - AC_DEFINE(HAVE_PROC_NET_IF_INET6,,/proc/net/if_inet6) - IF_PROC=if_proc.o - fi -fi -AC_SUBST(IF_PROC) - dnl ----------------------------- dnl check ipforward detect method dnl ----------------------------- diff --git a/zebra/Makefile.am b/zebra/Makefile.am index 0591a555..4a76317e 100644 --- a/zebra/Makefile.am +++ b/zebra/Makefile.am @@ -9,14 +9,13 @@ LIBCAP = @LIBCAP@ ipforward = @IPFORWARD@ if_method = @IF_METHOD@ -if_proc = @IF_PROC@ rt_method = @RT_METHOD@ rtread_method = @RTREAD_METHOD@ kernel_method = @KERNEL_METHOD@ other_method = @OTHER_METHOD@ ioctl_method = @IOCTL_METHOD@ -otherobj = $(ioctl_method) $(ipforward) $(if_method) $(if_proc) \ +otherobj = $(ioctl_method) $(ipforward) $(if_method) \ $(rt_method) $(rtread_method) $(kernel_method) $(other_method) if HAVE_NETLINK @@ -51,10 +50,10 @@ testzebra_LDADD = ../lib/libzebra.la $(LIBCAP) $(LIB_IPV6) zebra_DEPENDENCIES = $(otherobj) -EXTRA_DIST = if_ioctl.c if_ioctl_solaris.c if_netlink.c if_proc.c \ +EXTRA_DIST = if_ioctl.c if_ioctl_solaris.c if_netlink.c \ if_sysctl.c ipforward_aix.c ipforward_ews.c ipforward_proc.c \ ipforward_solaris.c ipforward_sysctl.c rt_ioctl.c rt_netlink.c \ - rt_socket.c rtread_netlink.c rtread_proc.c rtread_sysctl.c \ + rt_socket.c rtread_netlink.c rtread_sysctl.c \ rtread_getmsg.c kernel_socket.c kernel_netlink.c mtu_kvm.c \ ioctl.c ioctl_solaris.c \ GNOME-SMI GNOME-PRODUCT-ZEBRA-MIB diff --git a/zebra/if_proc.c b/zebra/if_proc.c deleted file mode 100644 index 2dbc4726..00000000 --- a/zebra/if_proc.c +++ /dev/null @@ -1,248 +0,0 @@ -/* Interface name and statistics get function using proc file system - * Copyright (C) 1999 Kunihiro Ishiguro - * - * This file is part of GNU Zebra. - * - * GNU Zebra is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * GNU Zebra is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Zebra; see the file COPYING. If not, write to the Free - * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - */ - -#include - -#include "if.h" -#include "prefix.h" -#include "log.h" - -#include "zebra/ioctl.h" -#include "zebra/connected.h" -#include "zebra/interface.h" - -/* Proc filesystem one line buffer. */ -#define PROCBUFSIZ 1024 - -/* Path to device proc file system. */ -#ifndef _PATH_PROC_NET_DEV -#define _PATH_PROC_NET_DEV "/proc/net/dev" -#endif /* _PATH_PROC_NET_DEV */ - -/* Return statistics data pointer. */ -static char * -interface_name_cut (char *buf, char **name) -{ - char *stat; - - /* Skip white space. Line will include header spaces. */ - while (*buf == ' ') - buf++; - *name = buf; - - /* Cut interface name. */ - stat = strrchr (buf, ':'); - *stat++ = '\0'; - - return stat; -} - -/* Fetch each statistics field. */ -static int -ifstat_dev_fields (int version, char *buf, struct interface *ifp) -{ - switch (version) - { - case 3: - sscanf(buf, - "%ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld", - &ifp->stats.rx_bytes, - &ifp->stats.rx_packets, - &ifp->stats.rx_errors, - &ifp->stats.rx_dropped, - &ifp->stats.rx_fifo_errors, - &ifp->stats.rx_frame_errors, - &ifp->stats.rx_compressed, - &ifp->stats.rx_multicast, - - &ifp->stats.tx_bytes, - &ifp->stats.tx_packets, - &ifp->stats.tx_errors, - &ifp->stats.tx_dropped, - &ifp->stats.tx_fifo_errors, - &ifp->stats.collisions, - &ifp->stats.tx_carrier_errors, - &ifp->stats.tx_compressed); - break; - case 2: - sscanf(buf, "%ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld", - &ifp->stats.rx_bytes, - &ifp->stats.rx_packets, - &ifp->stats.rx_errors, - &ifp->stats.rx_dropped, - &ifp->stats.rx_fifo_errors, - &ifp->stats.rx_frame_errors, - - &ifp->stats.tx_bytes, - &ifp->stats.tx_packets, - &ifp->stats.tx_errors, - &ifp->stats.tx_dropped, - &ifp->stats.tx_fifo_errors, - &ifp->stats.collisions, - &ifp->stats.tx_carrier_errors); - ifp->stats.rx_multicast = 0; - break; - case 1: - sscanf(buf, "%ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld", - &ifp->stats.rx_packets, - &ifp->stats.rx_errors, - &ifp->stats.rx_dropped, - &ifp->stats.rx_fifo_errors, - &ifp->stats.rx_frame_errors, - - &ifp->stats.tx_packets, - &ifp->stats.tx_errors, - &ifp->stats.tx_dropped, - &ifp->stats.tx_fifo_errors, - &ifp->stats.collisions, - &ifp->stats.tx_carrier_errors); - ifp->stats.rx_bytes = 0; - ifp->stats.tx_bytes = 0; - ifp->stats.rx_multicast = 0; - break; - } - return 0; -} - -/* Update interface's statistics. */ -void -ifstat_update_proc (void) -{ - FILE *fp; - char buf[PROCBUFSIZ]; - int version; - struct interface *ifp; - char *stat; - char *name; - - /* Open /proc/net/dev. */ - fp = fopen (_PATH_PROC_NET_DEV, "r"); - if (fp == NULL) - { - zlog_warn ("Can't open proc file %s: %s", - _PATH_PROC_NET_DEV, safe_strerror (errno)); - return; - } - - /* Drop header lines. */ - fgets (buf, PROCBUFSIZ, fp); - fgets (buf, PROCBUFSIZ, fp); - - /* To detect proc format veresion, parse second line. */ - if (strstr (buf, "compressed")) - version = 3; - else if (strstr (buf, "bytes")) - version = 2; - else - version = 1; - - /* Update each interface's statistics. */ - while (fgets (buf, PROCBUFSIZ, fp) != NULL) - { - stat = interface_name_cut (buf, &name); - ifp = if_get_by_name (name); - ifstat_dev_fields (version, stat, ifp); - } - fclose(fp); - return; -} - -/* Interface structure allocation by proc filesystem. */ -int -interface_list_proc () -{ - FILE *fp; - char buf[PROCBUFSIZ]; - struct interface *ifp; - char *name; - - /* Open /proc/net/dev. */ - fp = fopen (_PATH_PROC_NET_DEV, "r"); - if (fp == NULL) - { - zlog_warn ("Can't open proc file %s: %s", - _PATH_PROC_NET_DEV, safe_strerror (errno)); - return -1; - } - - /* Drop header lines. */ - fgets (buf, PROCBUFSIZ, fp); - fgets (buf, PROCBUFSIZ, fp); - - /* Only allocate interface structure. Other jobs will be done in - if_ioctl.c. */ - while (fgets (buf, PROCBUFSIZ, fp) != NULL) - { - interface_name_cut (buf, &name); - ifp = if_get_by_name (name); - if_add_update (ifp); - } - fclose(fp); - return 0; -} - -#if defined(HAVE_IPV6) && defined(HAVE_PROC_NET_IF_INET6) - -#ifndef _PATH_PROC_NET_IF_INET6 -#define _PATH_PROC_NET_IF_INET6 "/proc/net/if_inet6" -#endif /* _PATH_PROC_NET_IF_INET6 */ - -int -ifaddr_proc_ipv6 () -{ - FILE *fp; - char buf[PROCBUFSIZ]; - int n; - char addr[33]; - char ifname[21]; - int ifindex, plen, scope, status; - struct interface *ifp; - struct prefix_ipv6 p; - - /* Open proc file system. */ - fp = fopen (_PATH_PROC_NET_IF_INET6, "r"); - if (fp == NULL) - { - zlog_warn ("Can't open proc file %s: %s", - _PATH_PROC_NET_IF_INET6, safe_strerror (errno)); - return -1; - } - - /* Get interface's IPv6 address. */ - while (fgets (buf, PROCBUFSIZ, fp) != NULL) - { - n = sscanf (buf, "%32s %02x %02x %02x %02x %20s", - addr, &ifindex, &plen, &scope, &status, ifname); - if (n != 6) - continue; - - ifp = if_get_by_name (ifname); - - /* Fetch interface's IPv6 address. */ - str2in6_addr (addr, &p.prefix); - p.prefixlen = plen; - - connected_add_ipv6 (ifp, 0, &p.prefix, p.prefixlen, NULL, ifname); - } - fclose (fp); - return 0; -} -#endif /* HAVE_IPV6 && HAVE_PROC_NET_IF_INET6 */ diff --git a/zebra/rtread_proc.c b/zebra/rtread_proc.c deleted file mode 100644 index 07e8491a..00000000 --- a/zebra/rtread_proc.c +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Kernel routing readup by /proc filesystem - * Copyright (C) 1997 Kunihiro Ishiguro - * - * This file is part of GNU Zebra. - * - * GNU Zebra is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * GNU Zebra is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Zebra; see the file COPYING. If not, write to the Free - * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - */ - -#include - -#include "prefix.h" -#include "log.h" -#include "if.h" -#include "rib.h" - -#include "zebra/zserv.h" -#include "zebra/rt.h" - -/* Proc file system to read IPv4 routing table. */ -#ifndef _PATH_PROCNET_ROUTE -#define _PATH_PROCNET_ROUTE "/proc/net/route" -#endif /* _PATH_PROCNET_ROUTE */ - -/* Proc file system to read IPv6 routing table. */ -#ifndef _PATH_PROCNET_ROUTE6 -#define _PATH_PROCNET_ROUTE6 "/proc/net/ipv6_route" -#endif /* _PATH_PROCNET_ROUTE6 */ - -/* To read interface's name */ -#define INTERFACE_NAMSIZ 20 - -/* Reading buffer for one routing entry. */ -#define RT_BUFSIZ 1024 - -/* Kernel routing table read up by /proc filesystem. */ -static int -proc_route_read (void) -{ - FILE *fp; - char buf[RT_BUFSIZ]; - char iface[INTERFACE_NAMSIZ], dest[9], gate[9], mask[9]; - int flags, refcnt, use, metric, mtu, window, rtt; - - /* Open /proc filesystem */ - fp = fopen (_PATH_PROCNET_ROUTE, "r"); - if (fp == NULL) - { - zlog_warn ("Can't open %s : %s\n", _PATH_PROCNET_ROUTE, safe_strerror (errno)); - return -1; - } - - /* Drop first label line. */ - fgets (buf, RT_BUFSIZ, fp); - - while (fgets (buf, RT_BUFSIZ, fp) != NULL) - { - int n; - struct prefix_ipv4 p; - struct in_addr tmpmask; - struct in_addr gateway; - u_char zebra_flags = 0; - - n = sscanf (buf, "%s %s %s %x %d %d %d %s %d %d %d", - iface, dest, gate, &flags, &refcnt, &use, &metric, - mask, &mtu, &window, &rtt); - if (n != 11) - { - zlog_warn ("can't read all of routing information\n"); - continue; - } - if (! (flags & RTF_UP)) - continue; - if (! (flags & RTF_GATEWAY)) - continue; - - if (flags & RTF_DYNAMIC) - zebra_flags |= ZEBRA_FLAG_SELFROUTE; - - p.family = AF_INET; - sscanf (dest, "%lX", (unsigned long *)&p.prefix); - sscanf (mask, "%lX", (unsigned long *)&tmpmask); - p.prefixlen = ip_masklen (tmpmask); - sscanf (gate, "%lX", (unsigned long *)&gateway); - - rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p, &gateway, NULL, 0, 0, 0, 0, SAFI_UNICAST); - } - - fclose (fp); - return 0; -} - -#ifdef HAVE_IPV6 -static int -proc_ipv6_route_read () -{ - FILE *fp; - char buf [RT_BUFSIZ]; - - /* Open /proc filesystem */ - fp = fopen (_PATH_PROCNET_ROUTE6, "r"); - if (fp == NULL) - { - zlog_warn ("Can't open %s : %s", _PATH_PROCNET_ROUTE6, - safe_strerror (errno)); - return -1; - } - - /* There is no title line, so we don't drop first line. */ - while (fgets (buf, RT_BUFSIZ, fp) != NULL) - { - int n; - char dest[33], src[33], gate[33]; - char iface[INTERFACE_NAMSIZ]; - int dest_plen, src_plen; - int metric, use, refcnt, flags; - struct prefix_ipv6 p; - struct in6_addr gateway; - u_char zebra_flags = 0; - - /* Linux 2.1.x write this information at net/ipv6/route.c - rt6_info_node () */ - n = sscanf (buf, "%32s %02x %32s %02x %32s %08x %08x %08x %08x %s", - dest, &dest_plen, src, &src_plen, gate, - &metric, &use, &refcnt, &flags, iface); - - if (n != 10) - { - /* zlog_warn ("can't read all of routing information %d\n%s\n", n, buf); */ - continue; - } - - if (! (flags & RTF_UP)) - continue; - if (! (flags & RTF_GATEWAY)) - continue; - - if (flags & RTF_DYNAMIC) - zebra_flags |= ZEBRA_FLAG_SELFROUTE; - - p.family = AF_INET6; - str2in6_addr (dest, &p.prefix); - str2in6_addr (gate, &gateway); - p.prefixlen = dest_plen; - - rib_add_ipv6 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p, &gateway, 0, 0, - metric, 0); - } - - fclose (fp); - return 0; -} -#endif /* HAVE_IPV6 */ - -void -route_read (void) -{ - proc_route_read (); -#ifdef HAVE_IPV6 - proc_ipv6_route_read (); -#endif /* HAVE_IPV6 */ -} From 0f048b90b5d6e4bd185913945b68dd254126eb9f Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sat, 28 Jun 2014 21:01:32 +0200 Subject: [PATCH 460/482] build: remove AIX, NEC EWS and IRIX Valar morghulis. Signed-off-by: David Lamparter Acked-by: Greg Troxel Acked-by: Feng Lu Acked-by: Paul Jakma --- configure.ac | 29 +----------- lib/zebra.h | 14 ------ zebra/Makefile.am | 7 ++- zebra/interface.h | 4 -- zebra/ipforward_aix.c | 64 -------------------------- zebra/ipforward_ews.c | 60 ------------------------- zebra/ipforward_sysctl.c | 2 +- zebra/mtu_kvm.c | 97 ---------------------------------------- 8 files changed, 5 insertions(+), 272 deletions(-) delete mode 100644 zebra/ipforward_aix.c delete mode 100644 zebra/ipforward_ews.c delete mode 100644 zebra/mtu_kvm.c diff --git a/configure.ac b/configure.ac index f46673c4..b9001b37 100755 --- a/configure.ac +++ b/configure.ac @@ -605,23 +605,10 @@ case "$host" in opsys=gnu-linux AC_DEFINE(GNU_LINUX,,GNU Linux) ;; - *-nec-sysv4*) - AC_CHECK_LIB(nsl, gethostbyname) - AC_CHECK_LIB(socket, socket) - ;; *-openbsd*) opsys=openbsd AC_DEFINE(OPEN_BSD,,OpenBSD) ;; - *-bsdi*) - opsys=bsdi - OTHER_METHOD="mtu_kvm.o" - AC_CHECK_LIB(kvm, main) - ;; - *-irix6.5) - opsys=irix - AC_DEFINE(IRIX_65,,IRIX 6.5) - ;; esac AC_SYS_LARGEFILE @@ -813,10 +800,6 @@ elif test x"$opsys" = x"sol8";then AC_MSG_RESULT(Route socket) KERNEL_METHOD="kernel_socket.o" RT_METHOD="rt_socket.o" -elif test "$opsys" = "irix" ; then - AC_MSG_RESULT(Route socket) - KERNEL_METHOD="kernel_socket.o" - RT_METHOD="rt_socket.o" else AC_TRY_RUN([#include #include @@ -842,7 +825,6 @@ main () fi AC_SUBST(RT_METHOD) AC_SUBST(KERNEL_METHOD) -AC_SUBST(OTHER_METHOD) AM_CONDITIONAL([HAVE_NETLINK], [test "x$netlink" = "xyes"]) dnl -------------------------- @@ -949,9 +931,6 @@ elif test "$opsys" = "sol8";then AC_MSG_RESULT(Solaris GLIF) IF_METHOD=if_ioctl_solaris.o IOCTL_METHOD=ioctl_solaris.o -elif test "$opsys" = "irix" ; then - AC_MSG_RESULT(IRIX) - IF_METHOD=if_ioctl.o elif test "$opsys" = "openbsd";then AC_MSG_RESULT(openbsd) IF_METHOD=if_ioctl.o @@ -1058,7 +1037,6 @@ case $quagga_cv_ipforward_method in "/proc/net/snmp") quagga_cv_ipforward_method="proc";; "/dev/ip") case "$host" in - *-nec-sysv4*) quagga_cv_ipforward_method="ews";; *-freebsd*) quagga_cv_ipforward_method="sysctl";; *) quagga_cv_ipforward_method="solaris";; esac;; @@ -1125,12 +1103,7 @@ dnl --------- AC_DEFINE(NRL,1,NRL) RIPNGD="ripngd" OSPF6D="ospf6d" - if test x"$opsys" = x"bsdi";then - AC_DEFINE(BSDI_NRL,,BSDI) - AC_MSG_RESULT(BSDI_NRL) - else - AC_MSG_RESULT(NRL) - fi + AC_MSG_RESULT(NRL) dnl ------------------------------------ dnl Solaris 9, 10 and potentially higher dnl ------------------------------------ diff --git a/lib/zebra.h b/lib/zebra.h index a4e02148..a5ed20e4 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -244,20 +244,6 @@ typedef int socklen_t; #include #endif /* HAVE_GLIBC_BACKTRACE */ -#ifdef BSDI_NRL - -#ifdef HAVE_NETINET6_IN6_H -#include -#endif /* HAVE_NETINET6_IN6_H */ - -#ifdef NRL -#include -#endif /* NRL */ - -#define IN6_ARE_ADDR_EQUAL IN6_IS_ADDR_EQUAL - -#endif /* BSDI_NRL */ - /* Local includes: */ #if !(defined(__GNUC__) || defined(VTYSH_EXTRACT_PL)) #define __attribute__(x) diff --git a/zebra/Makefile.am b/zebra/Makefile.am index 4a76317e..8e3c99ba 100644 --- a/zebra/Makefile.am +++ b/zebra/Makefile.am @@ -12,11 +12,10 @@ if_method = @IF_METHOD@ rt_method = @RT_METHOD@ rtread_method = @RTREAD_METHOD@ kernel_method = @KERNEL_METHOD@ -other_method = @OTHER_METHOD@ ioctl_method = @IOCTL_METHOD@ otherobj = $(ioctl_method) $(ipforward) $(if_method) \ - $(rt_method) $(rtread_method) $(kernel_method) $(other_method) + $(rt_method) $(rtread_method) $(kernel_method) if HAVE_NETLINK othersrc = zebra_fpm_netlink.c @@ -51,10 +50,10 @@ testzebra_LDADD = ../lib/libzebra.la $(LIBCAP) $(LIB_IPV6) zebra_DEPENDENCIES = $(otherobj) EXTRA_DIST = if_ioctl.c if_ioctl_solaris.c if_netlink.c \ - if_sysctl.c ipforward_aix.c ipforward_ews.c ipforward_proc.c \ + if_sysctl.c ipforward_proc.c \ ipforward_solaris.c ipforward_sysctl.c rt_ioctl.c rt_netlink.c \ rt_socket.c rtread_netlink.c rtread_sysctl.c \ - rtread_getmsg.c kernel_socket.c kernel_netlink.c mtu_kvm.c \ + rtread_getmsg.c kernel_socket.c kernel_netlink.c \ ioctl.c ioctl_solaris.c \ GNOME-SMI GNOME-PRODUCT-ZEBRA-MIB diff --git a/zebra/interface.h b/zebra/interface.h index 7a05348c..2f3b7b93 100644 --- a/zebra/interface.h +++ b/zebra/interface.h @@ -236,8 +236,4 @@ extern int interface_list_proc (void); extern int ifaddr_proc_ipv6 (void); #endif /* HAVE_PROC_NET_IF_INET6 */ -#ifdef BSDI -extern int if_kvm_get_mtu (struct interface *); -#endif /* BSDI */ - #endif /* _ZEBRA_INTERFACE_H */ diff --git a/zebra/ipforward_aix.c b/zebra/ipforward_aix.c deleted file mode 100644 index c79e7f1c..00000000 --- a/zebra/ipforward_aix.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * ipforward value get function for aix. - * Copyright (C) 1997 Kunihiro Ishiguro - * - * This file is part of GNU Zebra. - * - * GNU Zebra is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * GNU Zebra is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Zebra; see the file COPYING. If not, write to the Free - * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - */ - -#include - -int -ipforward () -{ - int fd, ret; - int af = AF_INET; - char netopt[] = "ipforwarding"; - struct optreq oq; - - fd = socket(af, SOCK_DGRAM, 0); - if (fd < 0) { - /* need logging here */ - return -1; - } - - strcpy (oq.name, netopt); - oq.getnext = 0; - - ret = ioctl (fd, SIOCGNETOPT, (caddr_t)&oq); - close(fd); - - if (ret < 0) { - /* need logging here */ - return -1; - } - - ret = atoi (oq.data); - return ret; -} - -int -ipforward_on () -{ - ; -} - -int -ipforward_off () -{ - ; -} diff --git a/zebra/ipforward_ews.c b/zebra/ipforward_ews.c deleted file mode 100644 index c872000a..00000000 --- a/zebra/ipforward_ews.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Ipforward value get function for NEC EWS. - * Copyright (C) 1997 Kunihiro Ishiguro - * - * This file is part of GNU Zebra. - * - * GNU Zebra is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * GNU Zebra is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Zebra; see the file COPYING. If not, write to the Free - * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - */ - -#include - -int -ipforward () -{ - int fd; - char buf[BUFSIZ]; - struct mioc_rksym rks; - - fd = open ("/dev/kmem", O_RDWR); - if (fd < 0) { - /* need logging here */ - return -1; - } - - rks.mirk_symname = "ipforwarding"; - rks.mirk_buf = buf; - rks.mirk_buflen = sizeof (int); - - if (ioctl (fd, MIOC_READKSYM, &rks) < 0) { - /* need logging here */ - return -1; - } - close (fd); - return *(int *)buf; -} - -int -ipforward_on () -{ - ; -} - -int -ipforward_off () -{ - ; -} diff --git a/zebra/ipforward_sysctl.c b/zebra/ipforward_sysctl.c index 185aee3e..88244182 100644 --- a/zebra/ipforward_sysctl.c +++ b/zebra/ipforward_sysctl.c @@ -106,7 +106,7 @@ int mib_ipv6[MIB_SIZ] = { CTL_NET, PF_INET6, -#if defined(KAME) || (defined(__bsdi__) && _BSDI_VERSION >= 199802 ) || defined(NRL) +#if defined(KAME) || defined(NRL) IPPROTO_IPV6, IPV6CTL_FORWARDING #else /* NOT KAME */ diff --git a/zebra/mtu_kvm.c b/zebra/mtu_kvm.c deleted file mode 100644 index d37bb9bb..00000000 --- a/zebra/mtu_kvm.c +++ /dev/null @@ -1,97 +0,0 @@ -/* MTU get using kvm_read. - * Copyright (C) 1999 Kunihiro Ishiguro - * - * This file is part of GNU Zebra. - * - * GNU Zebra is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * GNU Zebra is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Zebra; see the file COPYING. If not, write to the Free - * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - */ - -#include - -#include -#include -#include - -#include "if.h" - -/* get interface MTU to use kvm_read */ -void -if_kvm_get_mtu (struct interface *ifp) -{ - kvm_t *kvmd; - struct ifnet ifnet; - unsigned long ifnetaddr; - int len; - - char ifname[IFNAMSIZ]; - char tname[INTERFACE_NAMSIZ + 1]; - char buf[_POSIX2_LINE_MAX]; - - struct nlist nl[] = - { - {"_ifnet"}, - {""} - }; - - ifp->mtu6 = ifp->mtu = -1; - - kvmd = kvm_openfiles (NULL, NULL, NULL, O_RDONLY, buf); - - if (kvmd == NULL) - return ; - - kvm_nlist(kvmd, nl); - - ifnetaddr = nl[0].n_value; - - if (kvm_read(kvmd, ifnetaddr, (char *)&ifnetaddr, sizeof ifnetaddr) < 0) - { - kvm_close (kvmd); - return ; - } - - while(ifnetaddr != 0) - { - if (kvm_read (kvmd, ifnetaddr, (char *)&ifnet, sizeof ifnet) < 0) - { - kvm_close (kvmd); - return ; - } - - if (kvm_read (kvmd, (u_long)ifnet.if_name, ifname, IFNAMSIZ) < 0) - { - kvm_close (kvmd); - return ; - } - - len = snprintf (tname, INTERFACE_NAMSIZ + 1, - "%s%d", ifname, ifnet.if_unit); - - if (strncmp (tname, ifp->name, len) == 0) - break; - - ifnetaddr = (u_long)ifnet.if_next; - } - - kvm_close (kvmd); - - if (ifnetaddr == 0) - { - return ; - } - - ifp->mtu6 = ifp->mtu = ifnet.if_mtu; -} From 6d6df30386423518b5daef93c2f047b4140f85f4 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sat, 28 Jun 2014 21:12:37 +0200 Subject: [PATCH 461/482] build: remove INRIA, NRL and MUSICA IPv6 quirks Valar dohaeris. Signed-off-by: David Lamparter Acked-by: Greg Troxel Acked-by: Feng Lu Acked-by: Paul Jakma --- bgpd/bgp_network.c | 4 ++-- configure.ac | 39 +-------------------------------------- lib/sockunion.c | 7 +------ lib/vty.c | 11 +++-------- ospf6d/ospf6d.h | 6 ------ ripngd/ripngd.c | 8 ++++---- zebra/connected.c | 2 +- zebra/ipforward_sysctl.c | 6 +----- zebra/rt_socket.c | 5 ----- zebra/zebra_rib.c | 4 ++-- 10 files changed, 15 insertions(+), 77 deletions(-) diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index c0527447..cea430cc 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -488,7 +488,7 @@ bgp_listener (int sock, struct sockaddr *sa, socklen_t salen) } /* IPv6 supported version of BGP server socket setup. */ -#if defined (HAVE_IPV6) && ! defined (NRL) +#ifdef HAVE_IPV6 int bgp_socket (unsigned short port, const char *address) { @@ -588,7 +588,7 @@ bgp_socket (unsigned short port, const char *address) } return sock; } -#endif /* HAVE_IPV6 && !NRL */ +#endif /* HAVE_IPV6 */ void bgp_close (void) diff --git a/configure.ac b/configure.ac index b9001b37..6684946a 100755 --- a/configure.ac +++ b/configure.ac @@ -1054,21 +1054,10 @@ AC_MSG_CHECKING(whether does this OS have IPv6 stack) if test "${enable_ipv6}" = "no"; then AC_MSG_RESULT(disabled) else -dnl ---------- -dnl INRIA IPv6 -dnl ---------- - if grep IPV6_INRIA_VERSION /usr/include/netinet/in.h >/dev/null 2>&1; then - zebra_cv_ipv6=yes - AC_DEFINE(HAVE_IPV6,1,INRIA IPv6) - AC_DEFINE(INRIA_IPV6,1,INRIA IPv6) - RIPNGD="ripngd" - OSPF6D="ospf6d" - LIB_IPV6="" - AC_MSG_RESULT(INRIA IPv6) dnl --------- dnl KAME IPv6 dnl --------- - elif grep WIDE /usr/include/netinet6/in6.h >/dev/null 2>&1; then + if grep WIDE /usr/include/netinet6/in6.h >/dev/null 2>&1; then zebra_cv_ipv6=yes AC_DEFINE(HAVE_IPV6,1,KAME IPv6) AC_DEFINE(KAME,1,KAME IPv6) @@ -1078,32 +1067,6 @@ dnl --------- LIB_IPV6="-L/usr/local/v6/lib -linet6" fi AC_MSG_RESULT(KAME) -dnl ------------------------- -dnl MUSICA IPv6 -dnl default host check -dnl It is not used by Kheops -dnl ------------------------- - elif grep MUSICA /usr/include6/netinet6/in6.h >/dev/null 2>&1; then - zebra_cv_ipv6=yes - AC_DEFINE(HAVE_IPV6,1,Musicia IPv6) - AC_DEFINE(MUSICA,1,Musica IPv6 stack) - AC_DEFINE(KAME,1,KAME IPv6 stack) - RIPNGD="ripngd" - OSPF6D="ospf6d" - if test -d /usr/local/v6/lib -a -f /usr/local/v6/lib/libinet6.a; then - LIB_IPV6="-L/usr/local/v6/lib -linet6" - fi - AC_MSG_RESULT(MUSICA) -dnl --------- -dnl NRL check -dnl --------- - elif grep NRL /usr/include/netinet6/in6.h >/dev/null 2>&1; then - zebra_cv_ipv6=yes - AC_DEFINE(HAVE_IPV6,1,NRL IPv6) - AC_DEFINE(NRL,1,NRL) - RIPNGD="ripngd" - OSPF6D="ospf6d" - AC_MSG_RESULT(NRL) dnl ------------------------------------ dnl Solaris 9, 10 and potentially higher dnl ------------------------------------ diff --git a/lib/sockunion.c b/lib/sockunion.c index 5dcf7256..1a355d3b 100644 --- a/lib/sockunion.c +++ b/lib/sockunion.c @@ -304,13 +304,8 @@ sockunion_connect (int fd, union sockunion *peersu, unsigned short port, { #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID /* su.sin6.sin6_scope_id = ifindex; */ -#ifdef MUSICA - su.sin6.sin6_scope_id = ifindex; -#endif #endif /* HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID */ -#ifndef MUSICA SET_IN6_LINKLOCAL_IFINDEX (su.sin6.sin6_addr, ifindex); -#endif } #endif /* KAME */ break; @@ -394,7 +389,7 @@ sockunion_bind (int sock, union sockunion *su, unsigned short port, #endif /* SIN6_LEN */ if (su_addr == NULL) { -#if defined(LINUX_IPV6) || defined(NRL) +#ifdef LINUX_IPV6 memset (&su->sin6.sin6_addr, 0, sizeof (struct in6_addr)); #else su->sin6.sin6_addr = in6addr_any; diff --git a/lib/vty.c b/lib/vty.c index b8db7c89..750f8856 100644 --- a/lib/vty.c +++ b/lib/vty.c @@ -1775,7 +1775,7 @@ vty_accept (struct thread *thread) return 0; } -#if defined(HAVE_IPV6) && !defined(NRL) +#ifdef HAVE_IPV6 static void vty_serv_sock_addrinfo (const char *hostname, unsigned short port) { @@ -1840,7 +1840,7 @@ vty_serv_sock_addrinfo (const char *hostname, unsigned short port) freeaddrinfo (ainfo_save); } -#else /* HAVE_IPV6 && ! NRL */ +#else /* HAVE_IPV6 */ /* Make vty server socket. */ static void @@ -1908,7 +1908,7 @@ vty_serv_sock_family (const char* addr, unsigned short port, int family) /* Add vty server event. */ vty_event (VTY_SERV, accept_sock, NULL); } -#endif /* HAVE_IPV6 && ! NRL */ +#endif /* HAVE_IPV6 */ #ifdef VTYSH /* For sockaddr_un. */ @@ -2143,12 +2143,7 @@ vty_serv_sock (const char *addr, unsigned short port, const char *path) { #ifdef HAVE_IPV6 -#ifdef NRL - vty_serv_sock_family (addr, port, AF_INET); - vty_serv_sock_family (addr, port, AF_INET6); -#else /* ! NRL */ vty_serv_sock_addrinfo (addr, port); -#endif /* NRL*/ #else /* ! HAVE_IPV6 */ vty_serv_sock_family (addr,port, AF_INET); #endif /* HAVE_IPV6 */ diff --git a/ospf6d/ospf6d.h b/ospf6d/ospf6d.h index 78ae1a14..4122b309 100644 --- a/ospf6d/ospf6d.h +++ b/ospf6d/ospf6d.h @@ -30,12 +30,6 @@ /* global variables */ extern struct thread_master *master; -#ifdef INRIA_IPV6 -#ifndef IPV6_PKTINFO -#define IPV6_PKTINFO IPV6_RECVPKTINFO -#endif /* IPV6_PKTINFO */ -#endif /* INRIA_IPV6 */ - /* Historical for KAME. */ #ifndef IPV6_JOIN_GROUP #ifdef IPV6_ADD_MEMBERSHIP diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c index 941c3a06..2dbbf9e1 100644 --- a/ripngd/ripngd.c +++ b/ripngd/ripngd.c @@ -928,7 +928,7 @@ ripng_redistribute_add (int type, int sub_type, struct prefix_ipv6 *p, return; if (IN6_IS_ADDR_LOOPBACK (&p->prefix)) return; -#if defined (MUSICA) || defined (LINUX) +#ifdef LINUX /* XXX As long as the RIPng redistribution is applied to all the connected * routes, one needs to filter the ::/96 prefixes. * However it could be a wanted case, it will be removed soon. @@ -936,7 +936,7 @@ ripng_redistribute_add (int type, int sub_type, struct prefix_ipv6 *p, if ((IN6_IS_ADDR_V4COMPAT(&p->prefix)) || (IN6_IS_ADDR_UNSPECIFIED (&p->prefix) && (p->prefixlen == 96))) return; -#endif /* MUSICA or LINUX */ +#endif /* LINUX */ rp = route_node_get (ripng->table, (struct prefix *) p); rinfo = rp->info; @@ -1025,7 +1025,7 @@ ripng_redistribute_delete (int type, int sub_type, struct prefix_ipv6 *p, return; if (IN6_IS_ADDR_LOOPBACK (&p->prefix)) return; -#if defined (MUSICA) || defined (LINUX) +#ifdef LINUX /* XXX As long as the RIPng redistribution is applied to all the connected * routes, one needs to filter the ::/96 prefixes. * However it could be a wanted case, it will be removed soon. @@ -1033,7 +1033,7 @@ ripng_redistribute_delete (int type, int sub_type, struct prefix_ipv6 *p, if ((IN6_IS_ADDR_V4COMPAT(&p->prefix)) || (IN6_IS_ADDR_UNSPECIFIED (&p->prefix) && (p->prefixlen == 96))) return; -#endif /* MUSICA or LINUX */ +#endif /* LINUX */ rp = route_node_lookup (ripng->table, (struct prefix *) p); diff --git a/zebra/connected.c b/zebra/connected.c index c4f87f4c..2db981b3 100644 --- a/zebra/connected.c +++ b/zebra/connected.c @@ -347,7 +347,7 @@ connected_up_ipv6 (struct interface *ifp, struct connected *ifc) /* Apply mask to the network. */ apply_mask_ipv6 (&p); -#if ! defined (MUSICA) && ! defined (LINUX) +#ifndef LINUX /* XXX: It is already done by rib_bogus_ipv6 within rib_add_ipv6 */ if (IN6_IS_ADDR_UNSPECIFIED (&p.prefix)) return; diff --git a/zebra/ipforward_sysctl.c b/zebra/ipforward_sysctl.c index 88244182..57ed1857 100644 --- a/zebra/ipforward_sysctl.c +++ b/zebra/ipforward_sysctl.c @@ -23,10 +23,6 @@ #include "privs.h" #include "zebra/ipforward.h" -#ifdef NRL -#include -#endif /* NRL */ - #include "log.h" #define MIB_SIZ 4 @@ -106,7 +102,7 @@ int mib_ipv6[MIB_SIZ] = { CTL_NET, PF_INET6, -#if defined(KAME) || defined(NRL) +#if defined(KAME) IPPROTO_IPV6, IPV6CTL_FORWARDING #else /* NOT KAME */ diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index 90ed73d0..cde71ef4 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -254,13 +254,8 @@ sin6_masklen (struct in6_addr mask) char *p, *lim; int len; -#if defined (INRIA) - if (IN_ANYADDR6 (mask)) - return sizeof (long); -#else /* ! INRIA */ if (IN6_IS_ADDR_UNSPECIFIED (&mask)) return sizeof (long); -#endif /* ! INRIA */ sin6.sin6_addr = mask; len = sizeof (struct sockaddr_in6); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index effe2338..31469ca7 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -2692,11 +2692,11 @@ rib_bogus_ipv6 (int type, struct prefix_ipv6 *p, struct in6_addr *gate, unsigned int ifindex, int table) { if (type == ZEBRA_ROUTE_CONNECT && IN6_IS_ADDR_UNSPECIFIED (&p->prefix)) { -#if defined (MUSICA) || defined (LINUX) +#ifdef LINUX /* IN6_IS_ADDR_V4COMPAT(&p->prefix) */ if (p->prefixlen == 96) return 0; -#endif /* MUSICA */ +#endif /* LINUX */ return 1; } if (type == ZEBRA_ROUTE_KERNEL && IN6_IS_ADDR_UNSPECIFIED (&p->prefix) From 86a82e99aa3728d78a1ec65b60a2162914cd7519 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sat, 28 Jun 2014 21:26:36 +0200 Subject: [PATCH 462/482] zebra, ripngd: remove ::/64 special-casing In the 90ies, IPv4 was believed to exist within IPv6, with some kernels implementing this belief in code... Our code here is keyed to "#ifdef LINUX", yet no Linux from the past 10 years had this, making the code completely useless. FreeBSD 10.0 does in fact have a "::/96 via ::1 dev lo0 reject" route. IMHO we shouldn't mess with that, the admin can filter as neccessary anyway. Signed-off-by: David Lamparter Acked-by: Greg Troxel Acked-by: Feng Lu [DL: slightly adjusted commit message to remove misunderstanding] Acked-by: Paul Jakma --- ripngd/ripngd.c | 18 ------------------ zebra/zebra_rib.c | 25 ------------------------- 2 files changed, 43 deletions(-) diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c index 2dbbf9e1..8c20a7a2 100644 --- a/ripngd/ripngd.c +++ b/ripngd/ripngd.c @@ -928,15 +928,6 @@ ripng_redistribute_add (int type, int sub_type, struct prefix_ipv6 *p, return; if (IN6_IS_ADDR_LOOPBACK (&p->prefix)) return; -#ifdef LINUX - /* XXX As long as the RIPng redistribution is applied to all the connected - * routes, one needs to filter the ::/96 prefixes. - * However it could be a wanted case, it will be removed soon. - */ - if ((IN6_IS_ADDR_V4COMPAT(&p->prefix)) || - (IN6_IS_ADDR_UNSPECIFIED (&p->prefix) && (p->prefixlen == 96))) - return; -#endif /* LINUX */ rp = route_node_get (ripng->table, (struct prefix *) p); rinfo = rp->info; @@ -1025,15 +1016,6 @@ ripng_redistribute_delete (int type, int sub_type, struct prefix_ipv6 *p, return; if (IN6_IS_ADDR_LOOPBACK (&p->prefix)) return; -#ifdef LINUX - /* XXX As long as the RIPng redistribution is applied to all the connected - * routes, one needs to filter the ::/96 prefixes. - * However it could be a wanted case, it will be removed soon. - */ - if ((IN6_IS_ADDR_V4COMPAT(&p->prefix)) || - (IN6_IS_ADDR_UNSPECIFIED (&p->prefix) && (p->prefixlen == 96))) - return; -#endif /* LINUX */ rp = route_node_lookup (ripng->table, (struct prefix *) p); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 31469ca7..0750e6eb 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -2687,27 +2687,6 @@ static_delete_ipv4_safi (safi_t safi, struct prefix *p, struct in_addr *gate, } #ifdef HAVE_IPV6 -static int -rib_bogus_ipv6 (int type, struct prefix_ipv6 *p, - struct in6_addr *gate, unsigned int ifindex, int table) -{ - if (type == ZEBRA_ROUTE_CONNECT && IN6_IS_ADDR_UNSPECIFIED (&p->prefix)) { -#ifdef LINUX - /* IN6_IS_ADDR_V4COMPAT(&p->prefix) */ - if (p->prefixlen == 96) - return 0; -#endif /* LINUX */ - return 1; - } - if (type == ZEBRA_ROUTE_KERNEL && IN6_IS_ADDR_UNSPECIFIED (&p->prefix) - && p->prefixlen == 96 && gate && IN6_IS_ADDR_UNSPECIFIED (gate)) - { - kernel_delete_ipv6_old (p, gate, ifindex, 0, table); - return 1; - } - return 0; -} - int rib_add_ipv6 (int type, int flags, struct prefix_ipv6 *p, struct in6_addr *gate, unsigned int ifindex, u_int32_t vrf_id, @@ -2734,10 +2713,6 @@ rib_add_ipv6 (int type, int flags, struct prefix_ipv6 *p, if (type == ZEBRA_ROUTE_BGP && CHECK_FLAG (flags, ZEBRA_FLAG_IBGP)) distance = 200; - /* Filter bogus route. */ - if (rib_bogus_ipv6 (type, p, gate, ifindex, 0)) - return 0; - /* Lookup route node.*/ rn = route_node_get (table, (struct prefix *) p); From 51bdebad99fe813d1b7104543b352f0e39b1c8dc Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sat, 28 Jun 2014 21:28:50 +0200 Subject: [PATCH 463/482] zebra: remove kernel_delete_ipv6_old() The only user of this was rib_bogus_ipv6(), which was removed in the previous commit. Good riddance. Signed-off-by: David Lamparter Acked-by: Greg Troxel Acked-by: Feng Lu Acked-by: Paul Jakma --- zebra/kernel_null.c | 4 ---- zebra/rt.h | 2 -- zebra/rt_ioctl.c | 8 -------- zebra/rt_netlink.c | 9 --------- zebra/rt_socket.c | 16 ---------------- 5 files changed, 39 deletions(-) diff --git a/zebra/kernel_null.c b/zebra/kernel_null.c index 29c7881b..4cd43db4 100644 --- a/zebra/kernel_null.c +++ b/zebra/kernel_null.c @@ -43,10 +43,6 @@ int kernel_add_ipv6 (struct prefix *a, struct rib *b) { return 0; } int kernel_delete_ipv6 (struct prefix *a, struct rib *b) { return 0; } #endif -int kernel_delete_ipv6_old (struct prefix_ipv6 *dest, struct in6_addr *gate, - unsigned int index, int flags, int table) -{ return 0; } - int kernel_add_route (struct prefix_ipv4 *a, struct in_addr *b, int c, int d) { return 0; } diff --git a/zebra/rt.h b/zebra/rt.h index 8bfe5a42..7faa127b 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -36,8 +36,6 @@ extern int kernel_address_delete_ipv4 (struct interface *, struct connected *); #ifdef HAVE_IPV6 extern int kernel_add_ipv6 (struct prefix *, struct rib *); extern int kernel_delete_ipv6 (struct prefix *, struct rib *); -extern int kernel_delete_ipv6_old (struct prefix_ipv6 *dest, struct in6_addr *gate, - unsigned int index, int flags, int table); #endif /* HAVE_IPV6 */ diff --git a/zebra/rt_ioctl.c b/zebra/rt_ioctl.c index e175d1e2..553f222c 100644 --- a/zebra/rt_ioctl.c +++ b/zebra/rt_ioctl.c @@ -517,12 +517,4 @@ kernel_delete_ipv6 (struct prefix *p, struct rib *rib) { return kernel_ioctl_ipv6_multipath (SIOCDELRT, p, rib, AF_INET6); } - -/* Delete IPv6 route from the kernel. */ -int -kernel_delete_ipv6_old (struct prefix_ipv6 *dest, struct in6_addr *gate, - unsigned int index, int flags, int table) -{ - return kernel_ioctl_ipv6 (SIOCDELRT, dest, gate, index, flags); -} #endif /* HAVE_IPV6 */ diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 12dbd1ad..2350070c 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -1855,15 +1855,6 @@ kernel_delete_ipv6 (struct prefix *p, struct rib *rib) { return netlink_route_multipath (RTM_DELROUTE, p, rib, AF_INET6); } - -/* Delete IPv6 route from the kernel. */ -int -kernel_delete_ipv6_old (struct prefix_ipv6 *dest, struct in6_addr *gate, - unsigned int index, int flags, int table) -{ - return netlink_route (RTM_DELROUTE, AF_INET6, &dest->prefix, - dest->prefixlen, gate, index, flags, table); -} #endif /* HAVE_IPV6 */ /* Interface address modification. */ diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index cde71ef4..63470adc 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -474,20 +474,4 @@ kernel_delete_ipv6 (struct prefix *p, struct rib *rib) return route; } - -/* Delete IPv6 route from the kernel. */ -int -kernel_delete_ipv6_old (struct prefix_ipv6 *dest, struct in6_addr *gate, - unsigned int index, int flags, int table) -{ - int route; - - if (zserv_privs.change(ZPRIVS_RAISE)) - zlog (NULL, LOG_ERR, "Can't raise privileges"); - route = kernel_rtm_ipv6 (RTM_DELETE, dest, gate, index, flags); - if (zserv_privs.change(ZPRIVS_LOWER)) - zlog (NULL, LOG_ERR, "Can't lower privileges"); - - return route; -} #endif /* HAVE_IPV6 */ From 29ed622f3dc32816236a89de6fce323e3b092cf0 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sat, 28 Jun 2014 21:42:25 +0200 Subject: [PATCH 464/482] build: remove ancient Linux/BSD IPv6 cruft IPv6 functions in a separate library... yeah, right. Signed-off-by: David Lamparter Acked-by: Greg Troxel Acked-by: Feng Lu Acked-by: Paul Jakma --- configure.ac | 82 +++++++---------------------------------------- zebra/Makefile.am | 5 ++- 2 files changed, 14 insertions(+), 73 deletions(-) diff --git a/configure.ac b/configure.ac index 6684946a..ac7eccb0 100755 --- a/configure.ac +++ b/configure.ac @@ -1059,91 +1059,33 @@ dnl KAME IPv6 dnl --------- if grep WIDE /usr/include/netinet6/in6.h >/dev/null 2>&1; then zebra_cv_ipv6=yes - AC_DEFINE(HAVE_IPV6,1,KAME IPv6) AC_DEFINE(KAME,1,KAME IPv6) - RIPNGD="ripngd" - OSPF6D="ospf6d" - if test -d /usr/local/v6/lib -a -f /usr/local/v6/lib/libinet6.a; then - LIB_IPV6="-L/usr/local/v6/lib -linet6" - fi AC_MSG_RESULT(KAME) dnl ------------------------------------ dnl Solaris 9, 10 and potentially higher dnl ------------------------------------ elif test x"$opsys" = x"sol8"; then zebra_cv_ipv6=yes; - AC_DEFINE(HAVE_IPV6, 1, IPv6) AC_DEFINE(SOLARIS_IPV6, 1, Solaris IPv6) - RIPNGD="ripngd" - OSPF6D="ospf6d" AC_MSG_RESULT(Solaris IPv6) dnl ---------- dnl Linux IPv6 dnl ---------- - elif test "${enable_ipv6}" = "yes"; then - AC_EGREP_CPP(yes, [ - #include - /* 2.1.128 or later */ - #if LINUX_VERSION_CODE >= 0x020180 - yes - #endif], - [zebra_cv_ipv6=yes - zebra_cv_linux_ipv6=yes - AC_MSG_RESULT(Linux IPv6)]) - else - if test x`ls /proc/net/ipv6_route 2>/dev/null` = x"/proc/net/ipv6_route" - then - zebra_cv_ipv6=yes - zebra_cv_linux_ipv6=yes - AC_MSG_RESULT(Linux IPv6) - fi - fi - - if test "$zebra_cv_linux_ipv6" = "yes";then - AC_MSG_CHECKING(whether libc has IPv6 support) - AC_TRY_LINK([#include - ],[ int a; a = (int) in6addr_any.s6_addr[0]; if (a != 12345) return a; ], - [AC_MSG_RESULT(yes) - zebra_cv_ipv6=yes - zebra_cv_linux_ipv6=yes], - [AC_MSG_RESULT(no) - zebra_cv_ipv6=no - zebra_cv_linux_ipv6=no]) - fi - - if test "$zebra_cv_linux_ipv6" = "yes";then - AC_MSG_CHECKING(for GNU libc >= 2.1) - AC_DEFINE(HAVE_IPV6,1,Linux IPv6) + elif test x"$opsys" = x"gnu-linux"; then + zebra_cv_ipv6=yes AC_DEFINE(LINUX_IPV6,1,Linux IPv6 stack) - - AC_EGREP_CPP(yes, [ -#include -#if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1 - yes -#endif], - [glibc=yes - AC_MSG_RESULT(yes)], - AC_MSG_RESULT(no) - ) - RIPNGD="ripngd" - OSPF6D="ospf6d" - if test "$glibc" != "yes"; then - if test x`ls /usr/inet6/lib/libinet6.a 2>/dev/null` != x;then - INCLUDES="-I/usr/inet6/include" - LIB_IPV6="-L/usr/inet6/lib -linet6" - fi - fi + AC_MSG_RESULT(Linux IPv6) + else + AC_MSG_RESULT(Unknown OS) fi +fi -dnl ----------------------- -dnl Set IPv6 related values -dnl ----------------------- - LIBS="$LIB_IPV6 $LIBS" - AC_SUBST(LIB_IPV6) - - if test x"$RIPNGD" = x""; then - AC_MSG_RESULT(IPv4 only) - fi +if test x"$zebra_cv_ipv6" = x"yes"; then + AC_DEFINE(HAVE_IPV6,1,IPv6) + RIPNGD="ripngd" + OSPF6D="ospf6d" +elif test x"${enable_ipv6}" = x"yes"; then + AC_MSG_ERROR([--enable-ipv6 given but IPv6 stack unknown]) fi dnl ------------------ diff --git a/zebra/Makefile.am b/zebra/Makefile.am index 8e3c99ba..002b4f2a 100644 --- a/zebra/Makefile.am +++ b/zebra/Makefile.am @@ -4,7 +4,6 @@ INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir) DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" -DMULTIPATH_NUM=@MULTIPATH_NUM@ INSTALL_SDATA=@INSTALL@ -m 600 -LIB_IPV6 = @LIB_IPV6@ LIBCAP = @LIBCAP@ ipforward = @IPFORWARD@ @@ -43,9 +42,9 @@ noinst_HEADERS = \ interface.h ipforward.h irdp.h router-id.h kernel_socket.h \ rt_netlink.h zebra_fpm.h zebra_fpm_private.h -zebra_LDADD = $(otherobj) ../lib/libzebra.la $(LIBCAP) $(LIB_IPV6) +zebra_LDADD = $(otherobj) ../lib/libzebra.la $(LIBCAP) -testzebra_LDADD = ../lib/libzebra.la $(LIBCAP) $(LIB_IPV6) +testzebra_LDADD = ../lib/libzebra.la $(LIBCAP) zebra_DEPENDENCIES = $(otherobj) From 39b233f0d789a6b5273c3dd279f17a67c718ea1a Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sat, 28 Jun 2014 21:49:18 +0200 Subject: [PATCH 465/482] zebra: remove rt_ioctl kernel interface None of the BSDs uses ioctls to set routes anymore. Signed-off-by: David Lamparter Acked-by: Greg Troxel Acked-by: Feng Lu Acked-by: Paul Jakma --- configure.ac | 28 +-- zebra/Makefile.am | 2 +- zebra/rt_ioctl.c | 520 ---------------------------------------------- 3 files changed, 2 insertions(+), 548 deletions(-) delete mode 100644 zebra/rt_ioctl.c diff --git a/configure.ac b/configure.ac index ac7eccb0..4465a084 100755 --- a/configure.ac +++ b/configure.ac @@ -792,36 +792,10 @@ if test x"$opsys" = x"gnu-linux"; then RT_METHOD=rt_netlink.o AC_DEFINE(HAVE_NETLINK,,netlink) netlink=yes -elif test x"$opsys" = x"sol2-6";then - AC_MSG_RESULT(Route socket) - KERNEL_METHOD="kernel_socket.o" - RT_METHOD="rt_socket.o" -elif test x"$opsys" = x"sol8";then +else AC_MSG_RESULT(Route socket) KERNEL_METHOD="kernel_socket.o" RT_METHOD="rt_socket.o" -else - AC_TRY_RUN([#include -#include -#include - -main () -{ - int ac_sock; - - ac_sock = socket (AF_ROUTE, SOCK_RAW, 0); - if (ac_sock < 0 && errno == EINVAL) - exit (1); - exit (0); -}], - [KERNEL_METHOD=kernel_socket.o - RT_METHOD=rt_socket.o - AC_MSG_RESULT(socket)], - [RT_METHOD=rt_ioctl.o - AC_MSG_RESULT(ioctl)], - [KERNEL_METHOD=kernel_socket.o - RT_METHOD=rt_socket.o - AC_MSG_RESULT(socket)]) fi AC_SUBST(RT_METHOD) AC_SUBST(KERNEL_METHOD) diff --git a/zebra/Makefile.am b/zebra/Makefile.am index 002b4f2a..045897ad 100644 --- a/zebra/Makefile.am +++ b/zebra/Makefile.am @@ -50,7 +50,7 @@ zebra_DEPENDENCIES = $(otherobj) EXTRA_DIST = if_ioctl.c if_ioctl_solaris.c if_netlink.c \ if_sysctl.c ipforward_proc.c \ - ipforward_solaris.c ipforward_sysctl.c rt_ioctl.c rt_netlink.c \ + ipforward_solaris.c ipforward_sysctl.c rt_netlink.c \ rt_socket.c rtread_netlink.c rtread_sysctl.c \ rtread_getmsg.c kernel_socket.c kernel_netlink.c \ ioctl.c ioctl_solaris.c \ diff --git a/zebra/rt_ioctl.c b/zebra/rt_ioctl.c deleted file mode 100644 index 553f222c..00000000 --- a/zebra/rt_ioctl.c +++ /dev/null @@ -1,520 +0,0 @@ -/* - * kernel routing table update by ioctl(). - * Copyright (C) 1997, 98 Kunihiro Ishiguro - * - * This file is part of GNU Zebra. - * - * GNU Zebra is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * GNU Zebra is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Zebra; see the file COPYING. If not, write to the Free - * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - */ - -#include - -#include "prefix.h" -#include "log.h" -#include "if.h" - -#include "zebra/zserv.h" -#include "zebra/rib.h" -#include "zebra/debug.h" -#include "zebra/rt.h" - -/* Initialize of kernel interface. There is no kernel communication - support under ioctl(). So this is dummy stub function. */ -void -kernel_init (void) -{ - return; -} - -/* Dummy function of routing socket. */ -static void -kernel_read (int sock) -{ - return; -} - -#if 0 -/* Initialization prototype of struct sockaddr_in. */ -static struct sockaddr_in sin_proto = -{ -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - sizeof (struct sockaddr_in), -#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ - AF_INET, 0, {0}, {0} -}; -#endif /* 0 */ - -/* Solaris has ortentry. */ -#ifdef HAVE_OLD_RTENTRY -#define rtentry ortentry -#endif /* HAVE_OLD_RTENTRY */ - -/* Interface to ioctl route message. */ -int -kernel_add_route (struct prefix_ipv4 *dest, struct in_addr *gate, - int index, int flags) -{ - int ret; - int sock; - struct rtentry rtentry; - struct sockaddr_in sin_dest, sin_mask, sin_gate; - - memset (&rtentry, 0, sizeof (struct rtentry)); - - /* Make destination. */ - memset (&sin_dest, 0, sizeof (struct sockaddr_in)); - sin_dest.sin_family = AF_INET; -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - sin_dest.sin_len = sizeof (struct sockaddr_in); -#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ - sin_dest.sin_addr = dest->prefix; - - /* Make gateway. */ - if (gate) - { - memset (&sin_gate, 0, sizeof (struct sockaddr_in)); - sin_gate.sin_family = AF_INET; -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - sin_gate.sin_len = sizeof (struct sockaddr_in); -#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ - sin_gate.sin_addr = *gate; - } - - memset (&sin_mask, 0, sizeof (struct sockaddr_in)); - sin_mask.sin_family = AF_INET; -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - sin_gate.sin_len = sizeof (struct sockaddr_in); -#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ - masklen2ip (dest->prefixlen, &sin_mask.sin_addr); - - /* Set destination address, mask and gateway.*/ - memcpy (&rtentry.rt_dst, &sin_dest, sizeof (struct sockaddr_in)); - if (gate) - memcpy (&rtentry.rt_gateway, &sin_gate, sizeof (struct sockaddr_in)); -#ifndef SUNOS_5 - memcpy (&rtentry.rt_genmask, &sin_mask, sizeof (struct sockaddr_in)); -#endif /* SUNOS_5 */ - - /* Routing entry flag set. */ - if (dest->prefixlen == 32) - rtentry.rt_flags |= RTF_HOST; - - if (gate && gate->s_addr != INADDR_ANY) - rtentry.rt_flags |= RTF_GATEWAY; - - rtentry.rt_flags |= RTF_UP; - - /* Additional flags */ - rtentry.rt_flags |= flags; - - - /* For tagging route. */ - /* rtentry.rt_flags |= RTF_DYNAMIC; */ - - /* Open socket for ioctl. */ - sock = socket (AF_INET, SOCK_DGRAM, 0); - if (sock < 0) - { - zlog_warn ("can't make socket\n"); - return -1; - } - - /* Send message by ioctl(). */ - ret = ioctl (sock, SIOCADDRT, &rtentry); - if (ret < 0) - { - switch (errno) - { - case EEXIST: - close (sock); - return ZEBRA_ERR_RTEXIST; - break; - case ENETUNREACH: - close (sock); - return ZEBRA_ERR_RTUNREACH; - break; - case EPERM: - close (sock); - return ZEBRA_ERR_EPERM; - break; - } - - close (sock); - zlog_warn ("write : %s (%d)", safe_strerror (errno), errno); - return 1; - } - close (sock); - - return ret; -} - -/* Interface to ioctl route message. */ -static int -kernel_ioctl_ipv4 (u_long cmd, struct prefix *p, struct rib *rib, int family) -{ - int ret; - int sock; - struct rtentry rtentry; - struct sockaddr_in sin_dest, sin_mask, sin_gate; - struct nexthop *nexthop, *tnexthop; - int recursing; - int nexthop_num = 0; - struct interface *ifp; - - memset (&rtentry, 0, sizeof (struct rtentry)); - - /* Make destination. */ - memset (&sin_dest, 0, sizeof (struct sockaddr_in)); - sin_dest.sin_family = AF_INET; -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - sin_dest.sin_len = sizeof (struct sockaddr_in); -#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ - sin_dest.sin_addr = p->u.prefix4; - - if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_BLACKHOLE)) - { - SET_FLAG (rtentry.rt_flags, RTF_REJECT); - - if (cmd == SIOCADDRT) - for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) - { - /* We shouldn't encounter recursive nexthops on discard routes, - * but it is probably better to handle that case correctly anyway. - */ - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - continue; - SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); - } - goto skip; - } - - memset (&sin_gate, 0, sizeof (struct sockaddr_in)); - - /* Make gateway. */ - for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) - { - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - continue; - - if ((cmd == SIOCADDRT - && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) - || (cmd == SIOCDELRT - && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))) - { - if (nexthop->type == NEXTHOP_TYPE_IPV4 || - nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) - { - sin_gate.sin_family = AF_INET; -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - sin_gate.sin_len = sizeof (struct sockaddr_in); -#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ - sin_gate.sin_addr = nexthop->gate.ipv4; - rtentry.rt_flags |= RTF_GATEWAY; - } - if (nexthop->type == NEXTHOP_TYPE_IFINDEX - || nexthop->type == NEXTHOP_TYPE_IFNAME) - { - ifp = if_lookup_by_index (nexthop->ifindex); - if (ifp) - rtentry.rt_dev = ifp->name; - else - return -1; - } - - if (cmd == SIOCADDRT) - SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); - - nexthop_num++; - break; - } - } - - /* If there is no useful nexthop then return. */ - if (nexthop_num == 0) - { - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug ("netlink_route_multipath(): No useful nexthop."); - return 0; - } - - skip: - - memset (&sin_mask, 0, sizeof (struct sockaddr_in)); - sin_mask.sin_family = AF_INET; -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - sin_mask.sin_len = sizeof (struct sockaddr_in); -#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ - masklen2ip (p->prefixlen, &sin_mask.sin_addr); - - /* Set destination address, mask and gateway.*/ - memcpy (&rtentry.rt_dst, &sin_dest, sizeof (struct sockaddr_in)); - - if (rtentry.rt_flags & RTF_GATEWAY) - memcpy (&rtentry.rt_gateway, &sin_gate, sizeof (struct sockaddr_in)); - -#ifndef SUNOS_5 - memcpy (&rtentry.rt_genmask, &sin_mask, sizeof (struct sockaddr_in)); -#endif /* SUNOS_5 */ - - /* Metric. It seems metric minus one value is installed... */ - rtentry.rt_metric = rib->metric; - - /* Routing entry flag set. */ - if (p->prefixlen == 32) - rtentry.rt_flags |= RTF_HOST; - - rtentry.rt_flags |= RTF_UP; - - /* Additional flags */ - /* rtentry.rt_flags |= flags; */ - - /* For tagging route. */ - /* rtentry.rt_flags |= RTF_DYNAMIC; */ - - /* Open socket for ioctl. */ - sock = socket (AF_INET, SOCK_DGRAM, 0); - if (sock < 0) - { - zlog_warn ("can't make socket\n"); - return -1; - } - - /* Send message by ioctl(). */ - ret = ioctl (sock, cmd, &rtentry); - if (ret < 0) - { - switch (errno) - { - case EEXIST: - close (sock); - return ZEBRA_ERR_RTEXIST; - break; - case ENETUNREACH: - close (sock); - return ZEBRA_ERR_RTUNREACH; - break; - case EPERM: - close (sock); - return ZEBRA_ERR_EPERM; - break; - } - - close (sock); - zlog_warn ("write : %s (%d)", safe_strerror (errno), errno); - return ret; - } - close (sock); - - return ret; -} - -int -kernel_add_ipv4 (struct prefix *p, struct rib *rib) -{ - return kernel_ioctl_ipv4 (SIOCADDRT, p, rib, AF_INET); -} - -int -kernel_delete_ipv4 (struct prefix *p, struct rib *rib) -{ - return kernel_ioctl_ipv4 (SIOCDELRT, p, rib, AF_INET); -} - -#ifdef HAVE_IPV6 - -/* Below is hack for GNU libc definition and Linux 2.1.X header. */ -#undef RTF_DEFAULT -#undef RTF_ADDRCONF - -#include - -#if defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1 -/* struct in6_rtmsg will be declared in net/route.h. */ -#else -#include -#endif - -static int -kernel_ioctl_ipv6 (u_long type, struct prefix_ipv6 *dest, struct in6_addr *gate, - int index, int flags) -{ - int ret; - int sock; - struct in6_rtmsg rtm; - - memset (&rtm, 0, sizeof (struct in6_rtmsg)); - - rtm.rtmsg_flags |= RTF_UP; - rtm.rtmsg_metric = 1; - memcpy (&rtm.rtmsg_dst, &dest->prefix, sizeof (struct in6_addr)); - rtm.rtmsg_dst_len = dest->prefixlen; - - /* We need link local index. But this should be done caller... - if (IN6_IS_ADDR_LINKLOCAL(&rtm.rtmsg_gateway)) - { - index = if_index_address (&rtm.rtmsg_gateway); - rtm.rtmsg_ifindex = index; - } - else - rtm.rtmsg_ifindex = 0; - */ - - rtm.rtmsg_flags |= RTF_GATEWAY; - - /* For tagging route. */ - /* rtm.rtmsg_flags |= RTF_DYNAMIC; */ - - memcpy (&rtm.rtmsg_gateway, gate, sizeof (struct in6_addr)); - - if (index) - rtm.rtmsg_ifindex = index; - else - rtm.rtmsg_ifindex = 0; - - rtm.rtmsg_metric = 1; - - sock = socket (AF_INET6, SOCK_DGRAM, 0); - if (sock < 0) - { - zlog_warn ("can't make socket\n"); - return -1; - } - - /* Send message via ioctl. */ - ret = ioctl (sock, type, &rtm); - if (ret < 0) - { - zlog_warn ("can't %s ipv6 route: %s\n", type == SIOCADDRT ? "add" : "delete", - safe_strerror(errno)); - ret = errno; - close (sock); - return ret; - } - close (sock); - - return ret; -} - -static int -kernel_ioctl_ipv6_multipath (u_long cmd, struct prefix *p, struct rib *rib, - int family) -{ - int ret; - int sock; - struct in6_rtmsg rtm; - struct nexthop *nexthop, *tnexthop; - int recursing; - int nexthop_num = 0; - - memset (&rtm, 0, sizeof (struct in6_rtmsg)); - - rtm.rtmsg_flags |= RTF_UP; - rtm.rtmsg_metric = rib->metric; - memcpy (&rtm.rtmsg_dst, &p->u.prefix, sizeof (struct in6_addr)); - rtm.rtmsg_dst_len = p->prefixlen; - - /* We need link local index. But this should be done caller... - if (IN6_IS_ADDR_LINKLOCAL(&rtm.rtmsg_gateway)) - { - index = if_index_address (&rtm.rtmsg_gateway); - rtm.rtmsg_ifindex = index; - } - else - rtm.rtmsg_ifindex = 0; - */ - - rtm.rtmsg_flags |= RTF_GATEWAY; - - /* For tagging route. */ - /* rtm.rtmsg_flags |= RTF_DYNAMIC; */ - - /* Make gateway. */ - for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) - { - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - continue; - - if ((cmd == SIOCADDRT - && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) - || (cmd == SIOCDELRT - && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))) - { - if (nexthop->type == NEXTHOP_TYPE_IPV6 - || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME - || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) - { - memcpy (&rtm.rtmsg_gateway, &nexthop->gate.ipv6, - sizeof (struct in6_addr)); - } - if (nexthop->type == NEXTHOP_TYPE_IFINDEX - || nexthop->type == NEXTHOP_TYPE_IFNAME - || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME - || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) - rtm.rtmsg_ifindex = nexthop->ifindex; - else - rtm.rtmsg_ifindex = 0; - - if (cmd == SIOCADDRT) - SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); - - nexthop_num++; - break; - } - } - - /* If there is no useful nexthop then return. */ - if (nexthop_num == 0) - { - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug ("netlink_route_multipath(): No useful nexthop."); - return 0; - } - - sock = socket (AF_INET6, SOCK_DGRAM, 0); - if (sock < 0) - { - zlog_warn ("can't make socket\n"); - return -1; - } - - /* Send message via ioctl. */ - ret = ioctl (sock, cmd, &rtm); - if (ret < 0) - { - zlog_warn ("can't %s ipv6 route: %s\n", - cmd == SIOCADDRT ? "add" : "delete", - safe_strerror(errno)); - ret = errno; - close (sock); - return ret; - } - close (sock); - - return ret; -} - -int -kernel_add_ipv6 (struct prefix *p, struct rib *rib) -{ - return kernel_ioctl_ipv6_multipath (SIOCADDRT, p, rib, AF_INET6); -} - -int -kernel_delete_ipv6 (struct prefix *p, struct rib *rib) -{ - return kernel_ioctl_ipv6_multipath (SIOCDELRT, p, rib, AF_INET6); -} -#endif /* HAVE_IPV6 */ From e8d0d24e7ac5e5ffdee04128b08a6004fdb831ba Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sat, 28 Jun 2014 22:07:41 +0200 Subject: [PATCH 466/482] build: remove --enable-solaris parameter This switch controlled descending into the solaris/ subdirectory, which contains package descriptions and init scripts. If they're not appropriate, they'd better be removed outright. Signed-off-by: David Lamparter Acked-by: Greg Troxel Acked-by: Feng Lu Acked-by: Paul Jakma --- configure.ac | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/configure.ac b/configure.ac index 4465a084..1583fb68 100755 --- a/configure.ac +++ b/configure.ac @@ -222,8 +222,6 @@ AC_ARG_ENABLE(isisd, [ --enable-isisd build isisd]) AC_ARG_ENABLE(pimd, [ --enable-pimd build pimd]) -AC_ARG_ENABLE(solaris, -[ --enable-solaris build solaris]) AC_ARG_ENABLE(bgp-announce, [ --disable-bgp-announce, turn off BGP route announcement]) AC_ARG_ENABLE(snmp, @@ -576,6 +574,7 @@ case "$host" in AC_DEFINE(SUNOS_5, 1, SunOS 5) AC_CHECK_LIB(xnet, main) CURSES=-lcurses + SOLARIS="solaris" ;; [*-sunos5.[8-9]] \ | [*-sunos5.1[0-9]] \ @@ -594,12 +593,14 @@ case "$host" in AC_DEFINE([HAVE_STACK_TRACE],1,[Stack symbols decode functionality]) ]) CURSES=-lcurses + SOLARIS="solaris" ;; *-sunos5* | *-solaris2*) AC_DEFINE(SUNOS_5,,SunOS 5, Unknown SunOS) AC_CHECK_LIB(socket, main) AC_CHECK_LIB(nsl, main) CURSES=-lcurses + SOLARIS="solaris" ;; *-linux*) opsys=gnu-linux @@ -1183,13 +1184,6 @@ case "${enable_pimd}" in esac AM_CONDITIONAL(PIMD, test "x$PIMD" = "xpimd") -# XXX Perhaps auto-enable on Solaris, but that's messy for cross builds. -case "${enable_solaris}" in - "yes") SOLARIS="solaris";; - "no" ) SOLARIS="";; - * ) ;; -esac - if test "${enable_bgp_announce}" = "no";then AC_DEFINE(DISABLE_BGP_ANNOUNCE,1,Disable BGP installation to zebra) else From b6fa76098d127f5641a7dda0dee21f06ca167edb Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sat, 28 Jun 2014 22:15:59 +0200 Subject: [PATCH 467/482] build: harmonize configure help strings Signed-off-by: David Lamparter Acked-by: Greg Troxel Acked-by: Feng Lu Acked-by: Paul Jakma --- configure.ac | 84 ++++++++++++++++++++++++++-------------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/configure.ac b/configure.ac index 1583fb68..0fd4db1d 100755 --- a/configure.ac +++ b/configure.ac @@ -32,7 +32,7 @@ AC_ARG_VAR([GAWK],[GNU AWK]) dnl default is to match previous behavior exampledir=${sysconfdir} AC_ARG_ENABLE([exampledir], - AC_HELP_STRING([--enable-exampledir], + AS_HELP_STRING([--enable-exampledir], [specify alternate directory for examples]), exampledir="$enableval",) dnl XXX add --exampledir to autoconf standard directory list somehow @@ -42,7 +42,7 @@ dnl default is to match previous behavior pkgsrcrcdir="" pkgsrcdir="" AC_ARG_ENABLE([pkgsrcrcdir], - AC_HELP_STRING([--enable-pkgsrcrcdir], + AS_HELP_STRING([--enable-pkgsrcrcdir], [specify directory for rc.d scripts]), pkgsrcrcdir="$enableval"; pkgsrcdir="pkgsrc",) dnl XXX add --pkgsrcrcdir to autoconf standard directory list somehow @@ -197,85 +197,85 @@ AC_ARG_WITH(pkg-git-version, AS_HELP_STRING([--with-pkg-git-version], [add git information to MOTD and build version string]), [ test "x$withval" != "xno" && with_pkg_git_version="yes" ]) AC_ARG_ENABLE(vtysh, -[ --enable-vtysh include integrated vty shell for Quagga]) + AS_HELP_STRING([--enable-vtysh], [include integrated vty shell for Quagga])) AC_ARG_ENABLE(ipv6, -[ --disable-ipv6 turn off IPv6 related features and daemons]) + AS_HELP_STRING([--disable-ipv6], [turn off IPv6 related features and daemons])) AC_ARG_ENABLE(doc, -[ --disable-doc do not build docs]) + AS_HELP_STRING([--disable-doc], [do not build docs])) AC_ARG_ENABLE(zebra, -[ --disable-zebra do not build zebra daemon]) + AS_HELP_STRING([--disable-zebra], [do not build zebra daemon])) AC_ARG_ENABLE(bgpd, -[ --disable-bgpd do not build bgpd]) + AS_HELP_STRING([--disable-bgpd], [do not build bgpd])) AC_ARG_ENABLE(ripd, -[ --disable-ripd do not build ripd]) + AS_HELP_STRING([--disable-ripd], [do not build ripd])) AC_ARG_ENABLE(ripngd, -[ --disable-ripngd do not build ripngd]) + AS_HELP_STRING([--disable-ripngd], [do not build ripngd])) AC_ARG_ENABLE(ospfd, -[ --disable-ospfd do not build ospfd]) + AS_HELP_STRING([--disable-ospfd], [do not build ospfd])) AC_ARG_ENABLE(ospf6d, -[ --disable-ospf6d do not build ospf6d]) + AS_HELP_STRING([--disable-ospf6d], [do not build ospf6d])) AC_ARG_ENABLE(babeld, -[ --disable-babeld do not build babeld]) + AS_HELP_STRING([--disable-babeld], [do not build babeld])) AC_ARG_ENABLE(watchquagga, -[ --disable-watchquagga do not build watchquagga]) + AS_HELP_STRING([--disable-watchquagga], [do not build watchquagga])) AC_ARG_ENABLE(isisd, -[ --enable-isisd build isisd]) + AS_HELP_STRING([--enable-isisd], [build isisd])) AC_ARG_ENABLE(pimd, -[ --enable-pimd build pimd]) + AS_HELP_STRING([--enable-pimd], [build pimd])) AC_ARG_ENABLE(bgp-announce, -[ --disable-bgp-announce, turn off BGP route announcement]) + AS_HELP_STRING([--disable-bgp-announce,], [turn off BGP route announcement])) AC_ARG_ENABLE(snmp, -[ --enable-snmp=ARG enable SNMP support (smux or agentx)]) + AS_HELP_STRING([--enable-snmp=ARG], [enable SNMP support (smux or agentx)])) AC_ARG_WITH(libpam, -[ --with-libpam use libpam for PAM support in vtysh]) + AS_HELP_STRING([--with-libpam], [use libpam for PAM support in vtysh])) AC_ARG_ENABLE(tcp-zebra, -[ --enable-tcp-zebra enable TCP/IP socket connection between zebra and protocol daemon]) + AS_HELP_STRING([--enable-tcp-zebra], [enable TCP/IP socket connection between zebra and protocol daemon])) AC_ARG_ENABLE(opaque-lsa, - AC_HELP_STRING([--disable-opaque-lsa],[do not build OSPF Opaque-LSA with OSPFAPI support (RFC2370)])) + AS_HELP_STRING([--disable-opaque-lsa],[do not build OSPF Opaque-LSA with OSPFAPI support (RFC2370)])) AC_ARG_ENABLE(ospfapi, -[ --disable-ospfapi do not build OSPFAPI to access the OSPF LSA Database]) + AS_HELP_STRING([--disable-ospfapi], [do not build OSPFAPI to access the OSPF LSA Database])) AC_ARG_ENABLE(ospfclient, -[ --disable-ospfclient do not build OSPFAPI client for OSPFAPI, - (this is the default if --disable-ospfapi is set)]) + AS_HELP_STRING([--disable-ospfclient], [do not build OSPFAPI client for OSPFAPI, + (this is the default if --disable-ospfapi is set)])) AC_ARG_ENABLE(ospf-te, - AC_HELP_STRING([--disable-ospf-te],[disable Traffic Engineering Extension to OSPF])) + AS_HELP_STRING([--disable-ospf-te],[disable Traffic Engineering Extension to OSPF])) AC_ARG_ENABLE(multipath, -[ --enable-multipath=ARG enable multipath function, ARG must be digit]) + AS_HELP_STRING([--enable-multipath=ARG], [enable multipath function, ARG must be digit])) AC_ARG_ENABLE(user, - AC_HELP_STRING([--enable-user=user], [user to run Quagga suite as (default quagga)])) + AS_HELP_STRING([--enable-user=USER], [user to run Quagga suite as (default quagga)])) AC_ARG_ENABLE(group, - AC_HELP_STRING([--enable-group=group], [group to run Quagga suite as (default quagga)])) + AS_HELP_STRING([--enable-group=GROUP], [group to run Quagga suite as (default quagga)])) AC_ARG_ENABLE(vty_group, -[ --enable-vty-group=ARG set vty sockets to have specified group as owner]) + AS_HELP_STRING([--enable-vty-group=ARG], [set vty sockets to have specified group as owner])) AC_ARG_ENABLE(configfile_mask, -[ --enable-configfile-mask=ARG set mask for config files]) + AS_HELP_STRING([--enable-configfile-mask=ARG], [set mask for config files])) AC_ARG_ENABLE(logfile_mask, -[ --enable-logfile-mask=ARG set mask for log files]) + AS_HELP_STRING([--enable-logfile-mask=ARG], [set mask for log files])) AC_ARG_ENABLE(rtadv, -[ --disable-rtadv disable IPV6 router advertisement feature]) + AS_HELP_STRING([--disable-rtadv], [disable IPV6 router advertisement feature])) AC_ARG_ENABLE(irdp, -[ --enable-irdp enable IRDP server support in zebra]) + AS_HELP_STRING([--enable-irdp], [enable IRDP server support in zebra])) AC_ARG_ENABLE(isis_topology, -[ --enable-isis-topology enable IS-IS topology generator]) + AS_HELP_STRING([--enable-isis-topology], [enable IS-IS topology generator])) AC_ARG_ENABLE(capabilities, -[ --disable-capabilities disable using POSIX capabilities]) + AS_HELP_STRING([--disable-capabilities], [disable using POSIX capabilities])) AC_ARG_ENABLE(rusage, -[ --disable-rusage disable using getrusage]) + AS_HELP_STRING([--disable-rusage], [disable using getrusage])) AC_ARG_ENABLE(gcc_ultra_verbose, -[ --enable-gcc-ultra-verbose enable ultra verbose GCC warnings]) + AS_HELP_STRING([--enable-gcc-ultra-verbose], [enable ultra verbose GCC warnings])) AC_ARG_ENABLE(linux24_tcp_md5, -[ --enable-linux24-tcp-md5 enable support for old, Linux-2.4 RFC2385 patch]) + AS_HELP_STRING([--enable-linux24-tcp-md5], [enable support for old, Linux-2.4 RFC2385 patch])) AC_ARG_ENABLE(gcc-rdynamic, -[ --enable-gcc-rdynamic enable linking with -rdynamic for better backtraces (default if gcc)]) + AS_HELP_STRING([--enable-gcc-rdynamic], [enable linking with -rdynamic for better backtraces (default if gcc)])) AC_ARG_ENABLE(backtrace, -[ --disable-backtrace, disable crash backtraces (default autodetect)]) + AS_HELP_STRING([--disable-backtrace,], [disable crash backtraces (default autodetect)])) AC_ARG_ENABLE(time-check, -[ --disable-time-check disable slow thread warning messages]) + AS_HELP_STRING([--disable-time-check], [disable slow thread warning messages])) AC_ARG_ENABLE(pcreposix, -[ --enable-pcreposix enable using PCRE Posix libs for regex functions]) + AS_HELP_STRING([--enable-pcreposix], [enable using PCRE Posix libs for regex functions])) AC_ARG_ENABLE(fpm, -[ --enable-fpm enable Forwarding Plane Manager support]) + AS_HELP_STRING([--enable-fpm], [enable Forwarding Plane Manager support])) if test x"${enable_gcc_ultra_verbose}" = x"yes" ; then CFLAGS="${CFLAGS} -W -Wcast-qual -Wstrict-prototypes" From 237aac56960575f6ad2451ba2796d94bd5ae4b33 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sat, 28 Jun 2014 22:23:10 +0200 Subject: [PATCH 468/482] build: get rid of INCLUDES, use AM_CPPFLAGS INCLUDES in configure.ac was not used at all, and INCLUDES in Makefile.am is supposed to be AM_CPPFLAGS these days. Reduces warnings spewed during bootstrap/autoreconf. Signed-off-by: David Lamparter Acked-by: Greg Troxel Acked-by: Feng Lu Acked-by: Paul Jakma --- babeld/Makefile.am | 2 +- bgpd/Makefile.am | 2 +- configure.ac | 2 -- isisd/Makefile.am | 2 +- isisd/topology/Makefile.am | 5 +---- lib/Makefile.am | 2 +- ospf6d/Makefile.am | 2 +- ospfclient/Makefile.am | 2 +- ospfd/Makefile.am | 2 +- pimd/Makefile.am | 2 +- ripd/Makefile.am | 2 +- ripngd/Makefile.am | 2 +- tests/Makefile.am | 2 +- vtysh/Makefile.am | 2 +- watchquagga/Makefile.am | 2 +- zebra/Makefile.am | 2 +- 16 files changed, 15 insertions(+), 20 deletions(-) diff --git a/babeld/Makefile.am b/babeld/Makefile.am index af1201a7..13922d0f 100644 --- a/babeld/Makefile.am +++ b/babeld/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in. -INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" INSTALL_SDATA=@INSTALL@ -m 600 diff --git a/bgpd/Makefile.am b/bgpd/Makefile.am index 9928734e..42f1f488 100644 --- a/bgpd/Makefile.am +++ b/bgpd/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in. -INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" INSTALL_SDATA=@INSTALL@ -m 600 diff --git a/configure.ac b/configure.ac index 0fd4db1d..715f35d8 100755 --- a/configure.ac +++ b/configure.ac @@ -1203,7 +1203,6 @@ AC_SUBST(ISISD) AC_SUBST(PIMD) AC_SUBST(SOLARIS) AC_SUBST(VTYSH) -AC_SUBST(INCLUDES) AC_SUBST(CURSES) AC_SUBST(OSPFCLIENT) AC_SUBST(OSPFAPI) @@ -1598,7 +1597,6 @@ source code location : ${srcdir} compiler : ${CC} compiler flags : ${CFLAGS} make : ${MAKE-make} -includes : ${INCLUDES} linker flags : ${LDFLAGS} ${LIBS} ${LIBCAP} ${LIBREADLINE} ${LIBM} state file directory : ${quagga_statedir} config file directory : `eval echo \`echo ${sysconfdir}\`` diff --git a/isisd/Makefile.am b/isisd/Makefile.am index 4e9b2441..dba681b1 100644 --- a/isisd/Makefile.am +++ b/isisd/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in. -INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib \ +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib \ @ISIS_TOPOLOGY_INCLUDES@ DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" INSTALL_SDATA=@INSTALL@ -m 600 diff --git a/isisd/topology/Makefile.am b/isisd/topology/Makefile.am index ccd2e717..fe73ae5c 100644 --- a/isisd/topology/Makefile.am +++ b/isisd/topology/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in. -INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" AM_CFLAGS = $(PICFLAGS) @@ -18,7 +18,4 @@ libtopology_a_LIBADD = @LIB_REGEX@ ../../lib/libzebra.la noinst_HEADERS = \ spgrid.h -depend: - @$(CPP) -MM $(INCLUDES) $(LDFLAGS) *.c - ## File dependency. diff --git a/lib/Makefile.am b/lib/Makefile.am index bd210929..5a806183 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in. -INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" lib_LTLIBRARIES = libzebra.la diff --git a/ospf6d/Makefile.am b/ospf6d/Makefile.am index 726ce543..d30ce581 100644 --- a/ospf6d/Makefile.am +++ b/ospf6d/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in. -INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" INSTALL_SDATA=@INSTALL@ -m 600 diff --git a/ospfclient/Makefile.am b/ospfclient/Makefile.am index 0f37aa98..09d2ba86 100644 --- a/ospfclient/Makefile.am +++ b/ospfclient/Makefile.am @@ -1,6 +1,6 @@ ## Automake.am for OSPF API client -INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib lib_LTLIBRARIES = libospfapiclient.la libospfapiclient_la_LDFLAGS = -version-info 0:0:0 diff --git a/ospfd/Makefile.am b/ospfd/Makefile.am index 4e1a4fe9..4160bfcc 100644 --- a/ospfd/Makefile.am +++ b/ospfd/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in. -INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib DEFS = @DEFS@ $(LOCAL_OPTS) -DSYSCONFDIR=\"$(sysconfdir)/\" INSTALL_SDATA=@INSTALL@ -m 600 diff --git a/pimd/Makefile.am b/pimd/Makefile.am index 7173a231..f13debd9 100644 --- a/pimd/Makefile.am +++ b/pimd/Makefile.am @@ -34,7 +34,7 @@ PIM_DEFS += -DPIM_ZCLIENT_DEBUG PIM_DEFS += -DPIM_ENFORCE_LOOPFREE_MFC #PIM_DEFS += -DPIM_UNEXPECTED_KERNEL_UPCALL -INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" $(PIM_DEFS) INSTALL_SDATA=@INSTALL@ -m 600 LIBS = @LIBS@ diff --git a/ripd/Makefile.am b/ripd/Makefile.am index b0bc7a87..65a626dd 100644 --- a/ripd/Makefile.am +++ b/ripd/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in. -INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" INSTALL_SDATA=@INSTALL@ -m 600 diff --git a/ripngd/Makefile.am b/ripngd/Makefile.am index de5bebae..9bd14ba0 100644 --- a/ripngd/Makefile.am +++ b/ripngd/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in. -INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" INSTALL_SDATA=@INSTALL@ -m 600 diff --git a/tests/Makefile.am b/tests/Makefile.am index 8a086d0b..1fe28c70 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -13,7 +13,7 @@ EXTRA_DIST = \ testcommands.in \ testcommands.refout -INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib DEFS = @DEFS@ $(LOCAL_OPTS) -DSYSCONFDIR=\"$(sysconfdir)/\" AM_CFLAGS = $(PICFLAGS) diff --git a/vtysh/Makefile.am b/vtysh/Makefile.am index f9dea2de..a66615be 100644 --- a/vtysh/Makefile.am +++ b/vtysh/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with Automake to create Makefile.in -INCLUDES = @INCLUDES@ -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" LIBS = @LIBS@ @CURSES@ @LIBPAM@ diff --git a/watchquagga/Makefile.am b/watchquagga/Makefile.am index badaa5b5..9256006c 100644 --- a/watchquagga/Makefile.am +++ b/watchquagga/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with Automake to create Makefile.in -INCLUDES = @INCLUDES@ -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib DEFS = @DEFS@ -DSTATEDIR=\"$(localstatedir)/\" AM_CFLAGS = $(PICFLAGS) diff --git a/zebra/Makefile.am b/zebra/Makefile.am index 045897ad..48087917 100644 --- a/zebra/Makefile.am +++ b/zebra/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in. -INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" -DMULTIPATH_NUM=@MULTIPATH_NUM@ INSTALL_SDATA=@INSTALL@ -m 600 From 7fe17e6975f4c4dd359364177a1d73ed770d6cd4 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sat, 22 Nov 2014 10:31:33 -0800 Subject: [PATCH 469/482] build: remove --disable-ipv6 Building with IPv6 disabled tends to break rather often and sprinkles ugly #ifdefs around the code. All that only to support systems where the C library doesn't have IPv6 capability. The year now being 2015, if this is a problem the thing to fix is the C library. The implication of this patch is that future patches need not care about HAVE_IPV6 = 0 and may remove ifdefs gratuitously. This patch doesn't remove these ifdefs to not create unneccessary churn. Signed-off-by: David Lamparter Acked-by: Paul Jakma --- buildtest.sh | 10 ++-------- configure.ac | 28 +++++----------------------- 2 files changed, 7 insertions(+), 31 deletions(-) diff --git a/buildtest.sh b/buildtest.sh index 4859bf4a..3bc25f23 100755 --- a/buildtest.sh +++ b/buildtest.sh @@ -4,22 +4,16 @@ # builds some git commit of Quagga in some different configurations # usage: buildtest.sh [commit [configurations...]] -basecfg="--prefix=/usr --enable-user=quagga --enable-group=quagga --enable-vty-group=quagga --enable-configfile-mask=0660 --enable-logfile-mask=0640 --enable-vtysh --sysconfdir=/etc/quagga --enable-exampledir=/etc/quagga/samples --localstatedir=/var/run/quagga --libdir=/usr/lib64/quagga --enable-ipv6 --enable-ripngd --enable-ospf6d --enable-rtadv --disable-static --enable-isisd --enable-multipath=0 --enable-babeld --enable-pimd" +basecfg="--prefix=/usr --enable-user=quagga --enable-group=quagga --enable-vty-group=quagga --enable-configfile-mask=0660 --enable-logfile-mask=0640 --enable-vtysh --sysconfdir=/etc/quagga --enable-exampledir=/etc/quagga/samples --localstatedir=/var/run/quagga --libdir=/usr/lib64/quagga --enable-rtadv --disable-static --enable-isisd --enable-multipath=0 --enable-babeld --enable-pimd" configs_base="gcc|$basecfg" -configs_nov6="gcc|$basecfg" -configs_nov6="${configs_nov6/enable-ipv6/disable-ipv6}" -configs_nov6="${configs_nov6/enable-ospf6d/disable-ospf6d}" -configs_nov6="${configs_nov6/enable-ripngd/disable-ripngd}" -configs_nov6="${configs_nov6/enable-babeld/disable-babeld}" - configs_ext="gcc|$basecfg --enable-opaque-lsa --enable-ospf-te --enable-ospfclient --enable-isis-topology" configs_snmp="gcc|$basecfg --enable-opaque-lsa --enable-ospf-te --enable-ospfclient --enable-isis-topology --enable-snmp" configs_clang="clang|$basecfg --enable-opaque-lsa --enable-ospf-te --enable-ospfclient --enable-isis-topology" configs_icc="icc|$basecfg --enable-opaque-lsa --enable-ospf-te --enable-ospfclient --enable-isis-topology" -defconfigs="base nov6 ext" +defconfigs="base ext" net-snmp-config --version &> /dev/null && defconfigs="$defconfigs snmp" clang --version &> /dev/null && defconfigs="$defconfigs clang" icc --version &> /dev/null && defconfigs="$defconfigs icc" diff --git a/configure.ac b/configure.ac index 715f35d8..33a1cd06 100755 --- a/configure.ac +++ b/configure.ac @@ -198,8 +198,6 @@ AC_ARG_WITH(pkg-git-version, [ test "x$withval" != "xno" && with_pkg_git_version="yes" ]) AC_ARG_ENABLE(vtysh, AS_HELP_STRING([--enable-vtysh], [include integrated vty shell for Quagga])) -AC_ARG_ENABLE(ipv6, - AS_HELP_STRING([--disable-ipv6], [turn off IPv6 related features and daemons])) AC_ARG_ENABLE(doc, AS_HELP_STRING([--disable-doc], [do not build docs])) AC_ARG_ENABLE(zebra, @@ -1026,51 +1024,37 @@ dnl ---------- dnl IPv6 check dnl ---------- AC_MSG_CHECKING(whether does this OS have IPv6 stack) -if test "${enable_ipv6}" = "no"; then - AC_MSG_RESULT(disabled) -else dnl --------- dnl KAME IPv6 dnl --------- if grep WIDE /usr/include/netinet6/in6.h >/dev/null 2>&1; then - zebra_cv_ipv6=yes AC_DEFINE(KAME,1,KAME IPv6) AC_MSG_RESULT(KAME) dnl ------------------------------------ dnl Solaris 9, 10 and potentially higher dnl ------------------------------------ elif test x"$opsys" = x"sol8"; then - zebra_cv_ipv6=yes; AC_DEFINE(SOLARIS_IPV6, 1, Solaris IPv6) AC_MSG_RESULT(Solaris IPv6) dnl ---------- dnl Linux IPv6 dnl ---------- elif test x"$opsys" = x"gnu-linux"; then - zebra_cv_ipv6=yes AC_DEFINE(LINUX_IPV6,1,Linux IPv6 stack) AC_MSG_RESULT(Linux IPv6) else - AC_MSG_RESULT(Unknown OS) + AC_MSG_ERROR([Failed to detect IPv6 stack]) fi -fi -if test x"$zebra_cv_ipv6" = x"yes"; then - AC_DEFINE(HAVE_IPV6,1,IPv6) - RIPNGD="ripngd" - OSPF6D="ospf6d" -elif test x"${enable_ipv6}" = x"yes"; then - AC_MSG_ERROR([--enable-ipv6 given but IPv6 stack unknown]) -fi +dnl this is unconditial, for compatibility +AC_DEFINE(HAVE_IPV6,1,IPv6) dnl ------------------ dnl IPv6 header checks dnl ------------------ -if test "x${zebra_cv_ipv6}" = "xyes"; then AC_CHECK_HEADERS([netinet6/in6.h netinet/in6_var.h netinet/icmp6.h \ netinet6/in6_var.h netinet6/nd6.h], [], [], QUAGGA_INCLUDES) -fi m4_define([QUAGGA_INCLUDES],dnl QUAGGA_INCLUDES @@ -1157,16 +1141,14 @@ fi AM_CONDITIONAL(OSPFCLIENT, test "x$OSPFCLIENT" = "xospfclient") case "${enable_ripngd}" in - "yes") RIPNGD="ripngd";; "no" ) RIPNGD="";; - * ) ;; + * ) RIPNGD="ripngd";; esac AM_CONDITIONAL(RIPNGD, test "x$RIPNGD" = "xripngd") case "${enable_ospf6d}" in - "yes") OSPF6D="ospf6d";; "no" ) OSPF6D="";; - * ) ;; + * ) OSPF6D="ospf6d";; esac AM_CONDITIONAL(OSPF6D, test "x$OSPF6D" = "xospf6d") From 85c63b844df4a295a64f37573e0ba08a7cc63659 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sat, 22 Nov 2014 10:31:44 -0800 Subject: [PATCH 470/482] tests: remove --disable-ipv6 With --disable-ipv6 gone, the IPv6 detection logic in the tests is not needed anymore either. Signed-off-by: David Lamparter Acked-by: Paul Jakma --- tests/bgpd.tests/testbgpcap.exp | 6 +++--- tests/bgpd.tests/testbgpmpattr.exp | 14 +++++++------- tests/config/unix.exp | 20 -------------------- 3 files changed, 10 insertions(+), 30 deletions(-) diff --git a/tests/bgpd.tests/testbgpcap.exp b/tests/bgpd.tests/testbgpcap.exp index 1bbdfd12..2572623f 100644 --- a/tests/bgpd.tests/testbgpcap.exp +++ b/tests/bgpd.tests/testbgpcap.exp @@ -8,10 +8,10 @@ spawn "./testbgpcap" # proc simpletest { start } { simpletest "MP4: MP IP/Uni" -simpletest_nov6 "MPv6: MP IPv6/Uni" +simpletest "MPv6: MP IPv6/Uni" simpletest "MP2: MP IP/Multicast" -simpletest_nov6 "MP3: MP IP6/MPLS-labeled VPN" -simpletest_nov6 "MP5: MP IP6/MPLS-VPN" +simpletest "MP3: MP IP6/MPLS-labeled VPN" +simpletest "MP5: MP IP6/MPLS-VPN" simpletest "MP6: MP IP4/MPLS-laveled VPN" simpletest "MP8: MP unknown AFI/SAFI" simpletest "MP-short: MP IP4/Unicast, length too short (< minimum)" diff --git a/tests/bgpd.tests/testbgpmpattr.exp b/tests/bgpd.tests/testbgpmpattr.exp index 93355ad7..646bbe50 100644 --- a/tests/bgpd.tests/testbgpmpattr.exp +++ b/tests/bgpd.tests/testbgpmpattr.exp @@ -7,10 +7,10 @@ spawn "./testbgpmpattr" # proc simpletest { start } { -simpletest_nov6 "IPv6: IPV6 MP Reach, global nexthop, 1 NLRI" -simpletest_nov6 "IPv6-2: IPV6 MP Reach, global nexthop, 2 NLRIs" -simpletest_nov6 "IPv6-default: IPV6 MP Reach, global nexthop, 2 NLRIs + default" -simpletest_nov6 "IPv6-lnh: IPV6 MP Reach, global+local nexthops, 2 NLRIs + default" +simpletest "IPv6: IPV6 MP Reach, global nexthop, 1 NLRI" +simpletest "IPv6-2: IPV6 MP Reach, global nexthop, 2 NLRIs" +simpletest "IPv6-default: IPV6 MP Reach, global nexthop, 2 NLRIs + default" +simpletest "IPv6-lnh: IPV6 MP Reach, global+local nexthops, 2 NLRIs + default" simpletest "IPv6-nhlen: IPV6 MP Reach, inappropriate nexthop length" simpletest "IPv6-nhlen2: IPV6 MP Reach, invalid nexthop length" simpletest "IPv6-nhlen3: IPV6 MP Reach, nexthop length overflow" @@ -21,9 +21,9 @@ simpletest "IPv4-nhlen: IPv4 MP Reach, nexthop lenth overflow" simpletest "IPv4-nlrilen: IPv4 MP Reach, nlri lenth overflow" simpletest "IPv4-MLVPN: IPv4/MPLS-labeled VPN MP Reach, RD, Nexthop, 3 NLRIs" simpletest "IPv6-bug: IPv6, global nexthop, 1 default NLRI" -simpletest_nov6 "IPv6-unreach: IPV6 MP Unreach, 1 NLRI" -simpletest_nov6 "IPv6-unreach2: IPV6 MP Unreach, 2 NLRIs" -simpletest_nov6 "IPv6-unreach-default: IPV6 MP Unreach, 2 NLRIs + default" +simpletest "IPv6-unreach: IPV6 MP Unreach, 1 NLRI" +simpletest "IPv6-unreach2: IPV6 MP Unreach, 2 NLRIs" +simpletest "IPv6-unreach-default: IPV6 MP Unreach, 2 NLRIs + default" simpletest "IPv6-unreach-nlri: IPV6 MP Unreach, NLRI bitlen overflow" simpletest "IPv4-unreach: IPv4 MP Unreach, 2 NLRIs + default" simpletest "IPv4-unreach-nlrilen: IPv4 MP Unreach, nlri length overflow" diff --git a/tests/config/unix.exp b/tests/config/unix.exp index b41f072c..2f6bcead 100644 --- a/tests/config/unix.exp +++ b/tests/config/unix.exp @@ -8,16 +8,6 @@ # be part of the output... #set color 1 -set config_h [open "../config.h" "r"] -set config_h_text [read $config_h] -close $config_h -set i [string first "#define HAVE_IPV6" $config_h_text] -if { $i >= 0 } { - set have_ipv6 1 -} else { - set have_ipv6 0 -} -send_user "IPv6 enabled: $have_ipv6\n" set xfail 0 proc onesimple { test_name match } { @@ -110,13 +100,3 @@ proc simpletest { start } { onetest "$start" "" "$start" } -proc simpletest_nov6 { start } { - global have_ipv6 - global xfail - - set xfail [expr 1-$have_ipv6] - onetest "$start" "" "$start" - set xfail 0 -} - - From 7abd87529499e5d76435213e2590838c5e320a9a Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sat, 22 Nov 2014 10:43:29 -0800 Subject: [PATCH 471/482] build: track config args Record the ./configure arguments used and make them user-visible. Signed-off-by: David Lamparter Acked-by: Paul Jakma --- configure.ac | 2 ++ lib/command.c | 3 +++ lib/version.h.in | 2 ++ 3 files changed, 7 insertions(+) diff --git a/configure.ac b/configure.ac index 33a1cd06..5a3f8169 100755 --- a/configure.ac +++ b/configure.ac @@ -8,6 +8,8 @@ AC_PREREQ(2.53) AC_INIT(Quagga, 0.99.24-rc1, [https://bugzilla.quagga.net]) +CONFIG_ARGS="$*" +AC_SUBST(CONFIG_ARGS) AC_CONFIG_SRCDIR(lib/zebra.h) AC_CONFIG_MACRO_DIR([m4]) diff --git a/lib/command.c b/lib/command.c index 8870a421..83177895 100644 --- a/lib/command.c +++ b/lib/command.c @@ -181,6 +181,7 @@ print_version (const char *progname) { printf ("%s version %s\n", progname, QUAGGA_VERSION); printf ("%s\n", QUAGGA_COPYRIGHT); + printf ("configured with:\n\t%s\n", QUAGGA_CONFIG_ARGS); } @@ -2951,6 +2952,8 @@ DEFUN (show_version, vty_out (vty, "Quagga %s (%s).%s", QUAGGA_VERSION, host.name?host.name:"", VTY_NEWLINE); vty_out (vty, "%s%s%s", QUAGGA_COPYRIGHT, GIT_INFO, VTY_NEWLINE); + vty_out (vty, "configured with:%s %s%s", VTY_NEWLINE, + QUAGGA_CONFIG_ARGS, VTY_NEWLINE); return CMD_SUCCESS; } diff --git a/lib/version.h.in b/lib/version.h.in index 7e9985f0..aef1d090 100644 --- a/lib/version.h.in +++ b/lib/version.h.in @@ -45,6 +45,8 @@ #define QUAGGA_COPYRIGHT "Copyright 1996-2005 Kunihiro Ishiguro, et al." +#define QUAGGA_CONFIG_ARGS "@CONFIG_ARGS@" + pid_t pid_output (const char *); #ifndef HAVE_DAEMON From f16195c173f8e2e17ea35f143b6ffcd50c0619fb Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Fri, 28 Nov 2014 08:40:58 +0100 Subject: [PATCH 472/482] doc: fix some warnings Signed-off-by: David Lamparter Acked-by: Paul Jakma --- doc/Makefile.am | 2 +- doc/basic.texi | 6 +++--- doc/quagga.texi | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/Makefile.am b/doc/Makefile.am index bb7e87a1..11eca73f 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -44,7 +44,7 @@ info_TEXINFOS = quagga.texi # because it cant just work from the png's directly it seems - contrary # to the documentation... quagga.pdf: $(info_TEXINFOS) $(figures_pdf) $(quagga_TEXINFOS) - $(TEXI2PDF) -o "$@" $< + $(TEXI2PDF) -o "$@" $< || true quagga_TEXINFOS = appendix.texi babeld.texi basic.texi bgpd.texi filter.texi \ install.texi ipv6.texi kernel.texi main.texi ospf6d.texi ospfd.texi \ diff --git a/doc/basic.texi b/doc/basic.texi index b3b23ca9..0f7bec9c 100644 --- a/doc/basic.texi +++ b/doc/basic.texi @@ -15,8 +15,8 @@ The following sections discuss commands common to all the routing daemons. @menu -* Terminal Mode Commands:: Common commands used in a VTY * Config Commands:: Commands used in config files +* Terminal Mode Commands:: Common commands used in a VTY * Common Invocation Options:: Starting the daemons * Virtual Terminal Interfaces:: Interacting with the daemons @end menu @@ -580,8 +580,8 @@ Move up to previous line in the history buffer. @kindex @key{TAB} Use command line completion by typing @key{TAB}. -@item -@kindex ? +@item ? +@kindex @key{?} You can use command line help by typing @code{help} at the beginning of the line. Typing @kbd{?} at any point in the line will show possible completions. diff --git a/doc/quagga.texi b/doc/quagga.texi index af82e515..83650710 100644 --- a/doc/quagga.texi +++ b/doc/quagga.texi @@ -2,9 +2,9 @@ @c %**start of header @setfilename quagga.info -@settitle @uref{http://www.quagga.net,,@value{PACKAGE_NAME}} @c Set variables - sourced from defines.texi @include defines.texi +@settitle @uref{http://www.quagga.net,,@value{PACKAGE_NAME}} @c %**end of header @c automake will automatically generate version.texi From 656a2c0724f0978d9cc5cf892f0373e808639288 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Mon, 9 Feb 2015 11:36:10 +0100 Subject: [PATCH 473/482] build: enable isisd by default Most distributors enable it anyway, and it's not THAT broken anymore to mandate disabling it by default. Signed-off-by: David Lamparter Acked-by: Paul Jakma --- configure.ac | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index 5a3f8169..20d21f67 100755 --- a/configure.ac +++ b/configure.ac @@ -219,7 +219,7 @@ AC_ARG_ENABLE(babeld, AC_ARG_ENABLE(watchquagga, AS_HELP_STRING([--disable-watchquagga], [do not build watchquagga])) AC_ARG_ENABLE(isisd, - AS_HELP_STRING([--enable-isisd], [build isisd])) + AS_HELP_STRING([--disable-isisd], [do not build isisd])) AC_ARG_ENABLE(pimd, AS_HELP_STRING([--enable-pimd], [build pimd])) AC_ARG_ENABLE(bgp-announce, @@ -331,7 +331,7 @@ if test "${enable_irdp}" = "yes"; then AC_DEFINE(HAVE_IRDP,, IRDP ) fi -if test "${enable_isisd}" = "yes" && test "${enable_isis_topology}" = yes; then +if test "${enable_isisd}" != "no" && test "${enable_isis_topology}" = yes; then AC_DEFINE(TOPOLOGY_GENERATE,,Enable IS-IS topology generator code) ISIS_TOPOLOGY_INCLUDES="-I\$(srcdir)/topology" ISIS_TOPOLOGY_DIR="topology" @@ -1155,9 +1155,8 @@ esac AM_CONDITIONAL(OSPF6D, test "x$OSPF6D" = "xospf6d") case "${enable_isisd}" in - "yes") ISISD="isisd";; "no" ) ISISD="";; - * ) ;; + * ) ISISD="isisd";; esac AM_CONDITIONAL(ISISD, test "x$ISISD" = "xisisd") From cc81308148271aeed2277e16885ddca7e2d5bb9b Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 10 Feb 2015 11:39:39 +0100 Subject: [PATCH 474/482] build: enable AM_SILENT_RULES This shuts up make by default (can be reversed with "make V=1" or --disable-silent-rules). This is useful since warnings and error messages become more visible with less noise. Tested on Linux with GNU make and FreeBSD with system's BSD make. Signed-off-by: David Lamparter Acked-by: Paul Jakma --- configure.ac | 1 + 1 file changed, 1 insertion(+) diff --git a/configure.ac b/configure.ac index 20d21f67..eeb24202 100755 --- a/configure.ac +++ b/configure.ac @@ -21,6 +21,7 @@ AC_CANONICAL_HOST() AC_CANONICAL_TARGET() AM_INIT_AUTOMAKE(1.6) +AM_SILENT_RULES([yes]) AC_CONFIG_HEADERS(config.h) AC_PATH_PROG(PERL, perl) From b8a893c38e97377b2a2582b1621b988e55811412 Mon Sep 17 00:00:00 2001 From: Brian Bennett Date: Tue, 17 Feb 2015 22:32:22 +0000 Subject: [PATCH 475/482] build: Extend ip_mreq hack to DragonFlyBSD and SunOS This extends the ip_mreq hack to DragonFlyBSD and SunOS. This has been in pkgsrc for some time. I've cleaned up the pkgsrc patch a little and am submitting it upstream. Credit is due to pkgsrc maintainers. Tested on SmartOS (illumos). Fixes: #819 Signed-off-by: Greg Troxel Signed-off-by: David Lamparter --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index eeb24202..75a48e4d 100755 --- a/configure.ac +++ b/configure.ac @@ -929,7 +929,7 @@ AC_CHECK_MEMBERS([struct ip_mreqn.imr_ifindex], [], [], QUAGGA_INCLUDES) AC_MSG_CHECKING([for BSD struct ip_mreq hack]) AC_TRY_COMPILE([#ifdef HAVE_SYS_PARAM_H #include -#endif],[#if (defined(__FreeBSD__) && ((__FreeBSD_version >= 500022 && __FreeBSD_version < 700000) || (__FreeBSD_version < 500000 && __FreeBSD_version >= 440000))) || (defined(__NetBSD__) && defined(__NetBSD_Version__) && __NetBSD_Version__ >= 106010000) || defined(__OpenBSD__) || defined(__APPLE__) +#endif],[#if (defined(__FreeBSD__) && ((__FreeBSD_version >= 500022 && __FreeBSD_version < 700000) || (__FreeBSD_version < 500000 && __FreeBSD_version >= 440000))) || (defined(__NetBSD__) && defined(__NetBSD_Version__) && __NetBSD_Version__ >= 106010000) || defined(__OpenBSD__) || defined(__APPLE__) || defined(__DragonFly__) || defined(__sun) return (0); #else #error No support for BSD struct ip_mreq hack detected From 75a3cf6cf69f6ab940f8421b0f79b2b1f689b904 Mon Sep 17 00:00:00 2001 From: Brian Bennett Date: Tue, 17 Feb 2015 23:26:12 +0000 Subject: [PATCH 476/482] solaris: fix SMF manifest dependency model and start method Resolves an issue where quagga daemons restart in an infinite loop. Quagga daemons declare a dependency on zebra that requires a restart of the daemon when zebra restarts and they explicitly restart zebra, which again triggers their own restart. Restarting zebra when other daemons are started is explicitly removed, leaving dependency management up to SMF rather than handling it in the start method. solaris/quagga.init.in: Remove calls to routeadm_zebra_enable, and the routeadm_zebra_enable function. solaris/quagga.xml.in: Set dependency zebra grouping to require_all. Fixes: #818 Signed-off-by: Greg Troxel Signed-off-by: David Lamparter --- solaris/quagga.init.in | 26 -------------------------- solaris/quagga.xml.in | 12 +++++++----- 2 files changed, 7 insertions(+), 31 deletions(-) diff --git a/solaris/quagga.init.in b/solaris/quagga.init.in index 00426241..ee3a987f 100755 --- a/solaris/quagga.init.in +++ b/solaris/quagga.init.in @@ -146,31 +146,6 @@ routeadm_daemon_args () { echo ${args} } -# certain daemons need zebra -routeadm_zebra_enable () { - - if [ "$DAEMON" = "zebra" ]; then - return - fi - - enable_zebra=`/usr/bin/svcprop -p \ - routing/enable_zebra $SMF_FMRI 2> /dev/null` - if [ "$enable_zebra" != "false" ]; then - zenabled=`/usr/bin/svcprop -p general/enabled zebra:quagga` - zenabledt=`/usr/bin/svcprop -p general_ovr/enabled zebra:quagga` - if [ "$zenabled" = "true" -o "$zenabledt" = "true" ]; then - /usr/sbin/svcadm disable zebra:quagga - /usr/sbin/svcadm enable -st zebra:quagga - else - /usr/sbin/svcadm enable -st zebra:quagga - fi - if [ "$?" != "0" ]; then - echo "Could not enable zebra:quagga" - exit $SMF_EXIT_ERR_FATAL - fi - fi -} - # Include smf functions, if available. If not, define smf_present to indicate # there is no SMF. Should allow this script to work pre-S10. if [ -f "$SMFINCLUDE" ] ; then @@ -247,7 +222,6 @@ esac if [ smf_present -a -f "$ROUTEADMINCLUDE" ]; then handle_routeadm_upgrade $DAEMON; DAEMON_ARGS=`routeadm_daemon_args`; - routeadm_zebra_enable $DAEMON; else if [ $# -gt 0 ] ; then shift diff --git a/solaris/quagga.xml.in b/solaris/quagga.xml.in index 50c52c22..60427b06 100644 --- a/solaris/quagga.xml.in +++ b/solaris/quagga.xml.in @@ -21,6 +21,8 @@ Copyright 2007 Sun Microsystems, Inc. All rights reserved. Use is subject to license terms. + Copyright 2015 Joyent, Inc. + ident "@(#)quagga.xml 1.0 05/03/15 SMI" --> @@ -189,7 +191,7 @@ @@ -320,7 +322,7 @@ @@ -449,7 +451,7 @@ @@ -580,7 +582,7 @@ @@ -715,7 +717,7 @@ From 4c421215a0330b96d85879810558d40027a96ca6 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Mon, 2 Mar 2015 06:42:11 +0100 Subject: [PATCH 477/482] zebra: print "no link-detect" The default for this is slated to change, so let's print the current default value for preexisting configurations. Signed-off-by: David Lamparter --- zebra/interface.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zebra/interface.c b/zebra/interface.c index 7e1d3dd8..0271061e 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -1579,6 +1579,8 @@ if_config_write (struct vty *vty) if (CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION)) vty_out(vty, " link-detect%s", VTY_NEWLINE); + else + vty_out(vty, " no link-detect%s", VTY_NEWLINE); for (ALL_LIST_ELEMENTS_RO (ifp->connected, addrnode, ifc)) { From f191f1e6d64e9f2cefacc91023a2359d037fea79 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 3 Mar 2015 00:50:57 +0100 Subject: [PATCH 478/482] release: 0.99.24 --- NEWS | 30 ++++++++++++++++++++++++++++++ configure.ac | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 57cc99f0..8f9dd7a1 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,35 @@ Note: this file lists major user-visible changes only. +* Changes in Quagga 0.99.24 + +User-visible changes: +- [pimd] New daemon: pimd provides IPv4 PIM-SSM multicast routing. +- [bgpd] New feature: "next-hop-self all" to override nexthop on iBGP route + reflector setups. +- [bgpd] route-maps have a new action "set ipv6 next-hop peer-address" +- [bgpd] route-maps have a new action "set as-path prepend last-as" +- [bgpd] Update validity checking (particularly MP-BGP / IPv6 routes) was + touched up significantly. Please report possible bugs. +- [ripd] New feature: RIP for IPv4 now supports equal-cost multipath (ECMP) +- [zebra] Multicast RIB support has been extended. It still is IPv4 only. +- [zebra] "no link-detect" is now printed in configurations since it won't + be the default anymore soon. To retain current behaviour, re-save your + configuration after updating to 0.99.24. + +Distributor-visible changes: +- --enable-pimd is added to enable pimd. It is considered experimental, though + unless the distribution target is embedded systems with little flash, there + is no reason to not include it in packages. +- --disable-ipv6 no longer exists as an option. It's 2015, your C library + really needs to have IPv6 support by now. +- --disable-netlink no longer exists as an option. It didn't work anyway. +- --disable-solaris no longer exists as an option. It only controlled some + init scripts. +- --enable-isisd is now the default. +- mrlg.cgi is no longer included (it was severely outdated). It can be found + independently at http://mrlg.op-sec.us/ +- build on Linux with the musl C library should now work + * Changes in Quagga 0.99.23 Known issues: diff --git a/configure.ac b/configure.ac index 75a48e4d..4d52eefa 100755 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,7 @@ ## AC_PREREQ(2.53) -AC_INIT(Quagga, 0.99.24-rc1, [https://bugzilla.quagga.net]) +AC_INIT(Quagga, 0.99.24, [https://bugzilla.quagga.net]) CONFIG_ARGS="$*" AC_SUBST(CONFIG_ARGS) AC_CONFIG_SRCDIR(lib/zebra.h) From 09037f838a5aee6fa920c977785c48cd7c482814 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Tue, 3 Mar 2015 09:08:27 +0100 Subject: [PATCH 479/482] zebra: don't print uninitialized string (3b02fe8) This crept in as part of the MRIB improvements and I missed the compiler warning between other noise. Unfortunately, printing an uninitialised variable can in fact make zebra crash, so this is not trivial. Fixes: 3b02fe8 ("zebra: add "show ip rpf" to get result of RPF lookup") Signed-off-by: David Lamparter --- zebra/zebra_vty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 598b40de..1e39ebdd 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -702,7 +702,7 @@ vty_show_ip_route_detail (struct vty *vty, struct route_node *rn, int mcast) RNODE_FOREACH_RIB (rn, rib) { - const char *mcast_info; + const char *mcast_info = ""; if (mcast) { rib_table_info_t *info = rn->table->info; From d35b51ee5fa3cc4866860f5718c4090de61f9922 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Sat, 7 Mar 2015 07:58:00 +0100 Subject: [PATCH 480/482] release: 0.99.24.1 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 4d52eefa..60e9f66a 100755 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,7 @@ ## AC_PREREQ(2.53) -AC_INIT(Quagga, 0.99.24, [https://bugzilla.quagga.net]) +AC_INIT(Quagga, 0.99.24.1, [https://bugzilla.quagga.net]) CONFIG_ARGS="$*" AC_SUBST(CONFIG_ARGS) AC_CONFIG_SRCDIR(lib/zebra.h) From 908c97326dc09b7318c9cd99f4bd1c8fafae754f Mon Sep 17 00:00:00 2001 From: Christian Pointner Date: Mon, 23 Nov 2015 02:35:24 +0100 Subject: [PATCH 481/482] updated olsr-plugin patch for quagga 0.99.24.1 Signed-off-by: Christian Pointner --- lib/log.c | 20 ++++++++++++++++---- lib/route_types.txt | 15 ++++++--------- ripd/rip_zebra.c | 7 +++++-- ripngd/ripng_zebra.c | 7 +++++-- zebra/rt_netlink.c | 6 +++++- zebra/zebra_rib.c | 29 +++++++++++++++++++++++++++++ zebra/zebra_snmp.c | 6 ++++++ zebra/zebra_vty.c | 20 ++++++++++++++++---- 8 files changed, 88 insertions(+), 22 deletions(-) diff --git a/lib/log.c b/lib/log.c index f02e4c73..0ceb0360 100644 --- a/lib/log.c +++ b/lib/log.c @@ -971,13 +971,19 @@ proto_redistnum(int afi, const char *s) return ZEBRA_ROUTE_STATIC; else if (strncmp (s, "r", 1) == 0) return ZEBRA_ROUTE_RIP; - else if (strncmp (s, "o", 1) == 0) + else if (strncmp (s, "os", 2) == 0) return ZEBRA_ROUTE_OSPF; else if (strncmp (s, "i", 1) == 0) return ZEBRA_ROUTE_ISIS; else if (strncmp (s, "bg", 2) == 0) return ZEBRA_ROUTE_BGP; - else if (strncmp (s, "ba", 2) == 0) + else if (strncmp (s, "h", 1) == 0) + return ZEBRA_ROUTE_HSLS; + else if (strncmp (s, "ol", 2) == 0) + return ZEBRA_ROUTE_OLSR; + else if (strncmp (s, "bat", 3) == 0) + return ZEBRA_ROUTE_BATMAN; + else if (strncmp (s, "bab", 3) == 0) return ZEBRA_ROUTE_BABEL; } if (afi == AFI_IP6) @@ -990,13 +996,19 @@ proto_redistnum(int afi, const char *s) return ZEBRA_ROUTE_STATIC; else if (strncmp (s, "r", 1) == 0) return ZEBRA_ROUTE_RIPNG; - else if (strncmp (s, "o", 1) == 0) + else if (strncmp (s, "os", 2) == 0) return ZEBRA_ROUTE_OSPF6; else if (strncmp (s, "i", 1) == 0) return ZEBRA_ROUTE_ISIS; else if (strncmp (s, "bg", 2) == 0) return ZEBRA_ROUTE_BGP; - else if (strncmp (s, "ba", 2) == 0) + else if (strncmp (s, "h", 1) == 0) + return ZEBRA_ROUTE_HSLS; + else if (strncmp (s, "ol", 2) == 0) + return ZEBRA_ROUTE_OLSR; + else if (strncmp (s, "bat", 3) == 0) + return ZEBRA_ROUTE_BATMAN; + else if (strncmp (s, "bab", 3) == 0) return ZEBRA_ROUTE_BABEL; } return -1; diff --git a/lib/route_types.txt b/lib/route_types.txt index 1b856079..2dfd1453 100644 --- a/lib/route_types.txt +++ b/lib/route_types.txt @@ -51,15 +51,11 @@ ZEBRA_ROUTE_OSPF, ospf, ospfd, 'O', 1, 0, "OSPF" ZEBRA_ROUTE_OSPF6, ospf6, ospf6d, 'O', 0, 1, "OSPFv6" ZEBRA_ROUTE_ISIS, isis, isisd, 'I', 1, 1, "IS-IS" ZEBRA_ROUTE_BGP, bgp, bgpd, 'B', 1, 1, "BGP" -ZEBRA_ROUTE_PIM, pim, pimd, 'P', 1, 0, "PIM" -# HSLS and OLSR both are AFI independent (so: 1, 1), however -# we want to disable for them for general Quagga distribution. -# This at least makes it trivial for users of these protocols -# to 'switch on' redist support (direct numeric entry remaining -# possible). -ZEBRA_ROUTE_HSLS, hsls, hslsd, 'H', 0, 0, "HSLS" -ZEBRA_ROUTE_OLSR, olsr, olsrd, 'o', 0, 0, "OLSR" +ZEBRA_ROUTE_HSLS, hsls, hslsd, 'H', 1, 1, "HSLS" +ZEBRA_ROUTE_OLSR, olsr, olsrd, 'o', 1, 1, "OLSR" +ZEBRA_ROUTE_BATMAN, batman, batmand,'b', 1, 1, "BATMAN" ZEBRA_ROUTE_BABEL, babel, babeld, 'A', 1, 1, "Babel" +ZEBRA_ROUTE_PIM, pim, pimd, 'P', 1, 0, "PIM" ## help strings ZEBRA_ROUTE_SYSTEM, "Reserved route type, for internal use only" @@ -74,5 +70,6 @@ ZEBRA_ROUTE_ISIS, "Intermediate System to Intermediate System (IS-IS)" ZEBRA_ROUTE_BGP, "Border Gateway Protocol (BGP)" ZEBRA_ROUTE_PIM, "Protocol Independent Multicast (PIM)" ZEBRA_ROUTE_HSLS, "Hazy-Sighted Link State Protocol (HSLS)" -ZEBRA_ROUTE_OLSR, "Optimised Link State Routing (OLSR)" +ZEBRA_ROUTE_OLSR, "Optimized Link State Routing (OLSR)" +ZEBRA_ROUTE_BATMAN, "Better Approach to Mobile Ad-Hoc Networking (BATMAN)" ZEBRA_ROUTE_BABEL, "Babel routing protocol (Babel)" diff --git a/ripd/rip_zebra.c b/ripd/rip_zebra.c index b005ece9..0e1e52eb 100644 --- a/ripd/rip_zebra.c +++ b/ripd/rip_zebra.c @@ -238,9 +238,12 @@ static struct { {ZEBRA_ROUTE_KERNEL, 1, "kernel"}, {ZEBRA_ROUTE_CONNECT, 1, "connected"}, {ZEBRA_ROUTE_STATIC, 1, "static"}, - {ZEBRA_ROUTE_OSPF, 1, "ospf"}, + {ZEBRA_ROUTE_OSPF, 2, "ospf"}, {ZEBRA_ROUTE_BGP, 2, "bgp"}, - {ZEBRA_ROUTE_BABEL, 2, "babel"}, + {ZEBRA_ROUTE_HSLS, 1, "hsls"}, + {ZEBRA_ROUTE_OLSR, 2, "olsr"}, + {ZEBRA_ROUTE_BATMAN, 3, "batman"}, + {ZEBRA_ROUTE_BABEL, 3, "babel"}, {0, 0, NULL} }; diff --git a/ripngd/ripng_zebra.c b/ripngd/ripng_zebra.c index 68f37be3..aa54bbed 100644 --- a/ripngd/ripng_zebra.c +++ b/ripngd/ripng_zebra.c @@ -216,9 +216,12 @@ static struct { {ZEBRA_ROUTE_KERNEL, 1, "kernel"}, {ZEBRA_ROUTE_CONNECT, 1, "connected"}, {ZEBRA_ROUTE_STATIC, 1, "static"}, - {ZEBRA_ROUTE_OSPF6, 1, "ospf6"}, + {ZEBRA_ROUTE_OSPF6, 2, "ospf6"}, {ZEBRA_ROUTE_BGP, 2, "bgp"}, - {ZEBRA_ROUTE_BABEL, 2, "babel"}, + {ZEBRA_ROUTE_HSLS, 1, "hsls"}, + {ZEBRA_ROUTE_OLSR, 2, "olsr"}, + {ZEBRA_ROUTE_BATMAN, 3, "batman"}, + {ZEBRA_ROUTE_BABEL, 3, "babel"}, {0, 0, NULL} }; diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 2350070c..d49a6e2b 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -1457,6 +1457,7 @@ static void _netlink_route_build_singlepath( const char *routedesc, int bytelen, + struct rib *rib, struct nexthop *nexthop, struct nlmsghdr *nlmsg, struct rtmsg *rtmsg, @@ -1506,6 +1507,9 @@ _netlink_route_build_singlepath( addattr_l (nlmsg, req_size, RTA_PREFSRC, &nexthop->src.ipv4, bytelen); + if (rib->type == ZEBRA_ROUTE_OLSR) + rtmsg->rtm_scope = RT_SCOPE_LINK; + if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("netlink_route_multipath() (%s): " "nexthop via if %u", routedesc, nexthop->ifindex); @@ -1755,7 +1759,7 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib, routedesc = recursing ? "recursive, 1 hop" : "single hop"; _netlink_route_debug(cmd, p, nexthop, routedesc, family); - _netlink_route_build_singlepath(routedesc, bytelen, + _netlink_route_build_singlepath(routedesc, bytelen, rib, nexthop, &req.n, &req.r, sizeof req); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 0750e6eb..faa314f5 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -68,6 +68,9 @@ static const struct [ZEBRA_ROUTE_OSPF6] = {ZEBRA_ROUTE_OSPF6, 110}, [ZEBRA_ROUTE_ISIS] = {ZEBRA_ROUTE_ISIS, 115}, [ZEBRA_ROUTE_BGP] = {ZEBRA_ROUTE_BGP, 20 /* IBGP is 200. */}, + [ZEBRA_ROUTE_HSLS] = {ZEBRA_ROUTE_HSLS, 0}, + [ZEBRA_ROUTE_OLSR] = {ZEBRA_ROUTE_OLSR, 0}, + [ZEBRA_ROUTE_BATMAN] = {ZEBRA_ROUTE_BATMAN, 0}, [ZEBRA_ROUTE_BABEL] = {ZEBRA_ROUTE_BABEL, 95}, /* no entry/default: 150 */ }; @@ -575,6 +578,18 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, } return resolved; } + else if (match->type == ZEBRA_ROUTE_OLSR) + { + for (newhop = match->nexthop; newhop; newhop = newhop->next) + if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB) + && newhop->type == NEXTHOP_TYPE_IFINDEX) + { + if (nexthop->type == NEXTHOP_TYPE_IPV4) + nexthop->ifindex = newhop->ifindex; + return 1; + } + return 0; + } else { return 0; @@ -711,6 +726,18 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, } return resolved; } + else if (match->type == ZEBRA_ROUTE_OLSR) + { + for (newhop = match->nexthop; newhop; newhop = newhop->next) + if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB) + && newhop->type == NEXTHOP_TYPE_IFINDEX) + { + if (nexthop->type == NEXTHOP_TYPE_IPV6) + nexthop->ifindex = newhop->ifindex; + return 1; + } + return 0; + } else { return 0; @@ -1630,6 +1657,8 @@ static const u_char meta_queue_map[ZEBRA_ROUTE_MAX] = { [ZEBRA_ROUTE_ISIS] = 2, [ZEBRA_ROUTE_BGP] = 3, [ZEBRA_ROUTE_HSLS] = 4, + [ZEBRA_ROUTE_OLSR] = 4, + [ZEBRA_ROUTE_BATMAN] = 4, [ZEBRA_ROUTE_BABEL] = 2, }; diff --git a/zebra/zebra_snmp.c b/zebra/zebra_snmp.c index f0d3e7e5..f0a2e9fd 100644 --- a/zebra/zebra_snmp.c +++ b/zebra/zebra_snmp.c @@ -245,6 +245,12 @@ proto_trans(int type) return 1; /* shouldn't happen */ case ZEBRA_ROUTE_BGP: return 14; /* bgp */ + case ZEBRA_ROUTE_HSLS: + return 1; /* other */ + case ZEBRA_ROUTE_OLSR: + return 1; /* other */ + case ZEBRA_ROUTE_BATMAN: + return 1; /* other */ default: return 1; /* other */ } diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 1e39ebdd..5ed03bf1 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -731,7 +731,10 @@ vty_show_ip_route_detail (struct vty *vty, struct route_node *rn, int mcast) || rib->type == ZEBRA_ROUTE_OSPF || rib->type == ZEBRA_ROUTE_BABEL || rib->type == ZEBRA_ROUTE_ISIS - || rib->type == ZEBRA_ROUTE_BGP) + || rib->type == ZEBRA_ROUTE_BGP + || rib->type == ZEBRA_ROUTE_HSLS + || rib->type == ZEBRA_ROUTE_OLSR + || rib->type == ZEBRA_ROUTE_BATMAN) { time_t uptime; struct tm *tm; @@ -926,7 +929,10 @@ vty_show_ip_route (struct vty *vty, struct route_node *rn, struct rib *rib) || rib->type == ZEBRA_ROUTE_OSPF || rib->type == ZEBRA_ROUTE_BABEL || rib->type == ZEBRA_ROUTE_ISIS - || rib->type == ZEBRA_ROUTE_BGP) + || rib->type == ZEBRA_ROUTE_BGP + || rib->type == ZEBRA_ROUTE_HSLS + || rib->type == ZEBRA_ROUTE_OLSR + || rib->type == ZEBRA_ROUTE_BATMAN) { time_t uptime; struct tm *tm; @@ -1827,7 +1833,10 @@ vty_show_ipv6_route_detail (struct vty *vty, struct route_node *rn) || rib->type == ZEBRA_ROUTE_OSPF6 || rib->type == ZEBRA_ROUTE_BABEL || rib->type == ZEBRA_ROUTE_ISIS - || rib->type == ZEBRA_ROUTE_BGP) + || rib->type == ZEBRA_ROUTE_BGP + || rib->type == ZEBRA_ROUTE_HSLS + || rib->type == ZEBRA_ROUTE_OLSR + || rib->type == ZEBRA_ROUTE_BATMAN) { time_t uptime; struct tm *tm; @@ -1969,7 +1978,10 @@ vty_show_ipv6_route (struct vty *vty, struct route_node *rn, || rib->type == ZEBRA_ROUTE_OSPF6 || rib->type == ZEBRA_ROUTE_BABEL || rib->type == ZEBRA_ROUTE_ISIS - || rib->type == ZEBRA_ROUTE_BGP) + || rib->type == ZEBRA_ROUTE_BGP + || rib->type == ZEBRA_ROUTE_HSLS + || rib->type == ZEBRA_ROUTE_OLSR + || rib->type == ZEBRA_ROUTE_BATMAN) { time_t uptime; struct tm *tm; From 31d4bd10574c89ee4648a4cb20dee5b8a97c98a5 Mon Sep 17 00:00:00 2001 From: Christian Pointner Date: Thu, 26 Nov 2015 01:36:31 +0100 Subject: [PATCH 482/482] also ported additional openwrt related changes to 0.99.24.1 Signed-off-by: Christian Pointner --- bgpd/bgp_network.c | 3 +-- bgpd/bgpd.h | 1 + lib/command.c | 23 +++++++++++++++++++++++ vtysh/vtysh.c | 6 +++--- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index cea430cc..d0da9fa8 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -256,8 +256,7 @@ bgp_accept (struct thread *thread) peer->fd = bgp_sock; peer->status = Active; peer->local_id = peer1->local_id; - peer->v_holdtime = peer1->v_holdtime; - peer->v_keepalive = peer1->v_keepalive; + peer->v_holdtime = BGP_LARGE_HOLDTIME; /* Make peer's address string. */ sockunion2str (&su, buf, SU_ADDRSTRLEN); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 40c381c2..fbff82f3 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -744,6 +744,7 @@ struct bgp_nlri /* BGP timers default value. */ #define BGP_INIT_START_TIMER 5 #define BGP_ERROR_START_TIMER 30 +#define BGP_LARGE_HOLDTIME 240 #define BGP_DEFAULT_HOLDTIME 180 #define BGP_DEFAULT_KEEPALIVE 60 #define BGP_DEFAULT_ASORIGINATE 15 diff --git a/lib/command.c b/lib/command.c index 83177895..44cda628 100644 --- a/lib/command.c +++ b/lib/command.c @@ -3071,6 +3071,13 @@ DEFUN (config_write_file, VTY_NEWLINE); goto finished; } + +#if 0 + /* This code fails on UNION MOUNTs and similar filesystems if the + * config file is still on the RO layer. Hardlinks across layers + * will not work and cause quagga to fail saving the configuration... + * should use rename() to move files around... + */ if (link (config_file, config_file_sav) != 0) { vty_out (vty, "Can't backup old configuration file %s.%s", config_file_sav, @@ -3084,7 +3091,23 @@ DEFUN (config_write_file, VTY_NEWLINE); goto finished; } +#else + /* And this is the code that hopefully does work */ + if (rename (config_file, config_file_sav) != 0) + { + vty_out (vty, "Can't backup old configuration file %s.%s", config_file_sav, + VTY_NEWLINE); + goto finished; + } + sync (); +#endif + +#if 0 + /* same here. Please no cross-filesystem hardlinks... */ if (link (config_file_tmp, config_file) != 0) +#else + if (rename (config_file_tmp, config_file) != 0) +#endif { vty_out (vty, "Can't save configuration file %s.%s", config_file, VTY_NEWLINE); diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 984f8d39..cc09eec9 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -269,7 +269,7 @@ vtysh_pager_init (void) if (pager_defined) vtysh_pager_name = strdup (pager_defined); else - vtysh_pager_name = strdup ("more"); + vtysh_pager_name = strdup ("cat"); } /* Command execution over the vty interface. */ @@ -1886,7 +1886,7 @@ DEFUN (vtysh_terminal_length, { int lines; char *endptr = NULL; - char default_pager[10]; + char default_pager[12]; lines = strtol (argv[0], &endptr, 10); if (lines < 0 || lines > 512 || *endptr != '\0') @@ -1903,7 +1903,7 @@ DEFUN (vtysh_terminal_length, if (lines != 0) { - snprintf(default_pager, 10, "more -%i", lines); + snprintf(default_pager, 12, "head -n %i", lines); vtysh_pager_name = strdup (default_pager); }