From: Serge Hallyn Date: Fri, 25 Oct 2013 23:03:57 +0000 (-0500) Subject: lxc-user-nic: rename nic inside container to desired name X-Git-Tag: lxc-1.0.0.alpha3~18 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4119204eef01e68c603f0f432b5ce653ea219eea;p=thirdparty%2Flxc.git lxc-user-nic: rename nic inside container to desired name To do so we do a quick setns into the container's netns. This (unexpectedly) turns out cleaner than trying to rename it from lxc_setup(), because we don't know the original nic name in the container until we created it which we do in the parent after the init has been cloned. Signed-off-by: Serge Hallyn Acked-by: Stéphane Graber --- diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 1db2f3e39..a75673126 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -2761,7 +2761,7 @@ int unpriv_assign_nic(struct lxc_netdev *netdev, pid_t pid) // Call lxc-user-nic pid type bridge char pidstr[20]; - char *args[] = { "lxc-user-nic", pidstr, "veth", netdev->link, NULL }; + char *args[] = { "lxc-user-nic", pidstr, "veth", netdev->link, netdev->name, NULL }; snprintf(pidstr, 19, "%lu", (unsigned long) pid); pidstr[19] = '\0'; execvp("lxc-user-nic", args); diff --git a/src/lxc/lxc_user_nic.c b/src/lxc/lxc_user_nic.c index bc1c26881..dc35e55d2 100644 --- a/src/lxc/lxc_user_nic.c +++ b/src/lxc/lxc_user_nic.c @@ -17,6 +17,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#define _GNU_SOURCE /* See feature_test_macros(7) */ #include #include #include @@ -27,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -39,9 +41,13 @@ #include #include #include +#include #include #include +#include +#include #include "config.h" +#include "utils.h" #ifndef HAVE_GETLINE #ifdef HAVE_FGETLN @@ -49,6 +55,19 @@ #endif #endif +/* Define setns() if missing from the C library */ +#ifndef HAVE_SETNS +static inline int setns(int fd, int nstype) +{ +#ifdef __NR_setns + return syscall(__NR_setns, fd, nstype); +#else + errno = ENOSYS; + return -1; +#endif +} +#endif + #if ISTEST #define CONF_FILE "/tmp/lxc-usernet" #define DB_FILE "/tmp/nics" @@ -94,7 +113,8 @@ void usage(char *me, bool fail) { - fprintf(stderr, "Usage: %s pid type bridge\n", me); + fprintf(stderr, "Usage: %s pid type bridge nicname\n", me); + fprintf(stderr, " nicname is the name to use inside the container\n"); exit(fail ? 1 : 0); } @@ -237,12 +257,13 @@ bool nic_exists(char *nic) return true; } -#if ! ISTEST struct link_req { struct nlmsg nlmsg; struct ifinfomsg ifinfomsg; }; +#if ! ISTEST + int lxc_veth_create(const char *name1, const char *name2) { struct nl_handler nlh; @@ -539,7 +560,7 @@ int lxc_netdev_delete_by_name(const char *name) #endif -bool create_nic(char *nic, char *br, char *pidstr) +bool create_nic(char *nic, char *br, char *pidstr, char **cnic) { #if ISTEST char path[200]; @@ -559,7 +580,7 @@ bool create_nic(char *nic, char *br, char *pidstr) ret = snprintf(veth1buf, IFNAMSIZ, "%s", nic); if (ret < 0 || ret >= IFNAMSIZ) { - fprintf(stderr, "nic name too long\n"); + fprintf(stderr, "host nic name too long\n"); exit(1); } @@ -581,6 +602,7 @@ bool create_nic(char *nic, char *br, char *pidstr) fprintf(stderr, "Error moving %s to netns %d\n", veth2buf, pid); goto out_del; } + *cnic = strdup(veth2buf); return true; out_del: @@ -589,14 +611,19 @@ out_del: #endif } -void get_new_nicname(char **dest, char *br, char *pid) +/* + * Get a new nic. + * *dest will container the name (lxcuser-%d) which is attached + * on the host to the lxc bridge + */ +void get_new_nicname(char **dest, char *br, char *pid, char **cnic) { int i = 0; // TODO - speed this up. For large installations we won't // want n stats for every nth container startup. while (1) { sprintf(*dest, "lxcuser-%d", i); - if (!nic_exists(*dest) && create_nic(*dest, br, pid)) + if (!nic_exists(*dest) && create_nic(*dest, br, pid, cnic)) return; i++; } @@ -672,7 +699,7 @@ int count_entries(char *buf, off_t len, char *me, char *t, char *br) * The dbfile has lines of the format: * user type bridge nicname */ -bool get_nic_if_avail(int fd, char *me, char *pid, char *intype, char *br, int allowed, char **nicname) +bool get_nic_if_avail(int fd, char *me, char *pid, char *intype, char *br, int allowed, char **nicname, char **cnic) { off_t len, slen; struct stat sb; @@ -696,7 +723,7 @@ bool get_nic_if_avail(int fd, char *me, char *pid, char *intype, char *br, int a } - get_new_nicname(nicname, br, pid); + get_new_nicname(nicname, br, pid, cnic); /* me ' ' intype ' ' br ' ' *nicname + '\n' + '\0' */ slen = strlen(me) + strlen(intype) + strlen(br) + strlen(*nicname) + 5; newline = alloca(slen); @@ -743,20 +770,134 @@ again: goto again; } +static int lxc_netdev_rename_by_index(int ifindex, const char *newname) +{ + struct nl_handler nlh; + struct nlmsg *nlmsg = NULL, *answer = NULL; + struct link_req *link_req; + int len, err; + + err = netlink_open(&nlh, NETLINK_ROUTE); + if (err) + return err; + + len = strlen(newname); + if (len == 1 || len >= IFNAMSIZ) + goto out; + + err = -ENOMEM; + nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); + if (!nlmsg) + goto out; + + answer = nlmsg_alloc(NLMSG_GOOD_SIZE); + if (!answer) + goto out; + + link_req = (struct link_req *)nlmsg; + link_req->ifinfomsg.ifi_family = AF_UNSPEC; + link_req->ifinfomsg.ifi_index = ifindex; + nlmsg->nlmsghdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + nlmsg->nlmsghdr.nlmsg_flags = NLM_F_ACK|NLM_F_REQUEST; + nlmsg->nlmsghdr.nlmsg_type = RTM_NEWLINK; + + if (nla_put_string(nlmsg, IFLA_IFNAME, newname)) + goto out; + + err = netlink_transaction(&nlh, nlmsg, answer); +out: + netlink_close(&nlh); + nlmsg_free(answer); + nlmsg_free(nlmsg); + return err; +} + +static int lxc_netdev_rename_by_name(const char *oldname, const char *newname) +{ + int len, index; + + len = strlen(oldname); + if (len == 1 || len >= IFNAMSIZ) + return -EINVAL; + + index = if_nametoindex(oldname); + if (!index) { + fprintf(stderr, "Error getting ifindex for %s\n", oldname); + return -EINVAL; + } + + return lxc_netdev_rename_by_index(index, newname); +} + +int rename_in_ns(int pid, char *oldname, char *newname) +{ + char nspath[MAXPATHLEN]; + int fd = -1, ofd = -1, ret; + + ret = snprintf(nspath, MAXPATHLEN, "/proc/%d/ns/net", getpid()); + if (ret < 0 || ret >= MAXPATHLEN) + return -1; + if ((ofd = open(nspath, O_RDONLY)) < 0) { + fprintf(stderr, "Opening %s\n", nspath); + return -1; + } + ret = snprintf(nspath, MAXPATHLEN, "/proc/%d/ns/net", pid); + if (ret < 0 || ret >= MAXPATHLEN) + goto out_err; + + if ((fd = open(nspath, O_RDONLY)) < 0) { + fprintf(stderr, "Opening %s\n", nspath); + goto out_err; + } + if (setns(fd, 0) < 0) { + fprintf(stderr, "setns to container network namespace\n"); + goto out_err; + } + close(fd); fd = -1; + if ((ret = lxc_netdev_rename_by_name(oldname, newname)) < 0) { + fprintf(stderr, "Error %d renaming netdev %s to %s in container\n", ret, oldname, newname); + goto out_err; + } + if (setns(ofd, 0) < 0) { + fprintf(stderr, "Error returning to original netns\n"); + close(ofd); + return -1; + } + close(ofd); + + return 0; + +out_err: + if (ofd >= 0) + close(ofd); + if (setns(ofd, 0) < 0) + fprintf(stderr, "Error returning to original network namespace\n"); + if (fd >= 0) + close(fd); + return -1; +} + int main(int argc, char *argv[]) { int n, fd; bool gotone = false; char *me, *buf = alloca(400); char *nicname = alloca(40); + char *cnic; // created nic name in container is returned here. + char *vethname; + int pid; if ((me = get_username(&buf)) == NULL) { fprintf(stderr, "Failed to get username\n"); exit(1); } - if (argc != 4) + if (argc < 4) usage(argv[0], true); + if (argc >= 5) + vethname = argv[4]; + else + vethname = "eth0"; if (!create_db_dir(DB_FILE)) { fprintf(stderr, "Failed to create directory for db file\n"); @@ -770,14 +911,19 @@ int main(int argc, char *argv[]) n = get_alloted(me, argv[2], argv[3]); if (n > 0) - gotone = get_nic_if_avail(fd, me, argv[1], argv[2], argv[3], n, &nicname); + gotone = get_nic_if_avail(fd, me, argv[1], argv[2], argv[3], n, &nicname, &cnic); close(fd); if (!gotone) { fprintf(stderr, "Quota reached\n"); exit(1); } - // Now create the link + pid = atoi(argv[1]); + // Now rename the link + if (rename_in_ns(pid, cnic, vethname) < 0) { + fprintf(stderr, "Failed to rename the link\n"); + exit(1); + } exit(0); }