Commit 94a182af by Serge Hallyn Committed by GitHub

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

lxc-user-nic: test privilege over netns on delete
parents 70a49815 74c6e2b0
...@@ -261,6 +261,12 @@ void lxc_log_configured_netdevs(const struct lxc_conf *conf) ...@@ -261,6 +261,12 @@ void lxc_log_configured_netdevs(const struct lxc_conf *conf)
if (netdev->priv.veth_attr.pair) if (netdev->priv.veth_attr.pair)
TRACE("veth pair: %s", TRACE("veth pair: %s",
netdev->priv.veth_attr.pair); netdev->priv.veth_attr.pair);
if (netdev->priv.veth_attr.veth1[0] != '\0')
TRACE("veth1 : %s",
netdev->priv.veth_attr.veth1);
if (netdev->priv.veth_attr.ifindex > 0)
TRACE("host side ifindex for veth device: %d",
netdev->priv.veth_attr.ifindex);
break; break;
case LXC_NET_MACVLAN: case LXC_NET_MACVLAN:
TRACE("type: macvlan"); TRACE("type: macvlan");
......
...@@ -797,7 +797,7 @@ again: ...@@ -797,7 +797,7 @@ again:
} }
static char *lxc_secure_rename_in_ns(int pid, char *oldname, char *newname, static char *lxc_secure_rename_in_ns(int pid, char *oldname, char *newname,
int *ifidx) int *container_veth_ifidx)
{ {
int ret; int ret;
uid_t ruid, suid, euid; uid_t ruid, suid, euid;
...@@ -881,7 +881,7 @@ static char *lxc_secure_rename_in_ns(int pid, char *oldname, char *newname, ...@@ -881,7 +881,7 @@ static char *lxc_secure_rename_in_ns(int pid, char *oldname, char *newname,
/* Allocation failure for strdup() is checked below. */ /* Allocation failure for strdup() is checked below. */
name = strdup(ifname); name = strdup(ifname);
string_ret = name; string_ret = name;
*ifidx = ifindex; *container_veth_ifidx = ifindex;
do_full_cleanup: do_full_cleanup:
ret = setresuid(ruid, euid, suid); ret = setresuid(ruid, euid, suid);
...@@ -976,13 +976,89 @@ struct user_nic_args { ...@@ -976,13 +976,89 @@ struct user_nic_args {
#define LXC_USERNIC_CREATE 0 #define LXC_USERNIC_CREATE 0
#define LXC_USERNIC_DELETE 1 #define LXC_USERNIC_DELETE 1
static bool is_privileged_over_netns(int netns_fd)
{
int ret;
uid_t euid, ruid, suid;
bool bret = false;
int ofd = -1;
ofd = lxc_preserve_ns(getpid(), "net");
if (ofd < 0) {
usernic_error("Failed opening network namespace path for %d", getpid());
return false;
}
ret = getresuid(&ruid, &euid, &suid);
if (ret < 0) {
usernic_error("Failed to retrieve real, effective, and saved "
"user IDs: %s\n",
strerror(errno));
goto do_partial_cleanup;
}
ret = setns(netns_fd, CLONE_NEWNET);
if (ret < 0) {
usernic_error("Failed to setns() to network namespace %s\n",
strerror(errno));
goto do_partial_cleanup;
}
ret = setresuid(ruid, ruid, 0);
if (ret < 0) {
usernic_error("Failed to drop privilege by setting effective "
"user id and real user id to %d, and saved user "
"ID to 0: %s\n",
ruid, strerror(errno));
/* It's ok to jump to do_full_cleanup here since setresuid()
* will succeed when trying to set real, effective, and saved to
* values they currently have.
*/
goto do_full_cleanup;
}
/* Test whether we are privileged over the network namespace. To do this
* we try to delete the loopback interface which is not possible. If we
* are privileged over the network namespace we will get ENOTSUP. If we
* are not privileged over the network namespace we will get EPERM.
*/
ret = lxc_netdev_delete_by_name("lo");
if (ret == -ENOTSUP)
bret = true;
do_full_cleanup:
ret = setresuid(ruid, euid, suid);
if (ret < 0) {
usernic_error("Failed to restore privilege by setting "
"effective user id to %d, real user id to %d, "
"and saved user ID to %d: %s\n", ruid, euid, suid,
strerror(errno));
bret = false;
}
ret = setns(ofd, CLONE_NEWNET);
if (ret < 0) {
usernic_error("Failed to setns() to original network namespace "
"of PID %d: %s\n", ofd, strerror(errno));
bret = false;
}
do_partial_cleanup:
close(ofd);
return bret;
}
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
int fd, ifindex, n, pid, request, ret; int container_veth_ifidx, fd, host_veth_ifidx, n, pid, request, ret;
char *me, *newname; char *me, *newname;
struct user_nic_args args;
int netns_fd = -1;
char *cnic = NULL, *nicname = NULL; char *cnic = NULL, *nicname = NULL;
struct alloted_s *alloted = NULL; struct alloted_s *alloted = NULL;
struct user_nic_args args;
if (argc < 7 || argc > 8) { if (argc < 7 || argc > 8) {
usage(argv[0], true); usage(argv[0], true);
...@@ -1027,27 +1103,51 @@ int main(int argc, char *argv[]) ...@@ -1027,27 +1103,51 @@ int main(int argc, char *argv[])
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (request == LXC_USERNIC_CREATE) {
ret = lxc_safe_int(args.pid, &pid); ret = lxc_safe_int(args.pid, &pid);
if (ret < 0) { if (ret < 0) {
usernic_error("Could not read pid: %s\n", args.pid); usernic_error("Could not read pid: %s\n", args.pid);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} else if (request == LXC_USERNIC_DELETE) {
netns_fd = open(args.pid, O_RDONLY);
if (netns_fd < 0) {
usernic_error("Could not open \"%s\": %s\n", args.pid,
strerror(errno));
exit(EXIT_FAILURE);
}
}
if (!create_db_dir(LXC_USERNIC_DB)) { if (!create_db_dir(LXC_USERNIC_DB)) {
usernic_error("%s", "Failed to create directory for db file\n"); usernic_error("%s", "Failed to create directory for db file\n");
if (netns_fd >= 0)
close(netns_fd);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
fd = open_and_lock(LXC_USERNIC_DB); fd = open_and_lock(LXC_USERNIC_DB);
if (fd < 0) { if (fd < 0) {
usernic_error("Failed to lock %s\n", LXC_USERNIC_DB); usernic_error("Failed to lock %s\n", LXC_USERNIC_DB);
if (netns_fd >= 0)
close(netns_fd);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (request == LXC_USERNIC_CREATE) {
if (!may_access_netns(pid)) { if (!may_access_netns(pid)) {
usernic_error("User %s may not modify netns for pid %d\n", me, pid); usernic_error("User %s may not modify netns for pid %d\n", me, pid);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} else if (request == LXC_USERNIC_DELETE) {
bool has_priv;
has_priv = is_privileged_over_netns(netns_fd);
close(netns_fd);
if (!has_priv) {
usernic_error("%s", "Process is not privileged over "
"network namespace\n");
exit(EXIT_FAILURE);
}
}
n = get_alloted(me, args.type, args.link, &alloted); n = get_alloted(me, args.type, args.link, &alloted);
...@@ -1104,7 +1204,8 @@ int main(int argc, char *argv[]) ...@@ -1104,7 +1204,8 @@ int main(int argc, char *argv[])
} }
/* Now rename the link. */ /* Now rename the link. */
newname = lxc_secure_rename_in_ns(pid, cnic, args.veth_name, &ifindex); newname = lxc_secure_rename_in_ns(pid, cnic, args.veth_name,
&container_veth_ifidx);
if (!newname) { if (!newname) {
usernic_error("%s", "Failed to rename the link\n"); usernic_error("%s", "Failed to rename the link\n");
ret = lxc_netdev_delete_by_name(cnic); ret = lxc_netdev_delete_by_name(cnic);
...@@ -1113,9 +1214,13 @@ int main(int argc, char *argv[]) ...@@ -1113,9 +1214,13 @@ int main(int argc, char *argv[])
free(nicname); free(nicname);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
host_veth_ifidx = if_nametoindex(nicname);
/* Write the name of the interface pair to the stdout: eth0:veth9MT2L4 */ /* Write names of veth pairs and their ifindeces to stout:
fprintf(stdout, "%s:%s:%d\n", newname, nicname, ifindex); * (e.g. eth0:731:veth9MT2L4:730)
*/
fprintf(stdout, "%s:%d:%s:%d\n", newname, container_veth_ifidx, nicname,
host_veth_ifidx);
free(newname); free(newname);
free(nicname); free(nicname);
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
......
...@@ -47,7 +47,6 @@ ...@@ -47,7 +47,6 @@
#include "conf.h" #include "conf.h"
#include "config.h" #include "config.h"
#include "confile_utils.h"
#include "log.h" #include "log.h"
#include "network.h" #include "network.h"
#include "nl.h" #include "nl.h"
...@@ -2033,7 +2032,7 @@ int lxc_find_gateway_addresses(struct lxc_handler *handler) ...@@ -2033,7 +2032,7 @@ int lxc_find_gateway_addresses(struct lxc_handler *handler)
} }
#define LXC_USERNIC_PATH LIBEXECDIR "/lxc/lxc-user-nic" #define LXC_USERNIC_PATH LIBEXECDIR "/lxc/lxc-user-nic"
static int lxc_create_network_unpriv(const char *lxcpath, char *lxcname, static int lxc_create_network_unpriv_exec(const char *lxcpath, char *lxcname,
struct lxc_netdev *netdev, pid_t pid) struct lxc_netdev *netdev, pid_t pid)
{ {
int ret; int ret;
...@@ -2107,7 +2106,7 @@ static int lxc_create_network_unpriv(const char *lxcpath, char *lxcname, ...@@ -2107,7 +2106,7 @@ static int lxc_create_network_unpriv(const char *lxcpath, char *lxcname,
bytes = read(pipefd[0], &buffer, MAXPATHLEN); bytes = read(pipefd[0], &buffer, MAXPATHLEN);
if (bytes < 0) { if (bytes < 0) {
SYSERROR("Failed to read from pipe file descriptor."); SYSERROR("Failed to read from pipe file descriptor");
close(pipefd[0]); close(pipefd[0]);
return -1; return -1;
} }
...@@ -2124,44 +2123,67 @@ static int lxc_create_network_unpriv(const char *lxcpath, char *lxcname, ...@@ -2124,44 +2123,67 @@ static int lxc_create_network_unpriv(const char *lxcpath, char *lxcname,
/* netdev->name */ /* netdev->name */
token = strtok_r(buffer, ":", &saveptr); token = strtok_r(buffer, ":", &saveptr);
if (!token) if (!token) {
ERROR("Failed to parse lxc-user-nic output");
return -1; return -1;
}
netdev->name = malloc(IFNAMSIZ + 1); netdev->name = malloc(IFNAMSIZ + 1);
if (!netdev->name) { if (!netdev->name) {
SYSERROR("Failed to allocate memory."); SYSERROR("Failed to allocate memory");
return -1; return -1;
} }
memset(netdev->name, 0, IFNAMSIZ + 1); memset(netdev->name, 0, IFNAMSIZ + 1);
strncpy(netdev->name, token, IFNAMSIZ); strncpy(netdev->name, token, IFNAMSIZ);
/* netdev->priv.veth_attr.pair */ /* netdev->ifindex */
token = strtok_r(NULL, ":", &saveptr); token = strtok_r(NULL, ":", &saveptr);
if (!token) if (!token) {
ERROR("Failed to parse lxc-user-nic output");
return -1; return -1;
}
netdev->priv.veth_attr.pair = strdup(token); ret = lxc_safe_int(token, &netdev->ifindex);
if (!netdev->priv.veth_attr.pair) { if (ret < 0) {
ERROR("Failed to allocate memory."); ERROR("%s - Failed to convert string \"%s\" to integer",
strerror(-ret), token);
return -1; return -1;
} }
/* netdev->ifindex */ /* netdev->priv.veth_attr.veth1 */
token = strtok_r(NULL, ":", &saveptr); token = strtok_r(NULL, ":", &saveptr);
if (!token) if (!token) {
ERROR("Failed to parse lxc-user-nic output");
return -1; return -1;
}
ret = lxc_safe_int(token, &netdev->ifindex); if (strlen(token) >= IFNAMSIZ) {
ERROR("Host side veth device name returned by lxc-user-nic is "
"too long");
return -E2BIG;
}
strcpy(netdev->priv.veth_attr.veth1, token);
/* netdev->priv.veth_attr.ifindex */
token = strtok_r(NULL, ":", &saveptr);
if (!token) {
ERROR("Failed to parse lxc-user-nic output");
return -1;
}
ret = lxc_safe_int(token, &netdev->priv.veth_attr.ifindex);
if (ret < 0) { if (ret < 0) {
ERROR("Failed to parse ifindex for network device \"%s\"", netdev->name); ERROR("%s - Failed to convert string \"%s\" to integer",
strerror(-ret), token);
return -1; return -1;
} }
return 0; return 0;
} }
static int lxc_delete_network_unpriv(const char *lxcpath, char *lxcname, static int lxc_delete_network_unpriv_exec(const char *lxcpath, char *lxcname,
struct lxc_netdev *netdev, pid_t pid) struct lxc_netdev *netdev,
const char *netns_path)
{ {
int bytes, ret; int bytes, ret;
pid_t child; pid_t child;
...@@ -2189,7 +2211,6 @@ static int lxc_delete_network_unpriv(const char *lxcpath, char *lxcname, ...@@ -2189,7 +2211,6 @@ static int lxc_delete_network_unpriv(const char *lxcpath, char *lxcname,
if (child == 0) { if (child == 0) {
int ret; int ret;
char pidstr[LXC_NUMSTRLEN64];
close(pipefd[0]); close(pipefd[0]);
...@@ -2202,20 +2223,22 @@ static int lxc_delete_network_unpriv(const char *lxcpath, char *lxcname, ...@@ -2202,20 +2223,22 @@ static int lxc_delete_network_unpriv(const char *lxcpath, char *lxcname,
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (!netdev->link) if (netdev->priv.veth_attr.veth1[0] == '\0') {
SYSERROR("Network link for network device \"%s\" is " SYSERROR("Host side veth device name is missing");
"missing", netdev->priv.veth_attr.pair); exit(EXIT_FAILURE);
}
ret = snprintf(pidstr, LXC_NUMSTRLEN64, "%d", pid); if (!netdev->link) {
if (ret < 0 || ret >= LXC_NUMSTRLEN64) SYSERROR("Network link for network device \"%s\" is "
"missing", netdev->priv.veth_attr.veth1);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
pidstr[LXC_NUMSTRLEN64 - 1] = '\0'; }
INFO("Execing lxc-user-nic delete %s %s %s veth %s %s", lxcpath, INFO("Execing lxc-user-nic delete %s %s %s veth %s %s", lxcpath,
lxcname, pidstr, netdev->link, netdev->priv.veth_attr.pair); lxcname, netns_path, netdev->link, netdev->priv.veth_attr.veth1);
execlp(LXC_USERNIC_PATH, LXC_USERNIC_PATH, "delete", lxcpath, execlp(LXC_USERNIC_PATH, LXC_USERNIC_PATH, "delete", lxcpath,
lxcname, pidstr, "veth", netdev->link, lxcname, netns_path, "veth", netdev->link,
netdev->priv.veth_attr.pair, (char *)NULL); netdev->priv.veth_attr.veth1, (char *)NULL);
SYSERROR("Failed to exec lxc-user-nic."); SYSERROR("Failed to exec lxc-user-nic.");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
...@@ -2242,6 +2265,91 @@ static int lxc_delete_network_unpriv(const char *lxcpath, char *lxcname, ...@@ -2242,6 +2265,91 @@ static int lxc_delete_network_unpriv(const char *lxcpath, char *lxcname,
return 0; return 0;
} }
bool lxc_delete_network_unpriv(struct lxc_handler *handler)
{
int ret;
struct lxc_list *iterator;
struct lxc_list *network = &handler->conf->network;
/* strlen("/proc/") = 6
* +
* LXC_NUMSTRLEN64
* +
* strlen("/fd/") = 4
* +
* LXC_NUMSTRLEN64
* +
* \0
*/
char netns_path[6 + LXC_NUMSTRLEN64 + 4 + LXC_NUMSTRLEN64 + 1];
bool deleted_all = true;
if (!am_unpriv())
return true;
*netns_path = '\0';
if (handler->netnsfd < 0) {
DEBUG("Cannot not guarantee safe deletion of network devices. "
"Manual cleanup maybe needed");
return false;
}
ret = snprintf(netns_path, sizeof(netns_path), "/proc/%d/fd/%d",
getpid(), handler->netnsfd);
if (ret < 0 || ret >= sizeof(netns_path))
return false;
lxc_list_for_each(iterator, network) {
char *hostveth = NULL;
struct lxc_netdev *netdev = iterator->elem;
/* We can only delete devices whose ifindex we have. If we don't
* have the index it means that we didn't create it.
*/
if (!netdev->ifindex)
continue;
if (netdev->type == LXC_NET_PHYS) {
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);
else
TRACE("Renamed interface with index %d to its "
"initial name \"%s\"",
netdev->ifindex, netdev->link);
continue;
}
ret = netdev_deconf[netdev->type](handler, netdev);
if (ret < 0)
WARN("Failed to deconfigure network device");
if (netdev->type != LXC_NET_VETH)
continue;
if (!is_ovs_bridge(netdev->link))
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);
continue;
}
INFO("Removed interface \"%s\" from \"%s\"", hostveth,
netdev->link);
}
return deleted_all;
}
int lxc_create_network_priv(struct lxc_handler *handler) int lxc_create_network_priv(struct lxc_handler *handler)
{ {
bool am_root; bool am_root;
...@@ -2271,33 +2379,19 @@ int lxc_create_network_priv(struct lxc_handler *handler) ...@@ -2271,33 +2379,19 @@ int lxc_create_network_priv(struct lxc_handler *handler)
return 0; return 0;
} }
int lxc_create_network(const char *lxcpath, char *lxcname, int lxc_network_move_created_netdev_priv(const char *lxcpath, char *lxcname,
struct lxc_list *network, pid_t pid) struct lxc_list *network, pid_t pid)
{ {
int err; int err;
bool am_root;
char ifname[IFNAMSIZ]; char ifname[IFNAMSIZ];
struct lxc_list *iterator; struct lxc_list *iterator;
am_root = (getuid() == 0); if (am_unpriv())
return 0;
lxc_list_for_each(iterator, network) { lxc_list_for_each(iterator, network) {
struct lxc_netdev *netdev = iterator->elem; struct lxc_netdev *netdev = iterator->elem;
if (netdev->type == LXC_NET_VETH && !am_root) {
if (netdev->mtu)
INFO("mtu ignored due to insufficient privilege");
if (lxc_create_network_unpriv(lxcpath, lxcname, netdev, pid))
return -1;
/* lxc-user-nic has moved the nic to the new ns.
* unpriv_assign_nic() fills in netdev->name.
* netdev->ifindex will be filled in at
* lxc_setup_netdev_in_child_namespaces().
*/
continue;
}
/* empty network namespace, nothing to move */
if (!netdev->ifindex) if (!netdev->ifindex)
continue; continue;
...@@ -2324,13 +2418,50 @@ int lxc_create_network(const char *lxcpath, char *lxcname, ...@@ -2324,13 +2418,50 @@ int lxc_create_network(const char *lxcpath, char *lxcname,
return 0; return 0;
} }
bool lxc_delete_network(struct lxc_handler *handler) int lxc_create_network_unpriv(const char *lxcpath, char *lxcname,
struct lxc_list *network, pid_t pid)
{
struct lxc_list *iterator;
if (!am_unpriv())
return 0;
lxc_list_for_each(iterator, network) {
struct lxc_netdev *netdev = iterator->elem;
if (netdev->type == LXC_NET_EMPTY)
continue;
if (netdev->type == LXC_NET_NONE)
continue;
if (netdev->type != LXC_NET_VETH) {
ERROR("Networks of type %s are not supported by "
"unprivileged containers",
lxc_net_type_to_str(netdev->type));
return -1;
}
if (netdev->mtu)
INFO("mtu ignored due to insufficient privilege");
if (lxc_create_network_unpriv_exec(lxcpath, lxcname, netdev, pid))
return -1;
}
return 0;
}
bool lxc_delete_network_priv(struct lxc_handler *handler)
{ {
int ret; int ret;
struct lxc_list *iterator; struct lxc_list *iterator;
struct lxc_list *network = &handler->conf->network; struct lxc_list *network = &handler->conf->network;
bool deleted_all = true; bool deleted_all = true;
if (am_unpriv())
return true;
lxc_list_for_each(iterator, network) { lxc_list_for_each(iterator, network) {
char *hostveth = NULL; char *hostveth = NULL;
struct lxc_netdev *netdev = iterator->elem; struct lxc_netdev *netdev = iterator->elem;
...@@ -2362,7 +2493,6 @@ bool lxc_delete_network(struct lxc_handler *handler) ...@@ -2362,7 +2493,6 @@ bool lxc_delete_network(struct lxc_handler *handler)
* namespace is destroyed but in case we did not move the * namespace is destroyed but in case we did not move the
* interface to the network namespace, we have to destroy it. * interface to the network namespace, we have to destroy it.
*/ */
if (!am_unpriv()) {
ret = lxc_netdev_delete_by_index(netdev->ifindex); ret = lxc_netdev_delete_by_index(netdev->ifindex);
if (-ret == ENODEV) { if (-ret == ENODEV) {
INFO("Interface \"%s\" with index %d already " INFO("Interface \"%s\" with index %d already "
...@@ -2381,26 +2511,10 @@ bool lxc_delete_network(struct lxc_handler *handler) ...@@ -2381,26 +2511,10 @@ bool lxc_delete_network(struct lxc_handler *handler)
INFO("Removed interface \"%s\" with index %d", INFO("Removed interface \"%s\" with index %d",
netdev->name ? netdev->name : "(null)", netdev->name ? netdev->name : "(null)",
netdev->ifindex); netdev->ifindex);
}
if (netdev->type != LXC_NET_VETH) if (netdev->type != LXC_NET_VETH)
continue; continue;
if (am_unpriv()) {
if (is_ovs_bridge(netdev->link)) {
ret = lxc_delete_network_unpriv(handler->lxcpath,
handler->name,
netdev, getpid());
if (ret < 0)
WARN("Failed to remove port \"%s\" "
"from openvswitch bridge \"%s\"",
netdev->priv.veth_attr.pair,
netdev->link);
}
continue;
}
/* Explicitly delete host veth device to prevent lingering /* Explicitly delete host veth device to prevent lingering
* devices. We had issues in LXD around this. * devices. We had issues in LXD around this.
*/ */
...@@ -2788,7 +2902,7 @@ static int lxc_setup_netdev_in_child_namespaces(struct lxc_netdev *netdev) ...@@ -2788,7 +2902,7 @@ static int lxc_setup_netdev_in_child_namespaces(struct lxc_netdev *netdev)
} }
} }
DEBUG("Network devie \"%s\" has been setup", current_ifname); DEBUG("Network device \"%s\" has been setup", current_ifname);
return 0; return 0;
} }
...@@ -2799,8 +2913,6 @@ int lxc_setup_network_in_child_namespaces(const struct lxc_conf *conf, ...@@ -2799,8 +2913,6 @@ int lxc_setup_network_in_child_namespaces(const struct lxc_conf *conf,
struct lxc_list *iterator; struct lxc_list *iterator;
struct lxc_netdev *netdev; struct lxc_netdev *netdev;
lxc_log_configured_netdevs(conf);
lxc_list_for_each(iterator, network) { lxc_list_for_each(iterator, network) {
netdev = iterator->elem; netdev = iterator->elem;
......
...@@ -79,9 +79,21 @@ struct lxc_route6 { ...@@ -79,9 +79,21 @@ struct lxc_route6 {
struct in6_addr addr; struct in6_addr addr;
}; };
/* Contains information about the host side veth device.
* @pair : Name of the host side veth device.
* If the user requested that the host veth device be created with a
* specific names this field will be set. If this field is set @veth1
* is not set.
* @veth1 : Name of the host side veth device.
* If the user did not request that the host veth device be created
* with a specific name this field will be set. If this field is set
* @pair is not set.
* @ifindex : Ifindex of the network device.
*/
struct ifla_veth { struct ifla_veth {
char *pair; /* pair name */ char *pair;
char veth1[IFNAMSIZ]; /* needed for deconf */ char veth1[IFNAMSIZ];
int ifindex;
}; };
struct ifla_vlan { struct ifla_vlan {
...@@ -103,20 +115,42 @@ union netdev_p { ...@@ -103,20 +115,42 @@ union netdev_p {
/* /*
* Defines a structure to configure a network device * Defines a structure to configure a network device
* @link : lxc.net.[i].link, name of bridge or host iface to attach if any * @idx : network counter
* @name : lxc.net.[i].name, name of iface on the container side * @ifindex : ifindex of the network device
* Note that this is the ifindex of the network device in
* the container's network namespace. If the network device
* consists of a pair of network devices (e.g. veth pairs
* attached to a network bridge) then this index cannot be
* used to identify or modify the host veth device. See
* struct ifla_veth for the host side information.
* @type : network type (veth, macvlan, vlan, ...)
* @flags : flag of the network device (IFF_UP, ... ) * @flags : flag of the network device (IFF_UP, ... )
* @link : lxc.net.[i].link, name of bridge or host iface to attach
* if any
* @name : lxc.net.[i].name, name of iface on the container side
* @hwaddr : mac address
* @mtu : maximum transmission unit
* @priv : information specific to the specificed network type
* Note that this is a union so whether accessing a struct
* is possible is dependent on the network type.
* @ipv4 : a list of ipv4 addresses to be set on the network device * @ipv4 : a list of ipv4 addresses to be set on the network device
* @ipv6 : a list of ipv6 addresses to be set on the network device * @ipv6 : a list of ipv6 addresses to be set on the network device
* @upscript : a script filename to be executed during interface configuration * @ipv4_gateway_auto : whether the ipv4 gateway is to be automatically gathered
* @downscript : a script filename to be executed during interface destruction * from the associated @link
* @idx : network counter * @ipv4_gateway : ipv4 gateway
* @ipv6_gateway_auto : whether the ipv6 gateway is to be automatically gathered
* from the associated @link
* @ipv6_gateway : ipv6 gateway
* @upscript : a script filename to be executed during interface
* configuration
* @downscript : a script filename to be executed during interface
* destruction
*/ */
struct lxc_netdev { struct lxc_netdev {
ssize_t idx; ssize_t idx;
int ifindex;
int type; int type;
int flags; int flags;
int ifindex;
char *link; char *link;
char *name; char *name;
char *hwaddr; char *hwaddr;
...@@ -124,10 +158,10 @@ struct lxc_netdev { ...@@ -124,10 +158,10 @@ struct lxc_netdev {
union netdev_p priv; union netdev_p priv;
struct lxc_list ipv4; struct lxc_list ipv4;
struct lxc_list ipv6; struct lxc_list ipv6;
struct in_addr *ipv4_gateway;
bool ipv4_gateway_auto; bool ipv4_gateway_auto;
struct in6_addr *ipv6_gateway; struct in_addr *ipv4_gateway;
bool ipv6_gateway_auto; bool ipv6_gateway_auto;
struct in6_addr *ipv6_gateway;
char *upscript; char *upscript;
char *downscript; char *downscript;
}; };
...@@ -224,9 +258,14 @@ extern const char *lxc_net_type_to_str(int type); ...@@ -224,9 +258,14 @@ extern const char *lxc_net_type_to_str(int type);
extern int setup_private_host_hw_addr(char *veth1); extern int setup_private_host_hw_addr(char *veth1);
extern int netdev_get_mtu(int ifindex); extern int netdev_get_mtu(int ifindex);
extern int lxc_create_network_priv(struct lxc_handler *handler); extern int lxc_create_network_priv(struct lxc_handler *handler);
extern bool lxc_delete_network(struct lxc_handler *handler); extern int lxc_network_move_created_netdev_priv(const char *lxcpath,
char *lxcname,
struct lxc_list *network,
pid_t pid);
extern bool lxc_delete_network_priv(struct lxc_handler *handler);
extern bool lxc_delete_network_unpriv(struct lxc_handler *handler);
extern int lxc_find_gateway_addresses(struct lxc_handler *handler); extern int lxc_find_gateway_addresses(struct lxc_handler *handler);
extern int lxc_create_network(const char *lxcpath, char *lxcname, extern int lxc_create_network_unpriv(const char *lxcpath, char *lxcname,
struct lxc_list *network, pid_t pid); struct lxc_list *network, pid_t pid);
extern int lxc_requests_empty_network(struct lxc_handler *handler); 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 void lxc_restore_phys_nics_to_netns(int netnsfd, struct lxc_conf *conf);
......
...@@ -68,6 +68,7 @@ ...@@ -68,6 +68,7 @@
#include "commands.h" #include "commands.h"
#include "commands_utils.h" #include "commands_utils.h"
#include "conf.h" #include "conf.h"
#include "confile_utils.h"
#include "console.h" #include "console.h"
#include "error.h" #include "error.h"
#include "log.h" #include "log.h"
...@@ -809,15 +810,19 @@ static int read_unpriv_netifindex(struct lxc_list *network) ...@@ -809,15 +810,19 @@ static int read_unpriv_netifindex(struct lxc_list *network)
if (netpipe == -1) if (netpipe == -1)
return 0; return 0;
lxc_list_for_each(iterator, network) { lxc_list_for_each(iterator, network) {
netdev = iterator->elem; netdev = iterator->elem;
if (netdev->type != LXC_NET_VETH) if (netdev->type != LXC_NET_VETH)
continue; continue;
if (!(netdev->name = malloc(IFNAMSIZ))) {
netdev->name = malloc(IFNAMSIZ);
if (!netdev->name) {
ERROR("Out of memory."); ERROR("Out of memory.");
close(netpipe); close(netpipe);
return -1; return -1;
} }
if (read(netpipe, netdev->name, IFNAMSIZ) != IFNAMSIZ) { if (read(netpipe, netdev->name, IFNAMSIZ) != IFNAMSIZ) {
close(netpipe); close(netpipe);
return -1; return -1;
...@@ -1312,6 +1317,7 @@ static int lxc_spawn(struct lxc_handler *handler) ...@@ -1312,6 +1317,7 @@ static int lxc_spawn(struct lxc_handler *handler)
SYSERROR("Failed to clone a new set of namespaces."); SYSERROR("Failed to clone a new set of namespaces.");
goto out_delete_net; goto out_delete_net;
} }
for (i = 0; i < LXC_NS_MAX; i++) for (i = 0; i < LXC_NS_MAX; i++)
if (flags & ns_info[i].clone_flag) if (flags & ns_info[i].clone_flag)
INFO("Cloned %s.", ns_info[i].flag_name); INFO("Cloned %s.", ns_info[i].flag_name);
...@@ -1363,13 +1369,34 @@ static int lxc_spawn(struct lxc_handler *handler) ...@@ -1363,13 +1369,34 @@ static int lxc_spawn(struct lxc_handler *handler)
if (failed_before_rename) if (failed_before_rename)
goto out_delete_net; goto out_delete_net;
handler->netnsfd = lxc_preserve_ns(handler->pid, "net");
if (handler->netnsfd < 0) {
ERROR("Failed to preserve network namespace");
goto out_delete_net;
}
/* Create the network configuration. */ /* Create the network configuration. */
if (handler->clone_flags & CLONE_NEWNET) { if (handler->clone_flags & CLONE_NEWNET) {
if (lxc_create_network(handler->lxcpath, handler->name, if (lxc_network_move_created_netdev_priv(handler->lxcpath,
&handler->conf->network, handler->pid)) { handler->name,
&handler->conf->network,
handler->pid)) {
ERROR("Failed to create the configured network.");
goto out_delete_net;
}
if (lxc_create_network_unpriv(handler->lxcpath, handler->name,
&handler->conf->network,
handler->pid)) {
ERROR("Failed to create the configured network."); ERROR("Failed to create the configured network.");
goto out_delete_net; 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) { if (netpipe != -1) {
...@@ -1437,15 +1464,22 @@ static int lxc_spawn(struct lxc_handler *handler) ...@@ -1437,15 +1464,22 @@ static int lxc_spawn(struct lxc_handler *handler)
} }
lxc_sync_fini(handler); lxc_sync_fini(handler);
handler->netnsfd = lxc_preserve_ns(handler->pid, "net");
return 0; return 0;
out_delete_net: out_delete_net:
if (cgroups_connected) if (cgroups_connected)
cgroup_disconnect(); cgroup_disconnect();
if (handler->clone_flags & CLONE_NEWNET)
lxc_delete_network(handler); if (handler->clone_flags & CLONE_NEWNET) {
DEBUG("Tearing down network devices");
if (!lxc_delete_network_priv(handler))
DEBUG("Failed tearing down network devices");
if (!lxc_delete_network_unpriv(handler))
DEBUG("Failed tearing down network devices");
}
out_abort: out_abort:
lxc_abort(name, handler); lxc_abort(name, handler);
lxc_sync_fini(handler); lxc_sync_fini(handler);
...@@ -1454,6 +1488,11 @@ out_abort: ...@@ -1454,6 +1488,11 @@ out_abort:
handler->pinfd = -1; handler->pinfd = -1;
} }
if (handler->netnsfd >= 0) {
close(handler->netnsfd);
handler->netnsfd = -1;
}
return -1; return -1;
} }
...@@ -1463,7 +1502,6 @@ int __lxc_start(const char *name, struct lxc_handler *handler, ...@@ -1463,7 +1502,6 @@ int __lxc_start(const char *name, struct lxc_handler *handler,
{ {
int status; int status;
int err = -1; int err = -1;
bool removed_all_netdevs = true;
struct lxc_conf *conf = handler->conf; struct lxc_conf *conf = handler->conf;
if (lxc_init(name, handler) < 0) { if (lxc_init(name, handler) < 0) {
...@@ -1509,10 +1547,6 @@ int __lxc_start(const char *name, struct lxc_handler *handler, ...@@ -1509,10 +1547,6 @@ int __lxc_start(const char *name, struct lxc_handler *handler,
err = lxc_poll(name, handler); err = lxc_poll(name, handler);
if (err) { if (err) {
ERROR("LXC mainloop exited with error: %d.", err); ERROR("LXC mainloop exited with error: %d.", err);
if (handler->netnsfd >= 0) {
close(handler->netnsfd);
handler->netnsfd = -1;
}
goto out_abort; goto out_abort;
} }
...@@ -1544,9 +1578,6 @@ int __lxc_start(const char *name, struct lxc_handler *handler, ...@@ -1544,9 +1578,6 @@ int __lxc_start(const char *name, struct lxc_handler *handler,
DEBUG("Pushing physical nics back to host namespace"); DEBUG("Pushing physical nics back to host namespace");
lxc_restore_phys_nics_to_netns(handler->netnsfd, handler->conf); lxc_restore_phys_nics_to_netns(handler->netnsfd, handler->conf);
DEBUG("Tearing down virtual network devices used by container \"%s\".", name);
removed_all_netdevs = lxc_delete_network(handler);
if (handler->pinfd >= 0) { if (handler->pinfd >= 0) {
close(handler->pinfd); close(handler->pinfd);
handler->pinfd = -1; handler->pinfd = -1;
...@@ -1554,12 +1585,18 @@ int __lxc_start(const char *name, struct lxc_handler *handler, ...@@ -1554,12 +1585,18 @@ int __lxc_start(const char *name, struct lxc_handler *handler,
lxc_monitor_send_exit_code(name, status, handler->lxcpath); lxc_monitor_send_exit_code(name, status, handler->lxcpath);
err = lxc_error_set_and_log(handler->pid, status); err = lxc_error_set_and_log(handler->pid, status);
out_fini: out_fini:
if (!removed_all_netdevs) { DEBUG("Tearing down network devices");
DEBUG("Failed tearing down network devices used by container. Trying again!"); if (!lxc_delete_network_priv(handler))
removed_all_netdevs = lxc_delete_network(handler); DEBUG("Failed tearing down network devices");
if (!removed_all_netdevs)
DEBUG("Failed tearing down network devices used by container. Not trying again!"); if (!lxc_delete_network_unpriv(handler))
DEBUG("Failed tearing down network devices");
if (handler->netnsfd >= 0) {
close(handler->netnsfd);
handler->netnsfd = -1;
} }
out_detach_blockdev: out_detach_blockdev:
......
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