[lxc-devel] [RFC 2/2] lxc-user-nic: rename nic inside container to desired name

Serge Hallyn serge.hallyn at ubuntu.com
Tue Nov 5 20:15:00 UTC 2013


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 <serge.hallyn at ubuntu.com>
---
 src/lxc/conf.c         |   2 +-
 src/lxc/lxc_user_nic.c | 168 +++++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 158 insertions(+), 12 deletions(-)

diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index afdaa14..5e1e18d 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 bc1c268..dc35e55 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 <stdio.h>
 #include <stdlib.h>
 #include <stdbool.h>
@@ -27,6 +28,7 @@
 #include <sys/file.h>
 #include <alloca.h>
 #include <string.h>
+#include <sched.h>
 #include <sys/mman.h>
 #include <sys/socket.h>
 #include <errno.h>
@@ -39,9 +41,13 @@
 #include <net/if_arp.h>
 #include <netinet/in.h>
 #include <linux/if_bridge.h>
+#include <linux/netlink.h>
 #include <linux/rtnetlink.h>
 #include <linux/sockios.h>
+#include <sys/param.h>
+#include <sched.h>
 #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);
 }
-- 
1.8.4.2





More information about the lxc-devel mailing list