From 20822f9d2e450cebfefbb14cc70a189a52df063c Mon Sep 17 00:00:00 2001 From: Stephen Worley Date: Fri, 29 Mar 2019 10:56:52 -0400 Subject: [PATCH] zebra: Add equivalence function for nhg_depends Add a helper function to allow us to check if two nhg_hash_entry's dependency lists are equal. Signed-off-by: Stephen Worley --- zebra/rt_netlink.c | 230 ++++++++++++++++++++++++--------------------- zebra/zebra_nhg.c | 61 ++++++++---- zebra/zebra_rib.c | 25 ++++- 3 files changed, 186 insertions(+), 130 deletions(-) diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 3e377eb770..7917e3046e 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -324,13 +324,14 @@ static int parse_encap_mpls(struct rtattr *tb, mpls_label_t *labels) static struct nexthop parse_nexthop_unicast(ns_id_t ns_id, struct rtmsg *rtm, struct rtattr **tb, enum blackhole_type bh_type, int index, void *prefsrc, - void *gate, afi_t afi, vrf_id_t nh_vrf_id) + void *gate, afi_t afi, vrf_id_t vrf_id) { struct interface *ifp = NULL; struct nexthop nh = {0}; mpls_label_t labels[MPLS_MAX_LABELS] = {0}; int num_labels = 0; + vrf_id_t nh_vrf_id = vrf_id; size_t sz = (afi == AFI_IP) ? 4 : 16; if (bh_type == BLACKHOLE_UNSPEC) { @@ -378,6 +379,114 @@ parse_nexthop_unicast(ns_id_t ns_id, struct rtmsg *rtm, struct rtattr **tb, return nh; } +static uint8_t parse_multipath_nexthops_unicast(ns_id_t ns_id, + struct route_entry *re, + struct rtmsg *rtm, + struct rtnexthop *rtnh, + struct rtattr **tb, + void *prefsrc, vrf_id_t vrf_id) +{ + void *gate = NULL; + struct interface *ifp = NULL; + int index = 0; + /* MPLS labels */ + mpls_label_t labels[MPLS_MAX_LABELS] = {0}; + int num_labels = 0; + struct rtattr *rtnh_tb[RTA_MAX + 1] = {}; + + int len = RTA_PAYLOAD(tb[RTA_MULTIPATH]); + vrf_id_t nh_vrf_id = vrf_id; + + re->ng = nexthop_group_new(); + + for (;;) { + struct nexthop *nh = NULL; + + if (len < (int)sizeof(*rtnh) || rtnh->rtnh_len > len) + break; + + index = rtnh->rtnh_ifindex; + if (index) { + /* + * Yes we are looking this up + * for every nexthop and just + * using the last one looked + * up right now + */ + ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id), + index); + if (ifp) + nh_vrf_id = ifp->vrf_id; + else { + flog_warn( + EC_ZEBRA_UNKNOWN_INTERFACE, + "%s: Unknown interface %u specified, defaulting to VRF_DEFAULT", + __PRETTY_FUNCTION__, index); + nh_vrf_id = VRF_DEFAULT; + } + } else + nh_vrf_id = vrf_id; + + if (rtnh->rtnh_len > sizeof(*rtnh)) { + memset(rtnh_tb, 0, sizeof(rtnh_tb)); + + netlink_parse_rtattr(rtnh_tb, RTA_MAX, RTNH_DATA(rtnh), + rtnh->rtnh_len - sizeof(*rtnh)); + if (rtnh_tb[RTA_GATEWAY]) + gate = RTA_DATA(rtnh_tb[RTA_GATEWAY]); + if (rtnh_tb[RTA_ENCAP] && rtnh_tb[RTA_ENCAP_TYPE] + && *(uint16_t *)RTA_DATA(rtnh_tb[RTA_ENCAP_TYPE]) + == LWTUNNEL_ENCAP_MPLS) { + num_labels = parse_encap_mpls( + rtnh_tb[RTA_ENCAP], labels); + } + } + + if (gate) { + if (rtm->rtm_family == AF_INET) { + if (index) + nh = route_entry_nexthop_ipv4_ifindex_add( + re, gate, prefsrc, index, + nh_vrf_id); + else + nh = route_entry_nexthop_ipv4_add( + re, gate, prefsrc, nh_vrf_id); + } else if (rtm->rtm_family == AF_INET6) { + if (index) + nh = route_entry_nexthop_ipv6_ifindex_add( + re, gate, index, nh_vrf_id); + else + nh = route_entry_nexthop_ipv6_add( + re, gate, nh_vrf_id); + } + } else + nh = route_entry_nexthop_ifindex_add(re, index, + nh_vrf_id); + + if (nh) { + if (num_labels) + nexthop_add_labels(nh, ZEBRA_LSP_STATIC, + num_labels, labels); + + if (rtnh->rtnh_flags & RTNH_F_ONLINK) + SET_FLAG(nh->flags, NEXTHOP_FLAG_ONLINK); + } + + if (rtnh->rtnh_len == 0) + break; + + len -= NLMSG_ALIGN(rtnh->rtnh_len); + rtnh = RTNH_NEXT(rtnh); + } + + uint8_t nhop_num = nexthop_group_nexthop_num(re->ng); + + if (!nhop_num) + nexthop_group_delete(&re->ng); + + return nhop_num; +} + /* Looking up routing table by netlink interface. */ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id, int startup) @@ -606,8 +715,6 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id, afi = AFI_IP6; if (h->nlmsg_type == RTM_NEWROUTE) { - struct interface *ifp; - vrf_id_t nh_vrf_id = vrf_id; if (!tb[RTA_MULTIPATH]) { struct nexthop nh = {0}; @@ -615,22 +722,16 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id, if (!nhe_id) { nh = parse_nexthop_unicast( ns_id, rtm, tb, bh_type, index, prefsrc, - gate, afi, nh_vrf_id); + gate, afi, vrf_id); } rib_add(afi, SAFI_UNICAST, vrf_id, proto, 0, flags, &p, &src_p, &nh, nhe_id, table, metric, mtu, distance, tag); } else { /* This is a multipath route */ - uint8_t nhop_num; struct route_entry *re; struct rtnexthop *rtnh = (struct rtnexthop *)RTA_DATA(tb[RTA_MULTIPATH]); - /* MPLS labels */ - mpls_label_t labels[MPLS_MAX_LABELS] = {0}; - int num_labels = 0; - - len = RTA_PAYLOAD(tb[RTA_MULTIPATH]); re = XCALLOC(MTYPE_RE, sizeof(struct route_entry)); re->type = proto; @@ -643,108 +744,23 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id, re->uptime = monotime(NULL); re->tag = tag; re->nhe_id = nhe_id; - re->ng = nexthop_group_new(); - for (;;) { - struct nexthop *nh = NULL; + if (!nhe_id) { + uint8_t nhop_num = + parse_multipath_nexthops_unicast( + ns_id, re, rtm, rtnh, tb, + prefsrc, vrf_id); - if (len < (int)sizeof(*rtnh) - || rtnh->rtnh_len > len) - break; - - index = rtnh->rtnh_ifindex; - if (index) { - /* - * Yes we are looking this up - * for every nexthop and just - * using the last one looked - * up right now - */ - ifp = if_lookup_by_index_per_ns( - zebra_ns_lookup(ns_id), - index); - if (ifp) - nh_vrf_id = ifp->vrf_id; - else { - flog_warn( - EC_ZEBRA_UNKNOWN_INTERFACE, - "%s: Unknown interface %u specified, defaulting to VRF_DEFAULT", - __PRETTY_FUNCTION__, - index); - nh_vrf_id = VRF_DEFAULT; - } - } else - nh_vrf_id = vrf_id; - - gate = 0; - if (rtnh->rtnh_len > sizeof(*rtnh)) { - memset(tb, 0, sizeof(tb)); - netlink_parse_rtattr( - tb, RTA_MAX, RTNH_DATA(rtnh), - rtnh->rtnh_len - sizeof(*rtnh)); - if (tb[RTA_GATEWAY]) - gate = RTA_DATA( - tb[RTA_GATEWAY]); - if (tb[RTA_ENCAP] && tb[RTA_ENCAP_TYPE] - && *(uint16_t *)RTA_DATA( - tb[RTA_ENCAP_TYPE]) - == LWTUNNEL_ENCAP_MPLS) { - num_labels = parse_encap_mpls( - tb[RTA_ENCAP], labels); - } - } - - if (gate) { - if (rtm->rtm_family == AF_INET) { - if (index) - nh = route_entry_nexthop_ipv4_ifindex_add( - re, gate, - prefsrc, index, - nh_vrf_id); - else - nh = route_entry_nexthop_ipv4_add( - re, gate, - prefsrc, - nh_vrf_id); - } else if (rtm->rtm_family - == AF_INET6) { - if (index) - nh = route_entry_nexthop_ipv6_ifindex_add( - re, gate, index, - nh_vrf_id); - else - nh = route_entry_nexthop_ipv6_add( - re, gate, - nh_vrf_id); - } - } else - nh = route_entry_nexthop_ifindex_add( - re, index, nh_vrf_id); - - if (nh && num_labels) - nexthop_add_labels(nh, ZEBRA_LSP_STATIC, - num_labels, labels); - - if (nh && (rtnh->rtnh_flags & RTNH_F_ONLINK)) - SET_FLAG(nh->flags, - NEXTHOP_FLAG_ONLINK); - - if (rtnh->rtnh_len == 0) - break; - - len -= NLMSG_ALIGN(rtnh->rtnh_len); - rtnh = RTNH_NEXT(rtnh); + zserv_nexthop_num_warn( + __func__, (const struct prefix *)&p, + nhop_num); } - nhop_num = nexthop_group_nexthop_num(re->ng); - zserv_nexthop_num_warn( - __func__, (const struct prefix *)&p, nhop_num); - if (nhop_num == 0) { - nexthop_group_delete(&re->ng); - XFREE(MTYPE_RE, re); - } else + if (nhe_id || re->ng) rib_add_multipath(afi, SAFI_UNICAST, &p, &src_p, re); + else + XFREE(MTYPE_RE, re); } } else { if (!tb[RTA_MULTIPATH]) { @@ -2400,7 +2416,7 @@ int netlink_nexthop_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) } else { /* This is a new nexthop group */ nhe = zebra_nhg_find(nhg, vrf_id, afi, id, nhg_depends, - dep_count); + true); zebra_nhg_free_group_depends(nhg, nhg_depends); if (!nhe) { diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index 92198bad03..c698d1099a 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -144,6 +144,41 @@ zebra_nhg_depends_lookup_id(const struct nhg_hash_entry *nhe, const uint32_t id) return match; } +/** + * zebra_nhg_depends_equal() - Are the dependencies of these nhe's equal + * + * @nhe1: Nexthop group hash entry + * @nhe2: Nexthop group hash entry + * + * Return: True if equal + * + * We don't care about ordering of the dependencies. If they contain + * the same nhe ID's, they are equivalent. + */ +static bool zebra_nhg_depends_equal(const struct nhg_hash_entry *nhe1, + const struct nhg_hash_entry *nhe2) +{ + struct listnode *ln = NULL; + struct nhg_depend *n_dp = NULL; + + if (!nhe1->nhg_depends && !nhe2->nhg_depends) + return true; + + if ((nhe1->nhg_depends && !nhe2->nhg_depends) + || (nhe2->nhg_depends && !nhe1->nhg_depends)) + return false; + + if (listcount(nhe1->nhg_depends) != listcount(nhe2->nhg_depends)) + return false; + + for (ALL_LIST_ELEMENTS_RO(nhe1->nhg_depends, ln, n_dp)) { + if (!zebra_nhg_depends_lookup_id(nhe2, n_dp->nhe->id)) + return false; + } + + return true; +} + /** * zebra_nhg_lookup_id() - Lookup the nexthop group id in the id table * @@ -212,6 +247,10 @@ static void *zebra_nhg_alloc(void *arg) zebra_nhg_insert_id(nhe); + /* Send it to the kernel */ + if (!nhe->is_kernel_nh) + zebra_nhg_install_kernel(nhe); + return nhe; } @@ -275,8 +314,6 @@ bool zebra_nhg_hash_equal(const void *arg1, const void *arg2) { const struct nhg_hash_entry *nhe1 = arg1; const struct nhg_hash_entry *nhe2 = arg2; - struct nexthop *nh1, *nh2; - uint32_t nh_count = 0; if (nhe1->vrf_id != nhe2->vrf_id) return false; @@ -284,24 +321,8 @@ bool zebra_nhg_hash_equal(const void *arg1, const void *arg2) if (nhe1->afi != nhe2->afi) return false; - /* - * Again we are not interested in looking at any recursively - * resolved nexthops. Top level only - */ - for (nh1 = nhe1->nhg->nexthop; nh1; nh1 = nh1->next) { - uint32_t inner_nh_count = 0; - for (nh2 = nhe2->nhg->nexthop; nh2; nh2 = nh2->next) { - if (inner_nh_count == nh_count) { - break; - } - inner_nh_count++; - } - - if (!nexthop_same(nh1, nh2)) - return false; - - nh_count++; - } + if (!zebra_nhg_depends_equal(nhe1, nhe2)) + return false; return true; } diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index a5b939baa9..558dbd33e0 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -2638,6 +2638,7 @@ int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p, struct route_node *rn; struct route_entry *same = NULL; struct nhg_hash_entry *nhe = NULL; + struct list *nhg_depends = NULL; int ret = 0; if (!re) @@ -2648,7 +2649,7 @@ int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p, /* Lookup table. */ table = zebra_vrf_table_with_table_id(afi, safi, re->vrf_id, re->table); if (!table) { - zebra_nhg_free_group_depends(re->ng, NULL); + zebra_nhg_free_group_depends(re->ng, nhg_depends); XFREE(MTYPE_RE, re); return 0; } @@ -2658,11 +2659,29 @@ int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p, if (src_p) apply_mask_ipv6(src_p); - nhe = zebra_nhg_find(re->ng, re->vrf_id, afi, re->nhe_id, NULL, 0); + /* If its a group, create a dependency list */ + if (re->ng && re->ng->nexthop->next) { + struct nexthop *nh = NULL; + struct nexthop lookup = {0}; + struct nhg_hash_entry *depend = NULL; + + nhg_depends = nhg_depend_new_list(); + + for (ALL_NEXTHOPS_PTR(re->ng, nh)) { + lookup = *nh; + /* Clear it, since its a group */ + lookup.next = NULL; + depend = zebra_nhg_find_nexthop(&lookup, afi); + nhg_depend_add(nhg_depends, depend); + } + } + + nhe = zebra_nhg_find(re->ng, re->vrf_id, afi, re->nhe_id, nhg_depends, + false); if (nhe) { // TODO: Add interface pointer - zebra_nhg_free_group_depends(re->ng, NULL); + zebra_nhg_free_group_depends(re->ng, nhg_depends); re->ng = nhe->nhg; re->nhe_id = nhe->id; nhe->refcnt++;