From 6e26278cbabccf64de9abf4a764ee71f2c6e9165 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Tue, 19 May 2015 17:47:22 -0700 Subject: [PATCH] zebra: zebra-static-route-nht.patch Use NHT to support static routes with NH derived from protocols. --- lib/nexthop.h | 1 + zebra/rib.h | 12 +- zebra/zebra_rib.c | 305 ++++++++++++++++++++++++++++++++------ zebra/zebra_rnh.c | 324 ++++++++++++++++++++++++----------------- zebra/zebra_rnh.h | 3 + zebra/zebra_rnh_null.c | 6 + 6 files changed, 477 insertions(+), 174 deletions(-) diff --git a/lib/nexthop.h b/lib/nexthop.h index bddac6554f..b3a128a2a8 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -63,6 +63,7 @@ struct nexthop #define NEXTHOP_FLAG_RECURSIVE (1 << 2) /* Recursive nexthop. */ #define NEXTHOP_FLAG_ONLINK (1 << 3) /* Nexthop should be installed onlink. */ #define NEXTHOP_FLAG_MATCHED (1 << 4) /* Already matched vs a nexthop */ +#define NEXTHOP_FLAG_FILTERED (1 << 5) /* rmap filtered, used by static only */ /* Nexthop address */ union g_addr gate; diff --git a/zebra/rib.h b/zebra/rib.h index c2b28d1967..65d1667b54 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -68,7 +68,9 @@ struct rib /* RIB internal status */ u_char status; -#define RIB_ENTRY_REMOVED (1 << 0) +#define RIB_ENTRY_REMOVED 0x1 + /* to simplify NHT logic when NHs change, instead of doing a NH by NH cmp */ +#define RIB_ENTRY_NEXTHOPS_CHANGED 0x2 /* Nexthop information. */ u_char nexthop_num; @@ -342,9 +344,10 @@ extern struct nexthop *nexthop_ipv4_ifindex_add (struct rib *, struct in_addr *, struct in_addr *, unsigned int); -extern void nexthop_free (struct nexthop *nexthop); -extern void nexthops_free (struct nexthop *nexthop); +extern void nexthop_free (struct nexthop *nexthop, struct route_node *); +extern void nexthops_free (struct nexthop *nexthop, struct route_node *); extern void nexthop_add (struct rib *rib, struct nexthop *nexthop); +extern void copy_nexthops (struct rib *rib, struct nexthop *nh); extern int nexthop_has_fib_child(struct nexthop *); extern void rib_lookup_and_dump (struct prefix_ipv4 *); @@ -399,6 +402,9 @@ extern void rib_sweep_route (void); extern void rib_close (void); extern void rib_init (void); extern unsigned long rib_score_proto (u_char proto); +struct zebra_t; +extern void rib_queue_add (struct zebra_t *zebra, struct route_node *rn); + extern int static_add_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index ee50ad7030..9499e80422 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -182,7 +182,7 @@ vrf_static_table (afi_t afi, safi_t safi, u_int32_t id) } /* Add nexthop to the end of a nexthop list. */ -static void +void _nexthop_add (struct nexthop **target, struct nexthop *nexthop) { struct nexthop *last; @@ -204,6 +204,50 @@ nexthop_add (struct rib *rib, struct nexthop *nexthop) rib->nexthop_num++; } +static void +_copy_nexthops (struct nexthop **tnh, struct nexthop *nh) +{ + struct nexthop *nexthop; + struct nexthop *nh1; + + for (nh1 = nh; nh1; nh1 = nh1->next) + { + nexthop = nexthop_new(); + nexthop->flags = nh->flags; + nexthop->type = nh->type; + nexthop->ifindex = nh->ifindex; + if (nh->ifname) + nexthop->ifname = XSTRDUP(0, nh->ifname); + memcpy(&(nexthop->gate), &(nh->gate), sizeof(union g_addr)); + memcpy(&(nexthop->src), &(nh->src), sizeof(union g_addr)); + _nexthop_add(tnh, nexthop); + + if (CHECK_FLAG(nh1->flags, NEXTHOP_FLAG_RECURSIVE)) + _copy_nexthops(&nexthop->resolved, nh1->resolved); + } +} + +/** + * copy_nexthop - copy a nexthop to the rib structure. + */ +void +copy_nexthops (struct rib *rib, struct nexthop *nh) +{ + struct nexthop *nexthop; + + nexthop = nexthop_new(); + nexthop->flags = nh->flags; + nexthop->type = nh->type; + nexthop->ifindex = nh->ifindex; + if (nh->ifname) + nexthop->ifname = XSTRDUP(0, nh->ifname); + memcpy(&(nexthop->gate), &(nh->gate), sizeof(union g_addr)); + memcpy(&(nexthop->src), &(nh->src), sizeof(union g_addr)); + nexthop_add(rib, nexthop); + if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_RECURSIVE)) + _copy_nexthops(&nexthop->resolved, nh->resolved); +} + /* Delete specified nexthop from the list. */ static void nexthop_delete (struct rib *rib, struct nexthop *nexthop) @@ -219,25 +263,39 @@ nexthop_delete (struct rib *rib, struct nexthop *nexthop) /* Free nexthop. */ void -nexthop_free (struct nexthop *nexthop) +nexthop_free (struct nexthop *nexthop, struct route_node *rn) { if (nexthop->ifname) XFREE (0, nexthop->ifname); if (nexthop->resolved) - nexthops_free(nexthop->resolved); + nexthops_free(nexthop->resolved, rn); XFREE (MTYPE_NEXTHOP, nexthop); } /* Frees a list of nexthops */ void -nexthops_free (struct nexthop *nexthop) +nexthops_free (struct nexthop *nexthop, struct route_node *rn) { struct nexthop *nh, *next; + struct prefix nh_p; for (nh = nexthop; nh; nh = next) { next = nh->next; - nexthop_free (nh); + if (nh->type == NEXTHOP_TYPE_IPV4) + { + nh_p.family = AF_INET; + nh_p.prefixlen = IPV4_MAX_BITLEN; + nh_p.u.prefix4 = nh->gate.ipv4; + } + else if (nh->type == NEXTHOP_TYPE_IPV6) + { + nh_p.family = AF_INET6; + nh_p.prefixlen = IPV6_MAX_BITLEN; + nh_p.u.prefix6 = nh->gate.ipv6; + } + zebra_deregister_rnh_static_nh(&nh_p, rn); + nexthop_free (nh, rn); } } @@ -394,8 +452,9 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, struct route_node *rn; struct rib *match; int resolved; - struct nexthop *newhop; + struct nexthop *newhop, *tnewhop; struct nexthop *resolved_hop; + int recursing = 0; if (nexthop->type == NEXTHOP_TYPE_IPV4) nexthop->ifindex = 0; @@ -403,10 +462,16 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, if (set) { UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE); - nexthops_free(nexthop->resolved); + nexthops_free(nexthop->resolved, top); nexthop->resolved = NULL; } + /* Skip nexthops that have been filtered out due to route-map */ + /* The nexthops are specific to this route and so the same */ + /* nexthop for a different route may not have this flag set */ + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FILTERED)) + return 0; + /* Make lookup prefix. */ memset (&p, 0, sizeof (struct prefix_ipv4)); p.family = AF_INET; @@ -438,8 +503,7 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, /* If there is no selected route or matched route is EGP, go up tree. */ - if (! match - || match->type == ZEBRA_ROUTE_BGP) + if (! match) { do { rn = rn->parent; @@ -474,6 +538,7 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, if (set) { SET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE); + SET_FLAG(rib->status, RIB_ENTRY_NEXTHOPS_CHANGED); resolved_hop = XCALLOC(MTYPE_NEXTHOP, sizeof (struct nexthop)); SET_FLAG (resolved_hop->flags, NEXTHOP_FLAG_ACTIVE); @@ -509,6 +574,57 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, resolved_hop->ifindex = newhop->ifindex; } + _nexthop_add(&nexthop->resolved, resolved_hop); + } + resolved = 1; + } + return resolved; + } + else if (rib->type == ZEBRA_ROUTE_STATIC) + { + resolved = 0; + for (ALL_NEXTHOPS_RO(match->nexthop, newhop, tnewhop, recursing)) + if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB)) + { + if (set) + { + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE); + + resolved_hop = XCALLOC(MTYPE_NEXTHOP, sizeof (struct nexthop)); + SET_FLAG (resolved_hop->flags, NEXTHOP_FLAG_ACTIVE); + /* 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) + { + 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); } resolved = 1; @@ -536,7 +652,8 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, struct route_node *rn; struct rib *match; int resolved; - struct nexthop *newhop; + struct nexthop *newhop, *tnewhop; + int recursing = 0; struct nexthop *resolved_hop; if (nexthop->type == NEXTHOP_TYPE_IPV6) @@ -545,10 +662,16 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, if (set) { UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE); - nexthops_free(nexthop->resolved); + nexthops_free(nexthop->resolved, top); nexthop->resolved = NULL; } + /* Skip nexthops that have been filtered out due to route-map */ + /* The nexthops are specific to this route and so the same */ + /* nexthop for a different route may not have this flag set */ + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FILTERED)) + return 0; + /* Make lookup prefix. */ memset (&p, 0, sizeof (struct prefix_ipv6)); p.family = AF_INET6; @@ -580,8 +703,7 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, /* If there is no selected route or matched route is EGP, go up tree. */ - if (! match - || match->type == ZEBRA_ROUTE_BGP) + if (! match) { do { rn = rn->parent; @@ -613,6 +735,50 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, for (newhop = match->nexthop; newhop; newhop = newhop->next) if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB) && ! CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_RECURSIVE)) + { + if (set) + { + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE); + SET_FLAG(rib->status, RIB_ENTRY_NEXTHOPS_CHANGED); + + resolved_hop = XCALLOC(MTYPE_NEXTHOP, sizeof (struct nexthop)); + SET_FLAG (resolved_hop->flags, NEXTHOP_FLAG_ACTIVE); + /* 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->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) + { + 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); + } + resolved = 1; + } + return resolved; + } + else if (rib->type == ZEBRA_ROUTE_STATIC) + { + resolved = 0; + for (ALL_NEXTHOPS_RO(match->nexthop, newhop, tnewhop, recursing)) + if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB)) { if (set) { @@ -698,8 +864,7 @@ rib_match_ipv4 (struct in_addr addr) /* If there is no selected route or matched route is EGP, go up tree. */ - if (! match - || match->type == ZEBRA_ROUTE_BGP) + if (! match) { do { rn = rn->parent; @@ -755,7 +920,7 @@ rib_lookup_ipv4 (struct prefix_ipv4 *p) break; } - if (! match || match->type == ZEBRA_ROUTE_BGP) + if (! match) return NULL; if (match->type == ZEBRA_ROUTE_CONNECT) @@ -883,8 +1048,7 @@ rib_match_ipv6 (struct in6_addr *addr) /* If there is no selected route or matched route is EGP, go up tree. */ - if (! match - || match->type == ZEBRA_ROUTE_BGP) + if (! match) { do { rn = rn->parent; @@ -1047,7 +1211,9 @@ static int nexthop_active_update (struct route_node *rn, struct rib *rib, int set) { struct nexthop *nexthop; - unsigned int prev_active, prev_index, new_active; + unsigned int prev_active, prev_index, new_active, old_num_nh; + + old_num_nh = rib->nexthop_active_num; rib->nexthop_active_num = 0; UNSET_FLAG (rib->flags, ZEBRA_FLAG_CHANGED); @@ -1060,8 +1226,20 @@ nexthop_active_update (struct route_node *rn, struct rib *rib, int set) rib->nexthop_active_num++; if (prev_active != new_active || prev_index != nexthop->ifindex) - SET_FLAG (rib->flags, ZEBRA_FLAG_CHANGED); + { + SET_FLAG (rib->flags, ZEBRA_FLAG_CHANGED); + SET_FLAG (rib->status, RIB_ENTRY_NEXTHOPS_CHANGED); + } } + + if (old_num_nh != rib->nexthop_active_num) + SET_FLAG (rib->flags, ZEBRA_FLAG_CHANGED); + + if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_CHANGED)) + { + SET_FLAG (rib->status, RIB_ENTRY_NEXTHOPS_CHANGED); + } + return rib->nexthop_active_num; } @@ -1232,6 +1410,8 @@ rib_process (struct route_node *rn) RNODE_FOREACH_RIB_SAFE (rn, rib, next) { + UNSET_FLAG(rib->status, RIB_ENTRY_NEXTHOPS_CHANGED); + /* Currently installed rib. */ if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) { @@ -1258,7 +1438,14 @@ rib_process (struct route_node *rn) } /* Skip unreachable nexthop. */ - if (! nexthop_active_update (rn, rib, 0)) + /* With static routes that may have recursive nexthops, calling + * nexthop_active_update will clear the ZEBRA_FLAG_CHANGED flag + * as the top level NH may not have changed. Those flags are set + * by the NHT evaluation. So, we skip an active_update_check here + * for static routes as its job has already been done. + */ + if (rib->type != ZEBRA_ROUTE_STATIC && + ! nexthop_active_update (rn, rib, 0)) continue; /* Infinit distance. */ @@ -1330,12 +1517,20 @@ rib_process (struct route_node *rn) rib_uninstall_kernel (rn, select); /* Set real nexthop. */ - nexthop_active_update (rn, select, 1); - - if (! RIB_SYSTEM_ROUTE (select)) - rib_install_kernel (rn, select); - redistribute_add (&rn->p, select); - } + /* Need to check if any NHs are active to clear the + * the selected flag + */ + if (nexthop_active_update (rn, select, 1)) + { + if (! RIB_SYSTEM_ROUTE (select)) + rib_install_kernel (rn, select); + redistribute_add (&rn->p, select); + } + else + { + UNSET_FLAG (select->flags, ZEBRA_FLAG_SELECTED); + } + } else if (! RIB_SYSTEM_ROUTE (select)) { /* Housekeeping code to deal with @@ -1391,12 +1586,13 @@ rib_process (struct route_node *rn) zfpm_trigger_update (rn, "new route selected"); /* Set real nexthop. */ - nexthop_active_update (rn, select, 1); - - if (! RIB_SYSTEM_ROUTE (select)) - rib_install_kernel (rn, select); - SET_FLAG (select->flags, ZEBRA_FLAG_SELECTED); - redistribute_add (&rn->p, select); + if (nexthop_active_update (rn, select, 1)) + { + if (! RIB_SYSTEM_ROUTE (select)) + rib_install_kernel (rn, select); + SET_FLAG (select->flags, ZEBRA_FLAG_SELECTED); + redistribute_add (&rn->p, select); + } } /* FIB route was removed, should be deleted */ @@ -1537,7 +1733,7 @@ rib_meta_queue_add (struct meta_queue *mq, struct route_node *rn) } /* Add route_node to work queue and schedule processing */ -static void +void rib_queue_add (struct zebra_t *zebra, struct route_node *rn) { char buf[INET_ADDRSTRLEN]; @@ -1772,7 +1968,7 @@ rib_unlink (struct route_node *rn, struct rib *rib) } /* free RIB and nexthops */ - nexthops_free(rib->nexthop); + nexthops_free(rib->nexthop, rn); XFREE (MTYPE_RIB, rib); } @@ -2284,6 +2480,7 @@ static_install_ipv4 (struct prefix *p, struct static_ipv4 *si) struct rib *rib; struct route_node *rn; struct route_table *table; + struct prefix nh_p; /* Lookup table. */ table = vrf_table (AFI_IP, SAFI_UNICAST, 0); @@ -2314,6 +2511,10 @@ static_install_ipv4 (struct prefix *p, struct static_ipv4 *si) { case STATIC_IPV4_GATEWAY: nexthop_ipv4_add (rib, &si->gate.ipv4, NULL); + nh_p.family = AF_INET; + nh_p.prefixlen = IPV4_MAX_BITLEN; + nh_p.u.prefix4 = si->gate.ipv4; + zebra_register_rnh_static_nh(&nh_p, rn); break; case STATIC_IPV4_IFNAME: nexthop_ifname_add (rib, si->gate.ifname); @@ -2340,6 +2541,10 @@ static_install_ipv4 (struct prefix *p, struct static_ipv4 *si) { case STATIC_IPV4_GATEWAY: nexthop_ipv4_add (rib, &si->gate.ipv4, NULL); + nh_p.family = AF_INET; + nh_p.prefixlen = IPV4_MAX_BITLEN; + nh_p.u.prefix4 = si->gate.ipv4; + zebra_register_rnh_static_nh(&nh_p, rn); break; case STATIC_IPV4_IFNAME: nexthop_ifname_add (rib, si->gate.ifname); @@ -2382,6 +2587,7 @@ static_uninstall_ipv4 (struct prefix *p, struct static_ipv4 *si) struct rib *rib; struct nexthop *nexthop; struct route_table *table; + struct prefix nh_p; /* Lookup table. */ table = vrf_table (AFI_IP, SAFI_UNICAST, 0); @@ -2428,8 +2634,13 @@ static_uninstall_ipv4 (struct prefix *p, struct static_ipv4 *si) { if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) rib_uninstall (rn, rib); + + nh_p.family = AF_INET; + nh_p.prefixlen = IPV4_MAX_BITLEN; + nh_p.u.prefix4 = nexthop->gate.ipv4; nexthop_delete (rib, nexthop); - nexthop_free (nexthop); + zebra_deregister_rnh_static_nh(&nh_p, rn); + nexthop_free (nexthop, rn); rib_queue_add (&zebrad, rn); } /* Unlock node. */ @@ -2737,11 +2948,8 @@ rib_add_ipv6_multipath (struct prefix_ipv6 *p, struct rib *rib, safi_t safi, struct rib *same = NULL; struct nexthop *nexthop; int ret = 0; - int table_id = 0; - if (rib) - table_id = rib->table; - else + if (!rib) return 0; /* why are we getting called with NULL rib */ /* Lookup table. */ @@ -2961,6 +3169,7 @@ static_install_ipv6 (struct prefix *p, struct static_ipv6 *si) struct rib *rib; struct route_table *table; struct route_node *rn; + struct prefix nh_p; /* Lookup table. */ table = vrf_table (AFI_IP6, SAFI_UNICAST, 0); @@ -2992,6 +3201,10 @@ static_install_ipv6 (struct prefix *p, struct static_ipv6 *si) { case STATIC_IPV6_GATEWAY: nexthop_ipv6_add (rib, &si->ipv6); + nh_p.family = AF_INET6; + nh_p.prefixlen = IPV6_MAX_BITLEN; + nh_p.u.prefix6 = si->ipv6; + zebra_register_rnh_static_nh(&nh_p, rn); break; case STATIC_IPV6_IFNAME: nexthop_ifname_add (rib, si->ifname); @@ -3018,6 +3231,10 @@ static_install_ipv6 (struct prefix *p, struct static_ipv6 *si) { case STATIC_IPV6_GATEWAY: nexthop_ipv6_add (rib, &si->ipv6); + nh_p.family = AF_INET6; + nh_p.prefixlen = IPV6_MAX_BITLEN; + nh_p.u.prefix6 = si->ipv6; + zebra_register_rnh_static_nh(&nh_p, rn); break; case STATIC_IPV6_IFNAME: nexthop_ifname_add (rib, si->ifname); @@ -3061,6 +3278,7 @@ static_uninstall_ipv6 (struct prefix *p, struct static_ipv6 *si) struct route_node *rn; struct rib *rib; struct nexthop *nexthop; + struct prefix nh_p; /* Lookup table. */ table = vrf_table (AFI_IP6, SAFI_UNICAST, 0); @@ -3109,8 +3327,13 @@ static_uninstall_ipv6 (struct prefix *p, struct static_ipv6 *si) { if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) rib_uninstall (rn, rib); + + nh_p.family = AF_INET6; + nh_p.prefixlen = IPV6_MAX_BITLEN; + nh_p.u.prefix6 = nexthop->gate.ipv6; nexthop_delete (rib, nexthop); - nexthop_free (nexthop); + zebra_deregister_rnh_static_nh(&nh_p, rn); + nexthop_free (nexthop, rn); rib_queue_add (&zebrad, rn); } /* Unlock node. */ diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index 85ab0910a4..67d27c8823 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -54,8 +54,11 @@ t; \ }) -static void free_state(struct rib *rib); -static void copy_state(struct rnh *rnh, struct rib *rib); +/* Default rtm_table for all clients */ +extern struct zebra_t zebrad; + +static void free_state(struct rib *rib, struct route_node *rn); +static void copy_state(struct rnh *rnh, struct rib *rib, struct route_node *rn); static int compare_state(struct rib *r1, struct rib *r2); static int send_client(struct rnh *rnh, struct zserv *client); static void print_rnh(struct route_node *rn, struct vty *vty); @@ -97,6 +100,7 @@ zebra_add_rnh (struct prefix *p, u_int32_t vrfid) { rnh = XCALLOC(MTYPE_RNH, sizeof(struct rnh)); rnh->client_list = list_new(); + rnh->zebra_static_route_list = list_new(); route_lock_node (rn); rn->info = rnh; rnh->node = rn; @@ -143,7 +147,8 @@ zebra_delete_rnh (struct rnh *rnh) } list_free(rnh->client_list); - free_state(rnh->state); + list_free(rnh->zebra_static_route_list); + free_state(rnh->state, rn); XFREE(MTYPE_RNH, rn->info); rn->info = NULL; route_unlock_node (rn); @@ -178,10 +183,70 @@ zebra_remove_rnh_client (struct rnh *rnh, struct zserv *client) rnh_str(rnh, buf, INET6_ADDRSTRLEN)); } listnode_delete(rnh->client_list, client); - if (list_isempty(rnh->client_list)) + if (list_isempty(rnh->client_list) && + list_isempty(rnh->zebra_static_route_list)) + zebra_delete_rnh(rnh); +} + +void +zebra_register_rnh_static_nh(struct prefix *nh, struct route_node *static_rn) +{ + struct rnh *rnh; + + rnh = zebra_add_rnh(nh, 0); + if (rnh && !listnode_lookup(rnh->zebra_static_route_list, static_rn)) + { + listnode_add(rnh->zebra_static_route_list, static_rn); + } +} + +void +zebra_deregister_rnh_static_nh(struct prefix *nh, struct route_node *static_rn) +{ + struct rnh *rnh; + + rnh = zebra_lookup_rnh(nh, 0); + if (!rnh) + return; + + listnode_delete(rnh->zebra_static_route_list, static_rn); + + if (list_isempty(rnh->client_list) && + list_isempty(rnh->zebra_static_route_list)) zebra_delete_rnh(rnh); } +static int +zebra_evaluate_rnh_nexthops(int family, struct rib *rib, struct route_node *prn, + int proto) +{ + int at_least_one = 0; + int rmap_family; /* Route map has diff AF family enum */ + struct nexthop *nexthop; + int ret; + + rmap_family = (family == AF_INET) ? AFI_IP : AFI_IP6; + + if (prn && rib) + { + for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) + { + ret = zebra_nht_route_map_check(rmap_family, proto, &prn->p, rib, + nexthop); + if (ret != RMAP_DENYMATCH) + { + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + at_least_one++; /* at least one valid NH */ + } + else + { + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + } + } + } + return (at_least_one); +} + int zebra_evaluate_rnh_table (int vrfid, int family, int force) { @@ -192,17 +257,14 @@ zebra_evaluate_rnh_table (int vrfid, int family, int force) struct rnh *rnh; struct zserv *client; struct listnode *node; - struct rib *rib; - int rmap_family; /* Route map has diff AF family enum */ - route_map_result_t ret = RMAP_MATCH; - struct nexthop *nexthop; + struct rib *rib, *srib; int state_changed = 0; int at_least_one = 0; char bufn[INET6_ADDRSTRLEN]; char bufp[INET6_ADDRSTRLEN]; - - rmap_family = (family == AF_INET) ? AFI_IP : AFI_IP6; - + char bufs[INET6_ADDRSTRLEN]; + struct route_node *static_rn; + struct nexthop *nexthop; ntable = lookup_rnh_table(vrfid, family); if (!ntable) { @@ -223,6 +285,8 @@ zebra_evaluate_rnh_table (int vrfid, int family, int force) continue; rnh = nrn->info; + at_least_one = 0; + prn = route_node_match(ptable, &nrn->p); if (!prn) rib = NULL; @@ -249,7 +313,10 @@ zebra_evaluate_rnh_table (int vrfid, int family, int force) if (compare_state(rib, rnh->state)) { - copy_state(rnh, rib); + if (rib) + UNSET_FLAG(rib->status, RIB_ENTRY_NEXTHOPS_CHANGED); + + copy_state(rnh, rib, nrn); state_changed = 1; } @@ -260,56 +327,117 @@ zebra_evaluate_rnh_table (int vrfid, int family, int force) prefix2str(&prn->p, bufp, INET6_ADDRSTRLEN); else strcpy(bufp, "null"); + + zlog_debug("%s: State changed for %s/%s", __FUNCTION__, bufn, bufp); + } /* Notify registered clients */ + rib = rnh->state; + if (state_changed || force) - for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client)) - { - rib = rnh->state; - if (prn && rib) - { - at_least_one = 0; - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) - { - ret = zebra_nht_route_map_check(rmap_family, client->proto, - &prn->p, rib, nexthop); - if (ret == RMAP_DENYMATCH) - { - UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); - } - else - { - SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); - at_least_one++; /* at least one valid NH */ - } - } - if (at_least_one) - rnh->filtered[client->proto] = 0; - else - rnh->filtered[client->proto] = 1; - - if (IS_ZEBRA_DEBUG_NHT && (state_changed || force)) - zlog_debug("%srnh %s resolved through route %s - sending " - "nexthop %s event to clients", - at_least_one ? "":"(filtered)", bufn, bufp, - rib ? "reachable" : "unreachable"); - - send_client(rnh, client); /* Route-map passed */ - } - else if (state_changed) - { - if (IS_ZEBRA_DEBUG_NHT && (state_changed || force)) - zlog_debug("rnh %s resolved through route %s - sending " - "nexthop %s event to clients", bufn, bufp, - rib ? "reachable" : "unreachable"); + { + for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client)) + { + if (prn && rib) + { + at_least_one = zebra_evaluate_rnh_nexthops(family, rib, prn, + client->proto); + if (at_least_one) + rnh->filtered[client->proto] = 0; + else + rnh->filtered[client->proto] = 1; + } + else if (state_changed) rnh->filtered[client->proto] = 0; - send_client(rnh, client); - /* We don't need to send a RNH update on force since no - * update really happened, just a route-map change. - */ - } - } + + if (IS_ZEBRA_DEBUG_NHT && (state_changed || force)) + zlog_debug("%srnh %s resolved through route %s - sending " + "nexthop %s event to clients", + at_least_one ? "":"(filtered)", bufn, bufp, + rib ? "reachable" : "unreachable"); + + send_client(rnh, client); /* Route-map passed */ + } + + /* Now evaluate static client */ + if (prn && rib) + { + at_least_one = zebra_evaluate_rnh_nexthops(family, rib, prn, + ZEBRA_ROUTE_STATIC); + if (at_least_one) + rnh->filtered[ZEBRA_ROUTE_STATIC] = 0; + else + rnh->filtered[ZEBRA_ROUTE_STATIC] = 1; + } + else if (state_changed) + rnh->filtered[ZEBRA_ROUTE_STATIC] = 0; + + for (ALL_LIST_ELEMENTS_RO(rnh->zebra_static_route_list, node, + static_rn)) + { + RNODE_FOREACH_RIB(static_rn, srib) + { + break; /* pick the first and only(?) rib for static */ + } + + if (!srib) + { + if (IS_ZEBRA_DEBUG_NHT) + { + prefix2str(&static_rn->p, bufs, INET6_ADDRSTRLEN); + zlog_debug("%s: Unable to find RIB for static route %s, skipping NH resolution", + __FUNCTION__, bufs); + continue; + } + } + + /* Mark the appropriate static route's NH as filtered */ + for (nexthop = srib->nexthop; nexthop; nexthop = nexthop->next) + { + switch (nexthop->type) + { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + /* Don't see a use case for *_IFNAME */ + if (nexthop->gate.ipv4.s_addr == nrn->p.u.prefix4.s_addr) + { + if (at_least_one) + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FILTERED); + else + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FILTERED); + } + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + /* Don't see a use case for *_IFNAME */ + if (memcmp(&nexthop->gate.ipv6,&nrn->p.u.prefix6, 16) == 0) + { + if (at_least_one) + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FILTERED); + else + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FILTERED); + } + break; + default: + break; + } + } + + if (IS_ZEBRA_DEBUG_NHT && (state_changed || force)) + zlog_debug("%srnh %s resolved through route %s - sending " + "nexthop %s event to zebra", + at_least_one ? "":"(filtered)", bufn, bufp, + rib ? "reachable" : "unreachable"); + + if (srib && (state_changed || force)) + { + SET_FLAG(srib->flags, ZEBRA_FLAG_CHANGED); + SET_FLAG(srib->status, RIB_ENTRY_NEXTHOPS_CHANGED); + rib_queue_add(&zebrad, static_rn); + } + } + } } return 1; } @@ -401,51 +529,26 @@ zebra_cleanup_rnh_client (int vrfid, int family, struct zserv *client) * free_state - free up the rib structure associated with the rnh. */ static void -free_state (struct rib *rib) +free_state (struct rib *rib, struct route_node *rn) { - struct nexthop *nexthop, *next; if (!rib) return; /* free RIB and nexthops */ - for (nexthop = rib->nexthop; nexthop; nexthop = next) - { - next = nexthop->next; - nexthop_free (nexthop); - } + nexthops_free(rib->nexthop, rn); XFREE (MTYPE_RIB, rib); } -/** - * copy_nexthop - copy a nexthop to the rib structure. - */ -static void -copy_nexthop (struct rib *state, struct nexthop *nh) -{ - struct nexthop *nexthop; - - nexthop = nexthop_new(); - nexthop->flags = nh->flags; - nexthop->type = nh->type; - nexthop->ifindex = nh->ifindex; - if (nh->ifname) - nexthop->ifname = XSTRDUP(0, nh->ifname); - memcpy(&(nexthop->gate), &(nh->gate), sizeof(union g_addr)); - memcpy(&(nexthop->src), &(nh->src), sizeof(union g_addr)); - - nexthop_add(state, nexthop); -} - static void -copy_state (struct rnh *rnh, struct rib *rib) +copy_state (struct rnh *rnh, struct rib *rib, struct route_node *rn) { struct rib *state; struct nexthop *nh; if (rnh->state) { - free_state(rnh->state); + free_state(rnh->state, rn); rnh->state = NULL; } @@ -457,16 +560,13 @@ copy_state (struct rnh *rnh, struct rib *rib) state->metric = rib->metric; for (nh = rib->nexthop; nh; nh = nh->next) - copy_nexthop(state, nh); + copy_nexthops(state, nh); rnh->state = state; } static int compare_state (struct rib *r1, struct rib *r2) { - struct nexthop *nh1; - struct nexthop *nh2; - u_char found_nh = 0; if (!r1 && !r2) return 0; @@ -480,46 +580,8 @@ compare_state (struct rib *r1, struct rib *r2) if (r1->nexthop_num != r2->nexthop_num) return 1; - /* We need to verify that the nexthops for r1 match the nexthops for r2. - * Since it is possible for a rib entry to have the same nexthop multiple - * times (Example: [a,a]) we need to keep track of which r2 nexthops we have - * already used as a match against a r1 nexthop. We track this - * via NEXTHOP_FLAG_MATCHED. Clear this flag for all r2 nexthops when you - * are finished. - * - * TRUE: r1 [a,b], r2 [a,b] - * TRUE: r1 [a,b], r2 [b,a] - * FALSE: r1 [a,b], r2 [a,c] - * FALSE: r1 [a,a], r2 [a,b] - */ - for (nh1 = r1->nexthop; nh1; nh1 = nh1->next) - { - found_nh = 0; - for (nh2 = r2->nexthop; nh2; nh2 = nh2->next) - { - if (CHECK_FLAG (nh2->flags, NEXTHOP_FLAG_MATCHED)) - continue; - - if (nexthop_same_no_recurse(nh1, nh2)) - { - SET_FLAG (nh2->flags, NEXTHOP_FLAG_MATCHED); - found_nh = 1; - break; - } - } - - if (!found_nh) - { - for (nh2 = r2->nexthop; nh2; nh2 = nh2->next) - if (CHECK_FLAG (nh2->flags, NEXTHOP_FLAG_MATCHED)) - UNSET_FLAG (nh2->flags, NEXTHOP_FLAG_MATCHED); - return 1; - } - } - - for (nh2 = r2->nexthop; nh2; nh2 = nh2->next) - if (CHECK_FLAG (nh2->flags, NEXTHOP_FLAG_MATCHED)) - UNSET_FLAG (nh2->flags, NEXTHOP_FLAG_MATCHED); + if (CHECK_FLAG(r1->status, RIB_ENTRY_NEXTHOPS_CHANGED)) + return 1; return 0; } @@ -665,5 +727,7 @@ print_rnh (struct route_node *rn, struct vty *vty) for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client)) vty_out(vty, " %s(fd %d)%s", zebra_route_string(client->proto), client->sock, rnh->filtered[client->proto] ? "(filtered)" : ""); + if (!list_isempty(rnh->zebra_static_route_list)) + vty_out(vty, " zebra%s", rnh->filtered[ZEBRA_ROUTE_STATIC] ? "(filtered)" : ""); vty_out(vty, "%s", VTY_NEWLINE); } diff --git a/zebra/zebra_rnh.h b/zebra/zebra_rnh.h index 92e4c7a88c..53e932aa49 100644 --- a/zebra/zebra_rnh.h +++ b/zebra/zebra_rnh.h @@ -33,6 +33,7 @@ struct rnh #define ZEBRA_NHT_CONNECTED 0x1 struct rib *state; struct list *client_list; + struct list *zebra_static_route_list; /* static routes dependent on this NH */ struct route_node *node; int filtered[ZEBRA_ROUTE_MAX]; /* if this has been filtered for client */ }; @@ -41,6 +42,8 @@ extern struct rnh *zebra_add_rnh(struct prefix *p, u_int32_t vrfid); extern struct rnh *zebra_lookup_rnh(struct prefix *p, u_int32_t vrfid); extern void zebra_delete_rnh(struct rnh *rnh); extern void zebra_add_rnh_client(struct rnh *rnh, struct zserv *client); +extern void zebra_register_rnh_static_nh(struct prefix *, struct route_node *); +extern void zebra_deregister_rnh_static_nh(struct prefix *, struct route_node *); extern void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client); extern int zebra_evaluate_rnh_table(int vrfid, int family, int force); extern int zebra_dispatch_rnh_table(int vrfid, int family, struct zserv *cl); diff --git a/zebra/zebra_rnh_null.c b/zebra/zebra_rnh_null.c index 40f0c87288..da1bab876a 100644 --- a/zebra/zebra_rnh_null.c +++ b/zebra/zebra_rnh_null.c @@ -8,3 +8,9 @@ int zebra_evaluate_rnh_table (int vrfid, int family, int force) void zebra_print_rnh_table (int vrfid, int family, struct vty *vty) {} + +void zebra_register_rnh_static_nh(struct prefix *p, struct route_node *rn) +{} + +void zebra_deregister_rnh_static_nh(struct prefix *p, struct route_node *rn) +{} -- 2.39.5