summaryrefslogtreecommitdiff
path: root/zebra/if_netlink.c
diff options
context:
space:
mode:
Diffstat (limited to 'zebra/if_netlink.c')
-rw-r--r--zebra/if_netlink.c364
1 files changed, 332 insertions, 32 deletions
diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c
index 5af6c3a08f..972e6447f6 100644
--- a/zebra/if_netlink.c
+++ b/zebra/if_netlink.c
@@ -20,6 +20,15 @@
*/
#include <zebra.h>
+
+/* The following definition is to workaround an issue in the Linux kernel
+ * header files with redefinition of 'struct in6_addr' in both
+ * netinet/in.h and linux/in6.h.
+ * Reference - https://sourceware.org/ml/libc-alpha/2013-01/msg00599.html
+ */
+#define _LINUX_IN6_H
+
+#include <linux/if_bridge.h>
#include <net/if_arp.h>
#include <linux/sockios.h>
#include <linux/ethtool.h>
@@ -175,6 +184,23 @@ netlink_to_zebra_link_type (unsigned int hwt)
}
}
+static void
+netlink_determine_zebra_iftype (char *kind, zebra_iftype_t *zif_type)
+{
+ *zif_type = ZEBRA_IF_OTHER;
+
+ if (!kind)
+ return;
+
+ if (strcmp(kind, "vrf") == 0)
+ *zif_type = ZEBRA_IF_VRF;
+ else if (strcmp(kind, "bridge") == 0)
+ *zif_type = ZEBRA_IF_BRIDGE;
+ else if (strcmp(kind, "vlan") == 0)
+ *zif_type = ZEBRA_IF_VLAN;
+ else if (strcmp(kind, "vxlan") == 0)
+ *zif_type = ZEBRA_IF_VXLAN;
+}
//Temporary Assignments to compile on older platforms.
#ifndef IFLA_BR_MAX
@@ -341,6 +367,173 @@ get_iflink_speed (const char *ifname)
return (ecmd.speed_hi << 16 ) | ecmd.speed;
}
+static int
+netlink_extract_bridge_info (struct rtattr *link_data,
+ struct zebra_l2info_bridge *bridge_info)
+{
+ struct rtattr *attr[IFLA_BR_MAX+1];
+
+ memset (bridge_info, 0, sizeof (*bridge_info));
+ memset (attr, 0, sizeof attr);
+ parse_rtattr_nested(attr, IFLA_BR_MAX, link_data);
+ if (attr[IFLA_BR_VLAN_FILTERING])
+ bridge_info->vlan_aware = *(u_char *)RTA_DATA(attr[IFLA_BR_VLAN_FILTERING]);
+ return 0;
+}
+
+static int
+netlink_extract_vlan_info (struct rtattr *link_data,
+ struct zebra_l2info_vlan *vlan_info)
+{
+ struct rtattr *attr[IFLA_VLAN_MAX+1];
+ vlanid_t vid_in_msg;
+
+ memset (vlan_info, 0, sizeof (*vlan_info));
+ memset (attr, 0, sizeof attr);
+ parse_rtattr_nested(attr, IFLA_VLAN_MAX, link_data);
+ if (!attr[IFLA_VLAN_ID])
+ {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug ("IFLA_VLAN_ID missing from VLAN IF message");
+ return -1;
+ }
+
+ vid_in_msg = *(vlanid_t *)RTA_DATA(attr[IFLA_VLAN_ID]);
+ vlan_info->vid = vid_in_msg;
+ return 0;
+}
+
+static int
+netlink_extract_vxlan_info (struct rtattr *link_data,
+ struct zebra_l2info_vxlan *vxl_info)
+{
+ struct rtattr *attr[IFLA_VXLAN_MAX+1];
+ vni_t vni_in_msg;
+ struct in_addr vtep_ip_in_msg;
+
+ memset (vxl_info, 0, sizeof (*vxl_info));
+ memset (attr, 0, sizeof attr);
+ parse_rtattr_nested(attr, IFLA_VXLAN_MAX, link_data);
+ if (!attr[IFLA_VXLAN_ID])
+ {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug ("IFLA_VXLAN_ID missing from VXLAN IF message");
+ return -1;
+ }
+
+ vni_in_msg = *(vni_t *)RTA_DATA(attr[IFLA_VXLAN_ID]);
+ vxl_info->vni = vni_in_msg;
+ if (!attr[IFLA_VXLAN_LOCAL])
+ {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug ("IFLA_VXLAN_LOCAL missing from VXLAN IF message");
+ }
+ else
+ {
+ vtep_ip_in_msg = *(struct in_addr *)RTA_DATA(attr[IFLA_VXLAN_LOCAL]);
+ vxl_info->vtep_ip = vtep_ip_in_msg;
+ }
+
+ return 0;
+}
+
+/*
+ * Extract and save L2 params (of interest) for an interface. When a
+ * bridge interface is added or updated, take further actions to map
+ * its members. Likewise, for VxLAN interface.
+ */
+static void
+netlink_interface_update_l2info (struct interface *ifp,
+ struct rtattr *link_data,
+ int add)
+{
+ if (!link_data)
+ return;
+
+ if (IS_ZEBRA_IF_BRIDGE(ifp))
+ {
+ struct zebra_l2info_bridge bridge_info;
+
+ netlink_extract_bridge_info (link_data, &bridge_info);
+ zebra_l2_bridge_add_update (ifp, &bridge_info, add);
+ }
+ else if (IS_ZEBRA_IF_VLAN(ifp))
+ {
+ struct zebra_l2info_vlan vlan_info;
+
+ netlink_extract_vlan_info (link_data, &vlan_info);
+ zebra_l2_vlanif_update (ifp, &vlan_info);
+ }
+ else if (IS_ZEBRA_IF_VXLAN(ifp))
+ {
+ struct zebra_l2info_vxlan vxlan_info;
+
+ netlink_extract_vxlan_info (link_data, &vxlan_info);
+ zebra_l2_vxlanif_add_update (ifp, &vxlan_info, add);
+ }
+}
+
+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
+ {
+ u_int16_t flags;
+ u_int16_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;
+}
+
/* Called from interface_lookup_netlink(). This function is only used
during bootstrap. */
static int
@@ -355,9 +548,12 @@ netlink_interface (struct sockaddr_nl *snl, struct nlmsghdr *h,
char *name = NULL;
char *kind = NULL;
char *slave_kind = NULL;
- int vrf_device = 0;
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);
@@ -369,11 +565,13 @@ netlink_interface (struct sockaddr_nl *snl, struct nlmsghdr *h,
if (len < 0)
return -1;
+ /* We are interested in some AF_BRIDGE notifications. */
if (ifi->ifi_family == AF_BRIDGE)
- return 0;
+ 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);
#ifdef IFLA_WIRELESS
@@ -392,7 +590,6 @@ netlink_interface (struct sockaddr_nl *snl, struct nlmsghdr *h,
if (tb[IFLA_LINKINFO])
{
- memset (linkinfo, 0, sizeof linkinfo);
parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]);
if (linkinfo[IFLA_INFO_KIND])
@@ -403,37 +600,64 @@ netlink_interface (struct sockaddr_nl *snl, struct nlmsghdr *h,
slave_kind = RTA_DATA(linkinfo[IFLA_INFO_SLAVE_KIND]);
#endif
- if (kind && strcmp(kind, "vrf") == 0)
- {
- vrf_device = 1;
- netlink_vrf_change(h, tb[IFLA_LINKINFO], name);
- vrf_id = (vrf_id_t)ifi->ifi_index;
- }
+ netlink_determine_zebra_iftype (kind, &zif_type);
+ }
+
+ /* If VRF, create the VRF structure itself. */
+ if (zif_type == ZEBRA_IF_VRF)
+ {
+ 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_id = *(u_int32_t *)RTA_DATA(tb[IFLA_MASTER]);
+ {
+ zif_slave_type = ZEBRA_IF_SLAVE_VRF;
+ vrf_id = *(u_int32_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 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);
set_ifindex(ifp, ifi->ifi_index, zns);
ifp->flags = ifi->ifi_flags & 0x0000fffff;
- if (vrf_device)
+ if (IS_ZEBRA_IF_VRF(ifp))
SET_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK);
ifp->mtu6 = ifp->mtu = *(uint32_t *) RTA_DATA (tb[IFLA_MTU]);
ifp->metric = 0;
ifp->speed = get_iflink_speed (name);
ifp->ptm_status = ZEBRA_PTM_STATUS_UNKNOWN;
+ /* Set zebra interface type */
+ zebra_if_set_ziftype (ifp, zif_type, zif_slave_type);
+
+ /* 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;
}
@@ -477,6 +701,15 @@ interface_lookup_netlink (struct zebra_ns *zns)
if (ret < 0)
return ret;
+ /* Get interface information - for bridge interfaces. */
+ ret = netlink_request_intf_addr (zns, AF_BRIDGE, RTM_GETLINK,
+ RTEXT_FILTER_BRVLAN);
+ if (ret < 0)
+ return ret;
+ ret = netlink_parse_info (netlink_interface, &zns->netlink_cmd, zns, 0, 0);
+ if (ret < 0)
+ return ret;
+
/* Get IPv4 address of the interfaces. */
ret = netlink_request_intf_addr (zns, AF_INET, RTM_GETADDR, 0);
if (ret < 0)
@@ -712,9 +945,13 @@ netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h,
char *name = NULL;
char *kind = NULL;
char *slave_kind = NULL;
- int vrf_device = 0;
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);
@@ -731,11 +968,13 @@ netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h,
if (len < 0)
return -1;
+ /* We are interested in some AF_BRIDGE notifications. */
if (ifi->ifi_family == AF_BRIDGE)
- return 0;
+ 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);
#ifdef IFLA_WIRELESS
@@ -754,7 +993,6 @@ netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h,
if (tb[IFLA_LINKINFO])
{
- memset (linkinfo, 0, sizeof linkinfo);
parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]);
if (linkinfo[IFLA_INFO_KIND])
@@ -765,12 +1003,18 @@ netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h,
slave_kind = RTA_DATA(linkinfo[IFLA_INFO_SLAVE_KIND]);
#endif
- if (kind && strcmp(kind, "vrf") == 0)
- {
- vrf_device = 1;
- netlink_vrf_change(h, tb[IFLA_LINKINFO], name);
- vrf_id = (vrf_id_t)ifi->ifi_index;
- }
+ 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 VRF, create or update the VRF structure itself. */
+ if (zif_type == ZEBRA_IF_VRF)
+ {
+ netlink_vrf_change(h, tb[IFLA_LINKINFO], name);
+ vrf_id = (vrf_id_t)ifi->ifi_index;
}
/* See if interface is present. */
@@ -781,15 +1025,27 @@ netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h,
if (tb[IFLA_MASTER])
{
if (slave_kind && (strcmp(slave_kind, "vrf") == 0))
- vrf_id = *(u_int32_t *)RTA_DATA(tb[IFLA_MASTER]);
+ {
+ zif_slave_type = ZEBRA_IF_SLAVE_VRF;
+ vrf_id = *(u_int32_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 (ifp == NULL || !CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE))
{
/* Add interface notification from kernel */
if (IS_ZEBRA_DEBUG_KERNEL)
- zlog_debug ("RTM_NEWLINK for %s(%u) (ifp %p) vrf_id %u flags 0x%x",
- name, ifi->ifi_index, ifp, vrf_id, ifi->ifi_flags);
+ 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)
{
@@ -806,16 +1062,27 @@ netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h,
/* Update interface information. */
set_ifindex(ifp, ifi->ifi_index, zns);
ifp->flags = ifi->ifi_flags & 0x0000fffff;
- if (vrf_device)
+ if (IS_ZEBRA_IF_VRF(ifp))
SET_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK);
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);
+
+ /* 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);
}
else if (ifp->vrf_id != vrf_id)
{
@@ -830,32 +1097,59 @@ netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h,
}
else
{
- /* Interface status change. */
+ int was_bridge_slave;
+
+ /* Interface update. */
if (IS_ZEBRA_DEBUG_KERNEL)
- zlog_debug ("RTM_NEWLINK status for %s(%u) flags 0x%x",
- name, ifp->ifindex, ifi->ifi_flags);
+ 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);
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_down (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. */
- zebra_interface_up_update (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_up (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);
}
}
else
@@ -873,7 +1167,13 @@ netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h,
UNSET_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK);
- if (!vrf_device)
+ /* 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);
}