netifd: Read current link state when processing netlink event
authorHans Dedecker <dedeckeh@gmail.com>
Thu, 23 Oct 2014 09:13:44 +0000 (11:13 +0200)
committerFelix Fietkau <nbd@openwrt.org>
Fri, 24 Oct 2014 17:50:24 +0000 (19:50 +0200)
Netifd commit b2dcb02570939d98b92c7c55db1c328693a5d52a introduces
a race condition resulting into infinite toggling interfaces
(eg static interfaces with linksensing enabled, vlan interfaces
with proto none (#18106)) when linksensing is enabled resulting into
a crash.
As netlink event messages will be queued on the netlink event socket
the included lower up interface flag will not always represent the
current link state when netifd processes the netlink messages;
by reading the current link state when a netlink event message is
parsed the correct info is passed to the device layer.
This will avoid continuous interface toggling (down/up) triggered
by link state changes based on outdated netlink interface info.

Signed-off-by: Hans Dedecker <dedeckeh@gmail.com>
system-linux.c

index 057bc39ee0cfcf86a3b29fedeb73779eb8d1eaa8..02574fcbeea6f5ab77ad0493c0ccb7d3840d9366 100644 (file)
@@ -299,16 +299,14 @@ static int system_get_disable_ipv6(struct device *dev, char *buf, const size_t b
                        dev->ifname, buf, buf_sz);
 }
 
-#ifndef IFF_LOWER_UP
-#define IFF_LOWER_UP   0x10000
-#endif
-
 // Evaluate netlink messages
 static int cb_rtnl_event(struct nl_msg *msg, void *arg)
 {
        struct nlmsghdr *nh = nlmsg_hdr(msg);
        struct ifinfomsg *ifi = NLMSG_DATA(nh);
        struct nlattr *nla[__IFLA_MAX];
+       int link_state = 0;
+       char buf[10];
 
        if (nh->nlmsg_type != RTM_NEWLINK)
                goto out;
@@ -322,8 +320,13 @@ static int cb_rtnl_event(struct nl_msg *msg, void *arg)
                goto out;
 
        device_set_ifindex(dev, ifi->ifi_index);
-       if (!dev->type->keep_link_status)
-               device_set_link(dev, ifi->ifi_flags & IFF_LOWER_UP ? true : false);
+       if (dev->type->keep_link_status)
+               goto out;
+
+       if (!system_get_dev_sysctl("/sys/class/net/%s/carrier", dev->ifname, buf, sizeof(buf)))
+               link_state = strtoul(buf, NULL, 0);
+
+       device_set_link(dev, link_state ? true : false);
 
 out:
        return 0;
@@ -1021,6 +1024,10 @@ struct if_check_data {
        int ret;
 };
 
+#ifndef IFF_LOWER_UP
+#define IFF_LOWER_UP   0x10000
+#endif
+
 static int cb_if_check_valid(struct nl_msg *msg, void *arg)
 {
        struct nlmsghdr *nh = nlmsg_hdr(msg);