diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c index dc234bc45d..5ac38d6685 100644 --- a/lib/nexthop_group.c +++ b/lib/nexthop_group.c @@ -331,6 +331,47 @@ static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc, XFREE(MTYPE_TMP, nh); } +static bool nexthop_group_parse_nexthop(struct nexthop *nhop, + const union sockunion *addr, + const char *intf, const char *name) +{ + struct vrf *vrf; + + memset(nhop, 0, sizeof(*nhop)); + + if (name) + vrf = vrf_lookup_by_name(name); + else + vrf = vrf_lookup_by_id(VRF_DEFAULT); + + if (!vrf) + return false; + + nhop->vrf_id = vrf->vrf_id; + + if (addr->sa.sa_family == AF_INET) { + nhop->gate.ipv4.s_addr = addr->sin.sin_addr.s_addr; + if (intf) { + nhop->type = NEXTHOP_TYPE_IPV4_IFINDEX; + nhop->ifindex = ifname2ifindex(intf, vrf->vrf_id); + if (nhop->ifindex == IFINDEX_INTERNAL) + return false; + } else + nhop->type = NEXTHOP_TYPE_IPV4; + } else { + memcpy(&nhop->gate.ipv6, &addr->sin6.sin6_addr, 16); + if (intf) { + nhop->type = NEXTHOP_TYPE_IPV6_IFINDEX; + nhop->ifindex = ifname2ifindex(intf, vrf->vrf_id); + if (nhop->ifindex == IFINDEX_INTERNAL) + return false; + } else + nhop->type = NEXTHOP_TYPE_IPV6; + } + + return true; +} + DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, "[no] nexthop $addr [INTERFACE]$intf [nexthop-vrf NAME$name]", NO_STR @@ -342,64 +383,26 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, "The nexthop-vrf Name\n") { VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc); - struct vrf *vrf; struct nexthop nhop; struct nexthop *nh; + bool legal; - if (name) - vrf = vrf_lookup_by_name(name); - else - vrf = vrf_lookup_by_id(VRF_DEFAULT); + legal = nexthop_group_parse_nexthop(&nhop, addr, intf, name); - if (!vrf) { - vty_out(vty, "Specified: %s is non-existent\n", name); - return CMD_WARNING; - } - - memset(&nhop, 0, sizeof(nhop)); - nhop.vrf_id = vrf->vrf_id; - - if (addr->sa.sa_family == AF_INET) { - nhop.gate.ipv4.s_addr = addr->sin.sin_addr.s_addr; - if (intf) { - nhop.type = NEXTHOP_TYPE_IPV4_IFINDEX; - nhop.ifindex = ifname2ifindex(intf, vrf->vrf_id); - if (nhop.ifindex == IFINDEX_INTERNAL) { - vty_out(vty, - "Specified Intf %s does not exist in vrf: %s\n", - intf, vrf->name); - return CMD_WARNING; - } - } else - nhop.type = NEXTHOP_TYPE_IPV4; - } else { - memcpy(&nhop.gate.ipv6, &addr->sin6.sin6_addr, 16); - if (intf) { - nhop.type = NEXTHOP_TYPE_IPV6_IFINDEX; - nhop.ifindex = ifname2ifindex(intf, vrf->vrf_id); - if (nhop.ifindex == IFINDEX_INTERNAL) { - vty_out(vty, - "Specified Intf %s does not exist in vrf: %s\n", - intf, vrf->name); - return CMD_WARNING; - } - } else { - if (IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) { - vty_out(vty, - "Specified a v6 LL with no interface, rejecting\n"); - return CMD_WARNING_CONFIG_FAILED; - } - nhop.type = NEXTHOP_TYPE_IPV6; - } + if (nhop.type == NEXTHOP_TYPE_IPV6 + && IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) { + vty_out(vty, + "Specified a v6 LL with no interface, rejecting\n"); + return CMD_WARNING_CONFIG_FAILED; } nh = nexthop_exists(&nhgc->nhg, &nhop); if (no) { + nexthop_group_unsave_nhop(nhgc, name, addr, intf); if (nh) { nexthop_del(&nhgc->nhg, nh); - nexthop_group_unsave_nhop(nhgc, name, addr, intf); if (nhg_hooks.del_nexthop) nhg_hooks.del_nexthop(nhgc, nh); @@ -407,14 +410,16 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, } } else if (!nh) { /* must be adding new nexthop since !no and !nexthop_exists */ - nh = nexthop_new(); + if (legal) { + nh = nexthop_new(); - memcpy(nh, &nhop, sizeof(nhop)); - nexthop_add(&nhgc->nhg.nexthop, nh); + memcpy(nh, &nhop, sizeof(nhop)); + nexthop_add(&nhgc->nhg.nexthop, nh); + } nexthop_group_save_nhop(nhgc, name, addr, intf); - if (nhg_hooks.add_nexthop) + if (legal && nhg_hooks.add_nexthop) nhg_hooks.add_nexthop(nhgc, nh); } @@ -504,6 +509,152 @@ static int nexthop_group_write(struct vty *vty) return 1; } +void nexthop_group_enable_vrf(struct vrf *vrf) +{ + struct nexthop_group_cmd *nhgc; + struct nexthop_hold *nhh; + + RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) { + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) { + struct nexthop nhop; + struct nexthop *nh; + + if (!nexthop_group_parse_nexthop(&nhop, &nhh->addr, + nhh->intf, + nhh->nhvrf_name)) + continue; + + nh = nexthop_exists(&nhgc->nhg, &nhop); + + if (nh) + continue; + + if (nhop.vrf_id != vrf->vrf_id) + continue; + + nh = nexthop_new(); + + memcpy(nh, &nhop, sizeof(nhop)); + nexthop_add(&nhgc->nhg.nexthop, nh); + + if (nhg_hooks.add_nexthop) + nhg_hooks.add_nexthop(nhgc, nh); + } + } +} + +void nexthop_group_disable_vrf(struct vrf *vrf) +{ + struct nexthop_group_cmd *nhgc; + struct nexthop_hold *nhh; + + RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) { + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) { + struct nexthop nhop; + struct nexthop *nh; + + if (!nexthop_group_parse_nexthop(&nhop, &nhh->addr, + nhh->intf, + nhh->nhvrf_name)) + continue; + + nh = nexthop_exists(&nhgc->nhg, &nhop); + + if (!nh) + continue; + + if (nh->vrf_id != vrf->vrf_id) + continue; + + nexthop_del(&nhgc->nhg, nh); + + if (nhg_hooks.del_nexthop) + nhg_hooks.del_nexthop(nhgc, nh); + + nexthop_free(nh); + } + } +} + +void nexthop_group_interface_state_change(struct interface *ifp, + ifindex_t oldifindex) +{ + struct nexthop_group_cmd *nhgc; + struct nexthop_hold *nhh; + + RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) { + struct listnode *node; + struct nexthop *nh; + + if (if_is_up(ifp)) { + for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) { + struct nexthop nhop; + + if (!nexthop_group_parse_nexthop( + &nhop, &nhh->addr, nhh->intf, + nhh->nhvrf_name)) + continue; + + switch (nhop.type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_BLACKHOLE: + continue; + case NEXTHOP_TYPE_IFINDEX: + case NEXTHOP_TYPE_IPV4_IFINDEX: + case NEXTHOP_TYPE_IPV6_IFINDEX: + break; + } + nh = nexthop_exists(&nhgc->nhg, &nhop); + + if (nh) + continue; + + if (ifp->ifindex != nhop.ifindex) + continue; + + nh = nexthop_new(); + + memcpy(nh, &nhop, sizeof(nhop)); + nexthop_add(&nhgc->nhg.nexthop, nh); + + if (nhg_hooks.add_nexthop) + nhg_hooks.add_nexthop(nhgc, nh); + } + } else { + struct nexthop *next_nh; + + for (nh = nhgc->nhg.nexthop; nh; nh = next_nh) { + next_nh = nh->next; + switch (nh->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_BLACKHOLE: + continue; + case NEXTHOP_TYPE_IFINDEX: + case NEXTHOP_TYPE_IPV4_IFINDEX: + case NEXTHOP_TYPE_IPV6_IFINDEX: + break; + } + + if (oldifindex != nh->ifindex) + continue; + + nexthop_del(&nhgc->nhg, nh); + + if (nhg_hooks.del_nexthop) + nhg_hooks.del_nexthop(nhgc, nh); + + nexthop_free(nh); + } + } + } +} + void nexthop_group_init(void (*new)(const char *name), void (*add_nexthop)(const struct nexthop_group_cmd *nhg, const struct nexthop *nhop), diff --git a/lib/nexthop_group.h b/lib/nexthop_group.h index e9b670d537..a44f4e3542 100644 --- a/lib/nexthop_group.h +++ b/lib/nexthop_group.h @@ -94,6 +94,11 @@ void nexthop_group_init( const struct nexthop *nhop), void (*delete)(const char *name)); +void nexthop_group_enable_vrf(struct vrf *vrf); +void nexthop_group_disable_vrf(struct vrf *vrf); +void nexthop_group_interface_state_change(struct interface *ifp, + ifindex_t oldifindex); + extern struct nexthop *nexthop_exists(struct nexthop_group *nhg, struct nexthop *nh); diff --git a/lib/vrf.c b/lib/vrf.c index b493f832f3..db539d375d 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -34,6 +34,7 @@ #include "command.h" #include "ns.h" #include "privs.h" +#include "nexthop_group.h" /* default VRF ID value used when VRF backend is not NETNS */ #define VRF_DEFAULT_INTERNAL 0 @@ -269,6 +270,13 @@ int vrf_enable(struct vrf *vrf) if (vrf_master.vrf_enable_hook) (*vrf_master.vrf_enable_hook)(vrf); + /* + * If we have any nexthop group entries that + * are awaiting vrf initialization then + * let's let people know about it + */ + nexthop_group_enable_vrf(vrf); + return 1; } diff --git a/lib/zclient.c b/lib/zclient.c index e1ce40ce70..48182d6b2c 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -37,6 +37,7 @@ #include "mpls.h" #include "sockopt.h" #include "pbr.h" +#include "nexthop_group.h" DEFINE_MTYPE_STATIC(LIB, ZCLIENT, "Zclient") DEFINE_MTYPE_STATIC(LIB, REDIST_INST, "Redistribution instance IDs") @@ -1697,7 +1698,9 @@ struct interface *zebra_interface_link_params_read(struct stream *s) void zebra_interface_if_set_value(struct stream *s, struct interface *ifp) { uint8_t link_params_status = 0; + ifindex_t old_ifindex; + old_ifindex = ifp->ifindex; /* Read interface's index. */ if_set_index(ifp, stream_getl(s)); ifp->status = stream_getc(s); @@ -1724,6 +1727,8 @@ void zebra_interface_if_set_value(struct stream *s, struct interface *ifp) struct if_link_params *iflp = if_link_params_get(ifp); link_params_set_value(s, iflp); } + + nexthop_group_interface_state_change(ifp, old_ifindex); } size_t zebra_interface_link_params_write(struct stream *s,