remove some more code duplication
[project/netifd.git] / system-linux.c
index 2425445..0308e6f 100644 (file)
 #include "device.h"
 #include "system.h"
 
+struct event_socket {
+       struct uloop_fd uloop;
+       struct nl_sock *sock;
+       struct nl_cb *cb;
+};
+
 static int sock_ioctl = -1;
 static struct nl_sock *sock_rtnl = NULL;
-static struct nl_sock *sock_rtnl_event = NULL;
 
-static void handler_rtnl_event(struct uloop_fd *u, unsigned int events);
 static int cb_rtnl_event(struct nl_msg *msg, void *arg);
-static struct uloop_fd rtnl_event = {.cb = handler_rtnl_event};
-static struct nl_cb *nl_cb_rtnl_event;
 
-int system_init(void)
+static void
+handler_nl_event(struct uloop_fd *u, unsigned int events)
 {
-       sock_ioctl = socket(AF_LOCAL, SOCK_DGRAM, 0);
-       fcntl(sock_ioctl, F_SETFD, fcntl(sock_ioctl, F_GETFD) | FD_CLOEXEC);
+       struct event_socket *ev = container_of(u, struct event_socket, uloop);
+       nl_recvmsgs(ev->sock, ev->cb);
+}
 
-       // Prepare socket for routing / address control
-       sock_rtnl = nl_socket_alloc();
-       if (!sock_rtnl)
-               return -1;
+static struct nl_sock *
+create_socket(int protocol)
+{
+       struct nl_sock *sock;
+
+       sock = nl_socket_alloc();
+       if (!sock)
+               return NULL;
 
-       if (nl_connect(sock_rtnl, NETLINK_ROUTE))
-               goto error_free_sock;
+       if (nl_connect(sock, protocol))
+               return NULL;
+
+       return sock;
+}
 
+static bool
+create_event_socket(struct event_socket *ev, int protocol,
+                   int (*cb)(struct nl_msg *msg, void *arg))
+{
        // Prepare socket for link events
-       nl_cb_rtnl_event = nl_cb_alloc(NL_CB_DEFAULT);
-       if (!nl_cb_rtnl_event)
-               goto error_free_sock;
+       ev->cb = nl_cb_alloc(NL_CB_DEFAULT);
+       if (!ev->cb)
+               return false;
 
-       nl_cb_set(nl_cb_rtnl_event, NL_CB_VALID, NL_CB_CUSTOM,
-                 cb_rtnl_event, NULL);
+       nl_cb_set(ev->cb, NL_CB_VALID, NL_CB_CUSTOM, cb, NULL);
 
-       sock_rtnl_event = nl_socket_alloc();
-       if (!sock_rtnl_event)
-               goto error_free_cb;
+       ev->sock = create_socket(protocol);
+       if (!ev->sock)
+               return false;
 
-       if (nl_connect(sock_rtnl_event, NETLINK_ROUTE))
-               goto error_free_event;
+       ev->uloop.fd = nl_socket_get_fd(ev->sock);
+       ev->uloop.cb = handler_nl_event;
+       uloop_fd_add(&ev->uloop, ULOOP_READ | ULOOP_EDGE_TRIGGER);
+       return true;
+}
 
-       // Receive network link events form kernel
-       nl_socket_add_membership(sock_rtnl_event, RTNLGRP_LINK);
+int system_init(void)
+{
+       static struct event_socket rtnl_event;
 
-       rtnl_event.fd = nl_socket_get_fd(sock_rtnl_event);
-       uloop_fd_add(&rtnl_event, ULOOP_READ | ULOOP_EDGE_TRIGGER);
+       sock_ioctl = socket(AF_LOCAL, SOCK_DGRAM, 0);
+       fcntl(sock_ioctl, F_SETFD, fcntl(sock_ioctl, F_GETFD) | FD_CLOEXEC);
 
-       return 0;
+       // Prepare socket for routing / address control
+       sock_rtnl = create_socket(NETLINK_ROUTE);
+       if (!sock_rtnl)
+               return -1;
 
-error_free_event:
-       nl_socket_free(sock_rtnl_event);
-       sock_rtnl_event = NULL;
-error_free_cb:
-       nl_cb_put(nl_cb_rtnl_event);
-       nl_cb_rtnl_event = NULL;
-error_free_sock:
-       nl_socket_free(sock_rtnl);
-       sock_rtnl = NULL;
-       return -1;
-}
+       if (!create_event_socket(&rtnl_event, NETLINK_ROUTE, cb_rtnl_event))
+               return -1;
 
-// If socket is ready for reading parse netlink events
-static void handler_rtnl_event(struct uloop_fd *u, unsigned int events)
-{
-       nl_recvmsgs(sock_rtnl_event, nl_cb_rtnl_event);
+       // Receive network link events form kernel
+       nl_socket_add_membership(rtnl_event.sock, RTNLGRP_LINK);
+
+       return 0;
 }
 
 static void system_set_sysctl(const char *path, const char *val)
@@ -99,12 +111,17 @@ static void system_set_sysctl(const char *path, const char *val)
        close(fd);
 }
 
-static void system_set_disable_ipv6(struct device *dev, const char *val)
+static void system_set_dev_sysctl(const char *path, const char *device, const char *val)
 {
-       char buf[256];
+       static char buf[256];
 
-       snprintf(buf, sizeof(buf), "/proc/sys/net/ipv6/conf/%s/disable_ipv6", dev->ifname);
-       system_set_sysctl(buf, "0");
+       snprintf(buf, sizeof(buf), path, val);
+       system_set_sysctl(buf, val);
+}
+
+static void system_set_disable_ipv6(struct device *dev, const char *val)
+{
+       system_set_dev_sysctl("/proc/sys/net/ipv6/conf/%s/disable_ipv6", dev->ifname, val);
 }
 
 // Evaluate netlink messages
@@ -362,9 +379,12 @@ out:
  */
 void system_if_clear_state(struct device *dev)
 {
-       char buf[256];
+       static char buf[256];
        char *bridge;
 
+       if (dev->external)
+               return;
+
        dev->ifindex = system_if_resolve(dev);
        if (!dev->ifindex)
                return;
@@ -411,6 +431,9 @@ int system_bridge_addbr(struct device *bridge, struct bridge_config *cfg)
        args[1] = sec_to_jiffies(cfg->forward_delay);
        system_bridge_if(bridge->ifname, NULL, SIOCDEVPRIVATE, &args);
 
+       system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/multicast_snooping",
+               bridge->ifname, cfg->igmp_snoop ? "1" : "0");
+
        if (cfg->flags & BRIDGE_OPT_AGEING_TIME) {
                args[0] = BRCTL_SET_AGEING_TIME;
                args[1] = sec_to_jiffies(cfg->ageing_time);
@@ -462,9 +485,32 @@ int system_vlan_del(struct device *dev)
        return system_vlan(dev, -1);
 }
 
-int system_if_up(struct device *dev)
+static void
+system_if_apply_settings(struct device *dev)
 {
+       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 (dev->flags & DEV_OPT_TXQUEUELEN) {
+               ifr.ifr_qlen = dev->txqueuelen;
+               ioctl(sock_ioctl, SIOCSIFTXQLEN, &ifr);
+       }
+       if (dev->flags & DEV_OPT_MACADDR) {
+               memcpy(&ifr.ifr_hwaddr, dev->macaddr, sizeof(dev->macaddr));
+               ioctl(sock_ioctl, SIOCSIFHWADDR, &ifr);
+       }
+
        dev->ifindex = system_if_resolve(dev);
+}
+
+int system_if_up(struct device *dev)
+{
+       system_if_apply_settings(dev);
        return system_if_flags(dev->ifname, IFF_UP, 0);
 }
 
@@ -531,7 +577,17 @@ static int system_addr(struct device *dev, struct device_addr *addr, int cmd)
                .ifa_index = dev->ifindex,
        };
 
-       struct nl_msg *msg = nlmsg_alloc_simple(cmd, 0);
+       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;
 
@@ -555,6 +611,7 @@ static int system_rt(struct device *dev, struct device_route *route, int cmd)
        int alen = ((route->flags & DEVADDR_FAMILY) == DEVADDR_INET4) ? 4 : 16;
        bool have_gw;
        unsigned int flags = 0;
+       int ifindex = dev->ifindex;
 
        if (alen == 4)
                have_gw = !!route->nexthop.in.s_addr;
@@ -575,11 +632,20 @@ static int system_rt(struct device *dev, struct device_route *route, int cmd)
                .rtm_scope = scope,
                .rtm_type = (cmd == RTM_DELROUTE) ? 0: RTN_UNICAST,
        };
+       struct nl_msg *msg;
 
        if (cmd == RTM_NEWROUTE)
                flags |= NLM_F_CREATE | NLM_F_REPLACE;
 
-       struct nl_msg *msg = nlmsg_alloc_simple(cmd, flags);
+       dev = route->device;
+       if (dev) {
+               if (!dev->ifindex)
+                       return -1;
+
+               ifindex = dev->ifindex;
+       }
+
+       msg = nlmsg_alloc_simple(cmd, flags);
        if (!msg)
                return -1;
 
@@ -592,7 +658,7 @@ static int system_rt(struct device *dev, struct device_route *route, int cmd)
                nla_put(msg, RTA_GATEWAY, alen, &route->nexthop);
 
        if (route->flags & DEVADDR_DEVICE)
-               nla_put_u32(msg, RTA_OIF, dev->ifindex);
+               nla_put_u32(msg, RTA_OIF, ifindex);
 
        return system_rtnl_call(msg);
 }