proto: move the dns search option handling to the core
[project/netifd.git] / system-linux.c
index 1fcf22f..0a989b0 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/sockios.h>
 #include <linux/if_vlan.h>
 #include <linux/if_bridge.h>
+#include <linux/ethtool.h>
 
 #include <unistd.h>
 #include <string.h>
@@ -569,37 +570,66 @@ int system_vlan_del(struct device *dev)
 }
 
 static void
-system_if_apply_settings(struct device *dev)
+system_if_get_settings(struct device *dev, struct device_settings *s)
 {
        struct ifreq ifr;
 
        memset(&ifr, 0, sizeof(ifr));
        strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name));
-       if (dev->flags & DEV_OPT_MTU) {
-               ifr.ifr_mtu = dev->mtu;
-               ioctl(sock_ioctl, SIOCSIFMTU, &ifr);
+
+       if (ioctl(sock_ioctl, SIOCGIFMTU, &ifr) == 0) {
+               s->mtu = ifr.ifr_mtu;
+               s->flags |= DEV_OPT_MTU;
        }
-       if (dev->flags & DEV_OPT_TXQUEUELEN) {
-               ifr.ifr_qlen = dev->txqueuelen;
-               ioctl(sock_ioctl, SIOCSIFTXQLEN, &ifr);
+
+       if (ioctl(sock_ioctl, SIOCGIFTXQLEN, &ifr) == 0) {
+               s->txqueuelen = ifr.ifr_qlen;
+               s->flags |= DEV_OPT_TXQUEUELEN;
        }
-       if (dev->flags & DEV_OPT_MACADDR) {
-               memcpy(&ifr.ifr_hwaddr, dev->macaddr, sizeof(dev->macaddr));
-               ioctl(sock_ioctl, SIOCSIFHWADDR, &ifr);
+
+       if (ioctl(sock_ioctl, SIOCGIFHWADDR, &ifr) == 0) {
+               memcpy(s->macaddr, &ifr.ifr_hwaddr, sizeof(s->macaddr));
+               s->flags |= DEV_OPT_MACADDR;
        }
+}
 
-       dev->ifindex = system_if_resolve(dev);
+static void
+system_if_apply_settings(struct device *dev, struct device_settings *s)
+{
+       struct ifreq ifr;
+
+       memset(&ifr, 0, sizeof(ifr));
+       strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name));
+       if (s->flags & 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_TXQUEUELEN) {
+               ifr.ifr_qlen = s->txqueuelen;
+               if (ioctl(sock_ioctl, SIOCSIFTXQLEN, &ifr) < 0)
+                       s->flags &= ~DEV_OPT_TXQUEUELEN;
+       }
+       if (s->flags & DEV_OPT_MACADDR) {
+               memcpy(&ifr.ifr_hwaddr, s->macaddr, sizeof(s->macaddr));
+               if (ioctl(sock_ioctl, SIOCSIFHWADDR, &ifr) < 0)
+                       s->flags &= ~DEV_OPT_MACADDR;
+       }
 }
 
 int system_if_up(struct device *dev)
 {
-       system_if_apply_settings(dev);
+       system_if_get_settings(dev, &dev->orig_settings);
+       system_if_apply_settings(dev, &dev->settings);
+       dev->ifindex = system_if_resolve(dev);
        return system_if_flags(dev->ifname, IFF_UP, 0);
 }
 
 int system_if_down(struct device *dev)
 {
-       return system_if_flags(dev->ifname, 0, IFF_UP);
+       int ret = system_if_flags(dev->ifname, 0, IFF_UP);
+       system_if_apply_settings(dev, &dev->orig_settings);
+       return ret;
 }
 
 int system_if_check(struct device *dev)
@@ -683,10 +713,35 @@ read_int_file(int dir_fd, const char *file, int *val)
        return ret;
 }
 
+/* Assume advertised flags == supported flags */
+static const struct {
+       uint32_t mask;
+       const char *name;
+} ethtool_link_modes[] = {
+       { ADVERTISED_10baseT_Half, "10H" },
+       { ADVERTISED_10baseT_Full, "10F" },
+       { ADVERTISED_100baseT_Half, "100H" },
+       { ADVERTISED_100baseT_Full, "100F" },
+       { ADVERTISED_1000baseT_Half, "1000H" },
+       { ADVERTISED_1000baseT_Full, "1000F" },
+};
+
+static void system_add_link_modes(struct blob_buf *b, __u32 mask)
+{
+       int i;
+       for (i = 0; i < ARRAY_SIZE(ethtool_link_modes); i++) {
+               if (mask & ethtool_link_modes[i].mask)
+                       blobmsg_add_string(b, NULL, ethtool_link_modes[i].name);
+       }
+}
+
 int
 system_if_dump_info(struct device *dev, struct blob_buf *b)
 {
-       char buf[64];
+       struct ethtool_cmd ecmd;
+       struct ifreq ifr;
+       char buf[64], *s;
+       void *c;
        int dir_fd, val = 0;
 
        snprintf(buf, sizeof(buf), "/sys/class/net/%s", dev->ifname);
@@ -694,8 +749,27 @@ system_if_dump_info(struct device *dev, struct blob_buf *b)
 
        if (read_int_file(dir_fd, "carrier", &val))
                blobmsg_add_u8(b, "link", !!val);
-       if (read_string_file(dir_fd, "address", buf, sizeof(buf)))
-               blobmsg_add_string(b, "macaddr", buf);
+
+       memset(&ecmd, 0, sizeof(ecmd));
+       memset(&ifr, 0, sizeof(ifr));
+       strcpy(ifr.ifr_name, dev->ifname);
+       ifr.ifr_data = (caddr_t) &ecmd;
+       ecmd.cmd = ETHTOOL_GSET;
+
+       if (ioctl(sock_ioctl, SIOCETHTOOL, &ifr) == 0) {
+               c = blobmsg_open_array(b, "link-advertising");
+               system_add_link_modes(b, ecmd.advertising);
+               blobmsg_close_array(b, c);
+
+               c = blobmsg_open_array(b, "link-supported");
+               system_add_link_modes(b, ecmd.supported);
+               blobmsg_close_array(b, c);
+
+               s = blobmsg_alloc_string_buffer(b, "speed", 8);
+               snprintf(s, 8, "%d%c", ethtool_cmd_speed(&ecmd),
+                       ecmd.duplex == DUPLEX_HALF ? 'H' : 'F');
+               blobmsg_add_string_buffer(b);
+       }
 
        close(dir_fd);
        return 0;
@@ -733,7 +807,8 @@ system_if_dump_stats(struct device *dev, struct blob_buf *b)
 
 static int system_addr(struct device *dev, struct device_addr *addr, int cmd)
 {
-       int alen = ((addr->flags & DEVADDR_FAMILY) == DEVADDR_INET4) ? 4 : 16;
+       bool v4 = ((addr->flags & DEVADDR_FAMILY) == DEVADDR_INET4);
+       int alen = v4 ? 4 : 16;
        struct ifaddrmsg ifa = {
                .ifa_family = (alen == 4) ? AF_INET : AF_INET6,
                .ifa_prefixlen = addr->mask,
@@ -742,20 +817,20 @@ static int system_addr(struct device *dev, struct device_addr *addr, int cmd)
 
        struct nl_msg *msg;
 
-       dev = addr->device;
-       if (dev) {
-               if (!dev->ifindex)
-                       return -1;
-
-               ifa.ifa_index = dev->ifindex;
-       }
-
        msg = nlmsg_alloc_simple(cmd, 0);
        if (!msg)
                return -1;
 
        nlmsg_append(msg, &ifa, sizeof(ifa), 0);
        nla_put(msg, IFA_LOCAL, alen, &addr->addr);
+       if (v4) {
+               uint32_t mask = ~0;
+               uint32_t *a = (uint32_t *) &addr->addr;
+
+               mask >>= addr->mask;
+               nla_put_u32(msg, IFA_BROADCAST, *a | mask);
+       }
+
        return system_rtnl_call(msg);
 }
 
@@ -800,14 +875,6 @@ static int system_rt(struct device *dev, struct device_route *route, int cmd)
        if (cmd == RTM_NEWROUTE)
                flags |= NLM_F_CREATE | NLM_F_REPLACE;
 
-       dev = route->device;
-       if (dev) {
-               if (!dev->ifindex)
-                       return -1;
-
-               ifindex = dev->ifindex;
-       }
-
        msg = nlmsg_alloc_simple(cmd, flags);
        if (!msg)
                return -1;
@@ -817,6 +884,9 @@ static int system_rt(struct device *dev, struct device_route *route, int cmd)
        if (route->mask)
                nla_put(msg, RTA_DST, alen, &route->addr);
 
+       if (route->metric >= 0)
+               nla_put_u32(msg, RTA_PRIORITY, route->metric);
+
        if (have_gw)
                nla_put(msg, RTA_GATEWAY, alen, &route->nexthop);