diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index 02b9ab9d7f..22dd2b956d 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -2522,4 +2522,236 @@ int netlink_tunneldump_read(struct zebra_ns *zns) return 0; } + +static const char *port_state2str(uint8_t state) +{ + switch (state) { + case BR_STATE_DISABLED: + return "DISABLED"; + case BR_STATE_LISTENING: + return "LISTENING"; + case BR_STATE_LEARNING: + return "LEARNING"; + case BR_STATE_FORWARDING: + return "FORWARDING"; + case BR_STATE_BLOCKING: + return "BLOCKING"; + } + + return "UNKNOWN"; +} + +static void vxlan_vni_state_change(struct zebra_if *zif, uint16_t id, + uint8_t state) +{ + struct zebra_vxlan_vni *vnip; + + vnip = zebra_vxlan_if_vlanid_vni_find(zif, id); + + if (!vnip) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Cannot find VNI for VID (%u) IF %s for vlan state update", + id, zif->ifp->name); + + return; + } + + switch (state) { + case BR_STATE_FORWARDING: + zebra_vxlan_if_vni_up(zif->ifp, vnip); + break; + case BR_STATE_BLOCKING: + zebra_vxlan_if_vni_down(zif->ifp, vnip); + break; + case BR_STATE_DISABLED: + case BR_STATE_LISTENING: + case BR_STATE_LEARNING: + default: + /* Not used for anything at the moment */ + break; + } +} + +static void vlan_id_range_state_change(struct interface *ifp, uint16_t id_start, + uint16_t id_end, uint8_t state) +{ + struct zebra_if *zif; + + zif = (struct zebra_if *)ifp->info; + + if (!zif) + return; + + for (uint16_t i = id_start; i <= id_end; i++) + vxlan_vni_state_change(zif, i, state); +} + +/** + * netlink_vlan_change() - Read in change about vlans from the kernel + * + * @h: Netlink message header + * @ns_id: Namspace id + * @startup: Are we reading under startup conditions? + * + * Return: Result status + */ +int netlink_vlan_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) +{ + int len, rem; + struct interface *ifp; + struct br_vlan_msg *bvm; + struct bridge_vlan_info *vinfo; + struct rtattr *vtb[BRIDGE_VLANDB_ENTRY_MAX + 1] = {}; + struct rtattr *attr; + uint8_t state; + uint32_t vrange; + int type; + + /* We only care about state changes for now */ + if (!(h->nlmsg_type == RTM_NEWVLAN)) + return 0; + + len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct br_vlan_msg)); + if (len < 0) { + zlog_warn( + "%s: Message received from netlink is of a broken size %d %zu", + __func__, h->nlmsg_len, + (size_t)NLMSG_LENGTH(sizeof(struct br_vlan_msg))); + return -1; + } + + bvm = NLMSG_DATA(h); + + if (bvm->family != AF_BRIDGE) + return 0; + + ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id), bvm->ifindex); + if (!ifp) { + zlog_debug("Cannot find bridge-vlan IF (%u) for vlan update", + bvm->ifindex); + return 0; + } + + if (!IS_ZEBRA_IF_VXLAN(ifp)) { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("Ignoring non-vxlan IF (%s) for vlan update", + ifp->name); + + return 0; + } + + if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("%s %s IF %s NS %u", + nl_msg_type_to_str(h->nlmsg_type), + nl_family_to_str(bvm->family), ifp->name, ns_id); + + /* Loop over "ALL" BRIDGE_VLANDB_ENTRY */ + rem = len; + for (attr = BRVLAN_RTA(bvm); RTA_OK(attr, rem); + attr = RTA_NEXT(attr, rem)) { + vinfo = NULL; + state = 0; + vrange = 0; + + type = attr->rta_type & NLA_TYPE_MASK; + + if (type != BRIDGE_VLANDB_ENTRY) + continue; + + /* Parse nested entry data */ + netlink_parse_rtattr_nested(vtb, BRIDGE_VLANDB_ENTRY_MAX, attr); + + /* It must have info for the ID */ + if (!vtb[BRIDGE_VLANDB_ENTRY_INFO]) + continue; + + vinfo = (struct bridge_vlan_info *)RTA_DATA( + vtb[BRIDGE_VLANDB_ENTRY_INFO]); + + /* + * We only care about state info, if there is none, just ignore + * it. + */ + if (!vtb[BRIDGE_VLANDB_ENTRY_STATE]) + continue; + + state = *(uint8_t *)RTA_DATA(vtb[BRIDGE_VLANDB_ENTRY_STATE]); + + if (vtb[BRIDGE_VLANDB_ENTRY_RANGE]) + vrange = *(uint32_t *)RTA_DATA( + vtb[BRIDGE_VLANDB_ENTRY_RANGE]); + + if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_VXLAN) { + if (vrange) + zlog_debug("VLANDB_ENTRY: VID (%u-%u) state=%s", + vinfo->vid, vrange, + port_state2str(state)); + else + zlog_debug("VLANDB_ENTRY: VID (%u) state=%s", + vinfo->vid, port_state2str(state)); + } + + vlan_id_range_state_change( + ifp, vinfo->vid, (vrange ? vrange : vinfo->vid), state); + } + + return 0; +} + +/** + * netlink_request_vlan() - Request vlan information from the kernel + * @zns: Zebra namespace + * @family: AF_* netlink family + * @type: RTM_* type + * + * Return: Result status + */ +static int netlink_request_vlan(struct zebra_ns *zns, int family, int type) +{ + struct { + struct nlmsghdr n; + struct br_vlan_msg bvm; + char buf[256]; + } req; + + /* Form the request, specifying filter (rtattr) if needed. */ + memset(&req, 0, sizeof(req)); + req.n.nlmsg_type = type; + req.n.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_vlan_msg)); + req.bvm.family = family; + + nl_attr_put32(&req.n, sizeof(req), BRIDGE_VLANDB_DUMP_FLAGS, + BRIDGE_VLANDB_DUMPF_STATS); + + return netlink_request(&zns->netlink_cmd, &req); +} + +/** + * netlink_vlan_read() - Vlan read function using netlink interface + * + * @zns: Zebra name space + * + * Return: Result status + * Only called at bootstrap time. + */ +int netlink_vlan_read(struct zebra_ns *zns) +{ + int ret; + struct zebra_dplane_info dp_info; + + zebra_dplane_info_from_zns(&dp_info, zns, true /*is_cmd*/); + + /* Get bridg vlan info */ + ret = netlink_request_vlan(zns, PF_BRIDGE, RTM_GETVLAN); + if (ret < 0) + return ret; + + ret = netlink_parse_info(netlink_vlan_change, &zns->netlink_cmd, + &dp_info, 0, 1); + + return ret; +} + #endif /* GNU_LINUX */ diff --git a/zebra/if_netlink.h b/zebra/if_netlink.h index 21ae1713be..f215968657 100644 --- a/zebra/if_netlink.h +++ b/zebra/if_netlink.h @@ -40,6 +40,9 @@ int netlink_interface_addr_dplane(struct nlmsghdr *h, ns_id_t ns_id, extern int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup); extern int interface_lookup_netlink(struct zebra_ns *zns); +extern int netlink_vlan_change(struct nlmsghdr *h, ns_id_t ns_id, int startup); +extern int netlink_vlan_read(struct zebra_ns *zns); + extern ssize_t netlink_intf_msg_encode(uint16_t cmd, const struct zebra_dplane_ctx *ctx, void *buf, size_t buflen); diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index 45fb24a210..2fd1543b31 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -124,6 +124,9 @@ static const struct message nlmsg_str[] = {{RTM_NEWROUTE, "RTM_NEWROUTE"}, {RTM_NEWTFILTER, "RTM_NEWTFILTER"}, {RTM_DELTFILTER, "RTM_DELTFILTER"}, {RTM_GETTFILTER, "RTM_GETTFILTER"}, + {RTM_NEWVLAN, "RTM_NEWVLAN"}, + {RTM_DELVLAN, "RTM_DELVLAN"}, + {RTM_GETVLAN, "RTM_GETVLAN"}, {0}}; static const struct message rtproto_str[] = { @@ -432,6 +435,10 @@ static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id, case RTM_NEWTFILTER: case RTM_DELTFILTER: return netlink_tfilter_change(h, ns_id, startup); + case RTM_NEWVLAN: + return netlink_vlan_change(h, ns_id, startup); + case RTM_DELVLAN: + return netlink_vlan_change(h, ns_id, startup); /* Messages handled in the dplane thread */ case RTM_NEWADDR: diff --git a/zebra/rt.h b/zebra/rt.h index f17d11a5fb..4ebf479754 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -129,6 +129,7 @@ extern void kernel_update_multi(struct dplane_ctx_list_head *ctx_list); * Called by the dplane pthread to read incoming OS messages and dispatch them. */ int kernel_dplane_read(struct zebra_dplane_info *info); +extern void vlan_read(struct zebra_ns *zns); #ifdef __cplusplus } diff --git a/zebra/rtread_netlink.c b/zebra/rtread_netlink.c index a85d8a4894..f9d4605562 100644 --- a/zebra/rtread_netlink.c +++ b/zebra/rtread_netlink.c @@ -28,6 +28,7 @@ #include "zebra/zebra_pbr.h" #include "zebra/zebra_tc.h" #include "zebra/rt_netlink.h" +#include "zebra/if_netlink.h" #include "zebra/rule_netlink.h" #include "zebra/tc_netlink.h" @@ -84,4 +85,9 @@ void kernel_read_tc_qdisc(struct zebra_ns *zns) netlink_qdisc_read(zns); } +void vlan_read(struct zebra_ns *zns) +{ + netlink_vlan_read(zns); +} + #endif /* GNU_LINUX */ diff --git a/zebra/rtread_sysctl.c b/zebra/rtread_sysctl.c index c8a89b3a55..f5b918c022 100644 --- a/zebra/rtread_sysctl.c +++ b/zebra/rtread_sysctl.c @@ -118,4 +118,8 @@ void kernel_read_tc_qdisc(struct zebra_ns *zns) { } +void vlan_read(struct zebra_ns *zns) +{ +} + #endif /* !defined(GNU_LINUX) */ diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 7b076b8a4a..457a4955b4 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -127,6 +127,8 @@ int zebra_ns_enable(ns_id_t ns_id, void **info) zebra_dplane_ns_enable(zns, true); interface_list(zns); route_read(zns); + + vlan_read(zns); kernel_read_pbr_rules(zns); kernel_read_tc_qdisc(zns); diff --git a/zebra/zebra_vxlan_if.c b/zebra/zebra_vxlan_if.c index df9ad7795f..e01d06c71a 100644 --- a/zebra/zebra_vxlan_if.c +++ b/zebra/zebra_vxlan_if.c @@ -672,6 +672,36 @@ struct zebra_vxlan_vni *zebra_vxlan_if_vni_find(const struct zebra_if *zif, return vnip; } +static int zif_vlanid_vni_walker(struct zebra_if *zif, + struct zebra_vxlan_vni *vnip, void *arg) +{ + struct zebra_vxlan_if_vlan_ctx *ctx; + + ctx = (struct zebra_vxlan_if_vlan_ctx *)arg; + + if (vnip->access_vlan == ctx->vid) { + ctx->vni = vnip; + return HASHWALK_ABORT; + } + + return HASHWALK_CONTINUE; +} + +struct zebra_vxlan_vni *zebra_vxlan_if_vlanid_vni_find(struct zebra_if *zif, + vlanid_t vid) +{ + struct zebra_vxlan_if_vlan_ctx ctx = {}; + + if (!IS_ZEBRA_VXLAN_IF_SVD(zif)) + return NULL; + + ctx.vid = vid; + + zebra_vxlan_if_vni_walk(zif, zif_vlanid_vni_walker, &ctx); + + return ctx.vni; +} + void zebra_vxlan_if_vni_iterate(struct zebra_if *zif, int (*func)(struct zebra_if *zif, struct zebra_vxlan_vni *, void *), diff --git a/zebra/zebra_vxlan_if.h b/zebra/zebra_vxlan_if.h index a09e3dbe8c..e5ee6dc2d9 100644 --- a/zebra/zebra_vxlan_if.h +++ b/zebra/zebra_vxlan_if.h @@ -50,6 +50,8 @@ extern int zebra_vxlan_if_vni_table_create(struct zebra_if *zif); extern int zebra_vxlan_if_vni_table_destroy(struct zebra_if *zif); extern struct zebra_vxlan_vni * zebra_vxlan_if_vni_find(const struct zebra_if *zif, vni_t vni); +extern struct zebra_vxlan_vni * +zebra_vxlan_if_vlanid_vni_find(struct zebra_if *zif, vlanid_t vni); extern void zebra_vxlan_if_vni_iterate(struct zebra_if *zif, int (*func)(struct zebra_if *zif, struct zebra_vxlan_vni *,