From: Vincent Bernat Date: Sat, 25 Jul 2015 15:23:01 +0000 (+0200) Subject: netlink: use netlink to retrieve bridge/bond/vlan information X-Git-Tag: 0.7.16~6 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=16eacc5bc262ccd7f6a798e3bac47416e085fe34;p=thirdparty%2Flldpd.git netlink: use netlink to retrieve bridge/bond/vlan information On Linux, Netlink is more efficient to get this kind of information. Use it all the time, unless we are configured to run on old kernels. In this case, fallback to the previous functions. An old kernel is now < 2.6.32 (RHEL 6, Ubuntu Lucid, Debian Squeeze). --- diff --git a/NEWS b/NEWS index d71485b1..ce748c0e 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,9 @@ lldpd (0.7.16) + * Change: + + For Linux, 2.6.32 is now the minimal required kernel. When using + an older kernel, use `--enable-oldies`. + + For Linux, use netlink to retrieve information about bridges, + VLAN and bonds. The code was contributed by Cumulus Networks. * Features: + Use symbol versioning for liblldpctl.so. + Ability to get local chassis information with "show diff --git a/configure.ac b/configure.ac index ee8e54f2..e1ddb011 100644 --- a/configure.ac +++ b/configure.ac @@ -297,7 +297,7 @@ lldp_ARG_ENABLE([dot3], [Dot3 extension (PHY stuff)], [yes]) lldp_ARG_ENABLE([custom], [Custom TLV support], [yes]) # Oldies -lldp_ARG_ENABLE([oldies], [compatibility with Linux kernel older than 2.6.18], [no]) +lldp_ARG_ENABLE([oldies], [compatibility with Linux kernel older than 2.6.32], [no]) ####################### # Output results diff --git a/src/daemon/interfaces-linux.c b/src/daemon/interfaces-linux.c index 4bae8a2a..c9342d48 100644 --- a/src/daemon/interfaces-linux.c +++ b/src/daemon/interfaces-linux.c @@ -111,12 +111,12 @@ static struct lldpd_ops eth_ops = { .cleanup = iflinux_eth_close, }; +#ifdef ENABLE_OLDIES static int old_iflinux_is_bridge(struct lldpd *cfg, struct interfaces_device_list *interfaces, struct interfaces_device *iface) { -#ifdef ENABLE_OLDIES int j; int ifptindices[MAX_PORTS] = {}; unsigned long args2[4] = { @@ -154,16 +154,15 @@ old_iflinux_is_bridge(struct lldpd *cfg, } } return 1; -#else - return 0; -#endif } +#endif static int iflinux_is_bridge(struct lldpd *cfg, struct interfaces_device_list *interfaces, struct interfaces_device *iface) { +#ifdef ENABLE_OLDIES struct interfaces_device *port; char path[SYSFS_PATH_MAX]; int f; @@ -193,6 +192,9 @@ iflinux_is_bridge(struct lldpd *cfg, } return 1; +#else + return 0; +#endif } static int @@ -200,6 +202,7 @@ iflinux_is_vlan(struct lldpd *cfg, struct interfaces_device_list *interfaces, struct interfaces_device *iface) { +#ifdef ENABLE_OLDIES struct vlan_ioctl_args ifv = {}; ifv.cmd = GET_VLAN_REALDEV_NAME_CMD; strlcpy(ifv.device1, iface->name, sizeof(ifv.device1)); @@ -228,6 +231,7 @@ iflinux_is_vlan(struct lldpd *cfg, iface->vlanid = ifv.u.VID; return 1; } +#endif return 0; } @@ -236,6 +240,7 @@ iflinux_is_bond(struct lldpd *cfg, struct interfaces_device_list *interfaces, struct interfaces_device *master) { +#ifdef ENABLE_OLDIES /* Shortcut if we detect the new team driver. Upper and lower links * should already be set with netlink in this case. */ if (master->driver && !strcmp(master->driver, "team")) { @@ -268,6 +273,7 @@ iflinux_is_bond(struct lldpd *cfg, } return 1; } +#endif return 0; } diff --git a/src/daemon/netlink.c b/src/daemon/netlink.c index 46d653f9..0e3dba8a 100644 --- a/src/daemon/netlink.c +++ b/src/daemon/netlink.c @@ -114,6 +114,66 @@ netlink_send(int s, int type, int family) return 0; } +static void +netlink_parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len) +{ + while (RTA_OK(rta, len)) { + if ((rta->rta_type <= max) && (!tb[rta->rta_type])) + tb[rta->rta_type] = rta; + rta = RTA_NEXT(rta,len); + } +} + +/** + * Parse a `linkinfo` attributes. + * + * @param iff where to put the result + * @param rta linkinfo attribute + * @param len length of attributes + */ +static void +netlink_parse_linkinfo(struct interfaces_device *iff, struct rtattr *rta, int len) +{ + struct rtattr *link_info_attrs[IFLA_INFO_MAX+1] = {}; + char *kind = NULL; + + netlink_parse_rtattr(link_info_attrs, IFLA_INFO_MAX, rta, len); + + if (link_info_attrs[IFLA_INFO_KIND]) { + kind = strdup(RTA_DATA(link_info_attrs[IFLA_INFO_KIND])); + if (kind) { + if (!strcmp(kind, "vlan")) { + log_debug("netlink", "interface %s is a VLAN", + iff->name); + iff->type |= IFACE_VLAN_T; + } else if (!strcmp(kind, "bridge")) { + log_debug("netlink", "interface %s is a bridge", + iff->name); + iff->type |= IFACE_BRIDGE_T; + } else if (!strcmp(kind, "bond")) { + log_debug("netlink", "interface %s is a bond", + iff->name); + iff->type |= IFACE_BOND_T; + } + } + } + + if (kind && !strcmp(kind, "vlan") && link_info_attrs[IFLA_INFO_DATA]) { + struct rtattr *vlan_link_info_data_attrs[IFLA_VLAN_MAX+1] = {}; + netlink_parse_rtattr(vlan_link_info_data_attrs, IFLA_VLAN_MAX, + RTA_DATA(link_info_attrs[IFLA_INFO_DATA]), + RTA_PAYLOAD(link_info_attrs[IFLA_INFO_DATA])); + + if (vlan_link_info_data_attrs[IFLA_VLAN_ID]) { + iff->vlanid = *(uint16_t *)RTA_DATA(vlan_link_info_data_attrs[IFLA_VLAN_ID]); + log_debug("netlink", "VLAN ID for interface %s is %d", + iff->name, iff->vlanid); + } + } + + free(kind); +} + /** * Parse a `link` netlink message. * @@ -181,6 +241,9 @@ netlink_parse_link(struct nlmsghdr *msg, /* Maximum Transmission Unit */ iff->mtu = *(int*)RTA_DATA(attribute); break; + case IFLA_LINKINFO: + netlink_parse_linkinfo(iff, RTA_DATA(attribute), RTA_PAYLOAD(attribute)); + break; default: log_debug("netlink", "unhandled link attribute type %d for iface %s", attribute->rta_type, iff->name ? iff->name : "(unknown)");