#include <limits.h>
#include <arpa/inet.h>
-#include <netinet/ether.h>
#include <netinet/in.h>
+#include <netinet/ether.h>
#include <linux/rtnetlink.h>
#include <linux/neighbour.h>
system_set_dev_sysctl("/proc/sys/net/ipv6/conf/%s/disable_ipv6", dev->ifname, val);
}
+static void system_set_ip6segmentrouting(struct device *dev, const char *val)
+{
+ system_set_dev_sysctl("/proc/sys/net/ipv6/conf/%s/seg6_enabled", dev->ifname, val);
+}
+
static void system_set_rpfilter(struct device *dev, const char *val)
{
system_set_dev_sysctl("/proc/sys/net/ipv4/conf/%s/rp_filter", dev->ifname, val);
system_set_dev_sysctl("/proc/sys/net/ipv6/conf/%s/dad_transmits", dev->ifname, val);
}
+static void system_set_sendredirects(struct device *dev, const char *val)
+{
+ system_set_dev_sysctl("/proc/sys/net/ipv4/conf/%s/send_redirects", dev->ifname, val);
+}
+
+static void system_set_drop_v4_unicast_in_l2_multicast(struct device *dev, const char *val)
+{
+ system_set_dev_sysctl("/proc/sys/net/ipv4/conf/%s/drop_unicast_in_l2_multicast", dev->ifname, val);
+}
+
+static void system_set_drop_v6_unicast_in_l2_multicast(struct device *dev, const char *val)
+{
+ system_set_dev_sysctl("/proc/sys/net/ipv6/conf/%s/drop_unicast_in_l2_multicast", dev->ifname, val);
+}
+
+static void system_set_drop_gratuitous_arp(struct device *dev, const char *val)
+{
+ system_set_dev_sysctl("/proc/sys/net/ipv4/conf/%s/drop_gratuitous_arp", dev->ifname, val);
+}
+
+static void system_set_drop_unsolicited_na(struct device *dev, const char *val)
+{
+ system_set_dev_sysctl("/proc/sys/net/ipv6/conf/%s/drop_unsolicited_na", dev->ifname, val);
+}
+
+static void system_set_arp_accept(struct device *dev, const char *val)
+{
+ system_set_dev_sysctl("/proc/sys/net/ipv4/conf/%s/arp_accept", dev->ifname, val);
+}
+
static void system_bridge_set_multicast_to_unicast(struct device *dev, const char *val)
{
system_set_dev_sysctl("/sys/class/net/%s/brport/multicast_to_unicast", dev->ifname, val);
system_set_dev_sysctl("/sys/class/net/%s/brport/unicast_flood", dev->ifname, val);
}
-static void system_set_sendredirects(struct device *dev, const char *val)
-{
- system_set_dev_sysctl("/proc/sys/net/ipv4/conf/%s/send_redirects", dev->ifname, val);
-}
-
static void system_bridge_set_vlan_filtering(struct device *dev, const char *val)
{
system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/vlan_filtering", dev->ifname, val);
dev->ifname, buf, buf_sz);
}
+static int system_get_ip6segmentrouting(struct device *dev, char *buf, const size_t buf_sz)
+{
+ return system_get_dev_sysctl("/proc/sys/net/ipv6/conf/%s/seg6_enabled",
+ dev->ifname, buf, buf_sz);
+}
+
static int system_get_rpfilter(struct device *dev, char *buf, const size_t buf_sz)
{
return system_get_dev_sysctl("/proc/sys/net/ipv4/conf/%s/rp_filter",
dev->ifname, buf, buf_sz);
}
+
+static int system_get_drop_v4_unicast_in_l2_multicast(struct device *dev, char *buf, const size_t buf_sz)
+{
+ return system_get_dev_sysctl("/proc/sys/net/ipv4/conf/%s/drop_unicast_in_l2_multicast",
+ dev->ifname, buf, buf_sz);
+}
+
+static int system_get_drop_v6_unicast_in_l2_multicast(struct device *dev, char *buf, const size_t buf_sz)
+{
+ return system_get_dev_sysctl("/proc/sys/net/ipv6/conf/%s/drop_unicast_in_l2_multicast",
+ dev->ifname, buf, buf_sz);
+}
+
+static int system_get_drop_gratuitous_arp(struct device *dev, char *buf, const size_t buf_sz)
+{
+ return system_get_dev_sysctl("/proc/sys/net/ipv4/conf/%s/drop_gratuitous_arp",
+ dev->ifname, buf, buf_sz);
+}
+
+static int system_get_drop_unsolicited_na(struct device *dev, char *buf, const size_t buf_sz)
+{
+ return system_get_dev_sysctl("/proc/sys/net/ipv6/conf/%s/drop_unsolicited_na",
+ dev->ifname, buf, buf_sz);
+}
+
+static int system_get_arp_accept(struct device *dev, char *buf, const size_t buf_sz)
+{
+ return system_get_dev_sysctl("/proc/sys/net/ipv4/conf/%s/arp_accept",
+ dev->ifname, buf, buf_sz);
+}
+
/* Evaluate netlink messages */
static int cb_rtnl_event(struct nl_msg *msg, void *arg)
{
if (!system_get_dev_sysctl("/sys/class/net/%s/carrier", dev->ifname, buf, sizeof(buf)))
link_state = strtoul(buf, NULL, 0);
- if (dev->type == &simple_device_type && !system_if_force_external(dev->ifname))
+ if (dev->type == &simple_device_type)
device_set_present(dev, true);
device_set_link(dev, link_state ? true : false);
{
const char *subsystem = NULL, *interface = NULL, *interface_old = NULL;
char *cur, *end, *sep;
- struct device *dev;
int skip;
- bool add, move = false;
+ bool add;
- if (!strncmp(data, "add@", 4))
+ if (!strncmp(data, "add@", 4) || !strncmp(data, "move@", 5))
add = true;
else if (!strncmp(data, "remove@", 7))
add = false;
- else if (!strncmp(data, "move@", 5)) {
- add = true;
- move = true;
- }
else
return;
}
}
- if (subsystem && interface) {
- if (move && interface_old)
- goto move;
- else
- goto found;
- }
-
- return;
-
-move:
- dev = device_find(interface_old);
- if (!dev)
- return;
-
- if (dev->type != &simple_device_type)
- goto found;
-
- device_set_present(dev, false);
-
- return;
-
-found:
- dev = device_find(interface);
- if (!dev)
+ if (!subsystem || !interface)
return;
- if (dev->type != &simple_device_type)
- return;
+ if (interface_old)
+ device_hotplug_event(interface_old, false);
- if (add && system_if_force_external(dev->ifname))
- return;
-
- device_set_present(dev, add);
+ device_hotplug_event(interface, add);
}
static void
{
char buf[64];
char *oldbr;
- int ret = 0;
+ int tries = 0;
+ int ret;
+retry:
+ ret = 0;
oldbr = system_get_bridge(dev->ifname, dev_buf, sizeof(dev_buf));
- if (!oldbr || strcmp(oldbr, bridge->ifname) != 0)
+ if (!oldbr || strcmp(oldbr, bridge->ifname) != 0) {
ret = system_bridge_if(bridge->ifname, dev, SIOCBRADDIF, NULL);
+ tries++;
+ D(SYSTEM, "Failed to add device '%s' to bridge '%s' (tries=%d): %s\n",
+ dev->ifname, bridge->ifname, tries, strerror(errno));
+ if (tries <= 3)
+ goto retry;
+ }
if (dev->wireless)
system_bridge_set_wireless(bridge, dev);
return system_link_del(vlandev->ifname);
}
+static void
+system_set_ethtool_settings(struct device *dev, struct device_settings *s)
+{
+ struct ethtool_cmd ecmd = {
+ .cmd = ETHTOOL_GSET,
+ };
+ struct ifreq ifr = {
+ .ifr_data = (caddr_t)&ecmd,
+ };
+ static const struct {
+ int speed;
+ uint8_t bit_half;
+ uint8_t bit_full;
+ } speed_mask[] = {
+ { 10, ETHTOOL_LINK_MODE_10baseT_Half_BIT, ETHTOOL_LINK_MODE_10baseT_Full_BIT },
+ { 100, ETHTOOL_LINK_MODE_100baseT_Half_BIT, ETHTOOL_LINK_MODE_100baseT_Full_BIT },
+ { 1000, ETHTOOL_LINK_MODE_1000baseT_Half_BIT, ETHTOOL_LINK_MODE_1000baseT_Full_BIT },
+ };
+ uint32_t adv;
+ int i;
+
+ strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name) - 1);
+
+ if (ioctl(sock_ioctl, SIOCETHTOOL, &ifr) != 0)
+ return;
+
+ adv = ecmd.supported;
+ for (i = 0; i < ARRAY_SIZE(speed_mask); i++) {
+ if (s->flags & DEV_OPT_DUPLEX) {
+ int bit = s->duplex ? speed_mask[i].bit_half : speed_mask[i].bit_full;
+ adv &= ~(1 << bit);
+ }
+
+ if (!(s->flags & DEV_OPT_SPEED) ||
+ s->speed == speed_mask[i].speed)
+ continue;
+
+ adv &= ~(1 << speed_mask[i].bit_full);
+ adv &= ~(1 << speed_mask[i].bit_half);
+ }
+
+
+ if (ecmd.autoneg && ecmd.advertising == adv)
+ return;
+
+ ecmd.autoneg = 1;
+ ecmd.advertising = adv;
+ ecmd.cmd = ETHTOOL_SSET;
+ ioctl(sock_ioctl, SIOCETHTOOL, &ifr);
+}
+
void
system_if_get_settings(struct device *dev, struct device_settings *s)
{
s->flags |= DEV_OPT_IPV6;
}
+ if (!system_get_ip6segmentrouting(dev, buf, sizeof(buf))) {
+ s->ip6segmentrouting = strtoul(buf, NULL, 0);
+ s->flags |= DEV_OPT_IP6SEGMENTROUTING;
+ }
+
if (ioctl(sock_ioctl, SIOCGIFFLAGS, &ifr) == 0) {
s->promisc = ifr.ifr_flags & IFF_PROMISC;
s->flags |= DEV_OPT_PROMISC;
s->sendredirects = strtoul(buf, NULL, 0);
s->flags |= DEV_OPT_SENDREDIRECTS;
}
+
+ if (!system_get_drop_v4_unicast_in_l2_multicast(dev, buf, sizeof(buf))) {
+ s->drop_v4_unicast_in_l2_multicast = strtoul(buf, NULL, 0);
+ s->flags |= DEV_OPT_DROP_V4_UNICAST_IN_L2_MULTICAST;
+ }
+
+ if (!system_get_drop_v6_unicast_in_l2_multicast(dev, buf, sizeof(buf))) {
+ s->drop_v6_unicast_in_l2_multicast = strtoul(buf, NULL, 0);
+ s->flags |= DEV_OPT_DROP_V6_UNICAST_IN_L2_MULTICAST;
+ }
+
+ if (!system_get_drop_gratuitous_arp(dev, buf, sizeof(buf))) {
+ s->drop_gratuitous_arp = strtoul(buf, NULL, 0);
+ s->flags |= DEV_OPT_DROP_GRATUITOUS_ARP;
+ }
+
+ if (!system_get_drop_unsolicited_na(dev, buf, sizeof(buf))) {
+ s->drop_unsolicited_na = strtoul(buf, NULL, 0);
+ s->flags |= DEV_OPT_DROP_UNSOLICITED_NA;
+ }
+
+ if (!system_get_arp_accept(dev, buf, sizeof(buf))) {
+ s->arp_accept = strtoul(buf, NULL, 0);
+ s->flags |= DEV_OPT_ARP_ACCEPT;
+ }
}
void
-system_if_apply_settings(struct device *dev, struct device_settings *s, unsigned int apply_mask)
+system_if_apply_settings(struct device *dev, struct device_settings *s, uint64_t apply_mask)
{
struct ifreq ifr;
char buf[12];
+ apply_mask &= s->flags;
+
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name) - 1);
- if (s->flags & DEV_OPT_MTU & apply_mask) {
+ if (apply_mask & DEV_OPT_MTU) {
ifr.ifr_mtu = s->mtu;
if (ioctl(sock_ioctl, SIOCSIFMTU, &ifr) < 0)
s->flags &= ~DEV_OPT_MTU;
}
- if (s->flags & DEV_OPT_MTU6 & apply_mask) {
+ if (apply_mask & DEV_OPT_MTU6) {
system_update_ipv6_mtu(dev, s->mtu6);
}
- if (s->flags & DEV_OPT_TXQUEUELEN & apply_mask) {
+ if (apply_mask & DEV_OPT_TXQUEUELEN) {
ifr.ifr_qlen = s->txqueuelen;
if (ioctl(sock_ioctl, SIOCSIFTXQLEN, &ifr) < 0)
s->flags &= ~DEV_OPT_TXQUEUELEN;
}
- if ((s->flags & DEV_OPT_MACADDR & apply_mask) && !dev->external) {
+ if ((apply_mask & (DEV_OPT_MACADDR | DEV_OPT_DEFAULT_MACADDR)) && !dev->external) {
ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
memcpy(&ifr.ifr_hwaddr.sa_data, s->macaddr, sizeof(s->macaddr));
if (ioctl(sock_ioctl, SIOCSIFHWADDR, &ifr) < 0)
s->flags &= ~DEV_OPT_MACADDR;
}
- if (s->flags & DEV_OPT_IPV6 & apply_mask)
+ if (apply_mask & DEV_OPT_IPV6)
system_set_disable_ipv6(dev, s->ipv6 ? "0" : "1");
- if (s->flags & DEV_OPT_PROMISC & apply_mask) {
+ if (s->flags & DEV_OPT_IP6SEGMENTROUTING & apply_mask) {
+ struct device dummy = {
+ .ifname = "all",
+ };
+ bool ip6segmentrouting = device_check_ip6segmentrouting();
+
+ system_set_ip6segmentrouting(dev, s->ip6segmentrouting ? "1" : "0");
+ system_set_ip6segmentrouting(&dummy, ip6segmentrouting ? "1" : "0");
+ }
+ if (apply_mask & DEV_OPT_PROMISC) {
if (system_if_flags(dev->ifname, s->promisc ? IFF_PROMISC : 0,
!s->promisc ? IFF_PROMISC : 0) < 0)
s->flags &= ~DEV_OPT_PROMISC;
}
- if (s->flags & DEV_OPT_RPFILTER & apply_mask) {
+ if (apply_mask & DEV_OPT_RPFILTER) {
snprintf(buf, sizeof(buf), "%u", s->rpfilter);
system_set_rpfilter(dev, buf);
}
- if (s->flags & DEV_OPT_ACCEPTLOCAL & apply_mask)
+ if (apply_mask & DEV_OPT_ACCEPTLOCAL)
system_set_acceptlocal(dev, s->acceptlocal ? "1" : "0");
- if (s->flags & DEV_OPT_IGMPVERSION & apply_mask) {
+ if (apply_mask & DEV_OPT_IGMPVERSION) {
snprintf(buf, sizeof(buf), "%u", s->igmpversion);
system_set_igmpversion(dev, buf);
}
- if (s->flags & DEV_OPT_MLDVERSION & apply_mask) {
+ if (apply_mask & DEV_OPT_MLDVERSION) {
snprintf(buf, sizeof(buf), "%u", s->mldversion);
system_set_mldversion(dev, buf);
}
- if (s->flags & DEV_OPT_NEIGHREACHABLETIME & apply_mask) {
+ if (apply_mask & DEV_OPT_NEIGHREACHABLETIME) {
snprintf(buf, sizeof(buf), "%u", s->neigh4reachabletime);
system_set_neigh4reachabletime(dev, buf);
snprintf(buf, sizeof(buf), "%u", s->neigh6reachabletime);
system_set_neigh6reachabletime(dev, buf);
}
- if (s->flags & DEV_OPT_NEIGHLOCKTIME & apply_mask) {
+ if (apply_mask & DEV_OPT_NEIGHLOCKTIME) {
snprintf(buf, sizeof(buf), "%d", s->neigh4locktime);
system_set_neigh4locktime(dev, buf);
}
- if (s->flags & DEV_OPT_NEIGHGCSTALETIME & apply_mask) {
+ if (apply_mask & DEV_OPT_NEIGHGCSTALETIME) {
snprintf(buf, sizeof(buf), "%u", s->neigh4gcstaletime);
system_set_neigh4gcstaletime(dev, buf);
snprintf(buf, sizeof(buf), "%u", s->neigh6gcstaletime);
system_set_neigh6gcstaletime(dev, buf);
}
- if (s->flags & DEV_OPT_DADTRANSMITS & apply_mask) {
+ if (apply_mask & DEV_OPT_DADTRANSMITS) {
snprintf(buf, sizeof(buf), "%u", s->dadtransmits);
system_set_dadtransmits(dev, buf);
}
- if (s->flags & DEV_OPT_MULTICAST & apply_mask) {
+ if (apply_mask & DEV_OPT_MULTICAST) {
if (system_if_flags(dev->ifname, s->multicast ? IFF_MULTICAST : 0,
!s->multicast ? IFF_MULTICAST : 0) < 0)
s->flags &= ~DEV_OPT_MULTICAST;
}
- if (s->flags & DEV_OPT_SENDREDIRECTS & apply_mask)
+ if (apply_mask & DEV_OPT_SENDREDIRECTS)
system_set_sendredirects(dev, s->sendredirects ? "1" : "0");
+ if (apply_mask & DEV_OPT_DROP_V4_UNICAST_IN_L2_MULTICAST)
+ system_set_drop_v4_unicast_in_l2_multicast(dev, s->drop_v4_unicast_in_l2_multicast ? "1" : "0");
+ if (apply_mask & DEV_OPT_DROP_V6_UNICAST_IN_L2_MULTICAST)
+ system_set_drop_v6_unicast_in_l2_multicast(dev, s->drop_v6_unicast_in_l2_multicast ? "1" : "0");
+ if (apply_mask & DEV_OPT_DROP_GRATUITOUS_ARP)
+ system_set_drop_gratuitous_arp(dev, s->drop_gratuitous_arp ? "1" : "0");
+ if (apply_mask & DEV_OPT_DROP_UNSOLICITED_NA)
+ system_set_drop_unsolicited_na(dev, s->drop_unsolicited_na ? "1" : "0");
+ if (apply_mask & DEV_OPT_ARP_ACCEPT)
+ system_set_arp_accept(dev, s->arp_accept ? "1" : "0");
+ system_set_ethtool_settings(dev, s);
}
int system_if_up(struct device *dev)
{
- system_if_get_settings(dev, &dev->orig_settings);
- /* Only keep orig settings based on what needs to be set */
- dev->orig_settings.valid_flags = dev->orig_settings.flags;
- dev->orig_settings.flags &= dev->settings.flags;
- system_if_apply_settings(dev, &dev->settings, dev->settings.flags);
return system_if_flags(dev->ifname, IFF_UP, 0);
}
int system_if_down(struct device *dev)
{
- int ret = system_if_flags(dev->ifname, 0, IFF_UP);
- system_if_apply_settings(dev, &dev->orig_settings, dev->orig_settings.flags);
- return ret;
+ return system_if_flags(dev->ifname, 0, IFF_UP);
}
struct if_check_data {
if (nh->nlmsg_type != RTM_NEWLINK)
return NL_SKIP;
- device_set_present(chk->dev, ifi->ifi_index > 0 ? true : false);
+ if (chk->dev->type == &simple_device_type)
+ device_set_present(chk->dev, ifi->ifi_index > 0 ? true : false);
device_set_link(chk->dev, ifi->ifi_flags & IFF_LOWER_UP ? true : false);
return NL_OK;
{
struct if_check_data *chk = (struct if_check_data *)arg;
- device_set_present(chk->dev, false);
+ if (chk->dev->type == &simple_device_type)
+ device_set_present(chk->dev, false);
device_set_link(chk->dev, false);
chk->pending = err->error;
return NL_STOP;
}
+struct bridge_vlan_check_data {
+ struct device *check_dev;
+ int ifindex;
+ int ret;
+ bool pending;
+};
+
+static void bridge_vlan_check_port(struct bridge_vlan_check_data *data,
+ struct bridge_vlan_port *port,
+ struct bridge_vlan_info *vinfo)
+{
+ uint16_t flags = 0, diff, mask;
+
+ if (port->flags & BRVLAN_F_PVID)
+ flags |= BRIDGE_VLAN_INFO_PVID;
+ if (port->flags & BRVLAN_F_UNTAGGED)
+ flags |= BRIDGE_VLAN_INFO_UNTAGGED;
+
+ diff = vinfo->flags ^ flags;
+ mask = BRVLAN_F_UNTAGGED | (flags & BRIDGE_VLAN_INFO_PVID);
+ if (diff & mask) {
+ data->ret = 1;
+ data->pending = false;
+ }
+
+ port->check = 1;
+}
+
+static void bridge_vlan_check_attr(struct bridge_vlan_check_data *data,
+ struct rtattr *attr)
+{
+ struct bridge_vlan_hotplug_port *port;
+ struct bridge_vlan_info *vinfo;
+ struct bridge_vlan *vlan;
+ struct rtattr *cur;
+ int rem = RTA_PAYLOAD(attr);
+ int i;
+
+ for (cur = RTA_DATA(attr); RTA_OK(cur, rem); cur = RTA_NEXT(cur, rem)) {
+ if (cur->rta_type != IFLA_BRIDGE_VLAN_INFO)
+ continue;
+
+ vinfo = RTA_DATA(cur);
+ vlan = vlist_find(&data->check_dev->vlans, &vinfo->vid, vlan, node);
+ if (!vlan) {
+ data->ret = 1;
+ data->pending = false;
+ return;
+ }
+
+ for (i = 0; i < vlan->n_ports; i++)
+ if (!vlan->ports[i].check)
+ bridge_vlan_check_port(data, &vlan->ports[i], vinfo);
+
+ list_for_each_entry(port, &vlan->hotplug_ports, list)
+ if (!port->port.check)
+ bridge_vlan_check_port(data, &port->port, vinfo);
+ }
+}
+
+static int bridge_vlan_check_cb(struct nl_msg *msg, void *arg)
+{
+ struct bridge_vlan_check_data *data = arg;
+ struct nlmsghdr *nh = nlmsg_hdr(msg);
+ struct ifinfomsg *ifi = NLMSG_DATA(nh);
+ struct rtattr *attr;
+ int rem;
+
+ if (nh->nlmsg_type != RTM_NEWLINK)
+ return NL_SKIP;
+
+ if (ifi->ifi_family != AF_BRIDGE)
+ return NL_SKIP;
+
+ if (ifi->ifi_index != data->ifindex)
+ return NL_SKIP;
+
+ attr = IFLA_RTA(ifi);
+ rem = nh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi));
+ while (RTA_OK(attr, rem)) {
+ if (attr->rta_type == IFLA_AF_SPEC)
+ bridge_vlan_check_attr(data, attr);
+
+ attr = RTA_NEXT(attr, rem);
+ }
+
+ return NL_SKIP;
+}
+
+static int bridge_vlan_ack_cb(struct nl_msg *msg, void *arg)
+{
+ struct bridge_vlan_check_data *data = arg;
+ data->pending = false;
+ return NL_STOP;
+}
+
+static int bridge_vlan_error_cb(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg)
+{
+ struct bridge_vlan_check_data *data = arg;
+ data->pending = false;
+ return NL_STOP;
+}
+
+int system_bridge_vlan_check(struct device *dev, char *ifname)
+{
+ struct bridge_vlan_check_data data = {
+ .check_dev = dev,
+ .ifindex = if_nametoindex(ifname),
+ .ret = -1,
+ .pending = true,
+ };
+ static struct ifinfomsg ifi = {
+ .ifi_family = AF_BRIDGE
+ };
+ static struct rtattr ext_req = {
+ .rta_type = IFLA_EXT_MASK,
+ .rta_len = RTA_LENGTH(sizeof(uint32_t)),
+ };
+ uint32_t filter = RTEXT_FILTER_BRVLAN;
+ struct nl_cb *cb = nl_cb_alloc(NL_CB_DEFAULT);
+ struct bridge_vlan *vlan;
+ struct nl_msg *msg;
+ int i;
+
+ if (!data.ifindex)
+ return 0;
+
+ msg = nlmsg_alloc_simple(RTM_GETLINK, NLM_F_DUMP);
+
+ if (nlmsg_append(msg, &ifi, sizeof(ifi), 0) ||
+ nlmsg_append(msg, &ext_req, sizeof(ext_req), NLMSG_ALIGNTO) ||
+ nlmsg_append(msg, &filter, sizeof(filter), 0))
+ goto free;
+
+ vlist_for_each_element(&dev->vlans, vlan, node) {
+ struct bridge_vlan_hotplug_port *port;
+
+ for (i = 0; i < vlan->n_ports; i++) {
+ if (!strcmp(vlan->ports[i].ifname, ifname))
+ vlan->ports[i].check = 0;
+ else
+ vlan->ports[i].check = -1;
+ }
+
+ list_for_each_entry(port, &vlan->hotplug_ports, list) {
+ if (!strcmp(port->port.ifname, ifname))
+ port->port.check = 0;
+ else
+ port->port.check = -1;
+ }
+ }
+
+ nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, bridge_vlan_check_cb, &data);
+ nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, bridge_vlan_ack_cb, &data);
+ nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, bridge_vlan_ack_cb, &data);
+ nl_cb_err(cb, NL_CB_CUSTOM, bridge_vlan_error_cb, &data);
+
+ if (nl_send_auto_complete(sock_rtnl, msg) < 0)
+ goto free;
+
+ data.ret = 0;
+ while (data.pending)
+ nl_recvmsgs(sock_rtnl, cb);
+
+ vlist_for_each_element(&dev->vlans, vlan, node) {
+ struct bridge_vlan_hotplug_port *port;
+
+ for (i = 0; i < vlan->n_ports; i++) {
+ if (!vlan->ports[i].check) {
+ data.ret = 1;
+ break;
+ }
+ }
+
+ list_for_each_entry(port, &vlan->hotplug_ports, list) {
+ if (!port->port.check) {
+ data.ret = 1;
+ break;
+ }
+ }
+ }
+
+ goto out;
+
+free:
+ nlmsg_free(msg);
+out:
+ nl_cb_put(cb);
+ return data.ret;
+}
+
int system_if_check(struct device *dev)
{
struct nl_cb *cb = nl_cb_alloc(NL_CB_DEFAULT);