From d93126d3b91ea9e3831b9fd0b5318989f82daebe Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Mon, 13 Apr 2020 20:03:35 +0100 Subject: [PATCH] interface: allow renaming interface when moving to jail netns Introduce jail_ifname option to define the name of a Linux network interface when moved into a jail's network namespace. This is useful for containers which expect the network interface to have a specific name (eg. 'host0' in case of systemd). While at it, clean-up and fix bugs in jail interface up/down routines. Signed-off-by: Daniel Golle --- interface.c | 57 +++++++++++++++++++++++++++++++++++++++----------- interface.h | 1 + system-dummy.c | 4 ++-- system-linux.c | 17 ++++++++++----- system.h | 2 +- ubus.c | 3 +++ 6 files changed, 64 insertions(+), 20 deletions(-) diff --git a/interface.c b/interface.c index 51f6e51..2c883d8 100644 --- a/interface.c +++ b/interface.c @@ -34,6 +34,7 @@ enum { IFACE_ATTR_PROTO, IFACE_ATTR_AUTO, IFACE_ATTR_JAIL, + IFACE_ATTR_JAIL_IFNAME, IFACE_ATTR_DEFAULTROUTE, IFACE_ATTR_PEERDNS, IFACE_ATTR_DNS, @@ -58,6 +59,7 @@ static const struct blobmsg_policy iface_attrs[IFACE_ATTR_MAX] = { [IFACE_ATTR_IFNAME] = { .name = "ifname", .type = BLOBMSG_TYPE_STRING }, [IFACE_ATTR_AUTO] = { .name = "auto", .type = BLOBMSG_TYPE_BOOL }, [IFACE_ATTR_JAIL] = { .name = "jail", .type = BLOBMSG_TYPE_STRING }, + [IFACE_ATTR_JAIL_IFNAME] = { .name = "jail_ifname", .type = BLOBMSG_TYPE_STRING }, [IFACE_ATTR_DEFAULTROUTE] = { .name = "defaultroute", .type = BLOBMSG_TYPE_BOOL }, [IFACE_ATTR_PEERDNS] = { .name = "peerdns", .type = BLOBMSG_TYPE_BOOL }, [IFACE_ATTR_METRIC] = { .name = "metric", .type = BLOBMSG_TYPE_INT32 }, @@ -686,6 +688,8 @@ interface_do_free(struct interface *iface) avl_delete(&interfaces.avl, &iface->node.avl); if (iface->jail) free(iface->jail); + if (iface->jail_ifname) + free(iface->jail_ifname); free(iface); } @@ -900,6 +904,10 @@ interface_alloc(const char *name, struct blob_attr *config, bool dynamic) iface->autostart = false; } + iface->jail_ifname = NULL; + if ((cur = tb[IFACE_ATTR_JAIL_IFNAME])) + iface->jail_ifname = strdup(blobmsg_get_string(cur)); + return iface; } @@ -1163,24 +1171,41 @@ interface_start_jail(const char *jail, const pid_t netns_pid) if (!iface->jail || strcmp(iface->jail, jail)) continue; - system_link_netns_move(iface->ifname, netns_fd); + system_link_netns_move(iface->ifname, netns_fd, iface->jail_ifname); } + close(netns_fd); + pr = fork(); if (pr) { waitpid(pr, &wstatus, WUNTRACED | WCONTINUED); - close(netns_fd); return; } + /* child process */ + netns_fd = system_netns_open(netns_pid); + if (netns_fd < 0) + return; + system_netns_set(netns_fd); system_init(); vlist_for_each_element(&interfaces, iface, node) { if (!iface->jail || strcmp(iface->jail, jail)) continue; + /* + * The interface has already been renamed and is inside target + * namespace, hence overwrite ifname with jail_ifname for + * interface_set_up(). + * We are inside a fork which got it's own copy of the interfaces + * list, so we can mess with it :) + */ + iface->ifname = iface->jail_ifname; + interface_do_reload(iface); interface_set_up(iface); } + + close(netns_fd); _exit(0); } @@ -1190,23 +1215,23 @@ interface_stop_jail(const char *jail, const pid_t netns_pid) struct interface *iface; int netns_fd, root_netns; int wstatus; + pid_t parent_pid = getpid(); pid_t pr = 0; - netns_fd = system_netns_open(netns_pid); - if (netns_fd < 0) + pr = fork(); + if (pr) { + waitpid(pr, &wstatus, WUNTRACED | WCONTINUED); return; + } - root_netns = system_netns_open(getpid()); + /* child process */ + root_netns = system_netns_open(parent_pid); if (root_netns < 0) return; - pr = fork(); - if (pr) { - waitpid(pr, &wstatus, WUNTRACED | WCONTINUED); - close(netns_fd); - close(root_netns); + netns_fd = system_netns_open(netns_pid); + if (netns_fd < 0) return; - } system_netns_set(netns_fd); system_init(); @@ -1215,8 +1240,11 @@ interface_stop_jail(const char *jail, const pid_t netns_pid) continue; interface_set_down(iface); - system_link_netns_move(iface->ifname, root_netns); + system_link_netns_move(iface->jail_ifname, root_netns, iface->ifname); } + + close(root_netns); + close(netns_fd); _exit(0); } @@ -1335,6 +1363,11 @@ interface_change_config(struct interface *if_old, struct interface *if_new) if (if_old->jail) if_old->autostart = false; + if (if_old->jail_ifname) + free(if_old->jail_ifname); + + if_old->jail_ifname = if_new->jail_ifname; + if_old->ifname = if_new->ifname; if_old->parent_ifname = if_new->parent_ifname; if_old->dynamic = if_new->dynamic; diff --git a/interface.h b/interface.h index cd09812..3d58aa3 100644 --- a/interface.h +++ b/interface.h @@ -109,6 +109,7 @@ struct interface { const char *name; const char *ifname; char *jail; + char *jail_ifname; int netns_fd; bool available; diff --git a/system-dummy.c b/system-dummy.c index 9c37bd5..2669a34 100644 --- a/system-dummy.c +++ b/system-dummy.c @@ -54,9 +54,9 @@ int system_bridge_delif(struct device *bridge, struct device *dev) return 0; } -int system_link_netns_move(const char *ifname, int netns_fd) +int system_link_netns_move(const char *ifname, int netns_fd, const char *target_ifname) { - D(SYSTEM, "ip link %s netns %d\n", ifname, netns_fd); + D(SYSTEM, "ip link dev %s name %s netns %d\n", ifname, target_ifname, netns_fd); return 0; } diff --git a/system-linux.c b/system-linux.c index 775b448..04b9bdf 100644 --- a/system-linux.c +++ b/system-linux.c @@ -843,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; @@ -1246,21 +1251,23 @@ nla_put_failure: return -ENOMEM; } -int system_link_netns_move(const char *ifname, int netns_fd) +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, - .ifi_index = 0, }; + 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); - nla_put_string(msg, IFLA_IFNAME, ifname); + 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); } diff --git a/system.h b/system.h index fe4497e..f8f8ec1 100644 --- a/system.h +++ b/system.h @@ -243,7 +243,7 @@ void system_fd_set_cloexec(int fd); int system_update_ipv6_mtu(struct device *dev, int mtu); -int system_link_netns_move(const char *ifname, const pid_t target_ns); +int system_link_netns_move(const char *ifname, const pid_t target_ns, const char *target_ifname); int system_netns_open(const pid_t target_ns); int system_netns_set(int netns_fd); diff --git a/ubus.c b/ubus.c index 85d834d..15c826b 100644 --- a/ubus.c +++ b/ubus.c @@ -765,6 +765,9 @@ netifd_dump_status(struct interface *iface) if (iface->jail) blobmsg_add_string(&b, "jail", iface->jail); + if (iface->jail_ifname) + blobmsg_add_string(&b, "jail_ifname", iface->jail_ifname); + if (iface->state == IFS_UP) { if (iface->updated) { a = blobmsg_open_array(&b, "updated"); -- 2.25.1