#include <stdbool.h>
#include <sys/types.h>
#include <pwd.h>
+#include <grp.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/file.h>
#include <linux/rtnetlink.h>
#include <linux/sockios.h>
#include <sys/param.h>
-#include <sched.h>
+
#include "config.h"
#include "utils.h"
+#include "network.h"
-#if ISTEST
-#define CONF_FILE "/tmp/lxc-usernet"
-#define DB_FILE "/tmp/nics"
-#else
-#define CONF_FILE LXC_USERNIC_CONF
-#define DB_FILE LXC_USERNIC_DB
-#endif
-
-#include "nl.h"
-
-#ifndef IFLA_LINKMODE
-# define IFLA_LINKMODE 17
-#endif
-
-#ifndef IFLA_LINKINFO
-# define IFLA_LINKINFO 18
-#endif
-
-#ifndef IFLA_NET_NS_PID
-# define IFLA_NET_NS_PID 19
-#endif
-
-#ifndef IFLA_INFO_KIND
-# define IFLA_INFO_KIND 1
-#endif
-
-#ifndef IFLA_VLAN_ID
-# define IFLA_VLAN_ID 1
-#endif
-
-#ifndef IFLA_INFO_DATA
-# define IFLA_INFO_DATA 2
-#endif
-
-#ifndef VETH_INFO_PEER
-# define VETH_INFO_PEER 1
-#endif
-
-#ifndef IFLA_MACVLAN_MODE
-# define IFLA_MACVLAN_MODE 1
-#endif
-
-void usage(char *me, bool fail)
+static void usage(char *me, bool fail)
{
fprintf(stderr, "Usage: %s pid type bridge nicname\n", me);
fprintf(stderr, " nicname is the name to use inside the container\n");
return pwd->pw_name;
}
+static void free_groupnames(char **groupnames)
+{
+ int i;
+ if (!groupnames)
+ return;
+ for (i = 0; groupnames[i]; i++)
+ free(groupnames[i]);
+ free(groupnames);
+}
+
+static char **get_groupnames(void)
+{
+ int ngroups;
+ gid_t *group_ids;
+ int ret, i;
+ char **groupnames;
+ struct group *gr;
+
+ ngroups = getgroups(0, NULL);
+
+ if (ngroups == -1) {
+ fprintf(stderr, "Failed to get number of groups user belongs to: %s\n", strerror(errno));
+ return NULL;
+ }
+ if (ngroups == 0)
+ return NULL;
+
+ group_ids = (gid_t *)malloc(sizeof(gid_t)*ngroups);
+
+ if (group_ids == NULL) {
+ fprintf(stderr, "Out of memory while getting groups the user belongs to\n");
+ return NULL;
+ }
+
+ ret = getgroups(ngroups, group_ids);
+
+ if (ret < 0) {
+ free(group_ids);
+ fprintf(stderr, "Failed to get process groups: %s\n", strerror(errno));
+ return NULL;
+ }
+
+ groupnames = (char **)malloc(sizeof(char *)*(ngroups+1));
+
+ if (groupnames == NULL) {
+ free(group_ids);
+ fprintf(stderr, "Out of memory while getting group names\n");
+ return NULL;
+ }
+
+ memset(groupnames, 0, sizeof(char *)*(ngroups+1));
+
+ for (i=0; i<ngroups; i++ ) {
+ gr = getgrgid(group_ids[i]);
+
+ if (gr == NULL) {
+ fprintf(stderr, "Failed to get group name\n");
+ free(group_ids);
+ free_groupnames(groupnames);
+ return NULL;
+ }
+
+ groupnames[i] = strdup(gr->gr_name);
+
+ if (groupnames[i] == NULL) {
+ fprintf(stderr, "Failed to copy group name: %s", gr->gr_name);
+ free(group_ids);
+ free_groupnames(groupnames);
+ return NULL;
+ }
+ }
+
+ free(group_ids);
+
+ return groupnames;
+}
+
+static bool name_is_in_groupnames(char *name, char **groupnames)
+{
+ while (groupnames != NULL) {
+ if (strcmp(name, *groupnames) == 0)
+ return true;
+ groupnames++;
+ }
+ return false;
+}
+
+struct alloted_s {
+ char *name;
+ int allowed;
+ struct alloted_s *next;
+};
+
+static struct alloted_s *append_alloted(struct alloted_s **head, char *name, int n)
+{
+ struct alloted_s *cur, *al;
+
+ if (head == NULL || name == NULL) {
+ // sanity check. parameters should not be null
+ fprintf(stderr, "NULL parameters to append_alloted not allowed\n");
+ return NULL;
+ }
+
+ al = (struct alloted_s *)malloc(sizeof(struct alloted_s));
+
+ if (al == NULL) {
+ // unable to allocate memory to new struct
+ fprintf(stderr, "Out of memory in append_alloted\n");
+ return NULL;
+ }
+
+ al->name = strdup(name);
+
+ if (al->name == NULL) {
+ free(al);
+ return NULL;
+ }
+
+ al->allowed = n;
+ al->next = NULL;
+
+ if (*head == NULL) {
+ *head = al;
+ return al;
+ }
+
+ cur = *head;
+ while (cur->next != NULL)
+ cur = cur->next;
+
+ cur->next = al;
+ return al;
+}
+
+static void free_alloted(struct alloted_s **head)
+{
+ struct alloted_s *cur;
+
+ if (head == NULL) {
+ return;
+ }
+
+ cur = *head;
+
+ while (cur != NULL) {
+ cur = cur->next;
+ free((*head)->name);
+ free(*head);
+ *head = cur;
+ }
+}
+
/* The configuration file consists of lines of the form:
*
* user type bridge count
+ * or
+ * @group type bridge count
*
* Return the count entry for the calling user if there is one. Else
* return -1.
*/
-static int get_alloted(char *me, char *intype, char *link)
+static int get_alloted(char *me, char *intype, char *link, struct alloted_s **alloted)
{
- FILE *fin = fopen(CONF_FILE, "r");
+ FILE *fin = fopen(LXC_USERNIC_CONF, "r");
char *line = NULL;
- char user[100], type[100], br[100];
+ char name[100], type[100], br[100];
size_t len = 0;
- int n = -1, ret;
+ int n, ret, count = 0;
+ char **groups;
if (!fin) {
- fprintf(stderr, "Failed to open %s: %s\n", CONF_FILE,
+ fprintf(stderr, "Failed to open %s: %s\n", LXC_USERNIC_CONF,
strerror(errno));
return -1;
}
+ groups = get_groupnames();
while ((getline(&line, &len, fin)) != -1) {
- ret = sscanf(line, "%99[^ \t] %99[^ \t] %99[^ \t] %d", user, type, br, &n);
+ ret = sscanf(line, "%99[^ \t] %99[^ \t] %99[^ \t] %d", name, type, br, &n);
if (ret != 4)
continue;
- if (strcmp(user, me) != 0)
+
+ if (strlen(name) == 0)
continue;
+
+ if (strcmp(name, me) != 0)
+ {
+ if (name[0] != '@')
+ continue;
+ if (!name_is_in_groupnames(name+1, groups))
+ continue;
+ }
if (strcmp(type, intype) != 0)
continue;
if (strcmp(link, br) != 0)
continue;
- free(line);
- fclose(fin);
- return n;
+
+ /* found the user or group with the appropriate settings, therefore finish the search.
+ * what to do if there are more than one applicable lines? not specified in the docs.
+ * since getline is implemented with realloc, we don't need to free line until exiting func.
+ *
+ * if append_alloted returns NULL, e.g. due to a malloc error, we set count to 0 and break the loop,
+ * allowing cleanup and then exiting from main()
+ */
+ if (append_alloted(alloted, name, n) == NULL) {
+ count = 0;
+ break;
+ }
+ count += n;
}
+
+ free_groupnames(groups);
fclose(fin);
- if (line)
- free(line);
- return -1;
+ free(line);
+
+ // now return the total number of nics that this user can create
+ return count;
}
static char *get_eol(char *s, char *e)
static char *find_line(char *p, char *e, char *u, char *t, char *l)
{
char *p1, *p2, *ret;
-
+
while (p<e && (p1 = get_eol(p, e)) < e) {
ret = p;
if (*p == '#')
int ret;
struct stat sb;
-#if ISTEST
- ret = snprintf(path, MAXPATHLEN, "/tmp/lxcnettest/%s", nic);
-#else
+ if (strcmp(nic, "none") == 0)
+ return true;
ret = snprintf(path, MAXPATHLEN, "/sys/class/net/%s", nic);
-#endif
if (ret < 0 || ret >= MAXPATHLEN) // should never happen!
- return true;
+ return false;
ret = stat(path, &sb);
if (ret != 0)
return false;
return true;
}
-struct link_req {
- struct nlmsg nlmsg;
- struct ifinfomsg ifinfomsg;
-};
-
-#if ! ISTEST
-
-static int lxc_veth_create(const char *name1, const char *name2)
-{
- struct nl_handler nlh;
- struct nlmsg *nlmsg = NULL, *answer = NULL;
- struct link_req *link_req;
- struct rtattr *nest1, *nest2, *nest3;
- int len, err;
-
- err = netlink_open(&nlh, NETLINK_ROUTE);
- if (err)
- return err;
-
- err = -EINVAL;
- len = strlen(name1);
- if (len == 1 || len >= IFNAMSIZ)
- goto out;
-
- len = strlen(name2);
- 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;
- nlmsg->nlmsghdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
- nlmsg->nlmsghdr.nlmsg_flags =
- NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL|NLM_F_ACK;
- nlmsg->nlmsghdr.nlmsg_type = RTM_NEWLINK;
-
- err = -EINVAL;
- nest1 = nla_begin_nested(nlmsg, IFLA_LINKINFO);
- if (!nest1)
- goto out;
-
- if (nla_put_string(nlmsg, IFLA_INFO_KIND, "veth"))
- goto out;
-
- nest2 = nla_begin_nested(nlmsg, IFLA_INFO_DATA);
- if (!nest2)
- goto out;
-
- nest3 = nla_begin_nested(nlmsg, VETH_INFO_PEER);
- if (!nest3)
- goto out;
-
- nlmsg->nlmsghdr.nlmsg_len += sizeof(struct ifinfomsg);
-
- if (nla_put_string(nlmsg, IFLA_IFNAME, name2))
- goto out;
-
- nla_end_nested(nlmsg, nest3);
-
- nla_end_nested(nlmsg, nest2);
-
- nla_end_nested(nlmsg, nest1);
-
- if (nla_put_string(nlmsg, IFLA_IFNAME, name1))
- goto out;
-
- err = netlink_transaction(&nlh, nlmsg, answer);
-out:
- netlink_close(&nlh);
- nlmsg_free(answer);
- nlmsg_free(nlmsg);
- return err;
-}
-
-static int lxc_netdev_move(char *ifname, pid_t pid)
-{
- struct nl_handler nlh;
- struct nlmsg *nlmsg = NULL;
- struct link_req *link_req;
- int err, index;
-
- index = if_nametoindex(ifname);
- if (!ifname)
- return -EINVAL;
-
- err = netlink_open(&nlh, NETLINK_ROUTE);
- if (err)
- return err;
-
- err = -ENOMEM;
- nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE);
- if (!nlmsg)
- goto out;
-
- link_req = (struct link_req *)nlmsg;
- link_req->ifinfomsg.ifi_family = AF_UNSPEC;
- link_req->ifinfomsg.ifi_index = index;
- nlmsg->nlmsghdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
- nlmsg->nlmsghdr.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK;
- nlmsg->nlmsghdr.nlmsg_type = RTM_NEWLINK;
-
- if (nla_put_u32(nlmsg, IFLA_NET_NS_PID, pid))
- goto out;
-
- err = netlink_transaction(&nlh, nlmsg, nlmsg);
-out:
- netlink_close(&nlh);
- nlmsg_free(nlmsg);
- return err;
-}
-
-static int setup_private_host_hw_addr(char *veth1)
-{
- struct ifreq ifr;
- int err;
- int sockfd;
-
- sockfd = socket(AF_INET, SOCK_DGRAM, 0);
- if (sockfd < 0)
- return -errno;
-
- snprintf((char *)ifr.ifr_name, IFNAMSIZ, "%s", veth1);
- err = ioctl(sockfd, SIOCGIFHWADDR, &ifr);
- if (err < 0) {
- close(sockfd);
- return -errno;
- }
-
- ifr.ifr_hwaddr.sa_data[0] = 0xfe;
- err = ioctl(sockfd, SIOCSIFHWADDR, &ifr);
- close(sockfd);
- if (err < 0)
- return -errno;
-
- return 0;
-}
-
-static int netdev_set_flag(const char *name, int flag)
-{
- struct nl_handler nlh;
- struct nlmsg *nlmsg = NULL, *answer = NULL;
- struct link_req *link_req;
- int index, len, err;
-
- err = netlink_open(&nlh, NETLINK_ROUTE);
- if (err)
- return err;
-
- err = -EINVAL;
- len = strlen(name);
- 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;
-
- err = -EINVAL;
- index = if_nametoindex(name);
- if (!index)
- goto out;
-
- link_req = (struct link_req *)nlmsg;
- link_req->ifinfomsg.ifi_family = AF_UNSPEC;
- link_req->ifinfomsg.ifi_index = index;
- link_req->ifinfomsg.ifi_change |= IFF_UP;
- link_req->ifinfomsg.ifi_flags |= flag;
- nlmsg->nlmsghdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
- nlmsg->nlmsghdr.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK;
- nlmsg->nlmsghdr.nlmsg_type = RTM_NEWLINK;
-
- err = netlink_transaction(&nlh, nlmsg, answer);
-out:
- netlink_close(&nlh);
- nlmsg_free(nlmsg);
- nlmsg_free(answer);
- return err;
-}
-
-static int instanciate_veth(char *n1, char **n2)
+static int instantiate_veth(char *n1, char **n2)
{
int err;
* of a container */
err = setup_private_host_hw_addr(n1);
if (err) {
- fprintf(stderr, "failed to change mac address of host interface '%s' : %s",
+ fprintf(stderr, "failed to change mac address of host interface '%s' : %s\n",
n1, strerror(-err));
}
return netdev_set_flag(n1, IFF_UP);
}
-static int lxc_bridge_attach(const char *bridge, const char *ifname)
-{
- int fd, index, err;
- struct ifreq ifr;
-
- if (strlen(ifname) >= IFNAMSIZ)
- return -EINVAL;
-
- index = if_nametoindex(ifname);
- if (!index)
- return -EINVAL;
-
- fd = socket(AF_INET, SOCK_STREAM, 0);
- if (fd < 0)
- return -errno;
-
- strncpy(ifr.ifr_name, bridge, IFNAMSIZ-1);
- ifr.ifr_name[IFNAMSIZ-1] = '\0';
- ifr.ifr_ifindex = index;
- err = ioctl(fd, SIOCBRADDIF, &ifr);
- close(fd);
- if (err)
- err = -errno;
-
- return err;
-}
-
-static int lxc_netdev_delete_by_index(int ifindex)
-{
- struct nl_handler nlh;
- struct nlmsg *nlmsg = NULL, *answer = NULL;
- struct link_req *link_req;
- int err;
-
- err = netlink_open(&nlh, NETLINK_ROUTE);
- if (err)
- return err;
-
- 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_DELLINK;
-
- err = netlink_transaction(&nlh, nlmsg, answer);
-out:
- netlink_close(&nlh);
- nlmsg_free(answer);
- nlmsg_free(nlmsg);
- return err;
-}
-
-static int lxc_netdev_delete_by_name(const char *name)
+static int get_mtu(char *name)
{
- int index;
-
- index = if_nametoindex(name);
- if (!index)
- return -EINVAL;
-
- return lxc_netdev_delete_by_index(index);
-}
-#else
-static int lxc_netdev_delete_by_name(const char *name)
-{
- char path[200];
- sprintf(path, "/tmp/lxcnettest/%s", name);
- return unlink(path);
+ int idx = if_nametoindex(name);
+ return netdev_get_mtu(idx);
}
-#endif
-
static bool create_nic(char *nic, char *br, int pid, char **cnic)
{
-#if ISTEST
- char path[200];
- sprintf(path, "/tmp/lxcnettest/%s", nic);
- int fd = open(path, O_RDWR|O_CREAT, S_IWUSR | S_IRUSR);
- if (fd < 0)
- return false;
- close(fd);
- return true;
-#else
char *veth1buf, *veth2buf;
veth1buf = alloca(IFNAMSIZ);
veth2buf = alloca(IFNAMSIZ);
- int ret;
+ int ret, mtu;
ret = snprintf(veth1buf, IFNAMSIZ, "%s", nic);
if (ret < 0 || ret >= IFNAMSIZ) {
}
/* create the nics */
- if (instanciate_veth(veth1buf, &veth2buf) < 0) {
+ if (instantiate_veth(veth1buf, &veth2buf) < 0) {
fprintf(stderr, "Error creating veth tunnel\n");
return false;
}
- /* attach veth1 to bridge */
- if (lxc_bridge_attach(br, veth1buf) < 0) {
- fprintf(stderr, "Error attaching %s to %s\n", veth1buf, br);
- goto out_del;
+ if (strcmp(br, "none") != 0) {
+ /* copy the bridge's mtu to both ends */
+ mtu = get_mtu(br);
+ if (mtu != -1) {
+ if (lxc_netdev_set_mtu(veth1buf, mtu) < 0 ||
+ lxc_netdev_set_mtu(veth2buf, mtu) < 0) {
+ fprintf(stderr, "Failed setting mtu\n");
+ goto out_del;
+ }
+ }
+
+ /* attach veth1 to bridge */
+ if (lxc_bridge_attach(br, veth1buf) < 0) {
+ fprintf(stderr, "Error attaching %s to %s\n", veth1buf, br);
+ goto out_del;
+ }
}
/* pass veth2 to target netns */
- ret = lxc_netdev_move(veth2buf, pid);
+ ret = lxc_netdev_move_by_name(veth2buf, pid, NULL);
if (ret < 0) {
fprintf(stderr, "Error moving %s to netns %d\n", veth2buf, pid);
goto out_del;
out_del:
lxc_netdev_delete_by_name(veth1buf);
return false;
-#endif
}
/*
* Get a new nic.
- * *dest will container the name (lxcuser-%d) which is attached
+ * *dest will container the name (vethXXXXXX) which is attached
* on the host to the lxc bridge
*/
-static void get_new_nicname(char **dest, char *br, int pid, char **cnic)
+static bool get_new_nicname(char **dest, char *br, int 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, cnic))
- return;
- i++;
+ char template[IFNAMSIZ];
+ snprintf(template, sizeof(template), "vethXXXXXX");
+ *dest = lxc_mkifname(template);
+
+ if (!create_nic(*dest, br, pid, cnic)) {
+ return false;
}
+ return true;
}
static bool get_nic_from_line(char *p, char **nic)
nic = alloca(100);
- fstat(fd, &sb);
+ if (fstat(fd, &sb) < 0) {
+ fprintf(stderr, "Failed to fstat: %s\n", strerror(errno));
+ return false;
+ }
len = sb.st_size;
if (len == 0)
return true;
p = buf;
e = buf + len;
while ((p = find_line(p, e, me, t, br)) != NULL) {
- struct entry_line *newe = realloc(entry_lines, n+1);
+ struct entry_line *newe = realloc(entry_lines, sizeof(*entry_lines)*(n+1));
if (!newe) {
free(entry_lines);
return false;
p += entry_lines[n-1].len + 1;
if (p >= e)
break;
- }
+ }
p = buf;
for (i=0; i<n; i++) {
if (!entry_lines[i].keep)
* The dbfile has lines of the format:
* user type bridge nicname
*/
-static bool get_nic_if_avail(int fd, char *me, int pid, char *intype, char *br, int allowed, char **nicname, char **cnic)
+static bool get_nic_if_avail(int fd, struct alloted_s *names, int pid, char *intype, char *br, int allowed, char **nicname, char **cnic)
{
off_t len, slen;
struct stat sb;
char *buf = NULL, *newline;
int ret, count = 0;
+ char *owner;
+ struct alloted_s *n;
- cull_entries(fd, me, intype, br);
+ for (n=names; n!=NULL; n=n->next)
+ cull_entries(fd, n->name, intype, br);
- fstat(fd, &sb);
+ if (allowed == 0)
+ return false;
+
+ owner = names->name;
+
+ if (fstat(fd, &sb) < 0) {
+ fprintf(stderr, "Failed to fstat: %s\n", strerror(errno));
+ return false;
+ }
len = sb.st_size;
if (len != 0) {
buf = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
return false;
}
- count = count_entries(buf, len, me, intype, br);
- if (count >= allowed)
- return false;
+ owner = NULL;
+ for (n=names; n!=NULL; n=n->next) {
+ count = count_entries(buf, len, n->name, intype, br);
+
+ if (count >= n->allowed)
+ continue;
+
+ owner = n->name;
+ break;
+ }
}
+ if (owner == NULL)
+ return false;
- get_new_nicname(nicname, br, pid, cnic);
- /* me ' ' intype ' ' br ' ' *nicname + '\n' + '\0' */
- slen = strlen(me) + strlen(intype) + strlen(br) + strlen(*nicname) + 5;
+ if (!get_new_nicname(nicname, br, pid, cnic))
+ return false;
+ /* owner ' ' intype ' ' br ' ' *nicname + '\n' + '\0' */
+ slen = strlen(owner) + strlen(intype) + strlen(br) + strlen(*nicname) + 5;
newline = alloca(slen);
- ret = snprintf(newline, slen, "%s %s %s %s\n", me, intype, br, *nicname);
+ ret = snprintf(newline, slen, "%s %s %s %s\n", owner, intype, br, *nicname);
if (ret < 0 || ret >= slen) {
if (lxc_netdev_delete_by_name(*nicname) != 0)
fprintf(stderr, "Error unlinking %s!\n", *nicname);
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);
-}
+#define VETH_DEF_NAME "eth%d"
-static int rename_in_ns(int pid, char *oldname, char *newname)
+static int rename_in_ns(int pid, char *oldname, char **newnamep)
{
char nspath[MAXPATHLEN];
- int fd = -1, ofd = -1, ret;
+ int fd = -1, ofd = -1, ret, ifindex = -1;
+ bool grab_newname = false;
ret = snprintf(nspath, MAXPATHLEN, "/proc/%d/ns/net", getpid());
if (ret < 0 || ret >= MAXPATHLEN)
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);
+ if (!*newnamep) {
+ grab_newname = true;
+ *newnamep = VETH_DEF_NAME;
+ if (!(ifindex = if_nametoindex(oldname))) {
+ fprintf(stderr, "failed to get netdev index\n");
+ goto out_err;
+ }
+ }
+ if ((ret = lxc_netdev_rename_by_name(oldname, *newnamep)) < 0) {
+ fprintf(stderr, "Error %d renaming netdev %s to %s in container\n", ret, oldname, *newnamep);
goto out_err;
}
+ if (grab_newname) {
+ char ifname[IFNAMSIZ], *namep = ifname;
+ if (!if_indextoname(ifindex, namep)) {
+ fprintf(stderr, "Failed to get new netdev name\n");
+ goto out_err;
+ }
+ *newnamep = strdup(namep);
+ if (!*newnamep)
+ goto out_err;
+ }
if (setns(ofd, 0) < 0) {
fprintf(stderr, "Error returning to original netns\n");
close(ofd);
/*
* If the caller (real uid, not effective uid) may read the
- * /proc/pid/net/ns, then it is either the caller's netns or one
+ * /proc/[pid]/ns/net, then it is either the caller's netns or one
* which it created.
*/
static bool may_access_netns(int pid)
char *me;
char *nicname = alloca(40);
char *cnic = NULL; // created nic name in container is returned here.
- char *vethname;
+ char *vethname = NULL;
int pid;
+ struct alloted_s *alloted = NULL;
+ /* set a sane env, because we are setuid-root */
+ if (clearenv() < 0) {
+ fprintf(stderr, "Failed to clear environment");
+ exit(1);
+ }
+ if (setenv("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 1) < 0) {
+ fprintf(stderr, "Failed to set PATH, exiting\n");
+ exit(1);
+ }
if ((me = get_username()) == NULL) {
fprintf(stderr, "Failed to get username\n");
exit(1);
usage(argv[0], true);
if (argc >= 5)
vethname = argv[4];
- else
- vethname = "eth0";
errno = 0;
pid = (int) strtol(argv[1], NULL, 10);
exit(1);
}
- if (!create_db_dir(DB_FILE)) {
+ if (!create_db_dir(LXC_USERNIC_DB)) {
fprintf(stderr, "Failed to create directory for db file\n");
exit(1);
}
- if ((fd = open_and_lock(DB_FILE)) < 0) {
- fprintf(stderr, "Failed to lock %s\n", DB_FILE);
+ if ((fd = open_and_lock(LXC_USERNIC_DB)) < 0) {
+ fprintf(stderr, "Failed to lock %s\n", LXC_USERNIC_DB);
exit(1);
}
if (!may_access_netns(pid)) {
fprintf(stderr, "User %s may not modify netns for pid %d\n",
- me, pid);
+ me, pid);
exit(1);
}
- n = get_alloted(me, argv[2], argv[3]);
+ n = get_alloted(me, argv[2], argv[3], &alloted);
if (n > 0)
- gotone = get_nic_if_avail(fd, me, pid, argv[2], argv[3], n, &nicname, &cnic);
+ gotone = get_nic_if_avail(fd, alloted, pid, argv[2], argv[3], n, &nicname, &cnic);
+
close(fd);
+ free_alloted(&alloted);
if (!gotone) {
fprintf(stderr, "Quota reached\n");
exit(1);
}
// Now rename the link
- if (rename_in_ns(pid, cnic, vethname) < 0) {
+ if (rename_in_ns(pid, cnic, &vethname) < 0) {
fprintf(stderr, "Failed to rename the link\n");
exit(1);
}
+ // write the name of the interface pair to the stdout - like eth0:veth9MT2L4
+ fprintf(stdout, "%s:%s\n", vethname, nicname);
exit(0);
}