/* Interface address modification. */ static int netlink_address(int cmd, int family, struct interface *ifp, struct connected *ifc) { int bytelen; struct prefix *p; struct { struct nlmsghdr n; struct ifaddrmsg ifa; char buf[NL_PKT_BUF_SIZE]; } req; struct zebra_ns *zns; if (vrf_is_backend_netns()) zns = zebra_ns_lookup((ns_id_t)ifp->vrf_id); else zns = zebra_ns_lookup(NS_DEFAULT); p = ifc->address; memset(&req, 0, sizeof req - NL_PKT_BUF_SIZE); bytelen = (family == AF_INET ? 4 : 16); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); req.n.nlmsg_flags = NLM_F_REQUEST; req.n.nlmsg_type = cmd; req.n.nlmsg_pid = zns->netlink_cmd.snl.nl_pid; req.ifa.ifa_family = family; req.ifa.ifa_index = ifp->ifindex; addattr_l(&req.n, sizeof req, IFA_LOCAL, &p->u.prefix, bytelen); if (family == AF_INET) { if (CONNECTED_PEER(ifc)) { p = ifc->destination; addattr_l(&req.n, sizeof req, IFA_ADDRESS, &p->u.prefix, bytelen); } else if (cmd == RTM_NEWADDR && ifc->destination) { p = ifc->destination; addattr_l(&req.n, sizeof req, IFA_BROADCAST, &p->u.prefix, bytelen); } } /* p is now either ifc->address or ifc->destination */ req.ifa.ifa_prefixlen = p->prefixlen; if (CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY)) SET_FLAG(req.ifa.ifa_flags, IFA_F_SECONDARY); if (ifc->label) addattr_l(&req.n, sizeof req, IFA_LABEL, ifc->label, strlen(ifc->label) + 1); return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, 0); }
int kernel_interface_set_master(struct interface *master, struct interface *slave) { struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); struct { struct nlmsghdr n; struct ifinfomsg ifa; char buf[NL_PKT_BUF_SIZE]; } req; memset(&req, 0, sizeof req); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); req.n.nlmsg_flags = NLM_F_REQUEST; req.n.nlmsg_type = RTM_SETLINK; req.n.nlmsg_pid = zns->netlink_cmd.snl.nl_pid; req.ifa.ifi_index = slave->ifindex; addattr_l(&req.n, sizeof req, IFLA_MASTER, &master->ifindex, 4); addattr_l(&req.n, sizeof req, IFLA_LINK, &slave->ifindex, 4); return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, 0); }
static int netlink_bridge_interface(struct nlmsghdr *h, int len, ns_id_t ns_id, int startup) { char *name = NULL; struct ifinfomsg *ifi; struct rtattr *tb[IFLA_MAX + 1]; struct interface *ifp; struct rtattr *aftb[IFLA_BRIDGE_MAX + 1]; struct { uint16_t flags; uint16_t vid; } * vinfo; vlanid_t access_vlan; /* Fetch name and ifindex */ ifi = NLMSG_DATA(h); memset(tb, 0, sizeof tb); netlink_parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); if (tb[IFLA_IFNAME] == NULL) return -1; name = (char *)RTA_DATA(tb[IFLA_IFNAME]); /* The interface should already be known, if not discard. */ ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id), ifi->ifi_index); if (!ifp) { zlog_warn("Cannot find bridge IF %s(%u)", name, ifi->ifi_index); return 0; } if (!IS_ZEBRA_IF_VXLAN(ifp)) return 0; /* We are only interested in the access VLAN i.e., AF_SPEC */ if (!tb[IFLA_AF_SPEC]) return 0; /* There is a 1-to-1 mapping of VLAN to VxLAN - hence * only 1 access VLAN is accepted. */ memset(aftb, 0, sizeof aftb); parse_rtattr_nested(aftb, IFLA_BRIDGE_MAX, tb[IFLA_AF_SPEC]); if (!aftb[IFLA_BRIDGE_VLAN_INFO]) return 0; vinfo = RTA_DATA(aftb[IFLA_BRIDGE_VLAN_INFO]); if (!(vinfo->flags & BRIDGE_VLAN_INFO_PVID)) return 0; access_vlan = (vlanid_t)vinfo->vid; if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("Access VLAN %u for VxLAN IF %s(%u)", access_vlan, name, ifi->ifi_index); zebra_l2_vxlanif_update_access_vlan(ifp, access_vlan); return 0; }
/* Install or uninstall specified rule for a specific interface. * Form netlink message and ship it. Currently, notify status after * waiting for netlink status. */ static int netlink_rule_update(int cmd, struct zebra_pbr_rule *rule) { int family; int bytelen; struct { struct nlmsghdr n; struct fib_rule_hdr frh; char buf[NL_PKT_BUF_SIZE]; } req; struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); struct sockaddr_nl snl; char buf1[PREFIX_STRLEN]; char buf2[PREFIX_STRLEN]; memset(&req, 0, sizeof(req) - NL_PKT_BUF_SIZE); family = PREFIX_FAMILY(&rule->rule.filter.src_ip); bytelen = (family == AF_INET ? 4 : 16); req.n.nlmsg_type = cmd; req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); req.n.nlmsg_flags = NLM_F_REQUEST; req.n.nlmsg_pid = zns->netlink_cmd.snl.nl_pid; req.frh.family = family; req.frh.action = FR_ACT_TO_TBL; /* rule's pref # */ addattr32(&req.n, sizeof(req), FRA_PRIORITY, rule->rule.priority); /* interface on which applied */ if (rule->ifp) addattr_l(&req.n, sizeof(req), FRA_IFNAME, rule->ifp->name, strlen(rule->ifp->name) + 1); /* source IP, if specified */ if (IS_RULE_FILTERING_ON_SRC_IP(rule)) { req.frh.src_len = rule->rule.filter.src_ip.prefixlen; addattr_l(&req.n, sizeof(req), FRA_SRC, &rule->rule.filter.src_ip.u.prefix, bytelen); } /* destination IP, if specified */ if (IS_RULE_FILTERING_ON_DST_IP(rule)) { req.frh.dst_len = rule->rule.filter.dst_ip.prefixlen; addattr_l(&req.n, sizeof(req), FRA_DST, &rule->rule.filter.dst_ip.u.prefix, bytelen); } /* fwmark, if specified */ if (IS_RULE_FILTERING_ON_FWMARK(rule)) { addattr32(&req.n, sizeof(req), FRA_FWMARK, rule->rule.filter.fwmark); } /* Route table to use to forward, if filter criteria matches. */ if (rule->rule.action.table < 256) req.frh.table = rule->rule.action.table; else { req.frh.table = RT_TABLE_UNSPEC; addattr32(&req.n, sizeof(req), FRA_TABLE, rule->rule.action.table); } if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( "Tx %s family %s IF %s(%u) Pref %u Fwmark %u Src %s Dst %s Table %u", nl_msg_type_to_str(cmd), nl_family_to_str(family), rule->ifp ? rule->ifp->name : "Unknown", rule->ifp ? rule->ifp->ifindex : 0, rule->rule.priority, rule->rule.filter.fwmark, prefix2str(&rule->rule.filter.src_ip, buf1, sizeof(buf1)), prefix2str(&rule->rule.filter.dst_ip, buf2, sizeof(buf2)), rule->rule.action.table); /* Ship off the message. * Note: Currently, netlink_talk() is a blocking call which returns * back the status. */ memset(&snl, 0, sizeof(snl)); snl.nl_family = AF_NETLINK; return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, 0); }
/* * Handle netlink notification informing a rule add or delete. * Handling of an ADD is TBD. * DELs are notified up, if other attributes indicate it may be a * notification of interest. The expectation is that if this corresponds * to a PBR rule added by FRR, it will be readded. */ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) { struct zebra_ns *zns; struct fib_rule_hdr *frh; struct rtattr *tb[FRA_MAX + 1]; int len; char *ifname; struct zebra_pbr_rule rule = {}; char buf1[PREFIX_STRLEN]; char buf2[PREFIX_STRLEN]; /* Basic validation followed by extracting attributes. */ if (h->nlmsg_type != RTM_NEWRULE && h->nlmsg_type != RTM_DELRULE) return 0; /* TBD */ if (h->nlmsg_type == RTM_NEWRULE) return 0; len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct fib_rule_hdr)); if (len < 0) { zlog_err("%s: Message received from netlink is of a broken size: %d %zu", __PRETTY_FUNCTION__, h->nlmsg_len, (size_t)NLMSG_LENGTH(sizeof(struct fib_rule_hdr))); return -1; } frh = NLMSG_DATA(h); if (frh->family != AF_INET && frh->family != AF_INET6) { flog_warn( EC_ZEBRA_NETLINK_INVALID_AF, "Invalid address family: %u received from kernel rule change: %u", frh->family, h->nlmsg_type); return 0; } if (frh->action != FR_ACT_TO_TBL) return 0; memset(tb, 0, sizeof(tb)); netlink_parse_rtattr(tb, FRA_MAX, RTM_RTA(frh), len); /* TBD: We don't care about rules not specifying an IIF. */ if (tb[FRA_IFNAME] == NULL) return 0; /* If we don't know the interface, we don't care. */ ifname = (char *)RTA_DATA(tb[FRA_IFNAME]); zns = zebra_ns_lookup(ns_id); rule.ifp = if_lookup_by_name_per_ns(zns, ifname); if (!rule.ifp) return 0; if (tb[FRA_PRIORITY]) rule.rule.priority = *(uint32_t *)RTA_DATA(tb[FRA_PRIORITY]); if (tb[FRA_SRC]) { if (frh->family == AF_INET) memcpy(&rule.rule.filter.src_ip.u.prefix4, RTA_DATA(tb[FRA_SRC]), 4); else memcpy(&rule.rule.filter.src_ip.u.prefix6, RTA_DATA(tb[FRA_SRC]), 16); rule.rule.filter.src_ip.prefixlen = frh->src_len; rule.rule.filter.filter_bm |= PBR_FILTER_SRC_IP; } if (tb[FRA_DST]) { if (frh->family == AF_INET) memcpy(&rule.rule.filter.dst_ip.u.prefix4, RTA_DATA(tb[FRA_DST]), 4); else memcpy(&rule.rule.filter.dst_ip.u.prefix6, RTA_DATA(tb[FRA_DST]), 16); rule.rule.filter.dst_ip.prefixlen = frh->dst_len; rule.rule.filter.filter_bm |= PBR_FILTER_DST_IP; } if (tb[FRA_TABLE]) rule.rule.action.table = *(uint32_t *)RTA_DATA(tb[FRA_TABLE]); else rule.rule.action.table = frh->table; if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( "Rx %s family %s IF %s(%u) Pref %u Src %s Dst %s Table %u", nl_msg_type_to_str(h->nlmsg_type), nl_family_to_str(frh->family), rule.ifp->name, rule.ifp->ifindex, rule.rule.priority, prefix2str(&rule.rule.filter.src_ip, buf1, sizeof(buf1)), prefix2str(&rule.rule.filter.dst_ip, buf2, sizeof(buf2)), rule.rule.action.table); return kernel_pbr_rule_del(&rule); }
int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id, int startup) { int len; struct ifaddrmsg *ifa; struct rtattr *tb[IFA_MAX + 1]; struct interface *ifp; void *addr; void *broad; uint8_t flags = 0; char *label = NULL; struct zebra_ns *zns; zns = zebra_ns_lookup(ns_id); ifa = NLMSG_DATA(h); if (ifa->ifa_family != AF_INET && ifa->ifa_family != AF_INET6) { zlog_warn( "Invalid address family: %u received from kernel interface addr change: %u", ifa->ifa_family, h->nlmsg_type); return 0; } if (h->nlmsg_type != RTM_NEWADDR && h->nlmsg_type != RTM_DELADDR) return 0; len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifaddrmsg)); if (len < 0) { zlog_err("%s: Message received from netlink is of a broken size: %d %zu", __PRETTY_FUNCTION__, h->nlmsg_len, (size_t)NLMSG_LENGTH(sizeof(struct ifaddrmsg))); return -1; } memset(tb, 0, sizeof tb); netlink_parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), len); ifp = if_lookup_by_index_per_ns(zns, ifa->ifa_index); if (ifp == NULL) { flog_err( LIB_ERR_INTERFACE, "netlink_interface_addr can't find interface by index %d", ifa->ifa_index); return -1; } if (IS_ZEBRA_DEBUG_KERNEL) /* remove this line to see initial ifcfg */ { char buf[BUFSIZ]; zlog_debug("netlink_interface_addr %s %s flags 0x%x:", nl_msg_type_to_str(h->nlmsg_type), ifp->name, ifa->ifa_flags); if (tb[IFA_LOCAL]) zlog_debug(" IFA_LOCAL %s/%d", inet_ntop(ifa->ifa_family, RTA_DATA(tb[IFA_LOCAL]), buf, BUFSIZ), ifa->ifa_prefixlen); if (tb[IFA_ADDRESS]) zlog_debug(" IFA_ADDRESS %s/%d", inet_ntop(ifa->ifa_family, RTA_DATA(tb[IFA_ADDRESS]), buf, BUFSIZ), ifa->ifa_prefixlen); if (tb[IFA_BROADCAST]) zlog_debug(" IFA_BROADCAST %s/%d", inet_ntop(ifa->ifa_family, RTA_DATA(tb[IFA_BROADCAST]), buf, BUFSIZ), ifa->ifa_prefixlen); if (tb[IFA_LABEL] && strcmp(ifp->name, RTA_DATA(tb[IFA_LABEL]))) zlog_debug(" IFA_LABEL %s", (char *)RTA_DATA(tb[IFA_LABEL])); if (tb[IFA_CACHEINFO]) { struct ifa_cacheinfo *ci = RTA_DATA(tb[IFA_CACHEINFO]); zlog_debug(" IFA_CACHEINFO pref %d, valid %d", ci->ifa_prefered, ci->ifa_valid); } } /* logic copied from iproute2/ip/ipaddress.c:print_addrinfo() */ if (tb[IFA_LOCAL] == NULL) tb[IFA_LOCAL] = tb[IFA_ADDRESS]; if (tb[IFA_ADDRESS] == NULL) tb[IFA_ADDRESS] = tb[IFA_LOCAL]; /* local interface address */ addr = (tb[IFA_LOCAL] ? RTA_DATA(tb[IFA_LOCAL]) : NULL); /* is there a peer address? */ if (tb[IFA_ADDRESS] && memcmp(RTA_DATA(tb[IFA_ADDRESS]), RTA_DATA(tb[IFA_LOCAL]), RTA_PAYLOAD(tb[IFA_ADDRESS]))) { broad = RTA_DATA(tb[IFA_ADDRESS]); SET_FLAG(flags, ZEBRA_IFA_PEER); } else /* seeking a broadcast address */ broad = (tb[IFA_BROADCAST] ? RTA_DATA(tb[IFA_BROADCAST]) : NULL); /* addr is primary key, SOL if we don't have one */ if (addr == NULL) { zlog_debug("%s: NULL address", __func__); return -1; } /* Flags. */ if (ifa->ifa_flags & IFA_F_SECONDARY) SET_FLAG(flags, ZEBRA_IFA_SECONDARY); /* Label */ if (tb[IFA_LABEL]) label = (char *)RTA_DATA(tb[IFA_LABEL]); if (label && strcmp(ifp->name, label) == 0) label = NULL; /* Register interface address to the interface. */ if (ifa->ifa_family == AF_INET) { if (ifa->ifa_prefixlen > IPV4_MAX_BITLEN) { zlog_err( "Invalid prefix length: %u received from kernel interface addr change: %u", ifa->ifa_prefixlen, h->nlmsg_type); return -1; } if (h->nlmsg_type == RTM_NEWADDR) connected_add_ipv4(ifp, flags, (struct in_addr *)addr, ifa->ifa_prefixlen, (struct in_addr *)broad, label); else connected_delete_ipv4( ifp, flags, (struct in_addr *)addr, ifa->ifa_prefixlen, (struct in_addr *)broad); } if (ifa->ifa_family == AF_INET6) { if (ifa->ifa_prefixlen > IPV6_MAX_BITLEN) { zlog_err( "Invalid prefix length: %u received from kernel interface addr change: %u", ifa->ifa_prefixlen, h->nlmsg_type); return -1; } if (h->nlmsg_type == RTM_NEWADDR) { /* Only consider valid addresses; we'll not get a * notification from * the kernel till IPv6 DAD has completed, but at init * time, Quagga * does query for and will receive all addresses. */ if (!(ifa->ifa_flags & (IFA_F_DADFAILED | IFA_F_TENTATIVE))) connected_add_ipv6(ifp, flags, (struct in6_addr *)addr, (struct in6_addr *)broad, ifa->ifa_prefixlen, label); } else connected_delete_ipv6(ifp, (struct in6_addr *)addr, (struct in6_addr *)broad, ifa->ifa_prefixlen); } return 0; }
/* * Called from interface_lookup_netlink(). This function is only used * during bootstrap. */ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup) { int len; struct ifinfomsg *ifi; struct rtattr *tb[IFLA_MAX + 1]; struct rtattr *linkinfo[IFLA_MAX + 1]; struct interface *ifp; char *name = NULL; char *kind = NULL; char *desc = NULL; char *slave_kind = NULL; struct zebra_ns *zns; vrf_id_t vrf_id = VRF_DEFAULT; zebra_iftype_t zif_type = ZEBRA_IF_OTHER; zebra_slave_iftype_t zif_slave_type = ZEBRA_IF_SLAVE_NONE; ifindex_t bridge_ifindex = IFINDEX_INTERNAL; ifindex_t link_ifindex = IFINDEX_INTERNAL; zns = zebra_ns_lookup(ns_id); ifi = NLMSG_DATA(h); if (h->nlmsg_type != RTM_NEWLINK) return 0; len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifinfomsg)); if (len < 0) { zlog_err("%s: Message received from netlink is of a broken size: %d %zu", __PRETTY_FUNCTION__, h->nlmsg_len, (size_t)NLMSG_LENGTH(sizeof(struct ifinfomsg))); return -1; } /* We are interested in some AF_BRIDGE notifications. */ if (ifi->ifi_family == AF_BRIDGE) return netlink_bridge_interface(h, len, ns_id, startup); /* Looking up interface name. */ memset(tb, 0, sizeof tb); memset(linkinfo, 0, sizeof linkinfo); netlink_parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); /* check for wireless messages to ignore */ if ((tb[IFLA_WIRELESS] != NULL) && (ifi->ifi_change == 0)) { if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("%s: ignoring IFLA_WIRELESS message", __func__); return 0; } if (tb[IFLA_IFNAME] == NULL) return -1; name = (char *)RTA_DATA(tb[IFLA_IFNAME]); if (tb[IFLA_IFALIAS]) desc = (char *)RTA_DATA(tb[IFLA_IFALIAS]); if (tb[IFLA_LINKINFO]) { parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]); if (linkinfo[IFLA_INFO_KIND]) kind = RTA_DATA(linkinfo[IFLA_INFO_KIND]); if (linkinfo[IFLA_INFO_SLAVE_KIND]) slave_kind = RTA_DATA(linkinfo[IFLA_INFO_SLAVE_KIND]); netlink_determine_zebra_iftype(kind, &zif_type); } /* If VRF, create the VRF structure itself. */ if (zif_type == ZEBRA_IF_VRF && !vrf_is_backend_netns()) { netlink_vrf_change(h, tb[IFLA_LINKINFO], name); vrf_id = (vrf_id_t)ifi->ifi_index; } if (tb[IFLA_MASTER]) { if (slave_kind && (strcmp(slave_kind, "vrf") == 0) && !vrf_is_backend_netns()) { zif_slave_type = ZEBRA_IF_SLAVE_VRF; vrf_id = *(uint32_t *)RTA_DATA(tb[IFLA_MASTER]); } else if (slave_kind && (strcmp(slave_kind, "bridge") == 0)) { zif_slave_type = ZEBRA_IF_SLAVE_BRIDGE; bridge_ifindex = *(ifindex_t *)RTA_DATA(tb[IFLA_MASTER]); } else zif_slave_type = ZEBRA_IF_SLAVE_OTHER; } if (vrf_is_backend_netns()) vrf_id = (vrf_id_t)ns_id; /* If linking to another interface, note it. */ if (tb[IFLA_LINK]) link_ifindex = *(ifindex_t *)RTA_DATA(tb[IFLA_LINK]); /* Add interface. */ ifp = if_get_by_name(name, vrf_id, 0); set_ifindex(ifp, ifi->ifi_index, zns); ifp->flags = ifi->ifi_flags & 0x0000fffff; ifp->mtu6 = ifp->mtu = *(uint32_t *)RTA_DATA(tb[IFLA_MTU]); ifp->metric = 0; ifp->speed = get_iflink_speed(ifp); ifp->ptm_status = ZEBRA_PTM_STATUS_UNKNOWN; if (desc) ifp->desc = XSTRDUP(MTYPE_TMP, desc); /* Set zebra interface type */ zebra_if_set_ziftype(ifp, zif_type, zif_slave_type); if (IS_ZEBRA_IF_VRF(ifp)) SET_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK); /* Update link. */ zebra_if_update_link(ifp, link_ifindex); /* Hardware type and address. */ ifp->ll_type = netlink_to_zebra_link_type(ifi->ifi_type); netlink_interface_update_hw_addr(tb, ifp); if_add_update(ifp); /* Extract and save L2 interface information, take additional actions. */ netlink_interface_update_l2info(ifp, linkinfo[IFLA_INFO_DATA], 1); if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp)) zebra_l2if_update_bridge_slave(ifp, bridge_ifindex); return 0; }
int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) { int len; struct ifinfomsg *ifi; struct rtattr *tb[IFLA_MAX + 1]; struct rtattr *linkinfo[IFLA_MAX + 1]; struct interface *ifp; char *name = NULL; char *kind = NULL; char *desc = NULL; char *slave_kind = NULL; struct zebra_ns *zns; vrf_id_t vrf_id = VRF_DEFAULT; zebra_iftype_t zif_type = ZEBRA_IF_OTHER; zebra_slave_iftype_t zif_slave_type = ZEBRA_IF_SLAVE_NONE; ifindex_t bridge_ifindex = IFINDEX_INTERNAL; ifindex_t link_ifindex = IFINDEX_INTERNAL; zns = zebra_ns_lookup(ns_id); ifi = NLMSG_DATA(h); /* assume if not default zns, then new VRF */ if (!(h->nlmsg_type == RTM_NEWLINK || h->nlmsg_type == RTM_DELLINK)) { /* If this is not link add/delete message so print warning. */ zlog_warn("netlink_link_change: wrong kernel message %d", h->nlmsg_type); return 0; } if (!(ifi->ifi_family == AF_UNSPEC || ifi->ifi_family == AF_BRIDGE || ifi->ifi_family == AF_INET6)) { zlog_warn( "Invalid address family: %u received from kernel link change: %u", ifi->ifi_family, h->nlmsg_type); return 0; } len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifinfomsg)); if (len < 0) { zlog_err("%s: Message received from netlink is of a broken size %d %zu", __PRETTY_FUNCTION__, h->nlmsg_len, (size_t)NLMSG_LENGTH(sizeof(struct ifinfomsg))); return -1; } /* We are interested in some AF_BRIDGE notifications. */ if (ifi->ifi_family == AF_BRIDGE) return netlink_bridge_interface(h, len, ns_id, startup); /* Looking up interface name. */ memset(tb, 0, sizeof tb); memset(linkinfo, 0, sizeof linkinfo); netlink_parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); /* check for wireless messages to ignore */ if ((tb[IFLA_WIRELESS] != NULL) && (ifi->ifi_change == 0)) { if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("%s: ignoring IFLA_WIRELESS message", __func__); return 0; } if (tb[IFLA_IFNAME] == NULL) return -1; name = (char *)RTA_DATA(tb[IFLA_IFNAME]); if (tb[IFLA_LINKINFO]) { parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]); if (linkinfo[IFLA_INFO_KIND]) kind = RTA_DATA(linkinfo[IFLA_INFO_KIND]); if (linkinfo[IFLA_INFO_SLAVE_KIND]) slave_kind = RTA_DATA(linkinfo[IFLA_INFO_SLAVE_KIND]); netlink_determine_zebra_iftype(kind, &zif_type); } /* If linking to another interface, note it. */ if (tb[IFLA_LINK]) link_ifindex = *(ifindex_t *)RTA_DATA(tb[IFLA_LINK]); if (tb[IFLA_IFALIAS]) { desc = (char *)RTA_DATA(tb[IFLA_IFALIAS]); } /* If VRF, create or update the VRF structure itself. */ if (zif_type == ZEBRA_IF_VRF && !vrf_is_backend_netns()) { netlink_vrf_change(h, tb[IFLA_LINKINFO], name); vrf_id = (vrf_id_t)ifi->ifi_index; } /* See if interface is present. */ ifp = if_lookup_by_name_per_ns(zns, name); if (ifp) { if (ifp->desc) XFREE(MTYPE_TMP, ifp->desc); if (desc) ifp->desc = XSTRDUP(MTYPE_TMP, desc); } if (h->nlmsg_type == RTM_NEWLINK) { if (tb[IFLA_MASTER]) { if (slave_kind && (strcmp(slave_kind, "vrf") == 0) && !vrf_is_backend_netns()) { zif_slave_type = ZEBRA_IF_SLAVE_VRF; vrf_id = *(uint32_t *)RTA_DATA(tb[IFLA_MASTER]); } else if (slave_kind && (strcmp(slave_kind, "bridge") == 0)) { zif_slave_type = ZEBRA_IF_SLAVE_BRIDGE; bridge_ifindex = *(ifindex_t *)RTA_DATA(tb[IFLA_MASTER]); } else zif_slave_type = ZEBRA_IF_SLAVE_OTHER; } if (vrf_is_backend_netns()) vrf_id = (vrf_id_t)ns_id; if (ifp == NULL || !CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) { /* Add interface notification from kernel */ if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( "RTM_NEWLINK ADD for %s(%u) vrf_id %u type %d " "sl_type %d master %u flags 0x%x", name, ifi->ifi_index, vrf_id, zif_type, zif_slave_type, bridge_ifindex, ifi->ifi_flags); if (ifp == NULL) { /* unknown interface */ ifp = if_get_by_name(name, vrf_id, 0); } else { /* pre-configured interface, learnt now */ if (ifp->vrf_id != vrf_id) if_update_to_new_vrf(ifp, vrf_id); } /* Update interface information. */ set_ifindex(ifp, ifi->ifi_index, zns); ifp->flags = ifi->ifi_flags & 0x0000fffff; if (!tb[IFLA_MTU]) { zlog_warn( "RTM_NEWLINK for interface %s(%u) without MTU set", name, ifi->ifi_index); return 0; } ifp->mtu6 = ifp->mtu = *(int *)RTA_DATA(tb[IFLA_MTU]); ifp->metric = 0; ifp->ptm_status = ZEBRA_PTM_STATUS_UNKNOWN; /* Set interface type */ zebra_if_set_ziftype(ifp, zif_type, zif_slave_type); if (IS_ZEBRA_IF_VRF(ifp)) SET_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK); /* Update link. */ zebra_if_update_link(ifp, link_ifindex); netlink_interface_update_hw_addr(tb, ifp); /* Inform clients, install any configured addresses. */ if_add_update(ifp); /* Extract and save L2 interface information, take * additional actions. */ netlink_interface_update_l2info( ifp, linkinfo[IFLA_INFO_DATA], 1); if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp)) zebra_l2if_update_bridge_slave(ifp, bridge_ifindex); if_netlink_check_ifp_instance_consistency(RTM_NEWLINK, ifp, ns_id); } else if (ifp->vrf_id != vrf_id) { /* VRF change for an interface. */ if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( "RTM_NEWLINK vrf-change for %s(%u) " "vrf_id %u -> %u flags 0x%x", name, ifp->ifindex, ifp->vrf_id, vrf_id, ifi->ifi_flags); if_handle_vrf_change(ifp, vrf_id); } else { int was_bridge_slave; /* Interface update. */ if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( "RTM_NEWLINK update for %s(%u) " "sl_type %d master %u flags 0x%x", name, ifp->ifindex, zif_slave_type, bridge_ifindex, ifi->ifi_flags); set_ifindex(ifp, ifi->ifi_index, zns); if (!tb[IFLA_MTU]) { zlog_warn( "RTM_NEWLINK for interface %s(%u) without MTU set", name, ifi->ifi_index); return 0; } ifp->mtu6 = ifp->mtu = *(int *)RTA_DATA(tb[IFLA_MTU]); ifp->metric = 0; /* Update interface type - NOTE: Only slave_type can * change. */ was_bridge_slave = IS_ZEBRA_IF_BRIDGE_SLAVE(ifp); zebra_if_set_ziftype(ifp, zif_type, zif_slave_type); netlink_interface_update_hw_addr(tb, ifp); if (if_is_no_ptm_operative(ifp)) { ifp->flags = ifi->ifi_flags & 0x0000fffff; if (!if_is_no_ptm_operative(ifp)) { if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( "Intf %s(%u) has gone DOWN", name, ifp->ifindex); if_down(ifp); } else if (if_is_operative(ifp)) { /* Must notify client daemons of new * interface status. */ if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( "Intf %s(%u) PTM up, notifying clients", name, ifp->ifindex); zebra_interface_up_update(ifp); } } else { ifp->flags = ifi->ifi_flags & 0x0000fffff; if (if_is_operative(ifp)) { if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( "Intf %s(%u) has come UP", name, ifp->ifindex); if_up(ifp); } } /* Extract and save L2 interface information, take * additional actions. */ netlink_interface_update_l2info( ifp, linkinfo[IFLA_INFO_DATA], 0); if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp) || was_bridge_slave) zebra_l2if_update_bridge_slave(ifp, bridge_ifindex); if_netlink_check_ifp_instance_consistency(RTM_NEWLINK, ifp, ns_id); } } else { /* Delete interface notification from kernel */ if (ifp == NULL) { zlog_warn("RTM_DELLINK for unknown interface %s(%u)", name, ifi->ifi_index); return 0; } if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("RTM_DELLINK for %s(%u)", name, ifp->ifindex); UNSET_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK); /* Special handling for bridge or VxLAN interfaces. */ if (IS_ZEBRA_IF_BRIDGE(ifp)) zebra_l2_bridge_del(ifp); else if (IS_ZEBRA_IF_VXLAN(ifp)) zebra_l2_vxlanif_del(ifp); if (!IS_ZEBRA_IF_VRF(ifp)) if_delete_update(ifp); if_netlink_check_ifp_instance_consistency(RTM_DELLINK, ifp, ns_id); } return 0; }