Commit 36259ede by Serge Hallyn Committed by GitHub

Merge pull request #1773 from brauner/2017-08-31/ensure_lxc_user_nic_tests_privilege_over_netns

network: improvements + bugfixes
parents db3c8336 b9f522c5
......@@ -3067,41 +3067,35 @@ static bool verify_start_hooks(struct lxc_conf *conf)
static int lxc_send_ttys_to_parent(struct lxc_handler *handler)
{
int i;
int *ttyfds;
struct lxc_pty_info *pty_info;
struct lxc_conf *conf = handler->conf;
const struct lxc_tty_info *tty_info = &conf->tty_info;
int sock = handler->ttysock[0];
struct lxc_tty_info *tty_info = &conf->tty_info;
int sock = handler->data_sock[0];
int ret = -1;
size_t num_ttyfds = (2 * conf->tty);
ttyfds = malloc(num_ttyfds * sizeof(int));
if (!ttyfds)
return -1;
for (i = 0; i < conf->tty; i++) {
int ttyfds[2];
struct lxc_pty_info *pty_info = &tty_info->pty_info[i];
for (i = 0; i < num_ttyfds; i++) {
pty_info = &tty_info->pty_info[i / 2];
ttyfds[i++] = pty_info->slave;
ttyfds[i] = pty_info->master;
TRACE("send pty \"%s\" with master fd %d and slave fd %d to "
"parent",
pty_info->name, pty_info->master, pty_info->slave);
ttyfds[0] = pty_info->master;
ttyfds[1] = pty_info->slave;
ret = lxc_abstract_unix_send_fds(sock, ttyfds, 2, NULL, 0);
if (ret < 0)
break;
TRACE("Send pty \"%s\" with master fd %d and slave fd %d to "
"parent", pty_info->name, pty_info->master, pty_info->slave);
}
ret = lxc_abstract_unix_send_fds(sock, ttyfds, num_ttyfds, NULL, 0);
if (ret < 0)
ERROR("failed to send %d ttys to parent: %s", conf->tty,
ERROR("Failed to send %d ttys to parent: %s", conf->tty,
strerror(errno));
else
TRACE("sent %d ttys to parent", conf->tty);
close(handler->ttysock[0]);
close(handler->ttysock[1]);
TRACE("Sent %d ttys to parent", conf->tty);
for (i = 0; i < num_ttyfds; i++)
close(ttyfds[i]);
free(ttyfds);
close(handler->data_sock[0]);
close(handler->data_sock[1]);
lxc_delete_tty(tty_info);
return ret;
}
......@@ -3129,6 +3123,11 @@ int lxc_setup(struct lxc_handler *handler)
return -1;
}
if (lxc_network_send_name_and_ifindex_to_parent(handler) < 0) {
ERROR("Failed to network device names and ifindices to parent");
return -1;
}
if (lxc_conf->autodev > 0) {
if (mount_autodev(name, &lxc_conf->rootfs, lxcpath)) {
ERROR("failed to mount /dev in the container");
......@@ -3460,17 +3459,6 @@ int lxc_clear_hooks(struct lxc_conf *c, const char *key)
return 0;
}
static void lxc_clear_saved_nics(struct lxc_conf *conf)
{
int i;
if (!conf->saved_nics)
return;
for (i=0; i < conf->num_savednics; i++)
free(conf->saved_nics[i].orig_name);
free(conf->saved_nics);
}
static inline void lxc_clear_aliens(struct lxc_conf *conf)
{
struct lxc_list *it,*next;
......@@ -3525,7 +3513,6 @@ void lxc_conf_free(struct lxc_conf *conf)
lxc_clear_cgroups(conf, "lxc.cgroup");
lxc_clear_hooks(conf, "lxc.hook");
lxc_clear_mount_entries(conf);
lxc_clear_saved_nics(conf);
lxc_clear_idmaps(conf);
lxc_clear_groups(conf);
lxc_clear_includes(conf);
......
......@@ -244,8 +244,6 @@ struct lxc_conf {
struct lxc_list cgroup;
struct lxc_list id_map;
struct lxc_list network;
struct saved_nic *saved_nics;
int num_savednics;
int auto_mounts;
struct lxc_list mount_list;
struct lxc_list caps;
......
......@@ -483,7 +483,7 @@ static int set_config_net_link(const char *key, const char *value,
if (value[strlen(value) - 1] == '+' && netdev->type == LXC_NET_PHYS)
ret = create_matched_ifnames(value, lxc_conf, netdev);
else
ret = network_ifname(&netdev->link, value);
ret = network_ifname(netdev->link, value);
return ret;
}
......@@ -503,7 +503,7 @@ static int set_config_net_name(const char *key, const char *value,
if (!netdev)
return -1;
return network_ifname(&netdev->name, value);
return network_ifname(netdev->name, value);
}
static int set_config_net_veth_pair(const char *key, const char *value,
......@@ -521,7 +521,7 @@ static int set_config_net_veth_pair(const char *key, const char *value,
if (!netdev)
return -1;
return network_ifname(&netdev->priv.veth_attr.pair, value);
return network_ifname(netdev->priv.veth_attr.pair, value);
}
static int set_config_net_macvlan_mode(const char *key, const char *value,
......@@ -3690,8 +3690,7 @@ static int clr_config_net_name(const char *key, struct lxc_conf *lxc_conf,
if (!netdev)
return -1;
free(netdev->name);
netdev->name = NULL;
netdev->name[0] = '\0';
return 0;
}
......@@ -3725,8 +3724,7 @@ static int clr_config_net_link(const char *key, struct lxc_conf *lxc_conf,
if (!netdev)
return -1;
free(netdev->link);
netdev->link = NULL;
netdev->link[0] = '\0';
return 0;
}
......@@ -3763,8 +3761,7 @@ static int clr_config_net_veth_pair(const char *key, struct lxc_conf *lxc_conf,
if (!netdev)
return -1;
free(netdev->priv.veth_attr.pair);
netdev->priv.veth_attr.pair = NULL;
netdev->priv.veth_attr.pair[0] = '\0';
return 0;
}
......@@ -4032,7 +4029,7 @@ static int get_config_net_link(const char *key, char *retv, int inlen,
if (!netdev)
return -1;
if (netdev->link)
if (netdev->link[0] != '\0')
strprint(retv, inlen, "%s", netdev->link);
return fulllen;
......@@ -4056,7 +4053,7 @@ static int get_config_net_name(const char *key, char *retv, int inlen,
if (!netdev)
return -1;
if (netdev->name)
if (netdev->name[0] != '\0')
strprint(retv, inlen, "%s", netdev->name);
return fulllen;
......@@ -4129,8 +4126,9 @@ static int get_config_net_veth_pair(const char *key, char *retv, int inlen,
return 0;
strprint(retv, inlen, "%s",
netdev->priv.veth_attr.pair ? netdev->priv.veth_attr.pair
: netdev->priv.veth_attr.veth1);
netdev->priv.veth_attr.pair[0] != '\0'
? netdev->priv.veth_attr.pair
: netdev->priv.veth_attr.veth1);
return fulllen;
}
......
......@@ -110,10 +110,6 @@ static void lxc_remove_nic(struct lxc_list *it)
lxc_list_del(it);
free(netdev->link);
free(netdev->name);
if (netdev->type == LXC_NET_VETH)
free(netdev->priv.veth_attr.pair);
free(netdev->upscript);
free(netdev->downscript);
free(netdev->hwaddr);
......@@ -174,6 +170,16 @@ int set_config_network_legacy_type(const char *key, const char *value,
lxc_list_init(&netdev->ipv4);
lxc_list_init(&netdev->ipv6);
netdev->name[0] = '\0';
netdev->link[0] = '\0';
memset(&netdev->priv, 0, sizeof(netdev->priv));
/* I'm not completely sure if the memset takes care to zero the arrays
* in the union as well. So let's make extra sure and set the first byte
* to zero so that we don't have any surprises.
*/
netdev->priv.veth_attr.pair[0] = '\0';
netdev->priv.veth_attr.veth1[0] = '\0';
list = malloc(sizeof(*list));
if (!list) {
SYSERROR("failed to allocate memory");
......@@ -423,7 +429,7 @@ int set_config_network_legacy_link(const char *key, const char *value,
free(it);
ret = create_matched_ifnames(value, lxc_conf, NULL);
} else {
ret = network_ifname(&netdev->link, value);
ret = network_ifname(netdev->link, value);
}
return ret;
......@@ -438,7 +444,7 @@ int set_config_network_legacy_name(const char *key, const char *value,
if (!netdev)
return -1;
return network_ifname(&netdev->name, value);
return network_ifname(netdev->name, value);
}
int set_config_network_legacy_veth_pair(const char *key, const char *value,
......@@ -455,7 +461,7 @@ int set_config_network_legacy_veth_pair(const char *key, const char *value,
return -1;
}
return network_ifname(&netdev->priv.veth_attr.pair, value);
return network_ifname(netdev->priv.veth_attr.pair, value);
}
int set_config_network_legacy_macvlan_mode(const char *key, const char *value,
......@@ -848,12 +854,12 @@ int get_config_network_legacy_item(const char *key, char *retv, int inlen,
if (!netdev)
return -1;
if (strcmp(p1, "name") == 0) {
if (netdev->name)
if (netdev->name[0] != '\0')
strprint(retv, inlen, "%s", netdev->name);
} else if (strcmp(p1, "type") == 0) {
strprint(retv, inlen, "%s", lxc_net_type_to_str(netdev->type));
} else if (strcmp(p1, "link") == 0) {
if (netdev->link)
if (netdev->link[0] != '\0')
strprint(retv, inlen, "%s", netdev->link);
} else if (strcmp(p1, "flags") == 0) {
if (netdev->flags & IFF_UP)
......@@ -895,7 +901,7 @@ int get_config_network_legacy_item(const char *key, char *retv, int inlen,
} else if (strcmp(p1, "veth.pair") == 0) {
if (netdev->type == LXC_NET_VETH) {
strprint(retv, inlen, "%s",
netdev->priv.veth_attr.pair
netdev->priv.veth_attr.pair[0] != '\0'
? netdev->priv.veth_attr.pair
: netdev->priv.veth_attr.veth1);
}
......
......@@ -183,6 +183,15 @@ struct lxc_netdev *lxc_network_add(struct lxc_list *networks, int idx, bool tail
memset(netdev, 0, sizeof(*netdev));
lxc_list_init(&netdev->ipv4);
lxc_list_init(&netdev->ipv6);
netdev->name[0] = '\0';
netdev->link[0] = '\0';
memset(&netdev->priv, 0, sizeof(netdev->priv));
/* I'm not completely sure if the memset takes care to zero the arrays
* in the union as well. So let's make extra sure and set the first byte
* to zero so that we don't have any surprises.
*/
netdev->priv.veth_attr.pair[0] = '\0';
netdev->priv.veth_attr.veth1[0] = '\0';
/* give network a unique index */
netdev->idx = idx;
......@@ -258,7 +267,7 @@ void lxc_log_configured_netdevs(const struct lxc_conf *conf)
switch (netdev->type) {
case LXC_NET_VETH:
TRACE("type: veth");
if (netdev->priv.veth_attr.pair)
if (netdev->priv.veth_attr.pair[0] != '\0')
TRACE("veth pair: %s",
netdev->priv.veth_attr.pair);
if (netdev->priv.veth_attr.veth1[0] != '\0')
......@@ -285,6 +294,10 @@ void lxc_log_configured_netdevs(const struct lxc_conf *conf)
break;
case LXC_NET_PHYS:
TRACE("type: phys");
if (netdev->priv.phys_attr.ifindex > 0) {
TRACE("host side ifindex for phys device: %d",
netdev->priv.phys_attr.ifindex);
}
break;
case LXC_NET_EMPTY:
TRACE("type: empty");
......@@ -300,9 +313,9 @@ void lxc_log_configured_netdevs(const struct lxc_conf *conf)
if (netdev->type != LXC_NET_EMPTY) {
TRACE("flags: %s",
netdev->flags == IFF_UP ? "up" : "none");
if (netdev->link)
if (netdev->link[0] != '\0')
TRACE("link: %s", netdev->link);
if (netdev->name)
if (netdev->name[0] != '\0')
TRACE("name: %s", netdev->name);
if (netdev->hwaddr)
TRACE("hwaddr: %s", netdev->hwaddr);
......@@ -350,10 +363,6 @@ static void lxc_free_netdev(struct lxc_netdev *netdev)
{
struct lxc_list *cur, *next;
free(netdev->link);
free(netdev->name);
if (netdev->type == LXC_NET_VETH)
free(netdev->priv.veth_attr.pair);
free(netdev->upscript);
free(netdev->downscript);
free(netdev->hwaddr);
......@@ -503,9 +512,15 @@ int config_ip_prefix(struct in_addr *addr)
return 0;
}
int network_ifname(char **valuep, const char *value)
int network_ifname(char *valuep, const char *value)
{
return set_config_string_item_max(valuep, value, IFNAMSIZ);
if (strlen(value) >= IFNAMSIZ) {
ERROR("Network devie name \"%s\" is too long (>= %zu)", value,
(size_t)IFNAMSIZ);
}
strcpy(valuep, value);
return 0;
}
int rand_complete_hwaddr(char *hwaddr)
......
......@@ -77,7 +77,7 @@ extern int set_config_string_item_max(char **conf_item, const char *value,
size_t max);
extern int set_config_path_item(char **conf_item, const char *value);
extern int config_ip_prefix(struct in_addr *addr);
extern int network_ifname(char **valuep, const char *value);
extern int network_ifname(char *valuep, const char *value);
extern int rand_complete_hwaddr(char *hwaddr);
extern bool lxc_config_net_hwaddr(const char *line);
extern void update_hwaddr(const char *line);
......
......@@ -524,7 +524,7 @@ static void exec_criu(struct criu_opts *opts)
case LXC_NET_VETH:
veth = n->priv.veth_attr.pair;
if (n->link) {
if (n->link[0] != '\0') {
if (external_not_veth)
fmt = "veth[%s]:%s@%s";
else
......@@ -543,7 +543,7 @@ static void exec_criu(struct criu_opts *opts)
goto err;
break;
case LXC_NET_MACVLAN:
if (!n->link) {
if (n->link[0] == '\0') {
ERROR("no host interface for macvlan %s", n->name);
goto err;
}
......@@ -765,11 +765,13 @@ static bool restore_net_info(struct lxc_container *c)
snprintf(template, sizeof(template), "vethXXXXXX");
if (!netdev->priv.veth_attr.pair)
netdev->priv.veth_attr.pair = lxc_mkifname(template);
if (netdev->priv.veth_attr.pair[0] == '\0' &&
netdev->priv.veth_attr.veth1[0] == '\0') {
if (!lxc_mkifname(template))
goto out_unlock;
if (!netdev->priv.veth_attr.pair)
goto out_unlock;
strcpy(netdev->priv.veth_attr.veth1, template);
}
}
has_error = false;
......
......@@ -61,8 +61,8 @@ static void usage(char *me, bool fail)
{
fprintf(stderr, "Usage: %s create {lxcpath} {name} {pid} {type} "
"{bridge} {nicname}\n", me);
fprintf(stderr, "Usage: %s delete {lxcpath} {name} {pid} {type} "
"{bridge} {nicname}\n", me);
fprintf(stderr, "Usage: %s delete {lxcpath} {name} "
"{/proc/<pid>/ns/net} {type} {bridge} {nicname}\n", me);
fprintf(stderr, "{nicname} is the name to use inside the container\n");
if (fail)
......@@ -357,95 +357,119 @@ static char *get_eow(char *s, char *e)
return s;
}
static char *find_line(char *p, char *e, char *u, char *t, char *l)
static char *find_line(char *buf_start, char *buf_end, char *name,
char *net_type, char *net_link, char *net_dev,
bool *owner, bool *found, bool *keep)
{
char *p1, *p2, *ret;
char *end_of_line, *end_of_word, *line;
while ((p < e) && (p1 = get_eol(p, e)) < e) {
ret = p;
if (*p == '#')
goto next;
while (buf_start < buf_end) {
size_t len;
char netdev_name[IFNAMSIZ];
while ((p < e) && isblank(*p))
p++;
*found = false;
*keep = true;
*owner = false;
end_of_line = get_eol(buf_start, buf_end);
if (end_of_line >= buf_end)
return NULL;
p2 = get_eow(p, e);
if (!p2 || ((size_t)(p2 - p)) != strlen(u) ||
strncmp(p, u, strlen(u)))
line = buf_start;
if (*buf_start == '#')
goto next;
p = p2 + 1;
while ((p < e) && isblank(*p))
p++;
while ((buf_start < buf_end) && isblank(*buf_start))
buf_start++;
p2 = get_eow(p, e);
if (!p2 || ((size_t)(p2 - p)) != strlen(t) ||
strncmp(p, t, strlen(t)))
goto next;
/* Check whether the line contains the caller's name. */
end_of_word = get_eow(buf_start, buf_end);
/* corrupt db */
if (!end_of_word)
return NULL;
p = p2 + 1;
while ((p < e) && isblank(*p))
p++;
if (strncmp(buf_start, name, strlen(name)))
*found = false;
p2 = get_eow(p, e);
if (!p2 || ((size_t)(p2 - p)) != strlen(l) ||
strncmp(p, l, strlen(l)))
goto next;
*owner = true;
return ret;
next:
p = p1 + 1;
}
buf_start = end_of_word + 1;
while ((buf_start < buf_end) && isblank(*buf_start))
buf_start++;
return NULL;
}
/* Check whether line is of the right network type. */
end_of_word = get_eow(buf_start, buf_end);
/* corrupt db */
if (!end_of_word)
return NULL;
static bool nic_exists(char *nic)
{
char path[MAXPATHLEN];
int ret;
struct stat sb;
if (strncmp(buf_start, net_type, strlen(net_type)))
*found = false;
if (!strcmp(nic, "none"))
return true;
buf_start = end_of_word + 1;
while ((buf_start < buf_end) && isblank(*buf_start))
buf_start++;
ret = snprintf(path, MAXPATHLEN, "/sys/class/net/%s", nic);
if (ret < 0 || ret >= MAXPATHLEN)
return false;
/* Check whether line is contains the right link. */
end_of_word = get_eow(buf_start, buf_end);
/* corrupt db */
if (!end_of_word)
return NULL;
ret = stat(path, &sb);
if (ret < 0)
return false;
if (strncmp(buf_start, net_link, strlen(net_link)))
*found = false;
return true;
}
buf_start = end_of_word + 1;
while ((buf_start < buf_end) && isblank(*buf_start))
buf_start++;
static int instantiate_veth(char *n1, char **n2)
{
int err;
/* Check whether line contains the right network device. */
end_of_word = get_eow(buf_start, buf_end);
/* corrupt db */
if (!end_of_word)
return NULL;
err = snprintf(*n2, IFNAMSIZ, "%sp", n1);
if (err < 0 || err >= IFNAMSIZ) {
usernic_error("%s\n", "Could not create nic name");
return -1;
len = end_of_word - buf_start;
/* corrupt db */
if (len >= IFNAMSIZ)
return NULL;
memcpy(netdev_name, buf_start, len);
netdev_name[len] = '\0';
*keep = lxc_nic_exists(netdev_name);
if (net_dev && !strcmp(netdev_name, net_dev))
*found = true;
return line;
next:
buf_start = end_of_line + 1;
}
err = lxc_veth_create(n1, *n2);
if (err) {
usernic_error("Failed to create %s-%s : %s.\n", n1, *n2,
strerror(-err));
return NULL;
}
static int instantiate_veth(char *veth1, char *veth2)
{
int ret;
ret = lxc_veth_create(veth1, veth2);
if (ret < 0) {
usernic_error("Failed to create %s-%s : %s.\n", veth1, veth2,
strerror(-ret));
return -1;
}
/* Changing the high byte of the mac address to 0xfe, the bridge
* interface will always keep the host's mac address and not take the
* mac address of a container. */
err = setup_private_host_hw_addr(n1);
if (err)
ret = setup_private_host_hw_addr(veth1);
if (ret < 0)
usernic_error("Failed to change mac address of host interface "
"%s : %s\n", n1, strerror(-err));
"%s : %s\n", veth1, strerror(-ret));
return netdev_set_flag(n1, IFF_UP);
return netdev_set_flag(veth1, IFF_UP);
}
static int get_mtu(char *name)
......@@ -453,31 +477,32 @@ static int get_mtu(char *name)
int idx;
idx = if_nametoindex(name);
if (idx < 0)
return -1;
return netdev_get_mtu(idx);
}
static bool create_nic(char *nic, char *br, int pid, char **cnic)
static int create_nic(char *nic, char *br, int pid, char **cnic)
{
char *veth1buf, *veth2buf;
char veth1buf[IFNAMSIZ], veth2buf[IFNAMSIZ];
int mtu, ret;
veth1buf = alloca(IFNAMSIZ);
veth2buf = alloca(IFNAMSIZ);
if (!veth1buf || !veth2buf) {
usernic_error("Failed allocate memory: %s\n", strerror(errno));
return false;
}
ret = snprintf(veth1buf, IFNAMSIZ, "%s", nic);
if (ret < 0 || ret >= IFNAMSIZ) {
usernic_error("%s", "Could not create nic name\n");
return false;
return -1;
}
ret = snprintf(veth2buf, IFNAMSIZ, "%sp", veth1buf);
if (ret < 0 || ret >= IFNAMSIZ) {
usernic_error("%s\n", "Could not create nic name");
return -1;
}
/* create the nics */
if (instantiate_veth(veth1buf, &veth2buf) < 0) {
ret = instantiate_veth(veth1buf, veth2buf);
if (ret < 0) {
usernic_error("%s", "Error creating veth tunnel\n");
return false;
return -1;
}
if (strcmp(br, "none")) {
......@@ -518,52 +543,14 @@ static bool create_nic(char *nic, char *br, int pid, char **cnic)
*cnic = strdup(veth2buf);
if (!*cnic) {
usernic_error("Failed to copy string \"%s\"\n", veth2buf);
return false;
return -1;
}
return true;
return 0;
out_del:
lxc_netdev_delete_by_name(veth1buf);
return false;
}
/* get_new_nicname() will return the name (vethXXXXXX) which is attached on the
* host to the lxc bridge. The returned string must be freed by caller.
*/
static char *get_new_nicname(char *br, int pid, char **cnic)
{
int ret;
char *nicname;
char template[IFNAMSIZ];
ret = snprintf(template, sizeof(template), "vethXXXXXX");
if (ret < 0 || (size_t)ret >= sizeof(template))
return NULL;
nicname = lxc_mkifname(template);
if (!nicname)
return NULL;
if (!create_nic(nicname, br, pid, cnic)) {
free(nicname);
return NULL;
}
return nicname;
}
static bool get_nic_from_line(char *p, char **nic)
{
int ret;
char user[100], type[100], br[100];
ret = sscanf(p, "%99[^ \t\n] %99[^ \t\n] %99[^ \t\n] %99[^ \t\n]", user,
type, br, *nic);
if (ret != 4)
return false;
return true;
return -1;
}
struct entry_line {
......@@ -572,29 +559,24 @@ struct entry_line {
bool keep;
};
static bool cull_entries(int fd, char *me, char *t, char *br, char *nicname,
bool *found_nicname)
static bool cull_entries(int fd, char *name, char *net_type, char *net_link,
char *net_dev, bool *found_nicname)
{
int i, ret;
off_t len;
char *buf, *e, *nic, *p;
char *buf, *buf_end, *buf_start;
struct stat sb;
int n = 0;
bool found, keep;
struct entry_line *entry_lines = NULL;
nic = alloca(100);
if (!nic)
return false;
ret = fstat(fd, &sb);
if (ret < 0) {
usernic_error("Failed to fstat: %s\n", strerror(errno));
return false;
}
len = sb.st_size;
if (len == 0)
return true;
if (!sb.st_size)
return false;
buf = lxc_strmmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (buf == MAP_FAILED) {
......@@ -603,51 +585,48 @@ static bool cull_entries(int fd, char *me, char *t, char *br, char *nicname,
return false;
}
p = buf;
e = buf + len;
while ((p = find_line(p, e, me, t, br))) {
buf_start = buf;
buf_end = buf + sb.st_size;
while ((buf_start = find_line(buf_start, buf_end, name, net_type,
net_link, net_dev, &(bool){true}, &found,
&keep))) {
struct entry_line *newe;
newe = realloc(entry_lines, sizeof(*entry_lines) * (n + 1));
if (!newe) {
free(entry_lines);
lxc_strmunmap(buf, sb.st_size);
return false;
}
if (found)
*found_nicname = true;
entry_lines = newe;
entry_lines[n].start = p;
entry_lines[n].len = get_eol(p, e) - entry_lines[n].start;
entry_lines[n].keep = true;
entry_lines[n].start = buf_start;
entry_lines[n].len = get_eol(buf_start, buf_end) - entry_lines[n].start;
entry_lines[n].keep = keep;
n++;
if (!get_nic_from_line(p, &nic))
continue;
if (nic && !nic_exists(nic))
entry_lines[n - 1].keep = false;
if (nicname)
if (!strcmp(nic, nicname))
*found_nicname = true;
p += entry_lines[n - 1].len + 1;
if (p >= e)
buf_start += entry_lines[n - 1].len + 1;
if (buf_start >= buf_end)
break;
}
p = buf;
buf_start = buf;
for (i = 0; i < n; i++) {
if (!entry_lines[i].keep)
continue;
memcpy(p, entry_lines[i].start, entry_lines[i].len);
p += entry_lines[i].len;
*p = '\n';
p++;
memcpy(buf_start, entry_lines[i].start, entry_lines[i].len);
buf_start += entry_lines[i].len;
*buf_start = '\n';
buf_start++;
}
free(entry_lines);
lxc_strmunmap(buf, sb.st_size);
ret = ftruncate(fd, p - buf);
ret = ftruncate(fd, buf_start - buf);
if (ret < 0)
usernic_error("Failed to set new file size: %s\n",
strerror(errno));
......@@ -655,16 +634,19 @@ static bool cull_entries(int fd, char *me, char *t, char *br, char *nicname,
return true;
}
static int count_entries(char *buf, off_t len, char *me, char *t, char *br)
static int count_entries(char *buf, off_t len, char *name, char *net_type, char *net_link)
{
char *e;
int count = 0;
e = &buf[len];
while ((buf = find_line(buf, e, me, t, br))) {
count++;
buf = get_eol(buf, e) + 1;
if (buf >= e)
bool owner = false;;
char *buf_end = &buf[len];
buf_end = &buf[len];
while ((buf = find_line(buf, buf_end, name, net_type, net_link, NULL,
&owner, &(bool){true}, &(bool){true}))) {
if (owner)
count++;
buf = get_eol(buf, buf_end) + 1;
if (buf >= buf_end)
break;
}
......@@ -676,8 +658,9 @@ static char *get_nic_if_avail(int fd, struct alloted_s *names, int pid,
char *intype, char *br, int allowed, char **cnic)
{
int ret;
off_t len, slen;
char *newline, *nicname, *owner;
size_t slen;
char *newline, *owner;
char nicname[IFNAMSIZ];
struct stat sb;
struct alloted_s *n;
int count = 0;
......@@ -691,79 +674,110 @@ static char *get_nic_if_avail(int fd, struct alloted_s *names, int pid,
owner = names->name;
if (fstat(fd, &sb) < 0) {
ret = fstat(fd, &sb);
if (ret < 0) {
usernic_error("Failed to fstat: %s\n", strerror(errno));
return NULL;
}
len = sb.st_size;
if (len > 0) {
buf =
mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (sb.st_size > 0) {
buf = lxc_strmmap(NULL, sb.st_size, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
if (buf == MAP_FAILED) {
usernic_error("Failed to establish shared memory mapping: %s\n",
strerror(errno));
usernic_error("Failed to establish shared memory "
"mapping: %s\n", strerror(errno));
return NULL;
}
owner = NULL;
for (n = names; n != NULL; n = n->next) {
count = count_entries(buf, len, n->name, intype, br);
count = count_entries(buf, sb.st_size, n->name, intype, br);
if (count >= n->allowed)
continue;
owner = n->name;
break;
}
lxc_strmunmap(buf, sb.st_size);
}
if (owner == NULL)
return NULL;
nicname = get_new_nicname(br, pid, cnic);
if (!nicname) {
usernic_error("%s", "Failed to get new nic name\n");
ret = snprintf(nicname, sizeof(nicname), "vethXXXXXX");
if (ret < 0 || (size_t)ret >= sizeof(nicname))
return NULL;
if (!lxc_mkifname(nicname))
return NULL;
ret = create_nic(nicname, br, pid, cnic);
if (ret < 0) {
usernic_error("%s", "Failed to create new nic\n");
return NULL;
}
/* owner ' ' intype ' ' br ' ' *nicname + '\n' + '\0' */
slen = strlen(owner) + strlen(intype) + strlen(br) + strlen(nicname) + 5;
newline = alloca(slen);
/* strlen(owner)
* +
* " "
* +
* strlen(intype)
* +
* " "
* +
* strlen(br)
* +
* " "
* +
* strlen(nicname)
* +
* \n
* +
* \0
*/
slen = strlen(owner) + strlen(intype) + strlen(br) + strlen(nicname) + 4;
newline = malloc(slen + 1);
if (!newline) {
free(nicname);
free(newline);
usernic_error("Failed allocate memory: %s\n", strerror(errno));
return NULL;
}
ret = snprintf(newline, slen, "%s %s %s %s\n", owner, intype, br, nicname);
if (ret < 0 || ret >= slen) {
ret = snprintf(newline, slen + 1, "%s %s %s %s\n", owner, intype, br, nicname);
if (ret < 0 || (size_t)ret >= (slen + 1)) {
if (lxc_netdev_delete_by_name(nicname) != 0)
usernic_error("Error unlinking %s\n", nicname);
free(nicname);
free(newline);
return NULL;
}
if (len)
munmap(buf, len);
if (ftruncate(fd, len + slen))
usernic_error("Failed to set new file size: %s\n",
strerror(errno));
/* Note that the file needs to be truncated to the size **without** the
* \0 byte! Files are not \0-terminated!
*/
ret = ftruncate(fd, sb.st_size + slen);
if (ret < 0)
usernic_error("Failed to truncate file: %s\n", strerror(errno));
buf = mmap(NULL, len + slen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
buf = lxc_strmmap(NULL, sb.st_size + slen, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
if (buf == MAP_FAILED) {
usernic_error("Failed to establish shared memory mapping: %s\n",
strerror(errno));
if (lxc_netdev_delete_by_name(nicname) != 0)
usernic_error("Error unlinking %s\n", nicname);
free(nicname);
free(newline);
return NULL;
}
strcpy(buf + len, newline);
munmap(buf, len + slen);
/* Note that the memory needs to be moved in the buffer **without** the
* \0 byte! Files are not \0-terminated!
*/
memmove(buf + sb.st_size, newline, slen);
free(newline);
lxc_strmunmap(buf, sb.st_size + slen);
return nicname;
return strdup(nicname);
}
static bool create_db_dir(char *fnam)
......@@ -1053,10 +1067,10 @@ do_partial_cleanup:
int main(int argc, char *argv[])
{
int container_veth_ifidx, fd, host_veth_ifidx, n, pid, request, ret;
int fd, n, pid, request, ret;
char *me, *newname;
struct user_nic_args args;
int netns_fd = -1;
int container_veth_ifidx = -1, host_veth_ifidx = -1, netns_fd = -1;
char *cnic = NULL, *nicname = NULL;
struct alloted_s *alloted = NULL;
......@@ -1177,8 +1191,8 @@ int main(int argc, char *argv[])
free_alloted(&alloted);
if (!found_nicname) {
usernic_error("%s", "Caller is not allowed to delete "
"network device\n");
usernic_error("Caller is not allowed to delete network "
"device \"%s\"\n", args.veth_name);
exit(EXIT_FAILURE);
}
......@@ -1214,7 +1228,14 @@ int main(int argc, char *argv[])
free(nicname);
exit(EXIT_FAILURE);
}
host_veth_ifidx = if_nametoindex(nicname);
if (!host_veth_ifidx) {
free(newname);
free(nicname);
usernic_error("Failed to get netdev index: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
/* Write names of veth pairs and their ifindeces to stout:
* (e.g. eth0:731:veth9MT2L4:730)
......
......@@ -45,6 +45,7 @@
#include <sys/stat.h>
#include <sys/types.h>
#include "af_unix.h"
#include "conf.h"
#include "config.h"
#include "log.h"
......@@ -101,7 +102,7 @@ static int instantiate_veth(struct lxc_handler *handler, struct lxc_netdev *netd
char veth1buf[IFNAMSIZ], veth2buf[IFNAMSIZ];
unsigned int mtu = 0;
if (netdev->priv.veth_attr.pair) {
if (netdev->priv.veth_attr.pair[0] != '\0') {
veth1 = netdev->priv.veth_attr.pair;
if (handler->conf->reboot)
lxc_netdev_delete_by_name(veth1);
......@@ -140,6 +141,18 @@ static int instantiate_veth(struct lxc_handler *handler, struct lxc_netdev *netd
goto out_delete;
}
/* Retrieve ifindex of the host's veth device. */
netdev->priv.veth_attr.ifindex = if_nametoindex(veth1);
if (!netdev->priv.veth_attr.ifindex) {
ERROR("Failed to retrieve ifindex for \"%s\"", veth1);
goto out_delete;
}
/* Note that we're retrieving the container's ifindex in the host's
* network namespace because we need it to move the device from the
* host's network namespace to the container's network namespace later
* on.
*/
netdev->ifindex = if_nametoindex(veth2);
if (!netdev->ifindex) {
ERROR("Failed to retrieve ifindex for \"%s\"", veth2);
......@@ -151,7 +164,7 @@ static int instantiate_veth(struct lxc_handler *handler, struct lxc_netdev *netd
WARN("Failed to parse mtu");
else
INFO("Retrieved mtu %d", mtu);
} else if (netdev->link) {
} else if (netdev->link[0] != '\0') {
bridge_index = if_nametoindex(netdev->link);
if (bridge_index) {
mtu = netdev_get_mtu(bridge_index);
......@@ -174,7 +187,7 @@ static int instantiate_veth(struct lxc_handler *handler, struct lxc_netdev *netd
}
}
if (netdev->link) {
if (netdev->link[0] != '\0') {
err = lxc_bridge_attach(netdev->link, veth1);
if (err) {
ERROR("Failed to attach \"%s\" to bridge \"%s\": %s",
......@@ -205,9 +218,6 @@ static int instantiate_veth(struct lxc_handler *handler, struct lxc_netdev *netd
out_delete:
if (netdev->ifindex != 0)
lxc_netdev_delete_by_name(veth1);
if (!netdev->priv.veth_attr.pair)
free(veth1);
free(veth2);
return -1;
}
......@@ -216,7 +226,7 @@ static int instantiate_macvlan(struct lxc_handler *handler, struct lxc_netdev *n
char peerbuf[IFNAMSIZ], *peer;
int err;
if (!netdev->link) {
if (netdev->link[0] == '\0') {
ERROR("No link for macvlan network device specified");
return -1;
}
......@@ -234,29 +244,29 @@ static int instantiate_macvlan(struct lxc_handler *handler, struct lxc_netdev *n
if (err) {
ERROR("Failed to create macvlan interface \"%s\" on \"%s\": %s",
peer, netdev->link, strerror(-err));
goto out;
goto on_error;
}
netdev->ifindex = if_nametoindex(peer);
if (!netdev->ifindex) {
ERROR("Failed to retrieve ifindex for \"%s\"", peer);
goto out;
goto on_error;
}
if (netdev->upscript) {
err = run_script(handler->name, "net", netdev->upscript, "up",
"macvlan", netdev->link, (char*) NULL);
if (err)
goto out;
goto on_error;
}
DEBUG("Instantiated macvlan \"%s\" with ifindex is %d and mode %d",
peer, netdev->ifindex, netdev->priv.macvlan_attr.mode);
return 0;
out:
on_error:
lxc_netdev_delete_by_name(peer);
free(peer);
return -1;
}
......@@ -267,7 +277,7 @@ static int instantiate_vlan(struct lxc_handler *handler, struct lxc_netdev *netd
static uint16_t vlan_cntr = 0;
unsigned int mtu = 0;
if (!netdev->link) {
if (netdev->link[0] == '\0') {
ERROR("No link for vlan network device specified");
return -1;
}
......@@ -296,7 +306,7 @@ static int instantiate_vlan(struct lxc_handler *handler, struct lxc_netdev *netd
if (lxc_safe_uint(netdev->mtu, &mtu) < 0) {
ERROR("Failed to retrieve mtu from \"%d\"/\"%s\".",
netdev->ifindex,
netdev->name ? netdev->name : "(null)");
netdev->name[0] != '\0' ? netdev->name : "(null)");
return -1;
}
err = lxc_netdev_set_mtu(peer, mtu);
......@@ -313,17 +323,29 @@ static int instantiate_vlan(struct lxc_handler *handler, struct lxc_netdev *netd
static int instantiate_phys(struct lxc_handler *handler, struct lxc_netdev *netdev)
{
if (!netdev->link) {
if (netdev->link[0] == '\0') {
ERROR("No link for physical interface specified");
return -1;
}
/* Note that we're retrieving the container's ifindex in the host's
* network namespace because we need it to move the device from the
* host's network namespace to the container's network namespace later
* on.
* Note that netdev->link will contain the name of the physical network
* device in the host's namespace.
*/
netdev->ifindex = if_nametoindex(netdev->link);
if (!netdev->ifindex) {
ERROR("Failed to retrieve ifindex for \"%s\"", netdev->link);
return -1;
}
/* Store the ifindex of the host's network device in the host's
* namespace.
*/
netdev->priv.phys_attr.ifindex = netdev->ifindex;
if (netdev->upscript) {
int err;
err = run_script(handler->name, "net", netdev->upscript,
......@@ -368,7 +390,7 @@ static int shutdown_veth(struct lxc_handler *handler, struct lxc_netdev *netdev)
char *veth1;
int err;
if (netdev->priv.veth_attr.pair)
if (netdev->priv.veth_attr.pair[0] != '\0')
veth1 = netdev->priv.veth_attr.pair;
else
veth1 = netdev->priv.veth_attr.veth1;
......@@ -1893,14 +1915,17 @@ const char *lxc_net_type_to_str(int type)
static const char padchar[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char *lxc_mkifname(const char *template)
char *lxc_mkifname(char *template)
{
int ifexists = 0;
size_t i = 0;
char *name = NULL;
unsigned int seed;
FILE *urandom;
struct ifaddrs *ifa, *ifaddr;
char name[IFNAMSIZ];
bool exists = false;
size_t i = 0;
if (strlen(template) >= IFNAMSIZ)
return NULL;
/* Get all the network interfaces. */
getifaddrs(&ifaddr);
......@@ -1921,17 +1946,14 @@ char *lxc_mkifname(const char *template)
/* Generate random names until we find one that doesn't exist. */
while (true) {
ifexists = 0;
name = strdup(template);
if (name == NULL)
return NULL;
name[0] = '\0';
strcpy(name, template);
exists = false;
for (i = 0; i < strlen(name); i++) {
if (name[i] == 'X') {
#ifdef HAVE_RAND_R
name[i] = padchar[rand_r(&seed) %
(strlen(padchar) - 1)];
name[i] = padchar[rand_r(&seed) % (strlen(padchar) - 1)];
#else
name[i] = padchar[rand() % (strlen(padchar) - 1)];
#endif
......@@ -1939,20 +1961,18 @@ char *lxc_mkifname(const char *template)
}
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
if (strcmp(ifa->ifa_name, name) == 0) {
ifexists = 1;
if (!strcmp(ifa->ifa_name, name)) {
exists = true;
break;
}
}
if (ifexists == 0)
if (!exists)
break;
free(name);
}
freeifaddrs(ifaddr);
return name;
return strcpy(template, name);
}
int setup_private_host_hw_addr(char *veth1)
......@@ -2002,7 +2022,7 @@ int lxc_find_gateway_addresses(struct lxc_handler *handler)
return -1;
}
if (!netdev->link) {
if (netdev->link[0] == '\0') {
ERROR("Automatic gateway detection needs a link interface");
return -1;
}
......@@ -2076,7 +2096,7 @@ static int lxc_create_network_unpriv_exec(const char *lxcpath, char *lxcname,
exit(EXIT_FAILURE);
}
if (netdev->link)
if (netdev->link[0] != '\0')
strncpy(netdev_link, netdev->link, IFNAMSIZ);
else
strncpy(netdev_link, "none", IFNAMSIZ);
......@@ -2088,8 +2108,8 @@ static int lxc_create_network_unpriv_exec(const char *lxcpath, char *lxcname,
INFO("Execing lxc-user-nic create %s %s %s veth %s %s", lxcpath,
lxcname, pidstr, netdev_link,
netdev->name ? netdev->name : "(null)");
if (netdev->name)
netdev->name[0] != '\0' ? netdev->name : "(null)");
if (netdev->name[0] != '\0')
execlp(LXC_USERNIC_PATH, LXC_USERNIC_PATH, "create",
lxcpath, lxcname, pidstr, "veth", netdev_link,
netdev->name, (char *)NULL);
......@@ -2128,11 +2148,6 @@ static int lxc_create_network_unpriv_exec(const char *lxcpath, char *lxcname,
return -1;
}
netdev->name = malloc(IFNAMSIZ + 1);
if (!netdev->name) {
SYSERROR("Failed to allocate memory");
return -1;
}
memset(netdev->name, 0, IFNAMSIZ + 1);
strncpy(netdev->name, token, IFNAMSIZ);
......@@ -2210,6 +2225,7 @@ static int lxc_delete_network_unpriv_exec(const char *lxcpath, char *lxcname,
}
if (child == 0) {
char *hostveth;
int ret;
close(pipefd[0]);
......@@ -2223,22 +2239,26 @@ static int lxc_delete_network_unpriv_exec(const char *lxcpath, char *lxcname,
exit(EXIT_FAILURE);
}
if (netdev->priv.veth_attr.veth1[0] == '\0') {
if (netdev->priv.veth_attr.pair[0] != '\0')
hostveth = netdev->priv.veth_attr.pair;
else
hostveth = netdev->priv.veth_attr.veth1;
if (hostveth[0] == '\0') {
SYSERROR("Host side veth device name is missing");
exit(EXIT_FAILURE);
}
if (!netdev->link) {
if (netdev->link[0] == '\0') {
SYSERROR("Network link for network device \"%s\" is "
"missing", netdev->priv.veth_attr.veth1);
exit(EXIT_FAILURE);
}
INFO("Execing lxc-user-nic delete %s %s %s veth %s %s", lxcpath,
lxcname, netns_path, netdev->link, netdev->priv.veth_attr.veth1);
lxcname, netns_path, netdev->link, hostveth);
execlp(LXC_USERNIC_PATH, LXC_USERNIC_PATH, "delete", lxcpath,
lxcname, netns_path, "veth", netdev->link,
netdev->priv.veth_attr.veth1, (char *)NULL);
lxcname, netns_path, "veth", netdev->link, hostveth,
(char *)NULL);
SYSERROR("Failed to exec lxc-user-nic.");
exit(EXIT_FAILURE);
}
......@@ -2283,7 +2303,7 @@ bool lxc_delete_network_unpriv(struct lxc_handler *handler)
char netns_path[6 + LXC_NUMSTRLEN64 + 4 + LXC_NUMSTRLEN64 + 1];
bool deleted_all = true;
if (!am_unpriv())
if (handler->am_root)
return true;
*netns_path = '\0';
......@@ -2333,14 +2353,20 @@ bool lxc_delete_network_unpriv(struct lxc_handler *handler)
if (!is_ovs_bridge(netdev->link))
continue;
if (netdev->priv.veth_attr.pair[0] != '\0')
hostveth = netdev->priv.veth_attr.pair;
else
hostveth = netdev->priv.veth_attr.veth1;
if (hostveth[0] == '\0')
continue;
ret = lxc_delete_network_unpriv_exec(handler->lxcpath,
handler->name, netdev,
netns_path);
if (ret < 0) {
deleted_all = false;
WARN("Failed to remove port \"%s\" from openvswitch "
"bridge \"%s\"",
netdev->priv.veth_attr.veth1, netdev->link);
"bridge \"%s\"", hostveth, netdev->link);
continue;
}
INFO("Removed interface \"%s\" from \"%s\"", hostveth,
......@@ -2352,13 +2378,10 @@ bool lxc_delete_network_unpriv(struct lxc_handler *handler)
int lxc_create_network_priv(struct lxc_handler *handler)
{
bool am_root;
struct lxc_list *iterator;
struct lxc_list *network = &handler->conf->network;
/* We need to be root. */
am_root = (getuid() == 0);
if (!am_root)
if (!handler->am_root)
return 0;
lxc_list_for_each(iterator, network) {
......@@ -2382,7 +2405,7 @@ int lxc_create_network_priv(struct lxc_handler *handler)
int lxc_network_move_created_netdev_priv(const char *lxcpath, char *lxcname,
struct lxc_list *network, pid_t pid)
{
int err;
int ret;
char ifname[IFNAMSIZ];
struct lxc_list *iterator;
......@@ -2402,16 +2425,17 @@ int lxc_network_move_created_netdev_priv(const char *lxcpath, char *lxcname,
return -1;
}
err = lxc_netdev_move_by_name(ifname, pid, NULL);
if (err) {
ret = lxc_netdev_move_by_name(ifname, pid, NULL);
if (ret) {
ERROR("Failed to move network device \"%s\" to "
"network namespace %d: %s", ifname, pid,
strerror(-err));
strerror(-ret));
return -1;
}
DEBUG("Moved network device \"%s\"/\"%s\" to network namespace "
"of %d:", ifname, netdev->name ? netdev->name : "(null)",
"of %d",
ifname, netdev->name[0] != '\0' ? netdev->name : "(null)",
pid);
}
......@@ -2459,7 +2483,7 @@ bool lxc_delete_network_priv(struct lxc_handler *handler)
struct lxc_list *network = &handler->conf->network;
bool deleted_all = true;
if (am_unpriv())
if (!handler->am_root)
return true;
lxc_list_for_each(iterator, network) {
......@@ -2476,12 +2500,13 @@ bool lxc_delete_network_priv(struct lxc_handler *handler)
ret = lxc_netdev_rename_by_index(netdev->ifindex, netdev->link);
if (ret < 0)
WARN("Failed to rename interface with index %d "
"to its initial name \"%s\"",
netdev->ifindex, netdev->link);
"from \"%s\" to its initial name \"%s\"",
netdev->ifindex, netdev->name, netdev->link);
else
TRACE("Renamed interface with index %d to its "
"initial name \"%s\"",
netdev->ifindex, netdev->link);
TRACE("Renamed interface with index %d from "
"\"%s\" to its initial name \"%s\"",
netdev->ifindex, netdev->name,
netdev->link);
continue;
}
......@@ -2498,18 +2523,18 @@ bool lxc_delete_network_priv(struct lxc_handler *handler)
INFO("Interface \"%s\" with index %d already "
"deleted or existing in different network "
"namespace",
netdev->name ? netdev->name : "(null)",
netdev->name[0] != '\0' ? netdev->name : "(null)",
netdev->ifindex);
} else if (ret < 0) {
deleted_all = false;
WARN("Failed to remove interface \"%s\" with "
"index %d: %s",
netdev->name ? netdev->name : "(null)",
netdev->name[0] != '\0' ? netdev->name : "(null)",
netdev->ifindex, strerror(-ret));
continue;
}
INFO("Removed interface \"%s\" with index %d",
netdev->name ? netdev->name : "(null)",
netdev->name[0] != '\0' ? netdev->name : "(null)",
netdev->ifindex);
if (netdev->type != LXC_NET_VETH)
......@@ -2518,11 +2543,11 @@ bool lxc_delete_network_priv(struct lxc_handler *handler)
/* Explicitly delete host veth device to prevent lingering
* devices. We had issues in LXD around this.
*/
if (netdev->priv.veth_attr.pair)
if (netdev->priv.veth_attr.pair[0] != '\0')
hostveth = netdev->priv.veth_attr.pair;
else
hostveth = netdev->priv.veth_attr.veth1;
if (*hostveth == '\0')
if (hostveth[0] == '\0')
continue;
ret = lxc_netdev_delete_by_name(hostveth);
......@@ -2577,51 +2602,69 @@ int lxc_requests_empty_network(struct lxc_handler *handler)
}
/* try to move physical nics to the init netns */
void lxc_restore_phys_nics_to_netns(int netnsfd, struct lxc_conf *conf)
int lxc_restore_phys_nics_to_netns(struct lxc_handler *handler)
{
int ret;
int i, oldfd;
int oldfd;
char ifname[IFNAMSIZ];
struct lxc_list *iterator;
int netnsfd = handler->netnsfd;
struct lxc_conf *conf = handler->conf;
if (netnsfd < 0 || conf->num_savednics == 0)
return;
/* We need CAP_NET_ADMIN in the parent namespace in order to setns() to
* the parent network namespace. We won't have this capability if we are
* unprivileged.
*/
if (!handler->am_root)
return 0;
INFO("Trying to restore network device names in original namespace for "
"%d network devices", conf->num_savednics);
TRACE("Moving physical network devices back to parent network namespace");
oldfd = lxc_preserve_ns(getpid(), "net");
if (oldfd < 0) {
SYSERROR("Failed to preserve network namespace");
return;
return -1;
}
ret = setns(netnsfd, 0);
ret = setns(netnsfd, CLONE_NEWNET);
if (ret < 0) {
SYSERROR("Failed to enter network namespace");
close(oldfd);
return;
return -1;
}
for (i = 0; i < conf->num_savednics; i++) {
struct saved_nic *s = &conf->saved_nics[i];
lxc_list_for_each(iterator, &conf->network) {
struct lxc_netdev *netdev = iterator->elem;
/* retrieve the name of the interface */
if (!if_indextoname(s->ifindex, ifname)) {
if (netdev->type != LXC_NET_PHYS)
continue;
/* Retrieve the name of the interface in the container's network
* namespace.
*/
if (!if_indextoname(netdev->ifindex, ifname)) {
WARN("No interface corresponding to ifindex %d",
s->ifindex);
netdev->ifindex);
continue;
}
if (lxc_netdev_move_by_name(ifname, 1, s->orig_name))
ret = lxc_netdev_move_by_name(ifname, 1, netdev->link);
if (ret < 0)
WARN("Error moving network device \"%s\" back to "
"network namespace", ifname);
free(s->orig_name);
else
TRACE("Moved network device \"%s\" back to network "
"namespace", ifname);
}
conf->num_savednics = 0;
ret = setns(oldfd, 0);
if (ret < 0)
SYSERROR("Failed to enter network namespace");
ret = setns(oldfd, CLONE_NEWNET);
close(oldfd);
if (ret < 0) {
SYSERROR("Failed to enter network namespace");
return -1;
}
return 0;
}
static int setup_hw_addr(char *hwaddr, const char *ifname)
......@@ -2726,8 +2769,7 @@ static int lxc_setup_netdev_in_child_namespaces(struct lxc_netdev *netdev)
if (netdev->type != LXC_NET_VETH) {
net_type_name = lxc_net_type_to_str(netdev->type);
ERROR("%s networks are not supported for containers "
"not setup up by privileged users",
net_type_name);
"not setup up by privileged users", net_type_name);
return -1;
}
......@@ -2755,9 +2797,12 @@ static int lxc_setup_netdev_in_child_namespaces(struct lxc_netdev *netdev)
* When the IFLA_IFNAME attribute is passed something like "<prefix>%d"
* netlink will replace the format specifier with an appropriate index.
*/
if (!netdev->name)
netdev->name = netdev->type == LXC_NET_PHYS ?
netdev->link : "eth%d";
if (netdev->name[0] == '\0') {
if (netdev->type == LXC_NET_PHYS)
strcpy(netdev->name, netdev->link);
else
strcpy(netdev->name, "eth%d");
}
/* rename the interface name */
if (strcmp(ifname, netdev->name) != 0) {
......@@ -2778,6 +2823,12 @@ static int lxc_setup_netdev_in_child_namespaces(struct lxc_netdev *netdev)
return -1;
}
/* Now update the recorded name of the network device to reflect the
* name of the network device in the child's network namespace. We will
* later on send this information back to the parent.
*/
strcpy(netdev->name, current_ifname);
/* set a mac address */
if (netdev->hwaddr) {
if (setup_hw_addr(netdev->hwaddr, current_ifname)) {
......@@ -2934,3 +2985,134 @@ int lxc_setup_network_in_child_namespaces(const struct lxc_conf *conf,
return 0;
}
int lxc_network_send_veth_names_to_child(struct lxc_handler *handler)
{
struct lxc_list *iterator;
struct lxc_list *network = &handler->conf->network;
int data_sock = handler->data_sock[0];
if (handler->am_root)
return 0;
lxc_list_for_each(iterator, network) {
int ret;
struct lxc_netdev *netdev = iterator->elem;
if (netdev->type != LXC_NET_VETH)
continue;
ret = send(data_sock, netdev->name, IFNAMSIZ, 0);
if (ret < 0) {
close(handler->data_sock[0]);
close(handler->data_sock[1]);
return -1;
} else {
TRACE("Sent network device name \"%s\" to child",
netdev->name);
}
}
return 0;
}
int lxc_network_recv_veth_names_from_parent(struct lxc_handler *handler)
{
struct lxc_list *iterator;
struct lxc_list *network = &handler->conf->network;
int data_sock = handler->data_sock[1];
if (handler->am_root)
return 0;
lxc_list_for_each(iterator, network) {
int ret;
struct lxc_netdev *netdev = iterator->elem;
if (netdev->type != LXC_NET_VETH)
continue;
ret = recv(data_sock, netdev->name, IFNAMSIZ, 0);
if (ret < 0) {
close(handler->data_sock[0]);
close(handler->data_sock[1]);
return -1;
} else {
TRACE("Received network device name \"%s\" from parent",
netdev->name);
}
}
return 0;
}
int lxc_network_send_name_and_ifindex_to_parent(struct lxc_handler *handler)
{
struct lxc_list *iterator, *network;
int data_sock = handler->data_sock[0];
if (!handler->am_root)
return 0;
network = &handler->conf->network;
lxc_list_for_each(iterator, network) {
int ret;
struct lxc_netdev *netdev = iterator->elem;
/* Send network device name in the child's namespace to parent. */
ret = send(data_sock, netdev->name, IFNAMSIZ, 0);
if (ret < 0)
goto on_error;
/* Send network device ifindex in the child's namespace to
* parent.
*/
ret = send(data_sock, &netdev->ifindex, sizeof(netdev->ifindex), 0);
if (ret < 0)
goto on_error;
}
TRACE("Sent network device names and ifindeces to parent");
return 0;
on_error:
close(handler->data_sock[0]);
close(handler->data_sock[1]);
return -1;
}
int lxc_network_recv_name_and_ifindex_from_child(struct lxc_handler *handler)
{
struct lxc_list *iterator, *network;
int data_sock = handler->data_sock[1];
if (!handler->am_root)
return 0;
network = &handler->conf->network;
lxc_list_for_each(iterator, network) {
int ret;
struct lxc_netdev *netdev = iterator->elem;
/* Receive network device name in the child's namespace to
* parent.
*/
ret = recv(data_sock, netdev->name, IFNAMSIZ, 0);
if (ret < 0)
goto on_error;
/* Receive network device ifindex in the child's namespace to
* parent.
*/
ret = recv(data_sock, &netdev->ifindex, sizeof(netdev->ifindex), 0);
if (ret < 0)
goto on_error;
}
return 0;
on_error:
close(handler->data_sock[0]);
close(handler->data_sock[1]);
return -1;
}
......@@ -91,7 +91,7 @@ struct lxc_route6 {
* @ifindex : Ifindex of the network device.
*/
struct ifla_veth {
char *pair;
char pair[IFNAMSIZ];
char veth1[IFNAMSIZ];
int ifindex;
};
......@@ -107,10 +107,19 @@ struct ifla_macvlan {
int mode; /* private, vepa, bridge, passthru */
};
/* Contains information about the physical network device as seen from the host.
* @ifindex : The ifindex of the physical network device in the host's network
* namespace.
*/
struct ifla_phys {
int ifindex;
};
union netdev_p {
struct ifla_macvlan macvlan_attr;
struct ifla_phys phys_attr;
struct ifla_veth veth_attr;
struct ifla_vlan vlan_attr;
struct ifla_macvlan macvlan_attr;
};
/*
......@@ -151,8 +160,8 @@ struct lxc_netdev {
int ifindex;
int type;
int flags;
char *link;
char *name;
char link[IFNAMSIZ];
char name[IFNAMSIZ];
char *hwaddr;
char *mtu;
union netdev_p priv;
......@@ -166,16 +175,11 @@ struct lxc_netdev {
char *downscript;
};
struct saved_nic {
int ifindex;
char *orig_name;
};
/* Convert a string mac address to a socket structure. */
extern int lxc_convert_mac(char *macaddr, struct sockaddr *sockaddr);
/* Move a device between namespaces. */
extern int lxc_netdev_move_by_index(int ifindex, pid_t pid, const char* ifname);
extern int lxc_netdev_move_by_index(int ifindex, pid_t pid, const char *ifname);
extern int lxc_netdev_move_by_name(const char *ifname, pid_t pid,
const char *newname);
......@@ -252,7 +256,7 @@ extern int lxc_neigh_proxy_off(const char *name, int family);
/* Generate a new unique network interface name.
* Allocated memory must be freed by caller.
*/
extern char *lxc_mkifname(const char *template);
extern char *lxc_mkifname(char *template);
extern const char *lxc_net_type_to_str(int type);
extern int setup_private_host_hw_addr(char *veth1);
......@@ -268,8 +272,12 @@ extern int lxc_find_gateway_addresses(struct lxc_handler *handler);
extern int lxc_create_network_unpriv(const char *lxcpath, char *lxcname,
struct lxc_list *network, pid_t pid);
extern int lxc_requests_empty_network(struct lxc_handler *handler);
extern void lxc_restore_phys_nics_to_netns(int netnsfd, struct lxc_conf *conf);
extern int lxc_restore_phys_nics_to_netns(struct lxc_handler *handler);
extern int lxc_setup_network_in_child_namespaces(const struct lxc_conf *conf,
struct lxc_list *network);
extern int lxc_network_send_veth_names_to_child(struct lxc_handler *handler);
extern int lxc_network_recv_veth_names_from_parent(struct lxc_handler *handler);
extern int lxc_network_send_name_and_ifindex_to_parent(struct lxc_handler *handler);
extern int lxc_network_recv_name_and_ifindex_from_child(struct lxc_handler *handler);
#endif /* __LXC_NETWORK_H */
......@@ -89,7 +89,7 @@
lxc_log_define(lxc_start, lxc);
extern void mod_all_rdeps(struct lxc_container *c, bool inc);
static bool do_destroy_container(struct lxc_conf *conf);
static bool do_destroy_container(struct lxc_handler *handler);
static int lxc_rmdir_onedev_wrapper(void *data);
static void lxc_destroy_container_on_signal(struct lxc_handler *handler,
const char *name);
......@@ -535,7 +535,12 @@ struct lxc_handler *lxc_init_handler(const char *name, struct lxc_conf *conf,
memset(handler, 0, sizeof(*handler));
handler->ttysock[0] = handler->ttysock[1] = -1;
/* Note that am_unpriv() checks the effective uid. We probably don't
* care if we are real root only if we are running as root so this
* should be fine.
*/
handler->am_root = !am_unpriv();
handler->data_sock[0] = handler->data_sock[1] = -1;
handler->conf = conf;
handler->lxcpath = lxcpath;
handler->pinfd = -1;
......@@ -759,9 +764,9 @@ void lxc_fini(const char *name, struct lxc_handler *handler)
free(cur);
}
if (handler->ttysock[0] != -1) {
close(handler->ttysock[0]);
close(handler->ttysock[1]);
if (handler->data_sock[0] != -1) {
close(handler->data_sock[0]);
close(handler->data_sock[1]);
}
if (handler->conf->ephemeral == 1 && handler->conf->reboot != 1)
......@@ -783,55 +788,6 @@ void lxc_abort(const char *name, struct lxc_handler *handler)
}
}
/* netpipe is used in the unprivileged case to transfer the ifindexes from
* parent to child
*/
static int netpipe = -1;
static inline int count_veths(struct lxc_list *network)
{
struct lxc_list *iterator;
struct lxc_netdev *netdev;
int count = 0;
lxc_list_for_each(iterator, network) {
netdev = iterator->elem;
if (netdev->type != LXC_NET_VETH)
continue;
count++;
}
return count;
}
static int read_unpriv_netifindex(struct lxc_list *network)
{
struct lxc_list *iterator;
struct lxc_netdev *netdev;
if (netpipe == -1)
return 0;
lxc_list_for_each(iterator, network) {
netdev = iterator->elem;
if (netdev->type != LXC_NET_VETH)
continue;
netdev->name = malloc(IFNAMSIZ);
if (!netdev->name) {
ERROR("Out of memory.");
close(netpipe);
return -1;
}
if (read(netpipe, netdev->name, IFNAMSIZ) != IFNAMSIZ) {
close(netpipe);
return -1;
}
}
close(netpipe);
return 0;
}
static int do_start(void *data)
{
struct lxc_list *iterator;
......@@ -884,8 +840,10 @@ static int do_start(void *data)
if (lxc_sync_barrier_parent(handler, LXC_SYNC_CONFIGURE))
return -1;
if (read_unpriv_netifindex(&handler->conf->network) < 0)
if (lxc_network_recv_veth_names_from_parent(handler) < 0) {
ERROR("Failed to receive veth names from parent");
goto out_warn_father;
}
/* If we are in a new user namespace, become root there to have
* privilege over our namespace.
......@@ -1099,46 +1057,14 @@ out_error:
return -1;
}
static int save_phys_nics(struct lxc_conf *conf)
{
struct lxc_list *iterator;
int am_root = (getuid() == 0);
if (!am_root)
return 0;
lxc_list_for_each(iterator, &conf->network) {
struct lxc_netdev *netdev = iterator->elem;
if (netdev->type != LXC_NET_PHYS)
continue;
conf->saved_nics = realloc(conf->saved_nics,
(conf->num_savednics+1)*sizeof(struct saved_nic));
if (!conf->saved_nics)
return -1;
conf->saved_nics[conf->num_savednics].ifindex = netdev->ifindex;
conf->saved_nics[conf->num_savednics].orig_name = strdup(netdev->link);
if (!conf->saved_nics[conf->num_savednics].orig_name)
return -1;
INFO("Stored saved_nic #%d idx %d name %s.", conf->num_savednics,
conf->saved_nics[conf->num_savednics].ifindex,
conf->saved_nics[conf->num_savednics].orig_name);
conf->num_savednics++;
}
return 0;
}
static int lxc_recv_ttys_from_child(struct lxc_handler *handler)
{
int i;
int *ttyfds;
struct lxc_pty_info *pty_info;
int ret = -1;
int sock = handler->ttysock[1];
int sock = handler->data_sock[1];
struct lxc_conf *conf = handler->conf;
struct lxc_tty_info *tty_info = &conf->tty_info;
size_t num_ttyfds = (2 * conf->tty);
if (!conf->tty)
return 0;
......@@ -1147,29 +1073,27 @@ static int lxc_recv_ttys_from_child(struct lxc_handler *handler)
if (!tty_info->pty_info)
return -1;
ttyfds = malloc(num_ttyfds * sizeof(int));
if (!ttyfds)
return -1;
for (i = 0; i < conf->tty; i++) {
int ttyfds[2];
ret = lxc_abstract_unix_recv_fds(sock, ttyfds, num_ttyfds, NULL, 0);
for (i = 0; (ret >= 0 && *ttyfds != -1) && (i < num_ttyfds); i++) {
pty_info = &tty_info->pty_info[i / 2];
ret = lxc_abstract_unix_recv_fds(sock, ttyfds, 2, NULL, 0);
if (ret < 0)
break;
pty_info = &tty_info->pty_info[i];
pty_info->busy = 0;
pty_info->slave = ttyfds[i++];
pty_info->master = ttyfds[i];
TRACE("received pty with master fd %d and slave fd %d from "
pty_info->master = ttyfds[0];
pty_info->slave = ttyfds[1];
TRACE("Received pty with master fd %d and slave fd %d from "
"parent", pty_info->master, pty_info->slave);
}
tty_info->nbtty = conf->tty;
free(ttyfds);
if (ret < 0)
ERROR("failed to receive %d ttys from child: %s", conf->tty,
ERROR("Failed to receive %d ttys from child: %s", conf->tty,
strerror(errno));
else
TRACE("received %d ttys from child", conf->tty);
TRACE("Received %d ttys from child", conf->tty);
tty_info->nbtty = conf->tty;
return ret;
}
......@@ -1208,16 +1132,14 @@ void resolve_clone_flags(struct lxc_handler *handler)
*/
static int lxc_spawn(struct lxc_handler *handler)
{
int failed_before_rename = 0;
int i, flags, ret;
const char *name = handler->name;
bool cgroups_connected = false;
int saved_ns_fd[LXC_NS_MAX];
int preserve_mask = 0, i, flags;
int netpipepair[2], nveths;
bool wants_to_map_ids;
int saved_ns_fd[LXC_NS_MAX];
struct lxc_list *id_map;
int failed_before_rename = 0, preserve_mask = 0;
bool cgroups_connected = false;
netpipe = -1;
id_map = &handler->conf->id_map;
wants_to_map_ids = !lxc_list_empty(id_map);
......@@ -1228,7 +1150,9 @@ static int lxc_spawn(struct lxc_handler *handler)
if (lxc_sync_init(handler))
return -1;
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, handler->ttysock) < 0) {
ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0,
handler->data_sock);
if (ret < 0) {
lxc_sync_fini(handler);
return -1;
}
......@@ -1258,11 +1182,6 @@ static int lxc_spawn(struct lxc_handler *handler)
return -1;
}
}
if (save_phys_nics(handler->conf)) {
ERROR("Failed to save physical nic info.");
goto out_abort;
}
}
if (!cgroup_init(handler)) {
......@@ -1293,15 +1212,6 @@ static int lxc_spawn(struct lxc_handler *handler)
if (attach_ns(handler->conf->inherit_ns_fd) < 0)
goto out_delete_net;
if (am_unpriv() && (nveths = count_veths(&handler->conf->network))) {
if (pipe(netpipepair) < 0) {
SYSERROR("Failed to create pipe.");
goto out_delete_net;
}
/* Store netpipe in the global var for do_start's use. */
netpipe = netpipepair[0];
}
/* Create a process in a new set of namespaces. */
flags = handler->clone_flags;
if (handler->clone_flags & CLONE_NEWUSER) {
......@@ -1391,29 +1301,11 @@ static int lxc_spawn(struct lxc_handler *handler)
ERROR("Failed to create the configured network.");
goto out_delete_net;
}
/* Now all networks are created and moved into place. The
* corresponding structs have now all been filled. So log them
* for debugging purposes.
*/
lxc_log_configured_netdevs(handler->conf);
}
if (netpipe != -1) {
struct lxc_list *iterator;
struct lxc_netdev *netdev;
close(netpipe);
lxc_list_for_each(iterator, &handler->conf->network) {
netdev = iterator->elem;
if (netdev->type != LXC_NET_VETH)
continue;
if (write(netpipepair[1], netdev->name, IFNAMSIZ) != IFNAMSIZ) {
ERROR("Error writing veth name to container.");
goto out_delete_net;
}
}
close(netpipepair[1]);
if (lxc_network_send_veth_names_to_child(handler) < 0) {
ERROR("Failed to send veth names to child");
goto out_delete_net;
}
/* Tell the child to continue its initialization. We'll get
......@@ -1448,6 +1340,19 @@ static int lxc_spawn(struct lxc_handler *handler)
if (lxc_sync_barrier_child(handler, LXC_SYNC_POST_CGROUP))
return -1;
if (lxc_network_recv_name_and_ifindex_from_child(handler) < 0) {
ERROR("Failed to receive names and ifindices for network "
"devices from child");
goto out_delete_net;
}
/* Now all networks are created, network devices are moved into place,
* and the correct names and ifindeces in the respective namespaces have
* been recorded. The corresponding structs have now all been filled. So
* log them for debugging purposes.
*/
lxc_log_configured_netdevs(handler->conf);
/* Read tty fds allocated by child. */
if (lxc_recv_ttys_from_child(handler) < 0) {
ERROR("Failed to receive tty info from child process.");
......@@ -1575,8 +1480,10 @@ int __lxc_start(const char *name, struct lxc_handler *handler,
}
}
DEBUG("Pushing physical nics back to host namespace");
lxc_restore_phys_nics_to_netns(handler->netnsfd, handler->conf);
err = lxc_restore_phys_nics_to_netns(handler);
if (err < 0)
ERROR("Failed to move physical network devices back to parent "
"network namespace");
if (handler->pinfd >= 0) {
close(handler->pinfd);
......@@ -1657,7 +1564,7 @@ static void lxc_destroy_container_on_signal(struct lxc_handler *handler,
int ret = 0;
struct lxc_container *c;
if (handler->conf->rootfs.path && handler->conf->rootfs.mount) {
bret = do_destroy_container(handler->conf);
bret = do_destroy_container(handler);
if (!bret) {
ERROR("Error destroying rootfs for container \"%s\".", name);
return;
......@@ -1683,7 +1590,7 @@ static void lxc_destroy_container_on_signal(struct lxc_handler *handler,
}
}
if (am_unpriv())
if (!handler->am_root)
ret = userns_exec_1(handler->conf, lxc_rmdir_onedev_wrapper,
destroy, "lxc_rmdir_onedev_wrapper");
else
......@@ -1702,14 +1609,14 @@ static int lxc_rmdir_onedev_wrapper(void *data)
return lxc_rmdir_onedev(arg, NULL);
}
static bool do_destroy_container(struct lxc_conf *conf) {
if (am_unpriv()) {
if (userns_exec_1(conf, storage_destroy_wrapper, conf,
"storage_destroy_wrapper") < 0)
static bool do_destroy_container(struct lxc_handler *handler) {
if (!handler->am_root) {
if (userns_exec_1(handler->conf, storage_destroy_wrapper,
handler->conf, "storage_destroy_wrapper") < 0)
return false;
return true;
}
return storage_destroy(conf);
return storage_destroy(handler->conf);
}
......@@ -35,6 +35,7 @@
#include "namespace.h"
struct lxc_handler {
bool am_root;
pid_t pid;
char *name;
lxc_state_t state;
......@@ -49,8 +50,10 @@ struct lxc_handler {
const char *lxcpath;
void *cgroup_data;
/* socketpair for child->parent tty fd passing */
int ttysock[2];
/* Abstract unix domain SOCK_DGRAM socketpair to pass arbitrary data
* between child and parent.
*/
int data_sock[2];
/* indicates whether should we close std{in,out,err} on start */
bool backgrounded;
......
......@@ -2409,3 +2409,24 @@ bool has_fs_type(const char *path, fs_type_magic magic_val)
return has_type;
}
bool lxc_nic_exists(char *nic)
{
#define __LXC_SYS_CLASS_NET_LEN 15 + IFNAMSIZ + 1
char path[__LXC_SYS_CLASS_NET_LEN];
int ret;
struct stat sb;
if (!strcmp(nic, "none"))
return true;
ret = snprintf(path, __LXC_SYS_CLASS_NET_LEN, "/sys/class/net/%s", nic);
if (ret < 0 || (size_t)ret >= __LXC_SYS_CLASS_NET_LEN)
return false;
ret = stat(path, &sb);
if (ret < 0)
return false;
return true;
}
......@@ -402,5 +402,6 @@ extern void *must_realloc(void *orig, size_t sz);
typedef __typeof__(((struct statfs *)NULL)->f_type) fs_type_magic;
extern bool has_fs_type(const char *path, fs_type_magic magic_val);
extern bool is_fs_type(const struct statfs *fs, fs_type_magic magic_val);
extern bool lxc_nic_exists(char *nic);
#endif /* __LXC_UTILS_H */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment