diff options
Diffstat (limited to 'zebra/rt_netlink.c')
| -rw-r--r-- | zebra/rt_netlink.c | 282 |
1 files changed, 225 insertions, 57 deletions
diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 216b719ff8..8bf59e02c7 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -153,6 +153,22 @@ static bool is_proto_nhg(uint32_t id, int type) return false; } +/* Is vni mcast group */ +static bool is_mac_vni_mcast_group(struct ethaddr *mac, vni_t vni, + struct in_addr grp_addr) +{ + if (!vni) + return false; + + if (!is_zero_mac(mac)) + return false; + + if (!IN_MULTICAST(ntohl(grp_addr.s_addr))) + return false; + + return true; +} + /* * The ipv4_ll data structure is used for all 5549 * additions to the kernel. Let's figure out the @@ -1309,6 +1325,7 @@ static bool _netlink_route_add_gateway_info(uint8_t route_family, } static int build_label_stack(struct mpls_label_stack *nh_label, + enum lsp_types_t nh_label_type, mpls_lse_t *out_lse, char *label_buf, size_t label_buf_size) { @@ -1316,7 +1333,8 @@ static int build_label_stack(struct mpls_label_stack *nh_label, int num_labels = 0; for (int i = 0; nh_label && i < nh_label->num_labels; i++) { - if (nh_label->label[i] == MPLS_LABEL_IMPLICIT_NULL) + if (nh_label_type != ZEBRA_LSP_EVPN && + nh_label->label[i] == MPLS_LABEL_IMPLICIT_NULL) continue; if (IS_ZEBRA_DEBUG_KERNEL) { @@ -1330,15 +1348,58 @@ static int build_label_stack(struct mpls_label_stack *nh_label, } } - out_lse[num_labels] = - mpls_lse_encode(nh_label->label[i], 0, 0, 0); + if (nh_label_type == ZEBRA_LSP_EVPN) + out_lse[num_labels] = label2vni(&nh_label->label[i]); + else + out_lse[num_labels] = + mpls_lse_encode(nh_label->label[i], 0, 0, 0); num_labels++; } return num_labels; } -static bool _netlink_route_encode_label_info(struct mpls_label_stack *nh_label, +static bool _netlink_nexthop_encode_dvni_label(const struct nexthop *nexthop, + struct nlmsghdr *nlmsg, + mpls_lse_t *out_lse, + size_t buflen, char *label_buf) +{ + struct in_addr ipv4; + + if (!nl_attr_put64(nlmsg, buflen, LWTUNNEL_IP_ID, + htonll((uint64_t)out_lse[0]))) + return false; + + if (nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) { + if (!nl_attr_put(nlmsg, buflen, LWTUNNEL_IP_DST, + &nexthop->gate.ipv4, 4)) + return false; + + } else if (nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) { + if (IS_MAPPED_IPV6(&nexthop->gate.ipv6)) { + ipv4_mapped_ipv6_to_ipv4(&nexthop->gate.ipv6, &ipv4); + if (!nl_attr_put(nlmsg, buflen, LWTUNNEL_IP_DST, &ipv4, + 4)) + return false; + + } else { + if (!nl_attr_put(nlmsg, buflen, LWTUNNEL_IP_DST, + &nexthop->gate.ipv6, 16)) + return false; + } + } else { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug( + "%s: nexthop %pNHv %s must NEXTHOP_TYPE_IPV*_IFINDEX to be vxlan encapped", + __func__, nexthop, label_buf); + + return false; + } + + return true; +} + +static bool _netlink_route_encode_label_info(const struct nexthop *nexthop, struct nlmsghdr *nlmsg, size_t buflen, struct rtmsg *rtmsg, char *label_buf, @@ -1346,6 +1407,12 @@ static bool _netlink_route_encode_label_info(struct mpls_label_stack *nh_label, { mpls_lse_t out_lse[MPLS_MAX_LABELS]; int num_labels; + struct rtattr *nest; + struct mpls_label_stack *nh_label; + enum lsp_types_t nh_label_type; + + nh_label = nexthop->nh_label; + nh_label_type = nexthop->nh_label_type; /* * label_buf is *only* currently used within debugging. @@ -1355,10 +1422,26 @@ static bool _netlink_route_encode_label_info(struct mpls_label_stack *nh_label, */ label_buf[0] = '\0'; - num_labels = - build_label_stack(nh_label, out_lse, label_buf, label_buf_size); + num_labels = build_label_stack(nh_label, nh_label_type, out_lse, + label_buf, label_buf_size); + + if (num_labels && nh_label_type == ZEBRA_LSP_EVPN) { + if (!nl_attr_put16(nlmsg, buflen, RTA_ENCAP_TYPE, + LWTUNNEL_ENCAP_IP)) + return false; + + nest = nl_attr_nest(nlmsg, buflen, RTA_ENCAP); + if (!nest) + return false; + + if (_netlink_nexthop_encode_dvni_label(nexthop, nlmsg, out_lse, + buflen, + label_buf) == false) + return false; - if (num_labels) { + nl_attr_nest_end(nlmsg, nest); + + } else if (num_labels) { /* Set the BoS bit */ out_lse[num_labels - 1] |= htonl(1 << MPLS_LS_S_SHIFT); @@ -1367,8 +1450,6 @@ static bool _netlink_route_encode_label_info(struct mpls_label_stack *nh_label, num_labels * sizeof(mpls_lse_t))) return false; } else { - struct rtattr *nest; - if (!nl_attr_put16(nlmsg, buflen, RTA_ENCAP_TYPE, LWTUNNEL_ENCAP_MPLS)) return false; @@ -1482,9 +1563,8 @@ static bool _netlink_route_build_singlepath(const struct prefix *p, vrf = vrf_lookup_by_id(nexthop->vrf_id); - if (!_netlink_route_encode_label_info(nexthop->nh_label, nlmsg, - req_size, rtmsg, label_buf, - sizeof(label_buf))) + if (!_netlink_route_encode_label_info(nexthop, nlmsg, req_size, rtmsg, + label_buf, sizeof(label_buf))) return false; if (nexthop->nh_srv6) { @@ -1765,9 +1845,8 @@ static bool _netlink_route_build_multipath( vrf = vrf_lookup_by_id(nexthop->vrf_id); - if (!_netlink_route_encode_label_info(nexthop->nh_label, nlmsg, - req_size, rtmsg, label_buf, - sizeof(label_buf))) + if (!_netlink_route_encode_label_info(nexthop, nlmsg, req_size, rtmsg, + label_buf, sizeof(label_buf))) return false; if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) @@ -2649,11 +2728,28 @@ ssize_t netlink_nexthop_msg_encode(uint16_t cmd, if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_ONLINK)) req->nhm.nh_flags |= RTNH_F_ONLINK; - num_labels = - build_label_stack(nh->nh_label, out_lse, - label_buf, sizeof(label_buf)); + num_labels = build_label_stack( + nh->nh_label, nh->nh_label_type, out_lse, + label_buf, sizeof(label_buf)); - if (num_labels) { + if (num_labels && nh->nh_label_type == ZEBRA_LSP_EVPN) { + if (!nl_attr_put16(&req->n, buflen, + NHA_ENCAP_TYPE, + LWTUNNEL_ENCAP_IP)) + return 0; + + nest = nl_attr_nest(&req->n, buflen, NHA_ENCAP); + if (!nest) + return 0; + + if (_netlink_nexthop_encode_dvni_label( + nh, &req->n, out_lse, buflen, + label_buf) == false) + return 0; + + nl_attr_nest_end(&req->n, nest); + + } else if (num_labels) { /* Set the BoS bit */ out_lse[num_labels - 1] |= htonl(1 << MPLS_LS_S_SHIFT); @@ -3400,17 +3496,32 @@ static ssize_t netlink_neigh_update_msg_encode( if (op == DPLANE_OP_MAC_INSTALL || op == DPLANE_OP_MAC_DELETE) { vlanid_t vid = dplane_ctx_mac_get_vlan(ctx); + vni_t vni = dplane_ctx_mac_get_vni(ctx); if (vid > 0) { if (!nl_attr_put16(&req->n, datalen, NDA_VLAN, vid)) return 0; } + if (vni > 0) { + if (!nl_attr_put32(&req->n, datalen, NDA_SRC_VNI, vni)) + return 0; + } + if (!nl_attr_put32(&req->n, datalen, NDA_MASTER, dplane_ctx_mac_get_br_ifindex(ctx))) return 0; } + if (op == DPLANE_OP_VTEP_ADD || op == DPLANE_OP_VTEP_DELETE) { + vni_t vni = dplane_ctx_neigh_get_vni(ctx); + + if (vni > 0) { + if (!nl_attr_put32(&req->n, datalen, NDA_SRC_VNI, vni)) + return 0; + } + } + return NLMSG_ALIGN(req->n.nlmsg_len); } @@ -3457,7 +3568,9 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id) bool sticky; bool local_inactive = false; bool dp_static = false; + vni_t vni = 0; uint32_t nhg_id = 0; + bool vni_mcast_grp = false; ndm = NLMSG_DATA(h); @@ -3503,7 +3616,8 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id) IPV4_MAX_BYTELEN); snprintfrr(dst_buf, sizeof(dst_buf), " dst %pI4", &vtep_ip); - } + } else + memset(&vtep_ip, 0, sizeof(vtep_ip)); if (tb[NDA_NH_ID]) nhg_id = *(uint32_t *)RTA_DATA(tb[NDA_NH_ID]); @@ -3528,12 +3642,16 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id) } } + if (tb[NDA_SRC_VNI]) + vni = *(vni_t *)RTA_DATA(tb[NDA_SRC_VNI]); + if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("Rx %s AF_BRIDGE IF %u%s st 0x%x fl 0x%x MAC %pEA%s nhg %d", - nl_msg_type_to_str(h->nlmsg_type), - ndm->ndm_ifindex, vid_present ? vid_buf : "", - ndm->ndm_state, ndm->ndm_flags, &mac, - dst_present ? dst_buf : "", nhg_id); + zlog_debug( + "Rx %s AF_BRIDGE IF %u%s st 0x%x fl 0x%x MAC %pEA%s nhg %d vni %d", + nl_msg_type_to_str(h->nlmsg_type), ndm->ndm_ifindex, + vid_present ? vid_buf : "", ndm->ndm_state, + ndm->ndm_flags, &mac, dst_present ? dst_buf : "", + nhg_id, vni); /* The interface should exist. */ ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id), @@ -3556,6 +3674,14 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id) return 0; } + /* For per vni device, vni comes from device itself */ + if (IS_ZEBRA_IF_VXLAN(ifp) && IS_ZEBRA_VXLAN_IF_VNI(zif)) { + struct zebra_vxlan_vni *vnip; + + vnip = zebra_vxlan_if_vni_find(zif, 0); + vni = vnip->vni; + } + sticky = !!(ndm->ndm_flags & NTF_STICKY); if (filter_vlan && vid != filter_vlan) { @@ -3565,6 +3691,11 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id) return 0; } + /* + * Check if this is a mcast group update (svd case) + */ + vni_mcast_grp = is_mac_vni_mcast_group(&mac, vni, vtep_ip); + /* If add or update, do accordingly if learnt on a "local" interface; if * the notification is over VxLAN, this has to be related to * multi-homing, @@ -3572,17 +3703,25 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id) */ if (h->nlmsg_type == RTM_NEWNEIGH) { /* Drop "permanent" entries. */ - if (ndm->ndm_state & NUD_PERMANENT) { + if (!vni_mcast_grp && (ndm->ndm_state & NUD_PERMANENT)) { if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( " Dropping entry because of NUD_PERMANENT"); return 0; } - if (IS_ZEBRA_IF_VXLAN(ifp)) + if (IS_ZEBRA_IF_VXLAN(ifp)) { + if (!dst_present) + return 0; + + if (vni_mcast_grp) + return zebra_vxlan_if_vni_mcast_group_add_update( + ifp, vni, &vtep_ip); + return zebra_vxlan_dp_network_mac_add( - ifp, br_if, &mac, vid, nhg_id, sticky, + ifp, br_if, &mac, vid, vni, nhg_id, sticky, !!(ndm->ndm_flags & NTF_EXT_LEARNED)); + } return zebra_vxlan_local_mac_add_update(ifp, br_if, &mac, vid, sticky, local_inactive, dp_static); @@ -3602,15 +3741,18 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id) return 0; if (dst_present) { - u_char zero_mac[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; + if (vni_mcast_grp) + return zebra_vxlan_if_vni_mcast_group_del(ifp, vni, + &vtep_ip); + + if (is_zero_mac(&mac) && vni) + return zebra_vxlan_check_readd_vtep(ifp, vni, vtep_ip); - if (!memcmp(zero_mac, mac.octet, ETH_ALEN)) - return zebra_vxlan_check_readd_vtep(ifp, vtep_ip); return 0; } if (IS_ZEBRA_IF_VXLAN(ifp)) - return zebra_vxlan_dp_network_mac_del(ifp, br_if, &mac, vid); + return 0; return zebra_vxlan_local_mac_del(ifp, br_if, &mac, vid); } @@ -3687,11 +3829,9 @@ int netlink_macfdb_read(struct zebra_ns *zns) * specific bridge and matching specific access VLAN (if VLAN-aware bridge). */ int netlink_macfdb_read_for_bridge(struct zebra_ns *zns, struct interface *ifp, - struct interface *br_if) + struct interface *br_if, vlanid_t vid) { struct zebra_if *br_zif; - struct zebra_if *zif; - struct zebra_l2info_vxlan *vxl; struct zebra_dplane_info dp_info; int ret = 0; @@ -3699,10 +3839,8 @@ int netlink_macfdb_read_for_bridge(struct zebra_ns *zns, struct interface *ifp, /* Save VLAN we're filtering on, if needed. */ br_zif = (struct zebra_if *)br_if->info; - zif = (struct zebra_if *)ifp->info; - vxl = &zif->l2info.vxl; if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif)) - filter_vlan = vxl->access_vlan; + filter_vlan = vid; /* Get bridge FDB table for specific bridge - we do the VLAN filtering. */ @@ -3720,40 +3858,45 @@ int netlink_macfdb_read_for_bridge(struct zebra_ns *zns, struct interface *ifp, /* Request for MAC FDB for a specific MAC address in VLAN from the kernel */ -static int netlink_request_specific_mac_in_bridge(struct zebra_ns *zns, - int family, int type, - struct interface *br_if, - const struct ethaddr *mac, - vlanid_t vid) +static int netlink_request_specific_mac(struct zebra_ns *zns, int family, + int type, struct interface *ifp, + const struct ethaddr *mac, vlanid_t vid, + vni_t vni, uint8_t flags) { struct { struct nlmsghdr n; struct ndmsg ndm; char buf[256]; } req; - struct zebra_if *br_zif; + struct zebra_if *zif; memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)); req.n.nlmsg_type = type; /* RTM_GETNEIGH */ req.n.nlmsg_flags = NLM_F_REQUEST; req.ndm.ndm_family = family; /* AF_BRIDGE */ + req.ndm.ndm_flags = flags; /* req.ndm.ndm_state = NUD_REACHABLE; */ nl_attr_put(&req.n, sizeof(req), NDA_LLADDR, mac, 6); - br_zif = (struct zebra_if *)br_if->info; - if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif) && vid > 0) - nl_attr_put16(&req.n, sizeof(req), NDA_VLAN, vid); - - nl_attr_put32(&req.n, sizeof(req), NDA_MASTER, br_if->ifindex); + zif = (struct zebra_if *)ifp->info; + /* Is this a read on a VXLAN interface? */ + if (IS_ZEBRA_IF_VXLAN(ifp)) { + nl_attr_put32(&req.n, sizeof(req), NDA_VNI, vni); + /* TBD: Why is ifindex not filled in the non-vxlan case? */ + req.ndm.ndm_ifindex = ifp->ifindex; + } else { + if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(zif) && vid > 0) + nl_attr_put16(&req.n, sizeof(req), NDA_VLAN, vid); + nl_attr_put32(&req.n, sizeof(req), NDA_MASTER, ifp->ifindex); + } if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug( - "%s: Tx family %s IF %s(%u) vrf %s(%u) MAC %pEA vid %u", - __func__, nl_family_to_str(req.ndm.ndm_family), - br_if->name, br_if->ifindex, br_if->vrf->name, - br_if->vrf->vrf_id, mac, vid); + zlog_debug("Tx %s %s IF %s(%u) MAC %pEA vid %u vni %u", + nl_msg_type_to_str(type), + nl_family_to_str(req.ndm.ndm_family), ifp->name, + ifp->ifindex, mac, vid, vni); return netlink_request(&zns->netlink_cmd, &req); } @@ -3769,9 +3912,34 @@ int netlink_macfdb_read_specific_mac(struct zebra_ns *zns, /* Get bridge FDB table for specific bridge - we do the VLAN filtering. */ - ret = netlink_request_specific_mac_in_bridge(zns, AF_BRIDGE, - RTM_GETNEIGH, - br_if, mac, vid); + ret = netlink_request_specific_mac(zns, AF_BRIDGE, RTM_GETNEIGH, br_if, + mac, vid, 0, 0); + if (ret < 0) + return ret; + + ret = netlink_parse_info(netlink_macfdb_table, &zns->netlink_cmd, + &dp_info, 1, 0); + + return ret; +} + +int netlink_macfdb_read_mcast_for_vni(struct zebra_ns *zns, + struct interface *ifp, vni_t vni) +{ + struct zebra_if *zif; + struct ethaddr mac = {.octet = {0}}; + struct zebra_dplane_info dp_info; + int ret = 0; + + zif = ifp->info; + if (IS_ZEBRA_VXLAN_IF_VNI(zif)) + return 0; + + zebra_dplane_info_from_zns(&dp_info, zns, true /*is_cmd*/); + + /* Get specific FDB entry for BUM handling, if any */ + ret = netlink_request_specific_mac(zns, AF_BRIDGE, RTM_GETNEIGH, ifp, + &mac, 0, vni, NTF_SELF); if (ret < 0) return ret; |
