interface: allow renaming interface when moving to jail netns
[project/netifd.git] / system-linux.c
index f63aeb26af55224ec803f9c52d343524b25c1ebb..04b9bdfc486e3888ee45c715bac05b9f5d63b209 100644 (file)
@@ -26,6 +26,7 @@
 #include <net/if.h>
 #include <net/if_arp.h>
 
+#include <limits.h>
 #include <arpa/inet.h>
 #include <netinet/ether.h>
 #include <netinet/in.h>
@@ -45,6 +46,8 @@
 #include <linux/veth.h>
 #include <linux/version.h>
 
+#include <sched.h>
+
 #ifndef RTN_FAILED_POLICY
 #define RTN_FAILED_POLICY 12
 #endif
@@ -139,8 +142,10 @@ create_socket(int protocol, int groups)
        if (groups)
                nl_join_groups(sock, groups);
 
-       if (nl_connect(sock, protocol))
+       if (nl_connect(sock, protocol)) {
+               nl_socket_free(sock);
                return NULL;
+       }
 
        return sock;
 }
@@ -838,16 +843,21 @@ int system_bridge_delif(struct device *bridge, struct device *dev)
        return system_bridge_if(bridge->ifname, dev, SIOCBRDELIF, NULL);
 }
 
-int system_if_resolve(struct device *dev)
+static int system_ifname_resolve(const char *ifname)
 {
        struct ifreq ifr;
-       strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name) - 1);
+       strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1);
        if (!ioctl(sock_ioctl, SIOCGIFINDEX, &ifr))
                return ifr.ifr_ifindex;
        else
                return 0;
 }
 
+int system_if_resolve(struct device *dev)
+{
+       return system_ifname_resolve(dev->ifname);
+}
+
 static int system_if_flags(const char *ifname, unsigned add, unsigned rem)
 {
        struct ifreq ifr;
@@ -904,7 +914,7 @@ static int cb_clear_event(struct nl_msg *msg, void *arg)
        struct clear_data *clr = arg;
        struct nlmsghdr *hdr = nlmsg_hdr(msg);
        bool (*cb)(struct nlmsghdr *, int ifindex);
-       int type;
+       int type, ret;
 
        switch(clr->type) {
        case RTM_GETADDR:
@@ -941,13 +951,23 @@ static int cb_clear_event(struct nl_msg *msg, void *arg)
                D(SYSTEM, "Remove %s from device %s\n",
                  type == RTM_DELADDR ? "an address" : "a route",
                  clr->dev->ifname);
+
        memcpy(nlmsg_hdr(clr->msg), hdr, hdr->nlmsg_len);
        hdr = nlmsg_hdr(clr->msg);
        hdr->nlmsg_type = type;
        hdr->nlmsg_flags = NLM_F_REQUEST;
 
        nl_socket_disable_auto_ack(sock_rtnl);
-       nl_send_auto_complete(sock_rtnl, clr->msg);
+       ret = nl_send_auto_complete(sock_rtnl, clr->msg);
+       if (ret < 0) {
+               if (type == RTM_DELRULE)
+                       D(SYSTEM, "Error deleting a rule: %d\n", ret);
+               else
+                       D(SYSTEM, "Error deleting %s from device '%s': %d\n",
+                               type == RTM_DELADDR ? "an address" : "a route",
+                               clr->dev->ifname, ret);
+       }
+
        nl_socket_enable_auto_ack(sock_rtnl);
 
        return NL_SKIP;
@@ -973,7 +993,7 @@ static void
 system_if_clear_entries(struct device *dev, int type, int af)
 {
        struct clear_data clr;
-       struct nl_cb *cb = nl_cb_alloc(NL_CB_DEFAULT);
+       struct nl_cb *cb;
        struct rtmsg rtm = {
                .rtm_family = af,
                .rtm_flags = RTM_F_CLONED,
@@ -996,6 +1016,7 @@ system_if_clear_entries(struct device *dev, int type, int af)
                return;
        }
 
+       cb = nl_cb_alloc(NL_CB_DEFAULT);
        if (!cb)
                return;
 
@@ -1008,10 +1029,13 @@ system_if_clear_entries(struct device *dev, int type, int af)
        nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, cb_finish_event, &pending);
        nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &pending);
 
-       nl_send_auto_complete(sock_rtnl, clr.msg);
+       if (nl_send_auto_complete(sock_rtnl, clr.msg) < 0)
+               goto free;
+
        while (pending > 0)
                nl_recvmsgs(sock_rtnl, cb);
 
+free:
        nlmsg_free(clr.msg);
 out:
        nl_cb_put(cb);
@@ -1227,6 +1251,27 @@ nla_put_failure:
        return -ENOMEM;
 }
 
+int system_link_netns_move(const char *ifname, int netns_fd, const char *target_ifname)
+{
+       struct nl_msg *msg;
+       struct ifinfomsg iim = {
+               .ifi_family = AF_UNSPEC,
+       };
+
+       iim.ifi_index = system_ifname_resolve(ifname);
+       msg = nlmsg_alloc_simple(RTM_NEWLINK, NLM_F_REQUEST);
+
+       if (!msg)
+               return -1;
+
+       nlmsg_append(msg, &iim, sizeof(iim), 0);
+       if (target_ifname)
+               nla_put_string(msg, IFLA_IFNAME, target_ifname);
+
+       nla_put_u32(msg, IFLA_NET_NS_FD, netns_fd);
+       return system_rtnl_call(msg);
+}
+
 static int system_link_del(const char *ifname)
 {
        struct nl_msg *msg;
@@ -1250,6 +1295,20 @@ int system_macvlan_del(struct device *macvlan)
        return system_link_del(macvlan->ifname);
 }
 
+int system_netns_open(const pid_t target_ns)
+{
+       char pid_net_path[PATH_MAX];
+
+       snprintf(pid_net_path, sizeof(pid_net_path), "/proc/%u/ns/net", target_ns);
+
+       return open(pid_net_path, O_RDONLY);
+}
+
+int system_netns_set(int netns_fd)
+{
+       return setns(netns_fd, CLONE_NEWNET);
+}
+
 int system_veth_add(struct device *veth, struct veth_config *cfg)
 {
        struct nl_msg *msg;
@@ -1656,7 +1715,10 @@ int system_if_check(struct device *dev)
        nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, cb_if_check_ack, &chk);
        nl_cb_err(cb, NL_CB_CUSTOM, cb_if_check_error, &chk);
 
-       nl_send_auto_complete(sock_rtnl, msg);
+       ret = nl_send_auto_complete(sock_rtnl, msg);
+       if (ret < 0)
+               goto free;
+
        while (chk.pending > 0)
                nl_recvmsgs(sock_rtnl, cb);
 
@@ -2367,7 +2429,7 @@ time_t system_get_rtime(void)
        struct timespec ts;
        struct timeval tv;
 
-       if (syscall(__NR_clock_gettime, CLOCK_MONOTONIC, &ts) == 0)
+       if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0)
                return ts.tv_sec;
 
        if (gettimeofday(&tv, NULL) == 0)