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

Stéphane Graber stgraber at ubuntu.com
Tue Nov 5 20:26:54 UTC 2013


On Tue, Nov 05, 2013 at 02:15:00PM -0600, Serge Hallyn wrote:
> 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>

I think it may make sense to centralize that setns definition in
something like namespace.h and the new netlinks function may make sense
in network.c, but we can always do that later.

Acked-by: Stéphane Graber <stgraber 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
> 
> 
> ------------------------------------------------------------------------------
> November Webinars for C, C++, Fortran Developers
> Accelerate application performance with scalable programming models. Explore
> techniques for threading, error checking, porting, and tuning. Get the most 
> from the latest Intel processors and coprocessors. See abstracts and register
> http://pubads.g.doubleclick.net/gampad/clk?id=60136231&iu=/4140/ostg.clktrk
> _______________________________________________
> Lxc-devel mailing list
> Lxc-devel at lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/lxc-devel

-- 
Stéphane Graber
Ubuntu developer
http://www.ubuntu.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20131105/2d778d00/attachment.pgp>


More information about the lxc-devel mailing list