Commit d3e7b8ad by Stéphane Graber Committed by GitHub

Merge pull request #1768 from brauner/2017-08-29/stable_2.0_cherry_picks

stable 2.0: cherry picks
parents d99d8dbc 4d26f247
...@@ -39,7 +39,9 @@ ...@@ -39,7 +39,9 @@
#include <sys/syscall.h> #include <sys/syscall.h>
#include <sys/wait.h> #include <sys/wait.h>
#if !HAVE_DECL_PR_CAPBSET_DROP #include <lxc/lxccontainer.h>
#ifndef HAVE_DECL_PR_CAPBSET_DROP
#define PR_CAPBSET_DROP 24 #define PR_CAPBSET_DROP 24
#endif #endif
...@@ -58,14 +60,12 @@ ...@@ -58,14 +60,12 @@
#include "namespace.h" #include "namespace.h"
#include "utils.h" #include "utils.h"
#include <lxc/lxccontainer.h>
#if HAVE_SYS_PERSONALITY_H #if HAVE_SYS_PERSONALITY_H
#include <sys/personality.h> #include <sys/personality.h>
#endif #endif
#ifndef SOCK_CLOEXEC #ifndef SOCK_CLOEXEC
# define SOCK_CLOEXEC 02000000 #define SOCK_CLOEXEC 02000000
#endif #endif
#ifndef MS_REC #ifndef MS_REC
...@@ -73,7 +73,7 @@ ...@@ -73,7 +73,7 @@
#endif #endif
#ifndef MS_SLAVE #ifndef MS_SLAVE
#define MS_SLAVE (1<<19) #define MS_SLAVE (1 << 19)
#endif #endif
lxc_log_define(lxc_attach, lxc); lxc_log_define(lxc_attach, lxc);
...@@ -118,7 +118,7 @@ static int lsm_openat(int procfd, pid_t pid, int on_exec) ...@@ -118,7 +118,7 @@ static int lsm_openat(int procfd, pid_t pid, int on_exec)
static int lsm_set_label_at(int lsm_labelfd, int on_exec, char *lsm_label) static int lsm_set_label_at(int lsm_labelfd, int on_exec, char *lsm_label)
{ {
int fret = -1; int fret = -1;
const char* name; const char *name;
char *command = NULL; char *command = NULL;
name = lsm_name(); name = lsm_name();
...@@ -136,7 +136,8 @@ static int lsm_set_label_at(int lsm_labelfd, int on_exec, char *lsm_label) ...@@ -136,7 +136,8 @@ static int lsm_set_label_at(int lsm_labelfd, int on_exec, char *lsm_label)
if (strcmp(name, "AppArmor") == 0) { if (strcmp(name, "AppArmor") == 0) {
int size; int size;
command = malloc(strlen(lsm_label) + strlen("changeprofile ") + 1); command =
malloc(strlen(lsm_label) + strlen("changeprofile ") + 1);
if (!command) { if (!command) {
SYSERROR("Failed to write apparmor profile."); SYSERROR("Failed to write apparmor profile.");
goto out; goto out;
...@@ -175,15 +176,15 @@ out: ...@@ -175,15 +176,15 @@ out:
} }
/* /proc/pid-to-str/status\0 = (5 + 21 + 7 + 1) */ /* /proc/pid-to-str/status\0 = (5 + 21 + 7 + 1) */
#define __PROC_STATUS_LEN (5 + 21 + 7 + 1) #define __PROC_STATUS_LEN (5 + (LXC_NUMSTRLEN64) + 7 + 1)
static struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid) static struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid)
{ {
int ret;
bool found;
FILE *proc_file; FILE *proc_file;
char proc_fn[__PROC_STATUS_LEN]; char proc_fn[__PROC_STATUS_LEN];
bool found;
int ret;
char *line = NULL;
size_t line_bufsz = 0; size_t line_bufsz = 0;
char *line = NULL;
struct lxc_proc_context_info *info = NULL; struct lxc_proc_context_info *info = NULL;
/* Read capabilities. */ /* Read capabilities. */
...@@ -217,7 +218,8 @@ static struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid) ...@@ -217,7 +218,8 @@ static struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid)
fclose(proc_file); fclose(proc_file);
if (!found) { if (!found) {
SYSERROR("Could not read capability bounding set from %s.", proc_fn); SYSERROR("Could not read capability bounding set from %s.",
proc_fn);
errno = ENOENT; errno = ENOENT;
goto on_error; goto on_error;
} }
...@@ -244,7 +246,6 @@ static int lxc_attach_to_ns(pid_t pid, int which) ...@@ -244,7 +246,6 @@ static int lxc_attach_to_ns(pid_t pid, int which)
int fd[LXC_NS_MAX]; int fd[LXC_NS_MAX];
int i, j, saved_errno; int i, j, saved_errno;
if (access("/proc/self/ns", X_OK)) { if (access("/proc/self/ns", X_OK)) {
ERROR("Does this kernel version support namespaces?"); ERROR("Does this kernel version support namespaces?");
return -1; return -1;
...@@ -268,7 +269,8 @@ static int lxc_attach_to_ns(pid_t pid, int which) ...@@ -268,7 +269,8 @@ static int lxc_attach_to_ns(pid_t pid, int which)
close(fd[j]); close(fd[j]);
errno = saved_errno; errno = saved_errno;
SYSERROR("Failed to open namespace: \"%s\".", ns_info[i].proc_name); SYSERROR("Failed to open namespace: \"%s\".",
ns_info[i].proc_name);
return -1; return -1;
} }
} }
...@@ -284,7 +286,8 @@ static int lxc_attach_to_ns(pid_t pid, int which) ...@@ -284,7 +286,8 @@ static int lxc_attach_to_ns(pid_t pid, int which)
close(fd[j]); close(fd[j]);
errno = saved_errno; errno = saved_errno;
SYSERROR("Failed to attach to namespace \"%s\".", ns_info[i].proc_name); SYSERROR("Failed to attach to namespace \"%s\".",
ns_info[i].proc_name);
return -1; return -1;
} }
...@@ -307,7 +310,7 @@ static int lxc_attach_remount_sys_proc(void) ...@@ -307,7 +310,7 @@ static int lxc_attach_remount_sys_proc(void)
} }
if (detect_shared_rootfs()) { if (detect_shared_rootfs()) {
if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL)) { if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL)) {
SYSERROR("Failed to make / rslave."); SYSERROR("Failed to make / rslave.");
ERROR("Continuing..."); ERROR("Continuing...");
} }
...@@ -347,9 +350,9 @@ static int lxc_attach_remount_sys_proc(void) ...@@ -347,9 +350,9 @@ static int lxc_attach_remount_sys_proc(void)
static int lxc_attach_drop_privs(struct lxc_proc_context_info *ctx) static int lxc_attach_drop_privs(struct lxc_proc_context_info *ctx)
{ {
int last_cap = lxc_caps_last_cap(); int cap, last_cap;
int cap;
last_cap = lxc_caps_last_cap();
for (cap = 0; cap <= last_cap; cap++) { for (cap = 0; cap <= last_cap; cap++) {
if (ctx->capability_mask & (1LL << cap)) if (ctx->capability_mask & (1LL << cap))
continue; continue;
...@@ -363,11 +366,12 @@ static int lxc_attach_drop_privs(struct lxc_proc_context_info *ctx) ...@@ -363,11 +366,12 @@ static int lxc_attach_drop_privs(struct lxc_proc_context_info *ctx)
return 0; return 0;
} }
static int lxc_attach_set_environment(enum lxc_attach_env_policy_t policy, char** extra_env, char** extra_keep) static int lxc_attach_set_environment(enum lxc_attach_env_policy_t policy,
char **extra_env, char **extra_keep)
{ {
if (policy == LXC_ATTACH_CLEAR_ENV) { if (policy == LXC_ATTACH_CLEAR_ENV) {
char **extra_keep_store = NULL;
int path_kept = 0; int path_kept = 0;
char **extra_keep_store = NULL;
if (extra_keep) { if (extra_keep) {
size_t count, i; size_t count, i;
...@@ -403,6 +407,7 @@ static int lxc_attach_set_environment(enum lxc_attach_env_policy_t policy, char* ...@@ -403,6 +407,7 @@ static int lxc_attach_set_environment(enum lxc_attach_env_policy_t policy, char*
if (clearenv()) { if (clearenv()) {
char **p; char **p;
SYSERROR("Failed to clear environment."); SYSERROR("Failed to clear environment.");
if (extra_keep_store) { if (extra_keep_store) {
for (p = extra_keep_store; *p; p++) for (p = extra_keep_store; *p; p++)
...@@ -414,6 +419,7 @@ static int lxc_attach_set_environment(enum lxc_attach_env_policy_t policy, char* ...@@ -414,6 +419,7 @@ static int lxc_attach_set_environment(enum lxc_attach_env_policy_t policy, char*
if (extra_keep_store) { if (extra_keep_store) {
size_t i; size_t i;
for (i = 0; extra_keep[i]; i++) { for (i = 0; extra_keep[i]; i++) {
if (extra_keep_store[i]) { if (extra_keep_store[i]) {
if (setenv(extra_keep[i], extra_keep_store[i], 1) < 0) if (setenv(extra_keep[i], extra_keep_store[i], 1) < 0)
...@@ -462,10 +468,9 @@ static int lxc_attach_set_environment(enum lxc_attach_env_policy_t policy, char* ...@@ -462,10 +468,9 @@ static int lxc_attach_set_environment(enum lxc_attach_env_policy_t policy, char*
static char *lxc_attach_getpwshell(uid_t uid) static char *lxc_attach_getpwshell(uid_t uid)
{ {
int fd, ret;
pid_t pid; pid_t pid;
int pipes[2]; int pipes[2];
int ret;
int fd;
char *result = NULL; char *result = NULL;
/* We need to fork off a process that runs the getent program, and we /* We need to fork off a process that runs the getent program, and we
...@@ -483,21 +488,20 @@ static char *lxc_attach_getpwshell(uid_t uid) ...@@ -483,21 +488,20 @@ static char *lxc_attach_getpwshell(uid_t uid)
} }
if (pid) { if (pid) {
int status;
FILE *pipe_f; FILE *pipe_f;
char *line = NULL;
size_t line_bufsz = 0;
int found = 0; int found = 0;
int status; size_t line_bufsz = 0;
char *line = NULL;
close(pipes[1]); close(pipes[1]);
pipe_f = fdopen(pipes[0], "r"); pipe_f = fdopen(pipes[0], "r");
while (getline(&line, &line_bufsz, pipe_f) != -1) { while (getline(&line, &line_bufsz, pipe_f) != -1) {
char *token;
char *saveptr = NULL;
long value;
char *endptr = NULL;
int i; int i;
long value;
char *token;
char *endptr = NULL, *saveptr = NULL;
/* If we already found something, just continue to read /* If we already found something, just continue to read
* until the pipe doesn't deliver any more data, but * until the pipe doesn't deliver any more data, but
...@@ -608,7 +612,7 @@ static char *lxc_attach_getpwshell(uid_t uid) ...@@ -608,7 +612,7 @@ static char *lxc_attach_getpwshell(uid_t uid)
} }
} }
static void lxc_attach_get_init_uidgid(uid_t* init_uid, gid_t* init_gid) static void lxc_attach_get_init_uidgid(uid_t *init_uid, gid_t *init_gid)
{ {
FILE *proc_file; FILE *proc_file;
char proc_fn[__PROC_STATUS_LEN]; char proc_fn[__PROC_STATUS_LEN];
...@@ -632,11 +636,11 @@ static void lxc_attach_get_init_uidgid(uid_t* init_uid, gid_t* init_gid) ...@@ -632,11 +636,11 @@ static void lxc_attach_get_init_uidgid(uid_t* init_uid, gid_t* init_gid)
*/ */
ret = sscanf(line, "Uid: %ld", &value); ret = sscanf(line, "Uid: %ld", &value);
if (ret != EOF && ret == 1) { if (ret != EOF && ret == 1) {
uid = (uid_t) value; uid = (uid_t)value;
} else { } else {
ret = sscanf(line, "Gid: %ld", &value); ret = sscanf(line, "Gid: %ld", &value);
if (ret != EOF && ret == 1) if (ret != EOF && ret == 1)
gid = (gid_t) value; gid = (gid_t)value;
} }
if (uid != (uid_t)-1 && gid != (gid_t)-1) if (uid != (uid_t)-1 && gid != (gid_t)-1)
break; break;
...@@ -658,42 +662,45 @@ static void lxc_attach_get_init_uidgid(uid_t* init_uid, gid_t* init_gid) ...@@ -658,42 +662,45 @@ static void lxc_attach_get_init_uidgid(uid_t* init_uid, gid_t* init_gid)
struct attach_clone_payload { struct attach_clone_payload {
int ipc_socket; int ipc_socket;
lxc_attach_options_t* options; lxc_attach_options_t *options;
struct lxc_proc_context_info* init_ctx; struct lxc_proc_context_info *init_ctx;
lxc_attach_exec_t exec_function; lxc_attach_exec_t exec_function;
void* exec_payload; void *exec_payload;
}; };
static int attach_child_main(void* data); static int attach_child_main(void* data);
/* Help the optimizer along if it doesn't know that exit always exits. */ /* Help the optimizer along if it doesn't know that exit always exits. */
#define rexit(c) do { int __c = (c); _exit(__c); return __c; } while(0) #define rexit(c) \
do { \
int __c = (c); \
_exit(__c); \
return __c; \
} while (0)
/* Define default options if no options are supplied by the user. */ /* Define default options if no options are supplied by the user. */
static lxc_attach_options_t attach_static_default_options = LXC_ATTACH_OPTIONS_DEFAULT; static lxc_attach_options_t attach_static_default_options = LXC_ATTACH_OPTIONS_DEFAULT;
static bool fetch_seccomp(const char *name, const char *lxcpath, static bool fetch_seccomp(struct lxc_container *c,
struct lxc_proc_context_info *i, lxc_attach_options_t *options) lxc_attach_options_t *options)
{ {
struct lxc_container *c;
char *path; char *path;
if (!(options->namespaces & CLONE_NEWNS) || !(options->attach_flags & LXC_ATTACH_LSM)) if (!(options->namespaces & CLONE_NEWNS) ||
!(options->attach_flags & LXC_ATTACH_LSM)) {
free(c->lxc_conf->seccomp);
c->lxc_conf->seccomp = NULL;
return true; return true;
}
c = lxc_container_new(name, lxcpath); /* Remove current setting. */
if (!c) if (!c->set_config_item(c, "lxc.seccomp", ""))
return false;
i->container = c;
/* Initialize an empty lxc_conf */
if (!c->set_config_item(c, "lxc.seccomp", "")) {
return false; return false;
}
/* Fetch the current profile path over the cmd interface. */ /* Fetch the current profile path over the cmd interface. */
path = c->get_running_config_item(c, "lxc.seccomp"); path = c->get_running_config_item(c, "lxc.seccomp");
if (!path) { if (!path) {
INFO("Failed to get running config item for lxc.seccomp");
return true; return true;
} }
...@@ -710,30 +717,35 @@ static bool fetch_seccomp(const char *name, const char *lxcpath, ...@@ -710,30 +717,35 @@ static bool fetch_seccomp(const char *name, const char *lxcpath,
return false; return false;
} }
INFO("Retrieved seccomp policy.");
return true; return true;
} }
static signed long get_personality(const char *name, const char *lxcpath) static signed long get_personality(const char *name, const char *lxcpath)
{ {
char *p = lxc_cmd_get_config_item(name, "lxc.arch", lxcpath); char *p;
signed long ret; signed long ret;
p = lxc_cmd_get_config_item(name, "lxc.arch", lxcpath);
if (!p) if (!p)
return -1; return -1;
ret = lxc_config_parse_arch(p); ret = lxc_config_parse_arch(p);
free(p); free(p);
return ret; return ret;
} }
int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_function, void* exec_payload, lxc_attach_options_t* options, pid_t* attached_process) int lxc_attach(const char *name, const char *lxcpath,
lxc_attach_exec_t exec_function, void *exec_payload,
lxc_attach_options_t *options, pid_t *attached_process)
{ {
int ret, status; int ret, status;
pid_t init_pid, pid, attached_pid, expected;
struct lxc_proc_context_info *init_ctx;
char* cwd;
char* new_cwd;
int ipc_sockets[2]; int ipc_sockets[2];
char *cwd, *new_cwd;
signed long personality; signed long personality;
pid_t attached_pid, expected, init_pid, pid;
struct lxc_proc_context_info *init_ctx;
if (!options) if (!options)
options = &attach_static_default_options; options = &attach_static_default_options;
...@@ -746,20 +758,23 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun ...@@ -746,20 +758,23 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun
init_ctx = lxc_proc_get_context_info(init_pid); init_ctx = lxc_proc_get_context_info(init_pid);
if (!init_ctx) { if (!init_ctx) {
ERROR("Failed to get context of init process: %ld.", ERROR("Failed to get context of init process: %ld", (long)init_pid);
(long)init_pid);
return -1; return -1;
} }
personality = get_personality(name, lxcpath); personality = get_personality(name, lxcpath);
if (init_ctx->personality < 0) { if (init_ctx->personality < 0) {
ERROR("Failed to get personality of the container."); ERROR("Failed to get personality of the container");
lxc_proc_put_context_info(init_ctx); lxc_proc_put_context_info(init_ctx);
return -1; return -1;
} }
init_ctx->personality = personality; init_ctx->personality = personality;
if (!fetch_seccomp(name, lxcpath, init_ctx, options)) init_ctx->container = lxc_container_new(name, lxcpath);
if (!init_ctx->container)
return -1;
if (!fetch_seccomp(init_ctx->container, options))
WARN("Failed to get seccomp policy."); WARN("Failed to get seccomp policy.");
cwd = getcwd(NULL, 0); cwd = getcwd(NULL, 0);
...@@ -831,7 +846,6 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun ...@@ -831,7 +846,6 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun
* setns() (otherwise, user namespaces will hate us). * setns() (otherwise, user namespaces will hate us).
*/ */
pid = fork(); pid = fork();
if (pid < 0) { if (pid < 0) {
SYSERROR("Failed to create first subprocess."); SYSERROR("Failed to create first subprocess.");
free(cwd); free(cwd);
...@@ -874,7 +888,8 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun ...@@ -874,7 +888,8 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun
} }
/* Get pid of attached process from intermediate process. */ /* Get pid of attached process from intermediate process. */
ret = lxc_read_nointr_expect(ipc_sockets[0], &attached_pid, sizeof(attached_pid), NULL); ret = lxc_read_nointr_expect(ipc_sockets[0], &attached_pid,
sizeof(attached_pid), NULL);
if (ret <= 0) { if (ret <= 0) {
if (ret != 0) if (ret != 0)
ERROR("Expected to receive pid: %s.", strerror(errno)); ERROR("Expected to receive pid: %s.", strerror(errno));
...@@ -905,7 +920,8 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun ...@@ -905,7 +920,8 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun
/* Wait for the attached process to finish initializing. */ /* Wait for the attached process to finish initializing. */
expected = 1; expected = 1;
ret = lxc_read_nointr_expect(ipc_sockets[0], &status, sizeof(status), &expected); ret = lxc_read_nointr_expect(ipc_sockets[0], &status,
sizeof(status), &expected);
if (ret <= 0) { if (ret <= 0) {
if (ret != 0) if (ret != 0)
ERROR("Expected to receive sequence number 1: %s.", strerror(errno)); ERROR("Expected to receive sequence number 1: %s.", strerror(errno));
...@@ -924,7 +940,8 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun ...@@ -924,7 +940,8 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun
* up its LSM labels. * up its LSM labels.
*/ */
expected = 3; expected = 3;
ret = lxc_read_nointr_expect(ipc_sockets[0], &status, sizeof(status), &expected); ret = lxc_read_nointr_expect(ipc_sockets[0], &status,
sizeof(status), &expected);
if (ret <= 0) { if (ret <= 0) {
ERROR("Expected to receive sequence number 3: %s.", ERROR("Expected to receive sequence number 3: %s.",
strerror(errno)); strerror(errno));
...@@ -932,9 +949,12 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun ...@@ -932,9 +949,12 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun
} }
/* Open LSM fd and send it to child. */ /* Open LSM fd and send it to child. */
if ((options->namespaces & CLONE_NEWNS) && (options->attach_flags & LXC_ATTACH_LSM) && init_ctx->lsm_label) { if ((options->namespaces & CLONE_NEWNS) &&
(options->attach_flags & LXC_ATTACH_LSM) &&
init_ctx->lsm_label) {
int on_exec, saved_errno; int on_exec, saved_errno;
int labelfd = -1; int labelfd = -1;
on_exec = options->attach_flags & LXC_ATTACH_LSM_EXEC ? 1 : 0; on_exec = options->attach_flags & LXC_ATTACH_LSM_EXEC ? 1 : 0;
/* Open fd for the LSM security module. */ /* Open fd for the LSM security module. */
labelfd = lsm_openat(procfd, attached_pid, on_exec); labelfd = lsm_openat(procfd, attached_pid, on_exec);
...@@ -975,7 +995,7 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun ...@@ -975,7 +995,7 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun
shutdown(ipc_sockets[0], SHUT_RDWR); shutdown(ipc_sockets[0], SHUT_RDWR);
close(ipc_sockets[0]); close(ipc_sockets[0]);
if (to_cleanup_pid) if (to_cleanup_pid)
(void) wait_for_pid(to_cleanup_pid); (void)wait_for_pid(to_cleanup_pid);
lxc_proc_put_context_info(init_ctx); lxc_proc_put_context_info(init_ctx);
return -1; return -1;
} }
...@@ -988,7 +1008,8 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun ...@@ -988,7 +1008,8 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun
/* Wait for the parent to have setup cgroups. */ /* Wait for the parent to have setup cgroups. */
expected = 0; expected = 0;
status = -1; status = -1;
ret = lxc_read_nointr_expect(ipc_sockets[1], &status, sizeof(status), &expected); ret = lxc_read_nointr_expect(ipc_sockets[1], &status, sizeof(status),
&expected);
if (ret <= 0) { if (ret <= 0) {
ERROR("Expected to receive sequence number 0: %s.", strerror(errno)); ERROR("Expected to receive sequence number 0: %s.", strerror(errno));
shutdown(ipc_sockets[1], SHUT_RDWR); shutdown(ipc_sockets[1], SHUT_RDWR);
...@@ -1025,7 +1046,7 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun ...@@ -1025,7 +1046,7 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun
.options = options, .options = options,
.init_ctx = init_ctx, .init_ctx = init_ctx,
.exec_function = exec_function, .exec_function = exec_function,
.exec_payload = exec_payload .exec_payload = exec_payload,
}; };
/* We use clone_parent here to make this subprocess a direct /* We use clone_parent here to make this subprocess a direct
* child of the initial process. Then this intermediate process * child of the initial process. Then this intermediate process
...@@ -1062,21 +1083,17 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun ...@@ -1062,21 +1083,17 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun
static int attach_child_main(void* data) static int attach_child_main(void* data)
{ {
struct attach_clone_payload* payload = (struct attach_clone_payload*)data; int expected, fd, lsm_labelfd, ret, status;
int ipc_socket = payload->ipc_socket; long flags;
lxc_attach_options_t* options = payload->options;
struct lxc_proc_context_info* init_ctx = payload->init_ctx;
#if HAVE_SYS_PERSONALITY_H #if HAVE_SYS_PERSONALITY_H
long new_personality; long new_personality;
#endif #endif
int ret;
int status;
int expected;
long flags;
int fd;
int lsm_labelfd;
uid_t new_uid; uid_t new_uid;
gid_t new_gid; gid_t new_gid;
struct attach_clone_payload* payload = (struct attach_clone_payload*)data;
int ipc_socket = payload->ipc_socket;
lxc_attach_options_t* options = payload->options;
struct lxc_proc_context_info* init_ctx = payload->init_ctx;
/* Wait for the initial thread to signal us that it's ready for us to /* Wait for the initial thread to signal us that it's ready for us to
* start initializing. * start initializing.
...@@ -1095,7 +1112,8 @@ static int attach_child_main(void* data) ...@@ -1095,7 +1112,8 @@ static int attach_child_main(void* data)
* parent process, otherwise /proc may not properly reflect the new pid * parent process, otherwise /proc may not properly reflect the new pid
* namespace. * namespace.
*/ */
if (!(options->namespaces & CLONE_NEWNS) && (options->attach_flags & LXC_ATTACH_REMOUNT_PROC_SYS)) { if (!(options->namespaces & CLONE_NEWNS) &&
(options->attach_flags & LXC_ATTACH_REMOUNT_PROC_SYS)) {
ret = lxc_attach_remount_sys_proc(); ret = lxc_attach_remount_sys_proc();
if (ret < 0) { if (ret < 0) {
shutdown(ipc_socket, SHUT_RDWR); shutdown(ipc_socket, SHUT_RDWR);
...@@ -1132,7 +1150,9 @@ static int attach_child_main(void* data) ...@@ -1132,7 +1150,9 @@ static int attach_child_main(void* data)
/* Always set the environment (specify (LXC_ATTACH_KEEP_ENV, NULL, NULL) /* Always set the environment (specify (LXC_ATTACH_KEEP_ENV, NULL, NULL)
* if you want this to be a no-op). * if you want this to be a no-op).
*/ */
ret = lxc_attach_set_environment(options->env_policy, options->extra_env_vars, options->extra_keep_env); ret = lxc_attach_set_environment(options->env_policy,
options->extra_env_vars,
options->extra_keep_env);
if (ret < 0) { if (ret < 0) {
ERROR("Could not set initial environment for attached process."); ERROR("Could not set initial environment for attached process.");
shutdown(ipc_socket, SHUT_RDWR); shutdown(ipc_socket, SHUT_RDWR);
...@@ -1176,7 +1196,8 @@ static int attach_child_main(void* data) ...@@ -1176,7 +1196,8 @@ static int attach_child_main(void* data)
rexit(-1); rexit(-1);
} }
} }
if ((new_uid != 0 || options->namespaces & CLONE_NEWUSER) && setuid(new_uid)) { if ((new_uid != 0 || options->namespaces & CLONE_NEWUSER) &&
setuid(new_uid)) {
SYSERROR("Switching to container uid."); SYSERROR("Switching to container uid.");
shutdown(ipc_socket, SHUT_RDWR); shutdown(ipc_socket, SHUT_RDWR);
rexit(-1); rexit(-1);
...@@ -1203,7 +1224,6 @@ static int attach_child_main(void* data) ...@@ -1203,7 +1224,6 @@ static int attach_child_main(void* data)
rexit(-1); rexit(-1);
} }
/* Tell the (grand)parent to send us LSM label fd. */ /* Tell the (grand)parent to send us LSM label fd. */
status = 3; status = 3;
ret = lxc_write_nointr(ipc_socket, &status, sizeof(status)); ret = lxc_write_nointr(ipc_socket, &status, sizeof(status));
...@@ -1213,7 +1233,8 @@ static int attach_child_main(void* data) ...@@ -1213,7 +1233,8 @@ static int attach_child_main(void* data)
rexit(-1); rexit(-1);
} }
if ((options->namespaces & CLONE_NEWNS) && (options->attach_flags & LXC_ATTACH_LSM) && init_ctx->lsm_label) { if ((options->namespaces & CLONE_NEWNS) &&
(options->attach_flags & LXC_ATTACH_LSM) && init_ctx->lsm_label) {
int on_exec; int on_exec;
/* Receive fd for LSM security module. */ /* Receive fd for LSM security module. */
ret = lxc_abstract_unix_recv_fds(ipc_socket, &lsm_labelfd, 1, NULL, 0); ret = lxc_abstract_unix_recv_fds(ipc_socket, &lsm_labelfd, 1, NULL, 0);
...@@ -1235,7 +1256,8 @@ static int attach_child_main(void* data) ...@@ -1235,7 +1256,8 @@ static int attach_child_main(void* data)
} }
if (init_ctx->container && init_ctx->container->lxc_conf && if (init_ctx->container && init_ctx->container->lxc_conf &&
lxc_seccomp_load(init_ctx->container->lxc_conf) != 0) { init_ctx->container->lxc_conf->seccomp &&
(lxc_seccomp_load(init_ctx->container->lxc_conf) != 0)) {
ERROR("Failed to load seccomp policy."); ERROR("Failed to load seccomp policy.");
shutdown(ipc_socket, SHUT_RDWR); shutdown(ipc_socket, SHUT_RDWR);
rexit(-1); rexit(-1);
......
...@@ -24,8 +24,8 @@ ...@@ -24,8 +24,8 @@
#ifndef __LXC_ATTACH_H #ifndef __LXC_ATTACH_H
#define __LXC_ATTACH_H #define __LXC_ATTACH_H
#include <sys/types.h>
#include <lxc/attach_options.h> #include <lxc/attach_options.h>
#include <sys/types.h>
struct lxc_conf; struct lxc_conf;
...@@ -36,6 +36,8 @@ struct lxc_proc_context_info { ...@@ -36,6 +36,8 @@ struct lxc_proc_context_info {
unsigned long long capability_mask; unsigned long long capability_mask;
}; };
extern int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_function, void* exec_payload, lxc_attach_options_t* options, pid_t* attached_process); extern int lxc_attach(const char *name, const char *lxcpath,
lxc_attach_exec_t exec_function, void *exec_payload,
lxc_attach_options_t *options, pid_t *attached_process);
#endif #endif /* __LXC_ATTACH_H */
...@@ -1176,6 +1176,7 @@ out_free: ...@@ -1176,6 +1176,7 @@ out_free:
static int cgroup_rmdir(char *dirname) static int cgroup_rmdir(char *dirname)
{ {
int ret;
struct dirent *direntp; struct dirent *direntp;
DIR *dir; DIR *dir;
int r = 0; int r = 0;
...@@ -1185,8 +1186,8 @@ static int cgroup_rmdir(char *dirname) ...@@ -1185,8 +1186,8 @@ static int cgroup_rmdir(char *dirname)
return -1; return -1;
while ((direntp = readdir(dir))) { while ((direntp = readdir(dir))) {
struct stat mystat;
char *pathname; char *pathname;
struct stat mystat;
if (!direntp) if (!direntp)
break; break;
...@@ -1197,32 +1198,40 @@ static int cgroup_rmdir(char *dirname) ...@@ -1197,32 +1198,40 @@ static int cgroup_rmdir(char *dirname)
pathname = must_make_path(dirname, direntp->d_name, NULL); pathname = must_make_path(dirname, direntp->d_name, NULL);
if (lstat(pathname, &mystat)) { ret = lstat(pathname, &mystat);
if (ret < 0) {
if (!r) if (!r)
WARN("failed to stat %s", pathname); WARN("Failed to stat %s", pathname);
r = -1; r = -1;
goto next; goto next;
} }
if (!S_ISDIR(mystat.st_mode)) if (!S_ISDIR(mystat.st_mode))
goto next; goto next;
if (cgroup_rmdir(pathname) < 0)
ret = cgroup_rmdir(pathname);
if (ret < 0)
r = -1; r = -1;
next: next:
free(pathname); free(pathname);
} }
if (rmdir(dirname) < 0) { ret = rmdir(dirname);
if (ret < 0) {
if (!r) if (!r)
WARN("failed to delete %s: %s", dirname, strerror(errno)); WARN("Failed to delete \"%s\": %s", dirname,
strerror(errno));
r = -1; r = -1;
} }
if (closedir(dir) < 0) { ret = closedir(dir);
if (ret < 0) {
if (!r) if (!r)
WARN("failed to delete %s: %s", dirname, strerror(errno)); WARN("Failed to delete \"%s\": %s", dirname,
strerror(errno));
r = -1; r = -1;
} }
return r; return r;
} }
......
...@@ -235,8 +235,6 @@ char *lxchook_names[NUM_LXC_HOOKS] = {"pre-start", "pre-mount", "mount", ...@@ -235,8 +235,6 @@ char *lxchook_names[NUM_LXC_HOOKS] = {"pre-start", "pre-mount", "mount",
"autodev", "start", "stop", "autodev", "start", "stop",
"post-stop", "clone", "destroy"}; "post-stop", "clone", "destroy"};
typedef int (*instantiate_cb)(struct lxc_handler *, struct lxc_netdev *);
struct mount_opt { struct mount_opt {
char *name; char *name;
int clear; int clear;
...@@ -262,38 +260,6 @@ struct lxc_conf *current_config; ...@@ -262,38 +260,6 @@ struct lxc_conf *current_config;
/* Declare this here, since we don't want to reshuffle the whole file. */ /* Declare this here, since we don't want to reshuffle the whole file. */
static int in_caplist(int cap, struct lxc_list *caps); static int in_caplist(int cap, struct lxc_list *caps);
static int instantiate_veth(struct lxc_handler *, struct lxc_netdev *);
static int instantiate_macvlan(struct lxc_handler *, struct lxc_netdev *);
static int instantiate_vlan(struct lxc_handler *, struct lxc_netdev *);
static int instantiate_phys(struct lxc_handler *, struct lxc_netdev *);
static int instantiate_empty(struct lxc_handler *, struct lxc_netdev *);
static int instantiate_none(struct lxc_handler *, struct lxc_netdev *);
static instantiate_cb netdev_conf[LXC_NET_MAXCONFTYPE + 1] = {
[LXC_NET_VETH] = instantiate_veth,
[LXC_NET_MACVLAN] = instantiate_macvlan,
[LXC_NET_VLAN] = instantiate_vlan,
[LXC_NET_PHYS] = instantiate_phys,
[LXC_NET_EMPTY] = instantiate_empty,
[LXC_NET_NONE] = instantiate_none,
};
static int shutdown_veth(struct lxc_handler *, struct lxc_netdev *);
static int shutdown_macvlan(struct lxc_handler *, struct lxc_netdev *);
static int shutdown_vlan(struct lxc_handler *, struct lxc_netdev *);
static int shutdown_phys(struct lxc_handler *, struct lxc_netdev *);
static int shutdown_empty(struct lxc_handler *, struct lxc_netdev *);
static int shutdown_none(struct lxc_handler *, struct lxc_netdev *);
static instantiate_cb netdev_deconf[LXC_NET_MAXCONFTYPE + 1] = {
[LXC_NET_VETH] = shutdown_veth,
[LXC_NET_MACVLAN] = shutdown_macvlan,
[LXC_NET_VLAN] = shutdown_vlan,
[LXC_NET_PHYS] = shutdown_phys,
[LXC_NET_EMPTY] = shutdown_empty,
[LXC_NET_NONE] = shutdown_none,
};
static struct mount_opt mount_opt[] = { static struct mount_opt mount_opt[] = {
{ "async", 1, MS_SYNCHRONOUS }, { "async", 1, MS_SYNCHRONOUS },
{ "atime", 1, MS_NOATIME }, { "atime", 1, MS_NOATIME },
...@@ -471,8 +437,7 @@ static int run_script_argv(const char *name, const char *section, ...@@ -471,8 +437,7 @@ static int run_script_argv(const char *name, const char *section,
return run_buffer(buffer); return run_buffer(buffer);
} }
static int run_script(const char *name, const char *section, const char *script, int run_script(const char *name, const char *section, const char *script, ...)
...)
{ {
int ret; int ret;
char *buffer, *p; char *buffer, *p;
...@@ -2286,328 +2251,6 @@ static int dropcaps_except(struct lxc_list *caps) ...@@ -2286,328 +2251,6 @@ static int dropcaps_except(struct lxc_list *caps)
return 0; return 0;
} }
static int setup_hw_addr(char *hwaddr, const char *ifname)
{
struct sockaddr sockaddr;
struct ifreq ifr;
int ret, fd, saved_errno;
ret = lxc_convert_mac(hwaddr, &sockaddr);
if (ret) {
ERROR("mac address '%s' conversion failed : %s",
hwaddr, strerror(-ret));
return -1;
}
memcpy(ifr.ifr_name, ifname, IFNAMSIZ);
ifr.ifr_name[IFNAMSIZ-1] = '\0';
memcpy((char *) &ifr.ifr_hwaddr, (char *) &sockaddr, sizeof(sockaddr));
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0) {
ERROR("socket failure : %s", strerror(errno));
return -1;
}
ret = ioctl(fd, SIOCSIFHWADDR, &ifr);
saved_errno = errno;
close(fd);
if (ret)
ERROR("ioctl failure : %s", strerror(saved_errno));
DEBUG("mac address '%s' on '%s' has been setup", hwaddr, ifr.ifr_name);
return ret;
}
static int setup_ipv4_addr(struct lxc_list *ip, int ifindex)
{
struct lxc_list *iterator;
struct lxc_inetdev *inetdev;
int err;
lxc_list_for_each(iterator, ip) {
inetdev = iterator->elem;
err = lxc_ipv4_addr_add(ifindex, &inetdev->addr,
&inetdev->bcast, inetdev->prefix);
if (err) {
ERROR("failed to setup_ipv4_addr ifindex %d : %s",
ifindex, strerror(-err));
return -1;
}
}
return 0;
}
static int setup_ipv6_addr(struct lxc_list *ip, int ifindex)
{
struct lxc_list *iterator;
struct lxc_inet6dev *inet6dev;
int err;
lxc_list_for_each(iterator, ip) {
inet6dev = iterator->elem;
err = lxc_ipv6_addr_add(ifindex, &inet6dev->addr,
&inet6dev->mcast, &inet6dev->acast,
inet6dev->prefix);
if (err) {
ERROR("failed to setup_ipv6_addr ifindex %d : %s",
ifindex, strerror(-err));
return -1;
}
}
return 0;
}
static int setup_netdev(struct lxc_netdev *netdev)
{
char ifname[IFNAMSIZ];
char *current_ifname = ifname;
int err;
/* empty network namespace */
if (!netdev->ifindex) {
if (netdev->flags & IFF_UP) {
err = lxc_netdev_up("lo");
if (err) {
ERROR("failed to set the loopback up : %s",
strerror(-err));
return -1;
}
}
if (netdev->type != LXC_NET_VETH)
return 0;
netdev->ifindex = if_nametoindex(netdev->name);
}
/* get the new ifindex in case of physical netdev */
if (netdev->type == LXC_NET_PHYS) {
if (!(netdev->ifindex = if_nametoindex(netdev->link))) {
ERROR("failed to get ifindex for %s",
netdev->link);
return -1;
}
}
/* retrieve the name of the interface */
if (!if_indextoname(netdev->ifindex, current_ifname)) {
ERROR("no interface corresponding to index '%d'",
netdev->ifindex);
return -1;
}
/* default: let the system to choose one interface name */
if (!netdev->name)
netdev->name = netdev->type == LXC_NET_PHYS ?
netdev->link : "eth%d";
/* rename the interface name */
if (strcmp(ifname, netdev->name) != 0) {
err = lxc_netdev_rename_by_name(ifname, netdev->name);
if (err) {
ERROR("failed to rename %s->%s : %s", ifname, netdev->name,
strerror(-err));
return -1;
}
}
/* Re-read the name of the interface because its name has changed
* and would be automatically allocated by the system
*/
if (!if_indextoname(netdev->ifindex, current_ifname)) {
ERROR("no interface corresponding to index '%d'",
netdev->ifindex);
return -1;
}
/* set a mac address */
if (netdev->hwaddr) {
if (setup_hw_addr(netdev->hwaddr, current_ifname)) {
ERROR("failed to setup hw address for '%s'",
current_ifname);
return -1;
}
}
/* setup ipv4 addresses on the interface */
if (setup_ipv4_addr(&netdev->ipv4, netdev->ifindex)) {
ERROR("failed to setup ip addresses for '%s'",
ifname);
return -1;
}
/* setup ipv6 addresses on the interface */
if (setup_ipv6_addr(&netdev->ipv6, netdev->ifindex)) {
ERROR("failed to setup ipv6 addresses for '%s'",
ifname);
return -1;
}
/* set the network device up */
if (netdev->flags & IFF_UP) {
int err;
err = lxc_netdev_up(current_ifname);
if (err) {
ERROR("failed to set '%s' up : %s", current_ifname,
strerror(-err));
return -1;
}
/* the network is up, make the loopback up too */
err = lxc_netdev_up("lo");
if (err) {
ERROR("failed to set the loopback up : %s",
strerror(-err));
return -1;
}
}
/* We can only set up the default routes after bringing
* up the interface, sine bringing up the interface adds
* the link-local routes and we can't add a default
* route if the gateway is not reachable. */
/* setup ipv4 gateway on the interface */
if (netdev->ipv4_gateway) {
if (!(netdev->flags & IFF_UP)) {
ERROR("Cannot add ipv4 gateway for %s when not bringing up the interface", ifname);
return -1;
}
if (lxc_list_empty(&netdev->ipv4)) {
ERROR("Cannot add ipv4 gateway for %s when not assigning an address", ifname);
return -1;
}
err = lxc_ipv4_gateway_add(netdev->ifindex, netdev->ipv4_gateway);
if (err) {
err = lxc_ipv4_dest_add(netdev->ifindex, netdev->ipv4_gateway);
if (err) {
ERROR("failed to add ipv4 dest for '%s': %s",
ifname, strerror(-err));
}
err = lxc_ipv4_gateway_add(netdev->ifindex, netdev->ipv4_gateway);
if (err) {
ERROR("failed to setup ipv4 gateway for '%s': %s",
ifname, strerror(-err));
if (netdev->ipv4_gateway_auto) {
char buf[INET_ADDRSTRLEN];
inet_ntop(AF_INET, netdev->ipv4_gateway, buf, sizeof(buf));
ERROR("tried to set autodetected ipv4 gateway '%s'", buf);
}
return -1;
}
}
}
/* setup ipv6 gateway on the interface */
if (netdev->ipv6_gateway) {
if (!(netdev->flags & IFF_UP)) {
ERROR("Cannot add ipv6 gateway for %s when not bringing up the interface", ifname);
return -1;
}
if (lxc_list_empty(&netdev->ipv6) && !IN6_IS_ADDR_LINKLOCAL(netdev->ipv6_gateway)) {
ERROR("Cannot add ipv6 gateway for %s when not assigning an address", ifname);
return -1;
}
err = lxc_ipv6_gateway_add(netdev->ifindex, netdev->ipv6_gateway);
if (err) {
err = lxc_ipv6_dest_add(netdev->ifindex, netdev->ipv6_gateway);
if (err) {
ERROR("failed to add ipv6 dest for '%s': %s",
ifname, strerror(-err));
}
err = lxc_ipv6_gateway_add(netdev->ifindex, netdev->ipv6_gateway);
if (err) {
ERROR("failed to setup ipv6 gateway for '%s': %s",
ifname, strerror(-err));
if (netdev->ipv6_gateway_auto) {
char buf[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, netdev->ipv6_gateway, buf, sizeof(buf));
ERROR("tried to set autodetected ipv6 gateway '%s'", buf);
}
return -1;
}
}
}
DEBUG("'%s' has been setup", current_ifname);
return 0;
}
static int setup_network(struct lxc_list *network)
{
struct lxc_list *iterator;
struct lxc_netdev *netdev;
lxc_list_for_each(iterator, network) {
netdev = iterator->elem;
if (setup_netdev(netdev)) {
ERROR("failed to setup netdev");
return -1;
}
}
if (!lxc_list_empty(network))
INFO("network has been setup");
return 0;
}
/* try to move physical nics to the init netns */
void lxc_restore_phys_nics_to_netns(int netnsfd, struct lxc_conf *conf)
{
int i, oldfd;
char ifname[IFNAMSIZ];
if (netnsfd < 0 || conf->num_savednics == 0)
return;
INFO("Running to reset %d nic names.", conf->num_savednics);
oldfd = lxc_preserve_ns(getpid(), "net");
if (oldfd < 0) {
SYSERROR("Failed to open monitor netns fd.");
return;
}
if (setns(netnsfd, 0) != 0) {
SYSERROR("Failed to enter container netns to reset nics");
close(oldfd);
return;
}
for (i=0; i<conf->num_savednics; i++) {
struct saved_nic *s = &conf->saved_nics[i];
/* retrieve the name of the interface */
if (!if_indextoname(s->ifindex, ifname)) {
WARN("no interface corresponding to index '%d'", s->ifindex);
continue;
}
if (lxc_netdev_move_by_name(ifname, 1, s->orig_name))
WARN("Error moving nic name:%s back to host netns", ifname);
free(s->orig_name);
}
conf->num_savednics = 0;
if (setns(oldfd, 0) != 0)
SYSERROR("Failed to re-enter monitor's netns");
close(oldfd);
}
static char *default_rootfs_mount = LXCROOTFSMOUNT; static char *default_rootfs_mount = LXCROOTFSMOUNT;
struct lxc_conf *lxc_conf_init(void) struct lxc_conf *lxc_conf_init(void)
...@@ -2672,651 +2315,6 @@ struct lxc_conf *lxc_conf_init(void) ...@@ -2672,651 +2315,6 @@ struct lxc_conf *lxc_conf_init(void)
return new; return new;
} }
static int instantiate_veth(struct lxc_handler *handler, struct lxc_netdev *netdev)
{
char *veth1, *veth2;
char veth1buf[IFNAMSIZ], veth2buf[IFNAMSIZ];
int bridge_index, err;
unsigned int mtu = 0;
if (netdev->priv.veth_attr.pair) {
veth1 = netdev->priv.veth_attr.pair;
if (handler->conf->reboot)
lxc_netdev_delete_by_name(veth1);
} else {
err = snprintf(veth1buf, sizeof(veth1buf), "vethXXXXXX");
if (err >= sizeof(veth1buf)) { /* can't *really* happen, but... */
ERROR("veth1 name too long");
return -1;
}
veth1 = lxc_mkifname(veth1buf);
if (!veth1) {
ERROR("failed to allocate a temporary name");
return -1;
}
/* store away for deconf */
memcpy(netdev->priv.veth_attr.veth1, veth1, IFNAMSIZ);
}
snprintf(veth2buf, sizeof(veth2buf), "vethXXXXXX");
veth2 = lxc_mkifname(veth2buf);
if (!veth2) {
ERROR("failed to allocate a temporary name");
goto out_delete;
}
err = lxc_veth_create(veth1, veth2);
if (err) {
ERROR("failed to create veth pair \"%s\" and \"%s\": %s", veth1,
veth2, strerror(-err));
goto out_delete;
}
/* 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(veth1);
if (err) {
ERROR("failed to change mac address of host interface \"%s\": %s",
veth1, strerror(-err));
goto out_delete;
}
netdev->ifindex = if_nametoindex(veth2);
if (!netdev->ifindex) {
ERROR("failed to retrieve the index for \"%s\"", veth2);
goto out_delete;
}
if (netdev->mtu) {
if (lxc_safe_uint(netdev->mtu, &mtu) < 0)
WARN("failed to parse mtu from");
else
INFO("retrieved mtu %d", mtu);
} else if (netdev->link) {
bridge_index = if_nametoindex(netdev->link);
if (bridge_index) {
mtu = netdev_get_mtu(bridge_index);
INFO("retrieved mtu %d from %s", mtu, netdev->link);
} else {
mtu = netdev_get_mtu(netdev->ifindex);
INFO("retrieved mtu %d from %s", mtu, veth2);
}
}
if (mtu) {
err = lxc_netdev_set_mtu(veth1, mtu);
if (!err)
err = lxc_netdev_set_mtu(veth2, mtu);
if (err) {
ERROR("failed to set mtu \"%d\" for veth pair \"%s\" "
"and \"%s\": %s",
mtu, veth1, veth2, strerror(-err));
goto out_delete;
}
}
if (netdev->link) {
err = lxc_bridge_attach(handler->lxcpath, handler->name, netdev->link, veth1);
if (err) {
ERROR("failed to attach \"%s\" to bridge \"%s\": %s",
veth1, netdev->link, strerror(-err));
goto out_delete;
}
INFO("attached \"%s\" to bridge \"%s\"", veth1, netdev->link);
}
err = lxc_netdev_up(veth1);
if (err) {
ERROR("failed to set \"%s\" up: %s", veth1, strerror(-err));
goto out_delete;
}
if (netdev->upscript) {
err = run_script(handler->name, "net", netdev->upscript, "up",
"veth", veth1, (char*) NULL);
if (err)
goto out_delete;
}
DEBUG("instantiated veth \"%s/%s\", index is \"%d\"", veth1, veth2,
netdev->ifindex);
return 0;
out_delete:
if (netdev->ifindex != 0)
lxc_netdev_delete_by_name(veth1);
if (!netdev->priv.veth_attr.pair)
free(veth1);
free(veth2);
return -1;
}
static int shutdown_veth(struct lxc_handler *handler, struct lxc_netdev *netdev)
{
char *veth1;
int err;
if (netdev->priv.veth_attr.pair)
veth1 = netdev->priv.veth_attr.pair;
else
veth1 = netdev->priv.veth_attr.veth1;
if (netdev->downscript) {
err = run_script(handler->name, "net", netdev->downscript,
"down", "veth", veth1, (char*) NULL);
if (err)
return -1;
}
return 0;
}
static int instantiate_macvlan(struct lxc_handler *handler, struct lxc_netdev *netdev)
{
char peerbuf[IFNAMSIZ], *peer;
int err;
if (!netdev->link) {
ERROR("no link specified for macvlan netdev");
return -1;
}
err = snprintf(peerbuf, sizeof(peerbuf), "mcXXXXXX");
if (err >= sizeof(peerbuf))
return -1;
peer = lxc_mkifname(peerbuf);
if (!peer) {
ERROR("failed to make a temporary name");
return -1;
}
err = lxc_macvlan_create(netdev->link, peer,
netdev->priv.macvlan_attr.mode);
if (err) {
ERROR("failed to create macvlan interface '%s' on '%s' : %s",
peer, netdev->link, strerror(-err));
goto out;
}
netdev->ifindex = if_nametoindex(peer);
if (!netdev->ifindex) {
ERROR("failed to retrieve the index for %s", peer);
goto out;
}
if (netdev->upscript) {
err = run_script(handler->name, "net", netdev->upscript, "up",
"macvlan", netdev->link, (char*) NULL);
if (err)
goto out;
}
DEBUG("instantiated macvlan '%s', index is '%d' and mode '%d'",
peer, netdev->ifindex, netdev->priv.macvlan_attr.mode);
return 0;
out:
lxc_netdev_delete_by_name(peer);
free(peer);
return -1;
}
static int shutdown_macvlan(struct lxc_handler *handler, struct lxc_netdev *netdev)
{
int err;
if (netdev->downscript) {
err = run_script(handler->name, "net", netdev->downscript,
"down", "macvlan", netdev->link,
(char*) NULL);
if (err)
return -1;
}
return 0;
}
/* XXX: merge with instantiate_macvlan */
static int instantiate_vlan(struct lxc_handler *handler, struct lxc_netdev *netdev)
{
char peer[IFNAMSIZ];
int err;
static uint16_t vlan_cntr = 0;
unsigned int mtu = 0;
if (!netdev->link) {
ERROR("no link specified for vlan netdev");
return -1;
}
err = snprintf(peer, sizeof(peer), "vlan%d-%d", netdev->priv.vlan_attr.vid, vlan_cntr++);
if (err >= sizeof(peer)) {
ERROR("peer name too long");
return -1;
}
err = lxc_vlan_create(netdev->link, peer, netdev->priv.vlan_attr.vid);
if (err) {
ERROR("failed to create vlan interface '%s' on '%s' : %s",
peer, netdev->link, strerror(-err));
return -1;
}
netdev->ifindex = if_nametoindex(peer);
if (!netdev->ifindex) {
ERROR("failed to retrieve the ifindex for %s", peer);
lxc_netdev_delete_by_name(peer);
return -1;
}
DEBUG("instantiated vlan '%s', ifindex is '%d'", " vlan1000",
netdev->ifindex);
if (netdev->mtu) {
if (lxc_safe_uint(netdev->mtu, &mtu) < 0) {
ERROR("Failed to retrieve mtu from: '%d'/'%s'.",
netdev->ifindex, netdev->name);
return -1;
}
err = lxc_netdev_set_mtu(peer, mtu);
if (err) {
ERROR("failed to set mtu '%s' for %s : %s",
netdev->mtu, peer, strerror(-err));
lxc_netdev_delete_by_name(peer);
return -1;
}
}
return 0;
}
static int shutdown_vlan(struct lxc_handler *handler, struct lxc_netdev *netdev)
{
return 0;
}
static int instantiate_phys(struct lxc_handler *handler, struct lxc_netdev *netdev)
{
if (!netdev->link) {
ERROR("no link specified for the physical interface");
return -1;
}
netdev->ifindex = if_nametoindex(netdev->link);
if (!netdev->ifindex) {
ERROR("failed to retrieve the index for %s", netdev->link);
return -1;
}
if (netdev->upscript) {
int err;
err = run_script(handler->name, "net", netdev->upscript,
"up", "phys", netdev->link, (char*) NULL);
if (err)
return -1;
}
return 0;
}
static int shutdown_phys(struct lxc_handler *handler, struct lxc_netdev *netdev)
{
int err;
if (netdev->downscript) {
err = run_script(handler->name, "net", netdev->downscript,
"down", "phys", netdev->link, (char*) NULL);
if (err)
return -1;
}
return 0;
}
static int instantiate_none(struct lxc_handler *handler, struct lxc_netdev *netdev)
{
netdev->ifindex = 0;
return 0;
}
static int instantiate_empty(struct lxc_handler *handler, struct lxc_netdev *netdev)
{
netdev->ifindex = 0;
if (netdev->upscript) {
int err;
err = run_script(handler->name, "net", netdev->upscript,
"up", "empty", (char*) NULL);
if (err)
return -1;
}
return 0;
}
static int shutdown_empty(struct lxc_handler *handler, struct lxc_netdev *netdev)
{
int err;
if (netdev->downscript) {
err = run_script(handler->name, "net", netdev->downscript,
"down", "empty", (char*) NULL);
if (err)
return -1;
}
return 0;
}
static int shutdown_none(struct lxc_handler *handler, struct lxc_netdev *netdev)
{
return 0;
}
int lxc_requests_empty_network(struct lxc_handler *handler)
{
struct lxc_list *network = &handler->conf->network;
struct lxc_list *iterator;
struct lxc_netdev *netdev;
bool found_none = false, found_nic = false;
if (lxc_list_empty(network))
return 0;
lxc_list_for_each(iterator, network) {
netdev = iterator->elem;
if (netdev->type == LXC_NET_NONE)
found_none = true;
else
found_nic = true;
}
if (found_none && !found_nic)
return 1;
return 0;
}
int lxc_create_network(struct lxc_handler *handler)
{
struct lxc_list *network = &handler->conf->network;
struct lxc_list *iterator;
struct lxc_netdev *netdev;
int am_root = (getuid() == 0);
if (!am_root)
return 0;
lxc_log_configured_netdevs(handler->conf);
lxc_list_for_each(iterator, network) {
netdev = iterator->elem;
if (netdev->type != LXC_NET_MACVLAN && netdev->priv.macvlan_attr.mode) {
ERROR("Invalid macvlan.mode for a non-macvlan netdev");
return -1;
}
if (netdev->type != LXC_NET_VETH && netdev->priv.veth_attr.pair) {
ERROR("Invalid veth pair for a non-veth netdev");
return -1;
}
if (netdev->type != LXC_NET_VLAN && netdev->priv.vlan_attr.vid > 0) {
ERROR("Invalid vlan.id for a non-macvlan netdev");
return -1;
}
if (netdev->type < 0 || netdev->type > LXC_NET_MAXCONFTYPE) {
ERROR("invalid network configuration type '%d'",
netdev->type);
return -1;
}
if (netdev_conf[netdev->type](handler, netdev)) {
ERROR("failed to create netdev");
return -1;
}
}
return 0;
}
bool lxc_delete_network(struct lxc_handler *handler)
{
int ret;
struct lxc_list *network = &handler->conf->network;
struct lxc_list *iterator;
struct lxc_netdev *netdev;
bool deleted_all = true;
lxc_list_for_each(iterator, network) {
netdev = iterator->elem;
if (netdev->ifindex != 0 && netdev->type == LXC_NET_PHYS) {
if (lxc_netdev_rename_by_index(netdev->ifindex, netdev->link))
WARN("Failed to rename interface with index %d "
"to its initial name \"%s\".",
netdev->ifindex, netdev->link);
continue;
}
if (netdev_deconf[netdev->type](handler, netdev)) {
WARN("Failed to destroy netdev");
}
/* Recent kernel remove the virtual interfaces when the network
* namespace is destroyed but in case we did not move the
* interface to the network namespace, we have to destroy it
*/
if (netdev->ifindex != 0) {
ret = lxc_netdev_delete_by_index(netdev->ifindex);
if (-ret == ENODEV) {
INFO("Interface \"%s\" with index %d already "
"deleted or existing in different network "
"namespace.",
netdev->name ? 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->ifindex, strerror(-ret));
} else {
INFO("Removed interface \"%s\" with index %d.",
netdev->name ? netdev->name : "(null)",
netdev->ifindex);
}
}
/* Explicitly delete host veth device to prevent lingering
* devices. We had issues in LXD around this.
*/
if (netdev->ifindex != 0 && netdev->type == LXC_NET_VETH && !am_unpriv()) {
char *hostveth;
if (netdev->priv.veth_attr.pair) {
hostveth = netdev->priv.veth_attr.pair;
ret = lxc_netdev_delete_by_name(hostveth);
if (ret < 0)
WARN("Failed to remove interface \"%s\" from host: %s.", hostveth, strerror(-ret));
else
INFO("Removed interface \"%s\" from host.", hostveth);
} else if (strlen(netdev->priv.veth_attr.veth1) > 0) {
hostveth = netdev->priv.veth_attr.veth1;
ret = lxc_netdev_delete_by_name(hostveth);
if (ret < 0) {
WARN("Failed to remove \"%s\" from host: %s.", hostveth, strerror(-ret));
} else {
INFO("Removed interface \"%s\" from host.", hostveth);
memset((void *)&netdev->priv.veth_attr.veth1, 0, sizeof(netdev->priv.veth_attr.veth1));
}
}
}
}
return deleted_all;
}
#define LXC_USERNIC_PATH LIBEXECDIR "/lxc/lxc-user-nic"
/* lxc-user-nic returns "interface_name:interface_name\n" */
#define MAX_BUFFER_SIZE IFNAMSIZ * 2 + 2
static int unpriv_assign_nic(const char *lxcpath, char *lxcname,
struct lxc_netdev *netdev, pid_t pid)
{
pid_t child;
int bytes, pipefd[2];
char *token, *saveptr = NULL;
char buffer[MAX_BUFFER_SIZE];
char netdev_link[IFNAMSIZ + 1];
if (netdev->type != LXC_NET_VETH) {
ERROR("nic type %d not support for unprivileged use",
netdev->type);
return -1;
}
if (pipe(pipefd) < 0) {
SYSERROR("pipe failed");
return -1;
}
child = fork();
if (child < 0) {
SYSERROR("fork");
close(pipefd[0]);
close(pipefd[1]);
return -1;
}
if (child == 0) { // child
/* Call lxc-user-nic pid type bridge. */
int ret;
char pidstr[LXC_NUMSTRLEN64];
close(pipefd[0]); /* Close the read-end of the pipe. */
/* Redirect stdout to write-end of the pipe. */
ret = dup2(pipefd[1], STDOUT_FILENO);
close(pipefd[1]); /* Close the write-end of the pipe. */
if (ret < 0) {
SYSERROR("Failed to dup2() to redirect stdout to pipe file descriptor.");
exit(EXIT_FAILURE);
}
if (netdev->link)
strncpy(netdev_link, netdev->link, IFNAMSIZ);
else
strncpy(netdev_link, "none", IFNAMSIZ);
ret = snprintf(pidstr, LXC_NUMSTRLEN64, "%d", pid);
if (ret < 0 || ret >= LXC_NUMSTRLEN64)
exit(EXIT_FAILURE);
pidstr[LXC_NUMSTRLEN64 - 1] = '\0';
INFO("Execing lxc-user-nic %s %s %s veth %s %s", lxcpath,
lxcname, pidstr, netdev_link, netdev->name);
execlp(LXC_USERNIC_PATH, LXC_USERNIC_PATH, lxcpath, lxcname,
pidstr, "veth", netdev_link, netdev->name, NULL);
SYSERROR("Failed to exec lxc-user-nic.");
exit(EXIT_FAILURE);
}
/* close the write-end of the pipe */
close(pipefd[1]);
bytes = read(pipefd[0], &buffer, MAX_BUFFER_SIZE);
if (bytes < 0) {
SYSERROR("Failed to read from pipe file descriptor.");
close(pipefd[0]);
return -1;
}
buffer[bytes - 1] = '\0';
if (wait_for_pid(child) != 0) {
TRACE("lxc-user-nic failed to configure requested network");
close(pipefd[0]);
return -1;
}
TRACE("Received output \"%s\" from lxc-user-nic", buffer);
/* close the read-end of the pipe */
close(pipefd[0]);
/* fill netdev->name field */
token = strtok_r(buffer, ":", &saveptr);
if (!token)
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);
/* fill netdev->veth_attr.pair field */
token = strtok_r(NULL, ":", &saveptr);
if (!token)
return -1;
netdev->priv.veth_attr.pair = strdup(token);
if (!netdev->priv.veth_attr.pair) {
ERROR("Failed to allocate memory.");
return -1;
}
return 0;
}
int lxc_assign_network(const char *lxcpath, char *lxcname,
struct lxc_list *network, pid_t pid)
{
struct lxc_list *iterator;
struct lxc_netdev *netdev;
char ifname[IFNAMSIZ];
int am_root = (getuid() == 0);
int err;
lxc_list_for_each(iterator, network) {
netdev = iterator->elem;
if (netdev->type == LXC_NET_VETH && !am_root) {
if (netdev->mtu)
INFO("mtu ignored due to insufficient privilege");
if (unpriv_assign_nic(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 filed in at setup_netdev.
continue;
}
/* empty network namespace, nothing to move */
if (!netdev->ifindex)
continue;
/* retrieve the name of the interface */
if (!if_indextoname(netdev->ifindex, ifname)) {
ERROR("no interface corresponding to index '%d'", netdev->ifindex);
return -1;
}
err = lxc_netdev_move_by_name(ifname, pid, NULL);
if (err) {
ERROR("failed to move '%s' to the container : %s",
netdev->link, strerror(-err));
return -1;
}
DEBUG("move '%s'/'%s' to '%d': .", ifname, netdev->name, pid);
}
return 0;
}
static int write_id_mapping(enum idtype idtype, pid_t pid, const char *buf, static int write_id_mapping(enum idtype idtype, pid_t pid, const char *buf,
size_t buf_size) size_t buf_size)
{ {
...@@ -3588,54 +2586,6 @@ again: ...@@ -3588,54 +2586,6 @@ again:
return freeid; return freeid;
} }
int lxc_find_gateway_addresses(struct lxc_handler *handler)
{
struct lxc_list *network = &handler->conf->network;
struct lxc_list *iterator;
struct lxc_netdev *netdev;
int link_index;
lxc_list_for_each(iterator, network) {
netdev = iterator->elem;
if (!netdev->ipv4_gateway_auto && !netdev->ipv6_gateway_auto)
continue;
if (netdev->type != LXC_NET_VETH && netdev->type != LXC_NET_MACVLAN) {
ERROR("gateway = auto only supported for "
"veth and macvlan");
return -1;
}
if (!netdev->link) {
ERROR("gateway = auto needs a link interface");
return -1;
}
link_index = if_nametoindex(netdev->link);
if (!link_index)
return -EINVAL;
if (netdev->ipv4_gateway_auto) {
if (lxc_ipv4_addr_get(link_index, &netdev->ipv4_gateway)) {
ERROR("failed to automatically find ipv4 gateway "
"address from link interface '%s'", netdev->link);
return -1;
}
}
if (netdev->ipv6_gateway_auto) {
if (lxc_ipv6_addr_get(link_index, &netdev->ipv6_gateway)) {
ERROR("failed to automatically find ipv6 gateway "
"address from link interface '%s'", netdev->link);
return -1;
}
}
}
return 0;
}
int lxc_create_tty(const char *name, struct lxc_conf *conf) int lxc_create_tty(const char *name, struct lxc_conf *conf)
{ {
struct lxc_tty_info *tty_info = &conf->tty_info; struct lxc_tty_info *tty_info = &conf->tty_info;
...@@ -4113,7 +3063,7 @@ int lxc_setup(struct lxc_handler *handler) ...@@ -4113,7 +3063,7 @@ int lxc_setup(struct lxc_handler *handler)
} }
} }
if (setup_network(&lxc_conf->network)) { if (lxc_setup_network_in_child_namespaces(lxc_conf, &lxc_conf->network)) {
ERROR("failed to setup the network for '%s'", name); ERROR("failed to setup the network for '%s'", name);
return -1; return -1;
} }
...@@ -4469,7 +3419,6 @@ int lxc_clear_environment(struct lxc_conf *c) ...@@ -4469,7 +3419,6 @@ int lxc_clear_environment(struct lxc_conf *c)
return 0; return 0;
} }
int lxc_clear_mount_entries(struct lxc_conf *c) int lxc_clear_mount_entries(struct lxc_conf *c)
{ {
struct lxc_list *it,*next; struct lxc_list *it,*next;
......
...@@ -43,103 +43,6 @@ typedef void * scmp_filter_ctx; ...@@ -43,103 +43,6 @@ typedef void * scmp_filter_ctx;
#define subuidfile "/etc/subuid" #define subuidfile "/etc/subuid"
#define subgidfile "/etc/subgid" #define subgidfile "/etc/subgid"
enum {
LXC_NET_EMPTY,
LXC_NET_VETH,
LXC_NET_MACVLAN,
LXC_NET_PHYS,
LXC_NET_VLAN,
LXC_NET_NONE,
LXC_NET_MAXCONFTYPE,
};
/*
* Defines the structure to configure an ipv4 address
* @address : ipv4 address
* @broadcast : ipv4 broadcast address
* @mask : network mask
*/
struct lxc_inetdev {
struct in_addr addr;
struct in_addr bcast;
unsigned int prefix;
};
struct lxc_route {
struct in_addr addr;
};
/*
* Defines the structure to configure an ipv6 address
* @flags : set the address up
* @address : ipv6 address
* @broadcast : ipv6 broadcast address
* @mask : network mask
*/
struct lxc_inet6dev {
struct in6_addr addr;
struct in6_addr mcast;
struct in6_addr acast;
unsigned int prefix;
};
struct lxc_route6 {
struct in6_addr addr;
};
struct ifla_veth {
char *pair; /* pair name */
char veth1[IFNAMSIZ]; /* needed for deconf */
};
struct ifla_vlan {
unsigned int flags;
unsigned int fmask;
unsigned short vid;
unsigned short pad;
};
struct ifla_macvlan {
int mode; /* private, vepa, bridge, passthru */
};
union netdev_p {
struct ifla_veth veth_attr;
struct ifla_vlan vlan_attr;
struct ifla_macvlan macvlan_attr;
};
/*
* Defines a structure to configure a network device
* @link : lxc.network.link, name of bridge or host iface to attach if any
* @name : lxc.network.name, name of iface on the container side
* @flags : flag of the network device (IFF_UP, ... )
* @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
* @upscript : a script filename to be executed during interface configuration
* @downscript : a script filename to be executed during interface destruction
* @idx : network counter
*/
struct lxc_netdev {
unsigned int idx;
int type;
int flags;
int ifindex;
char *link;
char *name;
char *hwaddr;
char *mtu;
union netdev_p priv;
struct lxc_list ipv4;
struct lxc_list ipv6;
struct in_addr *ipv4_gateway;
bool ipv4_gateway_auto;
struct in6_addr *ipv6_gateway;
bool ipv6_gateway_auto;
char *upscript;
char *downscript;
};
/* /*
* Defines a generic struct to configure the control group. * Defines a generic struct to configure the control group.
* It is up to the programmer to specify the right subsystem. * It is up to the programmer to specify the right subsystem.
...@@ -282,16 +185,20 @@ enum { ...@@ -282,16 +185,20 @@ enum {
* @lsm_se_context : selinux type to switch to or NULL * @lsm_se_context : selinux type to switch to or NULL
*/ */
enum lxchooks { enum lxchooks {
LXCHOOK_PRESTART, LXCHOOK_PREMOUNT, LXCHOOK_MOUNT, LXCHOOK_AUTODEV, LXCHOOK_PRESTART,
LXCHOOK_START, LXCHOOK_STOP, LXCHOOK_POSTSTOP, LXCHOOK_CLONE, LXCHOOK_DESTROY, LXCHOOK_PREMOUNT,
NUM_LXC_HOOKS}; LXCHOOK_MOUNT,
extern char *lxchook_names[NUM_LXC_HOOKS]; LXCHOOK_AUTODEV,
LXCHOOK_START,
struct saved_nic { LXCHOOK_STOP,
int ifindex; LXCHOOK_POSTSTOP,
char *orig_name; LXCHOOK_CLONE,
LXCHOOK_DESTROY,
NUM_LXC_HOOKS
}; };
extern char *lxchook_names[NUM_LXC_HOOKS];
struct lxc_conf { struct lxc_conf {
int is_execute; int is_execute;
char *fstab; char *fstab;
...@@ -400,15 +307,7 @@ extern struct lxc_conf *lxc_conf_init(void); ...@@ -400,15 +307,7 @@ extern struct lxc_conf *lxc_conf_init(void);
extern void lxc_conf_free(struct lxc_conf *conf); extern void lxc_conf_free(struct lxc_conf *conf);
extern int pin_rootfs(const char *rootfs); extern int pin_rootfs(const char *rootfs);
extern int lxc_requests_empty_network(struct lxc_handler *handler);
extern int lxc_create_network(struct lxc_handler *handler);
extern bool lxc_delete_network(struct lxc_handler *handler);
extern int lxc_assign_network(const char *lxcpath, char *lxcname,
struct lxc_list *networks, pid_t pid);
extern int lxc_map_ids(struct lxc_list *idmap, pid_t pid); extern int lxc_map_ids(struct lxc_list *idmap, pid_t pid);
extern int lxc_find_gateway_addresses(struct lxc_handler *handler);
extern int lxc_create_tty(const char *name, struct lxc_conf *conf); extern int lxc_create_tty(const char *name, struct lxc_conf *conf);
extern void lxc_delete_tty(struct lxc_tty_info *tty_info); extern void lxc_delete_tty(struct lxc_tty_info *tty_info);
...@@ -435,9 +334,6 @@ extern int do_rootfs_setup(struct lxc_conf *conf, const char *name, ...@@ -435,9 +334,6 @@ extern int do_rootfs_setup(struct lxc_conf *conf, const char *name,
struct cgroup_process_info; struct cgroup_process_info;
extern int lxc_setup(struct lxc_handler *handler); extern int lxc_setup(struct lxc_handler *handler);
extern void lxc_restore_phys_nics_to_netns(int netnsfd, struct lxc_conf *conf);
extern int find_unmapped_nsid(struct lxc_conf *conf, enum idtype idtype); extern int find_unmapped_nsid(struct lxc_conf *conf, enum idtype idtype);
extern int mapped_hostid(unsigned id, struct lxc_conf *conf, enum idtype idtype); extern int mapped_hostid(unsigned id, struct lxc_conf *conf, enum idtype idtype);
extern int chown_mapped_root(char *path, struct lxc_conf *conf); extern int chown_mapped_root(char *path, struct lxc_conf *conf);
...@@ -449,9 +345,11 @@ extern int parse_mntopts(const char *mntopts, unsigned long *mntflags, ...@@ -449,9 +345,11 @@ extern int parse_mntopts(const char *mntopts, unsigned long *mntflags,
extern void tmp_proc_unmount(struct lxc_conf *lxc_conf); extern void tmp_proc_unmount(struct lxc_conf *lxc_conf);
void remount_all_slave(void); void remount_all_slave(void);
extern void suggest_default_idmap(void); extern void suggest_default_idmap(void);
FILE *make_anonymous_mount_file(struct lxc_list *mount); extern FILE *make_anonymous_mount_file(struct lxc_list *mount);
struct lxc_list *sort_cgroup_settings(struct lxc_list* cgroup_settings); extern struct lxc_list *sort_cgroup_settings(struct lxc_list *cgroup_settings);
unsigned long add_required_remount_flags(const char *s, const char *d, extern unsigned long add_required_remount_flags(const char *s, const char *d,
unsigned long flags); unsigned long flags);
extern int run_script(const char *name, const char *section, const char *script,
#endif ...);
#endif /* __LXC_CONF_H */
...@@ -29,6 +29,8 @@ ...@@ -29,6 +29,8 @@
#include "error.h" #include "error.h"
#include "log.h" #include "log.h"
#include "list.h" #include "list.h"
#include "network.h"
#include "parse.h"
#include "utils.h" #include "utils.h"
lxc_log_define(lxc_confile_utils, lxc); lxc_log_define(lxc_confile_utils, lxc);
...@@ -253,7 +255,8 @@ void lxc_log_configured_netdevs(const struct lxc_conf *conf) ...@@ -253,7 +255,8 @@ void lxc_log_configured_netdevs(const struct lxc_conf *conf)
lxc_list_for_each(it, &conf->network) { lxc_list_for_each(it, &conf->network) {
netdev = it->elem; netdev = it->elem;
TRACE("index: %d", netdev->idx); TRACE("index: %zd", netdev->idx);
TRACE("ifindex: %d", netdev->ifindex);
switch (netdev->type) { switch (netdev->type) {
case LXC_NET_VETH: case LXC_NET_VETH:
TRACE("type: veth"); TRACE("type: veth");
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
#define _GNU_SOURCE /* See feature_test_macros(7) */ #define _GNU_SOURCE
#include <alloca.h> #include <alloca.h>
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
...@@ -59,21 +59,27 @@ ...@@ -59,21 +59,27 @@
static void usage(char *me, bool fail) static void usage(char *me, bool fail)
{ {
fprintf(stderr, "Usage: %s lxcpath name pid type bridge nicname\n", me); fprintf(stderr, "Usage: %s create {lxcpath} {name} {pid} {type} "
fprintf(stderr, " nicname is the name to use inside the container\n"); "{bridge} {nicname}\n", me);
exit(fail ? 1 : 0); fprintf(stderr, "Usage: %s delete {lxcpath} {name} {pid} {type} "
} "{bridge} {nicname}\n", me);
fprintf(stderr, "{nicname} is the name to use inside the container\n");
static char *lxcpath, *lxcname; if (fail)
exit(EXIT_FAILURE);
exit(EXIT_SUCCESS);
}
static int open_and_lock(char *path) static int open_and_lock(char *path)
{ {
int fd; int fd, ret;
struct flock lk; struct flock lk;
fd = open(path, O_RDWR|O_CREAT, S_IWUSR | S_IRUSR); fd = open(path, O_RDWR | O_CREAT, S_IWUSR | S_IRUSR);
if (fd < 0) { if (fd < 0) {
usernic_error("Failed to open %s: %s.\n", path, strerror(errno)); usernic_error("Failed to open %s: %s.\n", path,
strerror(errno));
return -1; return -1;
} }
...@@ -81,8 +87,11 @@ static int open_and_lock(char *path) ...@@ -81,8 +87,11 @@ static int open_and_lock(char *path)
lk.l_whence = SEEK_SET; lk.l_whence = SEEK_SET;
lk.l_start = 0; lk.l_start = 0;
lk.l_len = 0; lk.l_len = 0;
if (fcntl(fd, F_SETLKW, &lk) < 0) {
usernic_error("Failed to lock %s: %s.\n", path, strerror(errno)); ret = fcntl(fd, F_SETLKW, &lk);
if (ret < 0) {
usernic_error("Failed to lock \"%s\": %s\n", path,
strerror(errno));
close(fd); close(fd);
return -1; return -1;
} }
...@@ -90,14 +99,13 @@ static int open_and_lock(char *path) ...@@ -90,14 +99,13 @@ static int open_and_lock(char *path)
return fd; return fd;
} }
static char *get_username(void) static char *get_username(void)
{ {
struct passwd *pwd; struct passwd *pwd;
pwd = getpwuid(getuid()); pwd = getpwuid(getuid());
if (!pwd) { if (!pwd) {
usernic_error("Failed to call get username: %s.\n", strerror(errno)); usernic_error("Failed to get username: %s\n", strerror(errno));
return NULL; return NULL;
} }
...@@ -127,9 +135,8 @@ static char **get_groupnames(void) ...@@ -127,9 +135,8 @@ static char **get_groupnames(void)
ngroups = getgroups(0, NULL); ngroups = getgroups(0, NULL);
if (ngroups < 0) { if (ngroups < 0) {
usernic_error( usernic_error("Failed to get number of groups the user "
"Failed to get number of groups the user belongs to: %s.\n", "belongs to: %s\n", strerror(errno));
strerror(errno));
return NULL; return NULL;
} }
if (ngroups == 0) if (ngroups == 0)
...@@ -203,19 +210,21 @@ struct alloted_s { ...@@ -203,19 +210,21 @@ struct alloted_s {
struct alloted_s *next; struct alloted_s *next;
}; };
static struct alloted_s *append_alloted(struct alloted_s **head, char *name, int n) static struct alloted_s *append_alloted(struct alloted_s **head, char *name,
int n)
{ {
struct alloted_s *cur, *al; struct alloted_s *cur, *al;
if (!head || !name) { if (!head || !name) {
// sanity check. parameters should not be null /* Sanity check. Parameters should not be null. */
usernic_error("%s\n", "Unexpected NULL argument."); usernic_error("%s\n", "Unexpected NULL argument");
return NULL; return NULL;
} }
al = malloc(sizeof(struct alloted_s)); al = malloc(sizeof(struct alloted_s));
if (!al) { if (!al) {
usernic_error("Failed to allocate memory: %s.\n", strerror(errno)); usernic_error("Failed to allocate memory: %s\n",
strerror(errno));
return NULL; return NULL;
} }
...@@ -266,7 +275,8 @@ static void free_alloted(struct alloted_s **head) ...@@ -266,7 +275,8 @@ static void free_alloted(struct alloted_s **head)
* Return the count entry for the calling user if there is one. Else * Return the count entry for the calling user if there is one. Else
* return -1. * return -1.
*/ */
static int get_alloted(char *me, char *intype, char *link, struct alloted_s **alloted) static int get_alloted(char *me, char *intype, char *link,
struct alloted_s **alloted)
{ {
int n, ret; int n, ret;
char name[100], type[100], br[100]; char name[100], type[100], br[100];
...@@ -279,13 +289,15 @@ static int get_alloted(char *me, char *intype, char *link, struct alloted_s **al ...@@ -279,13 +289,15 @@ static int get_alloted(char *me, char *intype, char *link, struct alloted_s **al
fin = fopen(LXC_USERNIC_CONF, "r"); fin = fopen(LXC_USERNIC_CONF, "r");
if (!fin) { if (!fin) {
usernic_error("Failed to open \"%s\": %s.\n", LXC_USERNIC_CONF, strerror(errno)); usernic_error("Failed to open \"%s\": %s\n", LXC_USERNIC_CONF,
strerror(errno));
return -1; return -1;
} }
groups = get_groupnames(); groups = get_groupnames();
while ((getline(&line, &len, fin)) != -1) { while ((getline(&line, &len, fin)) != -1) {
ret = sscanf(line, "%99[^ \t] %99[^ \t] %99[^ \t] %d", name, type, br, &n); ret = sscanf(line, "%99[^ \t] %99[^ \t] %99[^ \t] %d", name,
type, br, &n);
if (ret != 4) if (ret != 4)
continue; continue;
...@@ -358,7 +370,8 @@ static char *find_line(char *p, char *e, char *u, char *t, char *l) ...@@ -358,7 +370,8 @@ static char *find_line(char *p, char *e, char *u, char *t, char *l)
p++; p++;
p2 = get_eow(p, e); p2 = get_eow(p, e);
if (!p2 || ((size_t)(p2 - p)) != strlen(u) || strncmp(p, u, strlen(u))) if (!p2 || ((size_t)(p2 - p)) != strlen(u) ||
strncmp(p, u, strlen(u)))
goto next; goto next;
p = p2 + 1; p = p2 + 1;
...@@ -366,7 +379,8 @@ static char *find_line(char *p, char *e, char *u, char *t, char *l) ...@@ -366,7 +379,8 @@ static char *find_line(char *p, char *e, char *u, char *t, char *l)
p++; p++;
p2 = get_eow(p, e); p2 = get_eow(p, e);
if (!p2 || ((size_t)(p2 - p)) != strlen(t) || strncmp(p, t, strlen(t))) if (!p2 || ((size_t)(p2 - p)) != strlen(t) ||
strncmp(p, t, strlen(t)))
goto next; goto next;
p = p2 + 1; p = p2 + 1;
...@@ -374,11 +388,12 @@ static char *find_line(char *p, char *e, char *u, char *t, char *l) ...@@ -374,11 +388,12 @@ static char *find_line(char *p, char *e, char *u, char *t, char *l)
p++; p++;
p2 = get_eow(p, e); p2 = get_eow(p, e);
if (!p2 || ((size_t)(p2 - p)) != strlen(l) || strncmp(p, l, strlen(l))) if (!p2 || ((size_t)(p2 - p)) != strlen(l) ||
strncmp(p, l, strlen(l)))
goto next; goto next;
return ret; return ret;
next: next:
p = p1 + 1; p = p1 + 1;
} }
...@@ -417,7 +432,8 @@ static int instantiate_veth(char *n1, char **n2) ...@@ -417,7 +432,8 @@ static int instantiate_veth(char *n1, char **n2)
err = lxc_veth_create(n1, *n2); err = lxc_veth_create(n1, *n2);
if (err) { if (err) {
usernic_error("Failed to create %s-%s : %s.\n", n1, *n2, strerror(-err)); usernic_error("Failed to create %s-%s : %s.\n", n1, *n2,
strerror(-err));
return -1; return -1;
} }
...@@ -427,8 +443,7 @@ static int instantiate_veth(char *n1, char **n2) ...@@ -427,8 +443,7 @@ static int instantiate_veth(char *n1, char **n2)
err = setup_private_host_hw_addr(n1); err = setup_private_host_hw_addr(n1);
if (err) if (err)
usernic_error("Failed to change mac address of host interface " usernic_error("Failed to change mac address of host interface "
"%s : %s.\n", "%s : %s\n", n1, strerror(-err));
n1, strerror(-err));
return netdev_set_flag(n1, IFF_UP); return netdev_set_flag(n1, IFF_UP);
} }
...@@ -471,19 +486,21 @@ static bool create_nic(char *nic, char *br, int pid, char **cnic) ...@@ -471,19 +486,21 @@ static bool create_nic(char *nic, char *br, int pid, char **cnic)
if (mtu > 0) { if (mtu > 0) {
ret = lxc_netdev_set_mtu(veth1buf, mtu); ret = lxc_netdev_set_mtu(veth1buf, mtu);
if (ret < 0) { if (ret < 0) {
usernic_error("Failed to set mtu to %d on %s.\n", mtu, veth1buf); usernic_error("Failed to set mtu to %d on %s\n",
mtu, veth1buf);
goto out_del; goto out_del;
} }
ret = lxc_netdev_set_mtu(veth2buf, mtu); ret = lxc_netdev_set_mtu(veth2buf, mtu);
if (ret < 0) { if (ret < 0) {
usernic_error("Failed to set mtu to %d on %s.\n", mtu, veth2buf); usernic_error("Failed to set mtu to %d on %s\n",
mtu, veth2buf);
goto out_del; goto out_del;
} }
} }
/* attach veth1 to bridge */ /* attach veth1 to bridge */
ret = lxc_bridge_attach(lxcpath, lxcname, br, veth1buf); ret = lxc_bridge_attach(br, veth1buf);
if (ret < 0) { if (ret < 0) {
usernic_error("Error attaching %s to %s.\n", veth1buf, br); usernic_error("Error attaching %s to %s.\n", veth1buf, br);
goto out_del; goto out_del;
...@@ -493,7 +510,8 @@ static bool create_nic(char *nic, char *br, int pid, char **cnic) ...@@ -493,7 +510,8 @@ static bool create_nic(char *nic, char *br, int pid, char **cnic)
/* pass veth2 to target netns */ /* pass veth2 to target netns */
ret = lxc_netdev_move_by_name(veth2buf, pid, NULL); ret = lxc_netdev_move_by_name(veth2buf, pid, NULL);
if (ret < 0) { if (ret < 0) {
usernic_error("Error moving %s to network namespace of %d.\n", veth2buf, pid); usernic_error("Error moving %s to network namespace of %d\n",
veth2buf, pid);
goto out_del; goto out_del;
} }
...@@ -510,25 +528,29 @@ out_del: ...@@ -510,25 +528,29 @@ out_del:
return false; return false;
} }
/* /* get_new_nicname() will return the name (vethXXXXXX) which is attached on the
* Get a new nic. * host to the lxc bridge. The returned string must be freed by caller.
* *dest will contain the name (vethXXXXXX) which is attached
* on the host to the lxc bridge
*/ */
static bool get_new_nicname(char **dest, char *br, int pid, char **cnic) static char *get_new_nicname(char *br, int pid, char **cnic)
{ {
int ret; int ret;
char *nicname;
char template[IFNAMSIZ]; char template[IFNAMSIZ];
ret = snprintf(template, sizeof(template), "vethXXXXXX"); ret = snprintf(template, sizeof(template), "vethXXXXXX");
if (ret < 0 || (size_t)ret >= sizeof(template)) if (ret < 0 || (size_t)ret >= sizeof(template))
return false; return NULL;
*dest = lxc_mkifname(template); nicname = lxc_mkifname(template);
if (!create_nic(*dest, br, pid, cnic)) if (!nicname)
return false; return NULL;
return true; if (!create_nic(nicname, br, pid, cnic)) {
free(nicname);
return NULL;
}
return nicname;
} }
static bool get_nic_from_line(char *p, char **nic) static bool get_nic_from_line(char *p, char **nic)
...@@ -536,7 +558,8 @@ static bool get_nic_from_line(char *p, char **nic) ...@@ -536,7 +558,8 @@ static bool get_nic_from_line(char *p, char **nic)
int ret; int ret;
char user[100], type[100], br[100]; 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); ret = sscanf(p, "%99[^ \t\n] %99[^ \t\n] %99[^ \t\n] %99[^ \t\n]", user,
type, br, *nic);
if (ret != 4) if (ret != 4)
return false; return false;
...@@ -549,20 +572,23 @@ struct entry_line { ...@@ -549,20 +572,23 @@ struct entry_line {
bool keep; bool keep;
}; };
static bool cull_entries(int fd, char *me, char *t, char *br) static bool cull_entries(int fd, char *me, char *t, char *br, char *nicname,
bool *found_nicname)
{ {
int i, n = 0; int i, ret;
off_t len; off_t len;
char *buf, *p, *e, *nic; char *buf, *e, *nic, *p;
struct stat sb; struct stat sb;
int n = 0;
struct entry_line *entry_lines = NULL; struct entry_line *entry_lines = NULL;
nic = alloca(100); nic = alloca(100);
if (!nic) if (!nic)
return false; return false;
if (fstat(fd, &sb) < 0) { ret = fstat(fd, &sb);
usernic_error("Failed to fstat: %s.\n", strerror(errno)); if (ret < 0) {
usernic_error("Failed to fstat: %s\n", strerror(errno));
return false; return false;
} }
...@@ -570,9 +596,10 @@ static bool cull_entries(int fd, char *me, char *t, char *br) ...@@ -570,9 +596,10 @@ static bool cull_entries(int fd, char *me, char *t, char *br)
if (len == 0) if (len == 0)
return true; return true;
buf = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); buf = lxc_strmmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (buf == MAP_FAILED) { 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 false; return false;
} }
...@@ -598,6 +625,10 @@ static bool cull_entries(int fd, char *me, char *t, char *br) ...@@ -598,6 +625,10 @@ static bool cull_entries(int fd, char *me, char *t, char *br)
if (nic && !nic_exists(nic)) if (nic && !nic_exists(nic))
entry_lines[n - 1].keep = false; entry_lines[n - 1].keep = false;
if (nicname)
if (!strcmp(nic, nicname))
*found_nicname = true;
p += entry_lines[n - 1].len + 1; p += entry_lines[n - 1].len + 1;
if (p >= e) if (p >= e)
break; break;
...@@ -615,9 +646,11 @@ static bool cull_entries(int fd, char *me, char *t, char *br) ...@@ -615,9 +646,11 @@ static bool cull_entries(int fd, char *me, char *t, char *br)
} }
free(entry_lines); free(entry_lines);
munmap(buf, sb.st_size); lxc_strmunmap(buf, sb.st_size);
if (ftruncate(fd, p - buf)) ret = ftruncate(fd, p - buf);
usernic_error("Failed to set new file size: %s.\n", strerror(errno)); if (ret < 0)
usernic_error("Failed to set new file size: %s\n",
strerror(errno));
return true; return true;
} }
...@@ -638,41 +671,39 @@ static int count_entries(char *buf, off_t len, char *me, char *t, char *br) ...@@ -638,41 +671,39 @@ static int count_entries(char *buf, off_t len, char *me, char *t, char *br)
return count; return count;
} }
/* /* The dbfile has lines of the format: user type bridge nicname. */
* The dbfile has lines of the format: static char *get_nic_if_avail(int fd, struct alloted_s *names, int pid,
* user type bridge nicname char *intype, char *br, int allowed, char **cnic)
*/
static bool get_nic_if_avail(int fd, struct alloted_s *names, int pid,
char *intype, char *br, int allowed,
char **nicname, char **cnic)
{ {
int ret; int ret;
off_t len, slen; off_t len, slen;
char *newline, *owner; char *newline, *nicname, *owner;
struct stat sb; struct stat sb;
struct alloted_s *n; struct alloted_s *n;
int count = 0; int count = 0;
char *buf = NULL; char *buf = NULL;
for (n = names; n != NULL; n = n->next) for (n = names; n != NULL; n = n->next)
cull_entries(fd, n->name, intype, br); cull_entries(fd, n->name, intype, br, NULL, NULL);
if (allowed == 0) if (allowed == 0)
return false; return NULL;
owner = names->name; owner = names->name;
if (fstat(fd, &sb) < 0) { if (fstat(fd, &sb) < 0) {
usernic_error("Failed to fstat: %s.\n", strerror(errno)); usernic_error("Failed to fstat: %s\n", strerror(errno));
return false; return NULL;
} }
len = sb.st_size; len = sb.st_size;
if (len > 0) { if (len > 0) {
buf = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); buf =
mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (buf == MAP_FAILED) { 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",
return false; strerror(errno));
return NULL;
} }
owner = NULL; owner = NULL;
...@@ -688,47 +719,56 @@ static bool get_nic_if_avail(int fd, struct alloted_s *names, int pid, ...@@ -688,47 +719,56 @@ static bool get_nic_if_avail(int fd, struct alloted_s *names, int pid,
} }
if (owner == NULL) if (owner == NULL)
return false; return NULL;
if (!get_new_nicname(nicname, br, pid, cnic)) nicname = get_new_nicname(br, pid, cnic);
return false; if (!nicname) {
usernic_error("%s", "Failed to get new nic name\n");
return NULL;
}
/* owner ' ' intype ' ' br ' ' *nicname + '\n' + '\0' */ /* owner ' ' intype ' ' br ' ' *nicname + '\n' + '\0' */
slen = strlen(owner) + strlen(intype) + strlen(br) + strlen(*nicname) + 5; slen = strlen(owner) + strlen(intype) + strlen(br) + strlen(nicname) + 5;
newline = alloca(slen); newline = alloca(slen);
if (!newline) { if (!newline) {
usernic_error("Failed allocate memory: %s.\n", strerror(errno)); free(nicname);
return false; usernic_error("Failed allocate memory: %s\n", strerror(errno));
return NULL;
} }
ret = snprintf(newline, slen, "%s %s %s %s\n", owner, intype, br, *nicname); ret = snprintf(newline, slen, "%s %s %s %s\n", owner, intype, br, nicname);
if (ret < 0 || ret >= slen) { if (ret < 0 || ret >= slen) {
if (lxc_netdev_delete_by_name(*nicname) != 0) if (lxc_netdev_delete_by_name(nicname) != 0)
usernic_error("Error unlinking %s.\n", *nicname); usernic_error("Error unlinking %s\n", nicname);
return false; free(nicname);
return NULL;
} }
if (len) if (len)
munmap(buf, len); munmap(buf, len);
if (ftruncate(fd, len + slen)) if (ftruncate(fd, len + slen))
usernic_error("Failed to set new file size: %s.\n", strerror(errno)); usernic_error("Failed to set new file size: %s\n",
strerror(errno));
buf = mmap(NULL, len + slen, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); buf = mmap(NULL, len + slen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (buf == MAP_FAILED) { 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",
if (lxc_netdev_delete_by_name(*nicname) != 0) strerror(errno));
usernic_error("Error unlinking %s.\n", *nicname); if (lxc_netdev_delete_by_name(nicname) != 0)
return false; usernic_error("Error unlinking %s\n", nicname);
free(nicname);
return NULL;
} }
strcpy(buf + len, newline); strcpy(buf + len, newline);
munmap(buf, len + slen); munmap(buf, len + slen);
return true; return nicname;
} }
static bool create_db_dir(char *fnam) static bool create_db_dir(char *fnam)
{ {
int ret;
char *p; char *p;
p = alloca(strlen(fnam) + 1); p = alloca(strlen(fnam) + 1);
...@@ -743,8 +783,11 @@ again: ...@@ -743,8 +783,11 @@ again:
return true; return true;
*p = '\0'; *p = '\0';
if (mkdir(fnam, 0755) && errno != EEXIST) {
usernic_error("Failed to create %s: %s.\n", fnam, strerror(errno)); ret = mkdir(fnam, 0755);
if (ret < 0 && errno != EEXIST) {
usernic_error("Failed to create %s: %s\n", fnam,
strerror(errno));
*p = '/'; *p = '/';
return false; return false;
} }
...@@ -753,18 +796,19 @@ again: ...@@ -753,18 +796,19 @@ again:
goto again; goto again;
} }
#define VETH_DEF_NAME "eth%d" static char *lxc_secure_rename_in_ns(int pid, char *oldname, char *newname,
static int rename_in_ns(int pid, char *oldname, char **newnamep) int *ifidx)
{ {
int ret;
uid_t ruid, suid, euid; uid_t ruid, suid, euid;
int fret = -1; char ifname[IFNAMSIZ];
int fd = -1, ifindex = -1, ofd = -1, ret; char *string_ret = NULL, *name = NULL;
bool grab_newname = false; int fd = -1, ifindex = -1, ofd = -1;
ofd = lxc_preserve_ns(getpid(), "net"); ofd = lxc_preserve_ns(getpid(), "net");
if (ofd < 0) { if (ofd < 0) {
usernic_error("Failed opening network namespace path for '%d'.", getpid()); usernic_error("Failed opening network namespace path for %d", getpid());
return fret; return NULL;
} }
fd = lxc_preserve_ns(pid, "net"); fd = lxc_preserve_ns(pid, "net");
...@@ -803,66 +847,70 @@ static int rename_in_ns(int pid, char *oldname, char **newnamep) ...@@ -803,66 +847,70 @@ static int rename_in_ns(int pid, char *oldname, char **newnamep)
goto do_full_cleanup; goto do_full_cleanup;
} }
if (!*newnamep) { /* Check if old interface exists. */
grab_newname = true; ifindex = if_nametoindex(oldname);
*newnamep = VETH_DEF_NAME; if (!ifindex) {
usernic_error("Failed to get netdev index: %s\n", strerror(errno));
ifindex = if_nametoindex(oldname); goto do_full_cleanup;
if (!ifindex) {
usernic_error("Failed to get netdev index: %s.\n", strerror(errno));
goto do_full_cleanup;
}
} }
ret = lxc_netdev_rename_by_name(oldname, *newnamep); /* When the IFLA_IFNAME attribute is passed something like "<prefix>%d"
* netlink will replace the format specifier with an appropriate index.
* So we pass "eth%d".
*/
if (newname)
name = newname;
else
name = "eth%d";
ret = lxc_netdev_rename_by_name(oldname, name);
name = NULL;
if (ret < 0) { if (ret < 0) {
usernic_error("Error %d renaming netdev %s to %s in container.\n", ret, oldname, *newnamep); usernic_error("Error %d renaming netdev %s to %s in container\n",
ret, oldname, newname ? newname : "eth%d");
goto do_full_cleanup; goto do_full_cleanup;
} }
if (grab_newname) { /* Retrieve new name for interface. */
char ifname[IFNAMSIZ]; if (!if_indextoname(ifindex, ifname)) {
char *namep = ifname; usernic_error("Failed to get new netdev name: %s\n", strerror(errno));
goto do_full_cleanup;
if (!if_indextoname(ifindex, namep)) {
usernic_error("Failed to get new netdev name: %s.\n", strerror(errno));
goto do_full_cleanup;
}
*newnamep = strdup(namep);
if (!*newnamep)
goto do_full_cleanup;
} }
fret = 0; /* Allocation failure for strdup() is checked below. */
name = strdup(ifname);
string_ret = name;
*ifidx = ifindex;
do_full_cleanup: do_full_cleanup:
ret = setresuid(ruid, euid, suid); ret = setresuid(ruid, euid, suid);
if (ret < 0) { if (ret < 0) {
usernic_error("Failed to restore privilege by setting effective " usernic_error("Failed to restore privilege by setting "
"user id to %d, real user id to %d, and saved user " "effective user id to %d, real user id to %d, "
"ID to %d: %s.\n", "and saved user ID to %d: %s\n", ruid, euid, suid,
ruid, euid, suid, strerror(errno)); strerror(errno));
fret = -1;
// COMMENT(brauner): setns() should fail if setresuid() doesn't string_ret = NULL;
// succeed but there's no harm in falling through; keeps the
// code cleaner.
} }
ret = setns(ofd, CLONE_NEWNET); ret = setns(ofd, CLONE_NEWNET);
if (ret < 0) { if (ret < 0) {
usernic_error("Failed to setns() to original network namespace " usernic_error("Failed to setns() to original network namespace "
"of PID %d: %s.\n", "of PID %d: %s\n", ofd, strerror(errno));
ofd, strerror(errno));
fret = -1; string_ret = NULL;
} }
do_partial_cleanup: do_partial_cleanup:
if (fd >= 0) if (fd >= 0)
close(fd); close(fd);
if (!string_ret && name)
free(name);
close(ofd); close(ofd);
return fret; return string_ret;
} }
/* /*
...@@ -916,50 +964,73 @@ static bool may_access_netns(int pid) ...@@ -916,50 +964,73 @@ static bool may_access_netns(int pid)
return may_access; return may_access;
} }
struct user_nic_args {
char *cmd;
char *lxc_path;
char *lxc_name;
char *pid;
char *type;
char *link;
char *veth_name;
};
#define LXC_USERNIC_CREATE 0
#define LXC_USERNIC_DELETE 1
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
int n, fd; int fd, ifindex, n, pid, request, ret;
char *me; char *me, *newname;
char *nicname; char *cnic = NULL, *nicname = NULL;
int pid;
char *cnic = NULL; /* Created nic name in container is returned here. */
char *vethname = NULL;
bool gotone = false;
struct alloted_s *alloted = NULL; struct alloted_s *alloted = NULL;
struct user_nic_args args;
nicname = alloca(40); if (argc < 7 || argc > 8) {
if (!nicname) { usage(argv[0], true);
usernic_error("Failed allocate memory: %s.\n", strerror(errno));
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
/* set a sane env, because we are setuid-root */ memset(&args, 0, sizeof(struct user_nic_args));
if (clearenv() < 0) { args.cmd = argv[1];
usernic_error("%s", "Failed to clear environment.\n"); args.lxc_path = argv[2];
args.lxc_name = argv[3];
args.pid = argv[4];
args.type = argv[5];
args.link = argv[6];
if (argc >= 8)
args.veth_name = argv[7];
if (!strcmp(args.cmd, "create")) {
request = LXC_USERNIC_CREATE;
} else if (!strcmp(args.cmd, "delete")) {
request = LXC_USERNIC_DELETE;
} else {
usage(argv[0], true);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (setenv("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 1) < 0) {
usernic_error("%s", "Failed to set PATH, exiting.\n"); /* Set a sane env, because we are setuid-root. */
ret = clearenv();
if (ret) {
usernic_error("%s", "Failed to clear environment\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if ((me = get_username()) == NULL) {
usernic_error("%s", "Failed to get username.\n"); ret = setenv("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 1);
if (ret < 0) {
usernic_error("%s", "Failed to set PATH, exiting\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (argc < 6) me = get_username();
usage(argv[0], true); if (!me) {
usernic_error("%s", "Failed to get username\n");
if (argc >= 7) exit(EXIT_FAILURE);
vethname = argv[6]; }
lxcpath = argv[1];
lxcname = argv[2];
errno = 0; ret = lxc_safe_int(args.pid, &pid);
pid = strtol(argv[3], NULL, 10); if (ret < 0) {
if (errno) { usernic_error("Could not read pid: %s\n", args.pid);
usernic_error("Could not read pid: %s.\n", argv[1]);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
...@@ -968,8 +1039,9 @@ int main(int argc, char *argv[]) ...@@ -968,8 +1039,9 @@ int main(int argc, char *argv[])
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if ((fd = open_and_lock(LXC_USERNIC_DB)) < 0) { fd = open_and_lock(LXC_USERNIC_DB);
usernic_error("Failed to lock %s.\n", LXC_USERNIC_DB); if (fd < 0) {
usernic_error("Failed to lock %s\n", LXC_USERNIC_DB);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
...@@ -978,28 +1050,74 @@ int main(int argc, char *argv[]) ...@@ -978,28 +1050,74 @@ int main(int argc, char *argv[])
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
n = get_alloted(me, argv[4], argv[5], &alloted); n = get_alloted(me, args.type, args.link, &alloted);
if (request == LXC_USERNIC_DELETE) {
int ret;
struct alloted_s *it;
bool found_nicname = false;
if (!is_ovs_bridge(args.link)) {
usernic_error("%s", "Deletion of non ovs type network "
"devices not implemented\n");
close(fd);
free_alloted(&alloted);
exit(EXIT_FAILURE);
}
/* Check whether the network device we are supposed to delete
* exists in the db. If it doesn't we will not delete it as we
* need to assume the network device is not under our control.
* As a side effect we also clear any invalid entries from the
* database.
*/
for (it = alloted; it; it = it->next)
cull_entries(fd, it->name, args.type, args.link,
args.veth_name, &found_nicname);
close(fd);
free_alloted(&alloted);
if (!found_nicname) {
usernic_error("%s", "Caller is not allowed to delete "
"network device\n");
exit(EXIT_FAILURE);
}
ret = lxc_ovs_delete_port(args.link, args.veth_name);
if (ret < 0) {
usernic_error("Failed to remove port \"%s\" from "
"openvswitch bridge \"%s\"",
args.veth_name, args.link);
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
if (n > 0) if (n > 0)
gotone = get_nic_if_avail(fd, alloted, pid, argv[4], argv[5], n, &nicname, &cnic); nicname = get_nic_if_avail(fd, alloted, pid, args.type,
args.link, n, &cnic);
close(fd); close(fd);
free_alloted(&alloted); free_alloted(&alloted);
if (!gotone) { if (!nicname) {
usernic_error("%s", "Quota reached.\n"); usernic_error("%s", "Quota reached\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
/* Now rename the link. */ /* Now rename the link. */
if (rename_in_ns(pid, cnic, &vethname) < 0) { newname = lxc_secure_rename_in_ns(pid, cnic, args.veth_name, &ifindex);
usernic_error("%s", "Failed to rename the link.\n"); if (!newname) {
if (lxc_netdev_delete_by_name(cnic) < 0) usernic_error("%s", "Failed to rename the link\n");
usernic_error("Failed to delete link \"%s\" the link. Manual cleanup needed.\n", cnic); ret = lxc_netdev_delete_by_name(cnic);
if (ret < 0)
usernic_error("Failed to delete \"%s\"\n", cnic);
free(nicname);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
/* Write the name of the interface pair to the stdout - like /* Write the name of the interface pair to the stdout: eth0:veth9MT2L4 */
* eth0:veth9MT2L4. fprintf(stdout, "%s:%s:%d\n", newname, nicname, ifindex);
*/ free(newname);
fprintf(stdout, "%s:%s\n", vethname, nicname); free(nicname);
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
...@@ -21,8 +21,7 @@ ...@@ -21,8 +21,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#include "config.h" #define _GNU_SOURCE
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
...@@ -47,6 +46,8 @@ ...@@ -47,6 +46,8 @@
#include <sys/types.h> #include <sys/types.h>
#include "conf.h" #include "conf.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"
...@@ -92,7 +93,356 @@ ...@@ -92,7 +93,356 @@
lxc_log_define(lxc_network, lxc); lxc_log_define(lxc_network, lxc);
int lxc_netdev_move_by_index(int ifindex, pid_t pid, const char* ifname) typedef int (*instantiate_cb)(struct lxc_handler *, struct lxc_netdev *);
static int instantiate_veth(struct lxc_handler *handler, struct lxc_netdev *netdev)
{
int bridge_index, err;
char *veth1, *veth2;
char veth1buf[IFNAMSIZ], veth2buf[IFNAMSIZ];
unsigned int mtu = 0;
if (netdev->priv.veth_attr.pair) {
veth1 = netdev->priv.veth_attr.pair;
if (handler->conf->reboot)
lxc_netdev_delete_by_name(veth1);
} else {
err = snprintf(veth1buf, sizeof(veth1buf), "vethXXXXXX");
if (err < 0 || (size_t)err >= sizeof(veth1buf))
return -1;
veth1 = lxc_mkifname(veth1buf);
if (!veth1)
return -1;
/* store away for deconf */
memcpy(netdev->priv.veth_attr.veth1, veth1, IFNAMSIZ);
}
snprintf(veth2buf, sizeof(veth2buf), "vethXXXXXX");
veth2 = lxc_mkifname(veth2buf);
if (!veth2)
goto out_delete;
err = lxc_veth_create(veth1, veth2);
if (err) {
ERROR("Failed to create veth pair \"%s\" and \"%s\": %s", veth1,
veth2, strerror(-err));
goto out_delete;
}
/* 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(veth1);
if (err) {
ERROR("Failed to change mac address of host interface \"%s\": %s",
veth1, strerror(-err));
goto out_delete;
}
netdev->ifindex = if_nametoindex(veth2);
if (!netdev->ifindex) {
ERROR("Failed to retrieve ifindex for \"%s\"", veth2);
goto out_delete;
}
if (netdev->mtu) {
if (lxc_safe_uint(netdev->mtu, &mtu) < 0)
WARN("Failed to parse mtu");
else
INFO("Retrieved mtu %d", mtu);
} else if (netdev->link) {
bridge_index = if_nametoindex(netdev->link);
if (bridge_index) {
mtu = netdev_get_mtu(bridge_index);
INFO("Retrieved mtu %d from %s", mtu, netdev->link);
} else {
mtu = netdev_get_mtu(netdev->ifindex);
INFO("Retrieved mtu %d from %s", mtu, veth2);
}
}
if (mtu) {
err = lxc_netdev_set_mtu(veth1, mtu);
if (!err)
err = lxc_netdev_set_mtu(veth2, mtu);
if (err) {
ERROR("Failed to set mtu \"%d\" for veth pair \"%s\" "
"and \"%s\": %s",
mtu, veth1, veth2, strerror(-err));
goto out_delete;
}
}
if (netdev->link) {
err = lxc_bridge_attach(netdev->link, veth1);
if (err) {
ERROR("Failed to attach \"%s\" to bridge \"%s\": %s",
veth1, netdev->link, strerror(-err));
goto out_delete;
}
INFO("Attached \"%s\" to bridge \"%s\"", veth1, netdev->link);
}
err = lxc_netdev_up(veth1);
if (err) {
ERROR("Failed to set \"%s\" up: %s", veth1, strerror(-err));
goto out_delete;
}
if (netdev->upscript) {
err = run_script(handler->name, "net", netdev->upscript, "up",
"veth", veth1, (char*) NULL);
if (err)
goto out_delete;
}
DEBUG("Instantiated veth \"%s/%s\", index is \"%d\"", veth1, veth2,
netdev->ifindex);
return 0;
out_delete:
if (netdev->ifindex != 0)
lxc_netdev_delete_by_name(veth1);
if (!netdev->priv.veth_attr.pair)
free(veth1);
free(veth2);
return -1;
}
static int instantiate_macvlan(struct lxc_handler *handler, struct lxc_netdev *netdev)
{
char peerbuf[IFNAMSIZ], *peer;
int err;
if (!netdev->link) {
ERROR("No link for macvlan network device specified");
return -1;
}
err = snprintf(peerbuf, sizeof(peerbuf), "mcXXXXXX");
if (err < 0 || (size_t)err >= sizeof(peerbuf))
return -1;
peer = lxc_mkifname(peerbuf);
if (!peer)
return -1;
err = lxc_macvlan_create(netdev->link, peer,
netdev->priv.macvlan_attr.mode);
if (err) {
ERROR("Failed to create macvlan interface \"%s\" on \"%s\": %s",
peer, netdev->link, strerror(-err));
goto out;
}
netdev->ifindex = if_nametoindex(peer);
if (!netdev->ifindex) {
ERROR("Failed to retrieve ifindex for \"%s\"", peer);
goto out;
}
if (netdev->upscript) {
err = run_script(handler->name, "net", netdev->upscript, "up",
"macvlan", netdev->link, (char*) NULL);
if (err)
goto out;
}
DEBUG("Instantiated macvlan \"%s\" with ifindex is %d and mode %d",
peer, netdev->ifindex, netdev->priv.macvlan_attr.mode);
return 0;
out:
lxc_netdev_delete_by_name(peer);
free(peer);
return -1;
}
static int instantiate_vlan(struct lxc_handler *handler, struct lxc_netdev *netdev)
{
char peer[IFNAMSIZ];
int err;
static uint16_t vlan_cntr = 0;
unsigned int mtu = 0;
if (!netdev->link) {
ERROR("No link for vlan network device specified");
return -1;
}
err = snprintf(peer, sizeof(peer), "vlan%d-%d", netdev->priv.vlan_attr.vid, vlan_cntr++);
if (err < 0 || (size_t)err >= sizeof(peer))
return -1;
err = lxc_vlan_create(netdev->link, peer, netdev->priv.vlan_attr.vid);
if (err) {
ERROR("Failed to create vlan interface \"%s\" on \"%s\": %s",
peer, netdev->link, strerror(-err));
return -1;
}
netdev->ifindex = if_nametoindex(peer);
if (!netdev->ifindex) {
ERROR("Failed to retrieve ifindex for \"%s\"", peer);
lxc_netdev_delete_by_name(peer);
return -1;
}
DEBUG("Instantiated vlan \"%s\" with ifindex is \"%d\" (vlan1000)",
peer, netdev->ifindex);
if (netdev->mtu) {
if (lxc_safe_uint(netdev->mtu, &mtu) < 0) {
ERROR("Failed to retrieve mtu from \"%d\"/\"%s\".",
netdev->ifindex,
netdev->name ? netdev->name : "(null)");
return -1;
}
err = lxc_netdev_set_mtu(peer, mtu);
if (err) {
ERROR("Failed to set mtu \"%s\" for \"%s\": %s",
netdev->mtu, peer, strerror(-err));
lxc_netdev_delete_by_name(peer);
return -1;
}
}
return 0;
}
static int instantiate_phys(struct lxc_handler *handler, struct lxc_netdev *netdev)
{
if (!netdev->link) {
ERROR("No link for physical interface specified");
return -1;
}
netdev->ifindex = if_nametoindex(netdev->link);
if (!netdev->ifindex) {
ERROR("Failed to retrieve ifindex for \"%s\"", netdev->link);
return -1;
}
if (netdev->upscript) {
int err;
err = run_script(handler->name, "net", netdev->upscript,
"up", "phys", netdev->link, (char*) NULL);
if (err)
return -1;
}
return 0;
}
static int instantiate_empty(struct lxc_handler *handler, struct lxc_netdev *netdev)
{
netdev->ifindex = 0;
if (netdev->upscript) {
int err;
err = run_script(handler->name, "net", netdev->upscript,
"up", "empty", (char*) NULL);
if (err)
return -1;
}
return 0;
}
static int instantiate_none(struct lxc_handler *handler, struct lxc_netdev *netdev)
{
netdev->ifindex = 0;
return 0;
}
static instantiate_cb netdev_conf[LXC_NET_MAXCONFTYPE + 1] = {
[LXC_NET_VETH] = instantiate_veth,
[LXC_NET_MACVLAN] = instantiate_macvlan,
[LXC_NET_VLAN] = instantiate_vlan,
[LXC_NET_PHYS] = instantiate_phys,
[LXC_NET_EMPTY] = instantiate_empty,
[LXC_NET_NONE] = instantiate_none,
};
static int shutdown_veth(struct lxc_handler *handler, struct lxc_netdev *netdev)
{
char *veth1;
int err;
if (netdev->priv.veth_attr.pair)
veth1 = netdev->priv.veth_attr.pair;
else
veth1 = netdev->priv.veth_attr.veth1;
if (netdev->downscript) {
err = run_script(handler->name, "net", netdev->downscript,
"down", "veth", veth1, (char*) NULL);
if (err)
return -1;
}
return 0;
}
static int shutdown_macvlan(struct lxc_handler *handler, struct lxc_netdev *netdev)
{
int err;
if (netdev->downscript) {
err = run_script(handler->name, "net", netdev->downscript,
"down", "macvlan", netdev->link,
(char*) NULL);
if (err)
return -1;
}
return 0;
}
static int shutdown_vlan(struct lxc_handler *handler, struct lxc_netdev *netdev)
{
return 0;
}
static int shutdown_phys(struct lxc_handler *handler, struct lxc_netdev *netdev)
{
int err;
if (netdev->downscript) {
err = run_script(handler->name, "net", netdev->downscript,
"down", "phys", netdev->link, (char*) NULL);
if (err)
return -1;
}
return 0;
}
static int shutdown_empty(struct lxc_handler *handler, struct lxc_netdev *netdev)
{
int err;
if (netdev->downscript) {
err = run_script(handler->name, "net", netdev->downscript,
"down", "empty", (char*) NULL);
if (err)
return -1;
}
return 0;
}
static int shutdown_none(struct lxc_handler *handler, struct lxc_netdev *netdev)
{
return 0;
}
static instantiate_cb netdev_deconf[LXC_NET_MAXCONFTYPE + 1] = {
[LXC_NET_VETH] = shutdown_veth,
[LXC_NET_MACVLAN] = shutdown_macvlan,
[LXC_NET_VLAN] = shutdown_vlan,
[LXC_NET_PHYS] = shutdown_phys,
[LXC_NET_EMPTY] = shutdown_empty,
[LXC_NET_NONE] = shutdown_none,
};
int lxc_netdev_move_by_index(int ifindex, pid_t pid, const char *ifname)
{ {
struct nl_handler nlh; struct nl_handler nlh;
struct nlmsg *nlmsg = NULL; struct nlmsg *nlmsg = NULL;
...@@ -1395,7 +1745,7 @@ int lxc_ipv6_dest_add(int ifindex, struct in6_addr *dest) ...@@ -1395,7 +1745,7 @@ int lxc_ipv6_dest_add(int ifindex, struct in6_addr *dest)
return ip_route_dest_add(AF_INET6, ifindex, dest); return ip_route_dest_add(AF_INET6, ifindex, dest);
} }
static bool is_ovs_bridge(const char *bridge) bool is_ovs_bridge(const char *bridge)
{ {
char brdirname[22 + IFNAMSIZ + 1] = {0}; char brdirname[22 + IFNAMSIZ + 1] = {0};
struct stat sb; struct stat sb;
...@@ -1406,70 +1756,71 @@ static bool is_ovs_bridge(const char *bridge) ...@@ -1406,70 +1756,71 @@ static bool is_ovs_bridge(const char *bridge)
return false; return false;
} }
struct ovs_veth_args {
const char *bridge;
const char *nic;
};
/* Called from a background thread - when nic goes away, remove it from the /* Called from a background thread - when nic goes away, remove it from the
* bridge. * bridge.
*/ */
static void ovs_cleanup_nic(const char *lxcpath, const char *name, static int lxc_ovs_delete_port_exec(void *data)
const char *bridge, const char *nic)
{ {
int ret; struct ovs_veth_args *args = data;
ret = lxc_check_inherited(NULL, true, &(int){-1}, 1); execlp("ovs-vsctl", "ovs-vsctl", "del-port", args->bridge, args->nic,
if (ret < 0) (char *)NULL);
return; return -1;
}
TRACE("Registering cleanup thread to remove nic \"%s\" from " int lxc_ovs_delete_port(const char *bridge, const char *nic)
"openvswitch bridge \"%s\"", nic, bridge); {
int ret;
char cmd_output[MAXPATHLEN];
struct ovs_veth_args args;
ret = lxc_wait(name, "STOPPED", -1, lxcpath); args.bridge = bridge;
args.nic = nic;
ret = run_command(cmd_output, sizeof(cmd_output),
lxc_ovs_delete_port_exec, (void *)&args);
if (ret < 0) { if (ret < 0) {
ERROR("Failed to register cleanup thread to remove nic \"%s\" " ERROR("Failed to delete \"%s\" from openvswitch bridge \"%s\": "
"from openvswitch bridge \"%s\"", nic, bridge); "%s", bridge, nic, cmd_output);
return; return -1;
} }
execlp("ovs-vsctl", "ovs-vsctl", "del-port", bridge, nic, (char *)NULL); return 0;
exit(EXIT_FAILURE);
} }
static int attach_to_ovs_bridge(const char *lxcpath, const char *name, const char *bridge, const char *nic) static int lxc_ovs_attach_bridge_exec(void *data)
{ {
pid_t pid; struct ovs_veth_args *args = data;
char *cmd;
int ret;
cmd = on_path("ovs-vsctl", NULL); execlp("ovs-vsctl", "ovs-vsctl", "add-port", args->bridge, args->nic,
if (!cmd) (char *)NULL);
return -1; return -1;
free(cmd); }
pid = fork(); static int lxc_ovs_attach_bridge(const char *bridge, const char *nic)
if (pid < 0) {
int ret;
char cmd_output[MAXPATHLEN];
struct ovs_veth_args args;
args.bridge = bridge;
args.nic = nic;
ret = run_command(cmd_output, sizeof(cmd_output),
lxc_ovs_attach_bridge_exec, (void *)&args);
if (ret < 0) {
ERROR("Failed to attach \"%s\" to openvswitch bridge \"%s\": %s",
bridge, nic, cmd_output);
return -1; return -1;
if (pid > 0) {
ret = wait_for_pid(pid);
if (ret < 0)
return ret;
pid = fork();
if (pid < 0)
return -1; // how to properly recover?
if (pid > 0)
return 0;
ovs_cleanup_nic(lxcpath, name, bridge, nic);
exit(0);
} }
if (execlp("ovs-vsctl", "ovs-vsctl", "add-port", bridge, nic, (char *)NULL)) return 0;
exit(1);
// not reached
exit(1);
} }
/* int lxc_bridge_attach(const char *bridge, const char *ifname)
* There is a lxc_bridge_attach, but no need of a bridge detach
* as automatically done by kernel when a netdev is deleted.
*/
int lxc_bridge_attach(const char *lxcpath, const char *name, const char *bridge, const char *ifname)
{ {
int fd, index, err; int fd, index, err;
struct ifreq ifr; struct ifreq ifr;
...@@ -1482,7 +1833,7 @@ int lxc_bridge_attach(const char *lxcpath, const char *name, const char *bridge, ...@@ -1482,7 +1833,7 @@ int lxc_bridge_attach(const char *lxcpath, const char *name, const char *bridge,
return -EINVAL; return -EINVAL;
if (is_ovs_bridge(bridge)) if (is_ovs_bridge(bridge))
return attach_to_ovs_bridge(lxcpath, name, bridge, ifname); return lxc_ovs_attach_bridge(bridge, ifname);
fd = socket(AF_INET, SOCK_STREAM, 0); fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) if (fd < 0)
...@@ -1518,7 +1869,7 @@ const char *lxc_net_type_to_str(int type) ...@@ -1518,7 +1869,7 @@ const char *lxc_net_type_to_str(int type)
static const char padchar[] = static const char padchar[] =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char *lxc_mkifname(char *template) char *lxc_mkifname(const char *template)
{ {
char *name = NULL; char *name = NULL;
size_t i = 0; size_t i = 0;
...@@ -1604,3 +1955,842 @@ int setup_private_host_hw_addr(char *veth1) ...@@ -1604,3 +1955,842 @@ int setup_private_host_hw_addr(char *veth1)
return 0; return 0;
} }
int lxc_find_gateway_addresses(struct lxc_handler *handler)
{
struct lxc_list *network = &handler->conf->network;
struct lxc_list *iterator;
struct lxc_netdev *netdev;
int link_index;
lxc_list_for_each(iterator, network) {
netdev = iterator->elem;
if (!netdev->ipv4_gateway_auto && !netdev->ipv6_gateway_auto)
continue;
if (netdev->type != LXC_NET_VETH && netdev->type != LXC_NET_MACVLAN) {
ERROR("Automatic gateway detection is only supported "
"for veth and macvlan");
return -1;
}
if (!netdev->link) {
ERROR("Automatic gateway detection needs a link interface");
return -1;
}
link_index = if_nametoindex(netdev->link);
if (!link_index)
return -EINVAL;
if (netdev->ipv4_gateway_auto) {
if (lxc_ipv4_addr_get(link_index, &netdev->ipv4_gateway)) {
ERROR("Failed to automatically find ipv4 gateway "
"address from link interface \"%s\"", netdev->link);
return -1;
}
}
if (netdev->ipv6_gateway_auto) {
if (lxc_ipv6_addr_get(link_index, &netdev->ipv6_gateway)) {
ERROR("Failed to automatically find ipv6 gateway "
"address from link interface \"%s\"", netdev->link);
return -1;
}
}
}
return 0;
}
#define LXC_USERNIC_PATH LIBEXECDIR "/lxc/lxc-user-nic"
static int lxc_create_network_unpriv(const char *lxcpath, char *lxcname,
struct lxc_netdev *netdev, pid_t pid)
{
int ret;
pid_t child;
int bytes, pipefd[2];
char *token, *saveptr = NULL;
char netdev_link[IFNAMSIZ + 1];
char buffer[MAXPATHLEN] = {0};
if (netdev->type != LXC_NET_VETH) {
ERROR("Network type %d not support for unprivileged use", netdev->type);
return -1;
}
ret = pipe(pipefd);
if (ret < 0) {
SYSERROR("Failed to create pipe");
return -1;
}
child = fork();
if (child < 0) {
SYSERROR("Failed to create new process");
close(pipefd[0]);
close(pipefd[1]);
return -1;
}
if (child == 0) {
int ret;
char pidstr[LXC_NUMSTRLEN64];
close(pipefd[0]);
ret = dup2(pipefd[1], STDOUT_FILENO);
if (ret >= 0)
ret = dup2(pipefd[1], STDERR_FILENO);
close(pipefd[1]);
if (ret < 0) {
SYSERROR("Failed to duplicate std{err,out} file descriptor");
exit(EXIT_FAILURE);
}
if (netdev->link)
strncpy(netdev_link, netdev->link, IFNAMSIZ);
else
strncpy(netdev_link, "none", IFNAMSIZ);
ret = snprintf(pidstr, LXC_NUMSTRLEN64, "%d", pid);
if (ret < 0 || ret >= LXC_NUMSTRLEN64)
exit(EXIT_FAILURE);
pidstr[LXC_NUMSTRLEN64 - 1] = '\0';
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)
execlp(LXC_USERNIC_PATH, LXC_USERNIC_PATH, "create",
lxcpath, lxcname, pidstr, "veth", netdev_link,
netdev->name, (char *)NULL);
else
execlp(LXC_USERNIC_PATH, LXC_USERNIC_PATH, "create",
lxcpath, lxcname, pidstr, "veth", netdev_link,
(char *)NULL);
SYSERROR("Failed to execute lxc-user-nic");
exit(EXIT_FAILURE);
}
/* close the write-end of the pipe */
close(pipefd[1]);
bytes = read(pipefd[0], &buffer, MAXPATHLEN);
if (bytes < 0) {
SYSERROR("Failed to read from pipe file descriptor.");
close(pipefd[0]);
return -1;
}
buffer[bytes - 1] = '\0';
ret = wait_for_pid(child);
close(pipefd[0]);
if (ret != 0) {
ERROR("lxc-user-nic failed to configure requested network: %s",
buffer[0] != '\0' ? buffer : "(null)");
return -1;
}
TRACE("Received output \"%s\" from lxc-user-nic", buffer);
/* netdev->name */
token = strtok_r(buffer, ":", &saveptr);
if (!token)
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);
/* netdev->priv.veth_attr.pair */
token = strtok_r(NULL, ":", &saveptr);
if (!token)
return -1;
netdev->priv.veth_attr.pair = strdup(token);
if (!netdev->priv.veth_attr.pair) {
ERROR("Failed to allocate memory.");
return -1;
}
/* netdev->ifindex */
token = strtok_r(NULL, ":", &saveptr);
if (!token)
return -1;
ret = lxc_safe_int(token, &netdev->ifindex);
if (ret < 0) {
ERROR("Failed to parse ifindex for network device \"%s\"", netdev->name);
return -1;
}
return 0;
}
static int lxc_delete_network_unpriv(const char *lxcpath, char *lxcname,
struct lxc_netdev *netdev, pid_t pid)
{
int bytes, ret;
pid_t child;
int pipefd[2];
char buffer[MAXPATHLEN] = {0};
if (netdev->type != LXC_NET_VETH) {
ERROR("Network type %d not support for unprivileged use", netdev->type);
return -1;
}
ret = pipe(pipefd);
if (ret < 0) {
SYSERROR("Failed to create pipe");
return -1;
}
child = fork();
if (child < 0) {
SYSERROR("Failed to create new process");
close(pipefd[0]);
close(pipefd[1]);
return -1;
}
if (child == 0) {
int ret;
char pidstr[LXC_NUMSTRLEN64];
close(pipefd[0]);
ret = dup2(pipefd[1], STDOUT_FILENO);
if (ret >= 0)
ret = dup2(pipefd[1], STDERR_FILENO);
close(pipefd[1]);
if (ret < 0) {
SYSERROR("Failed to duplicate std{err,out} file descriptor");
exit(EXIT_FAILURE);
}
if (!netdev->link)
SYSERROR("Network link for network device \"%s\" is "
"missing", netdev->priv.veth_attr.pair);
ret = snprintf(pidstr, LXC_NUMSTRLEN64, "%d", pid);
if (ret < 0 || ret >= LXC_NUMSTRLEN64)
exit(EXIT_FAILURE);
pidstr[LXC_NUMSTRLEN64 - 1] = '\0';
INFO("Execing lxc-user-nic delete %s %s %s veth %s %s", lxcpath,
lxcname, pidstr, netdev->link, netdev->priv.veth_attr.pair);
execlp(LXC_USERNIC_PATH, LXC_USERNIC_PATH, "delete", lxcpath,
lxcname, pidstr, "veth", netdev->link,
netdev->priv.veth_attr.pair, (char *)NULL);
SYSERROR("Failed to exec lxc-user-nic.");
exit(EXIT_FAILURE);
}
close(pipefd[1]);
bytes = read(pipefd[0], &buffer, MAXPATHLEN);
if (bytes < 0) {
SYSERROR("Failed to read from pipe file descriptor.");
close(pipefd[0]);
return -1;
}
buffer[bytes - 1] = '\0';
if (wait_for_pid(child) != 0) {
ERROR("lxc-user-nic failed to delete requested network: %s",
buffer[0] != '\0' ? buffer : "(null)");
close(pipefd[0]);
return -1;
}
close(pipefd[0]);
return 0;
}
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)
return 0;
lxc_list_for_each(iterator, network) {
struct lxc_netdev *netdev = iterator->elem;
if (netdev->type < 0 || netdev->type > LXC_NET_MAXCONFTYPE) {
ERROR("Invalid network configuration type %d", netdev->type);
return -1;
}
if (netdev_conf[netdev->type](handler, netdev)) {
ERROR("Failed to create network device");
return -1;
}
}
return 0;
}
int lxc_create_network(const char *lxcpath, char *lxcname,
struct lxc_list *network, pid_t pid)
{
int err;
bool am_root;
char ifname[IFNAMSIZ];
struct lxc_list *iterator;
am_root = (getuid() == 0);
lxc_list_for_each(iterator, network) {
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)
continue;
/* retrieve the name of the interface */
if (!if_indextoname(netdev->ifindex, ifname)) {
ERROR("No interface corresponding to ifindex \"%d\"",
netdev->ifindex);
return -1;
}
err = lxc_netdev_move_by_name(ifname, pid, NULL);
if (err) {
ERROR("Failed to move network device \"%s\" to "
"network namespace %d: %s", ifname, pid,
strerror(-err));
return -1;
}
DEBUG("Moved network device \"%s\"/\"%s\" to network namespace "
"of %d:", ifname, netdev->name ? netdev->name : "(null)",
pid);
}
return 0;
}
bool lxc_delete_network(struct lxc_handler *handler)
{
int ret;
struct lxc_list *iterator;
struct lxc_list *network = &handler->conf->network;
bool deleted_all = true;
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");
/* Recent kernels remove the virtual interfaces when the network
* namespace is destroyed but in case we did not move the
* interface to the network namespace, we have to destroy it.
*/
if (!am_unpriv()) {
ret = lxc_netdev_delete_by_index(netdev->ifindex);
if (-ret == ENODEV) {
INFO("Interface \"%s\" with index %d already "
"deleted or existing in different network "
"namespace",
netdev->name ? 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->ifindex, strerror(-ret));
continue;
}
INFO("Removed interface \"%s\" with index %d",
netdev->name ? netdev->name : "(null)",
netdev->ifindex);
}
if (netdev->type != LXC_NET_VETH)
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
* devices. We had issues in LXD around this.
*/
if (netdev->priv.veth_attr.pair)
hostveth = netdev->priv.veth_attr.pair;
else
hostveth = netdev->priv.veth_attr.veth1;
if (*hostveth == '\0')
continue;
ret = lxc_netdev_delete_by_name(hostveth);
if (ret < 0) {
deleted_all = false;
WARN("Failed to remove interface \"%s\" from \"%s\": %s",
hostveth, netdev->link, strerror(-ret));
continue;
}
INFO("Removed interface \"%s\" from \"%s\"", hostveth, netdev->link);
if (!is_ovs_bridge(netdev->link)) {
netdev->priv.veth_attr.veth1[0] = '\0';
continue;
}
/* Delete the openvswitch port. */
ret = lxc_ovs_delete_port(netdev->link, hostveth);
if (ret < 0)
WARN("Failed to remove port \"%s\" from openvswitch "
"bridge \"%s\"", hostveth, netdev->link);
else
INFO("Removed port \"%s\" from openvswitch bridge \"%s\"",
hostveth, netdev->link);
netdev->priv.veth_attr.veth1[0] = '\0';
}
return deleted_all;
}
int lxc_requests_empty_network(struct lxc_handler *handler)
{
struct lxc_list *network = &handler->conf->network;
struct lxc_list *iterator;
bool found_none = false, found_nic = false;
if (lxc_list_empty(network))
return 0;
lxc_list_for_each(iterator, network) {
struct lxc_netdev *netdev = iterator->elem;
if (netdev->type == LXC_NET_NONE)
found_none = true;
else
found_nic = true;
}
if (found_none && !found_nic)
return 1;
return 0;
}
/* try to move physical nics to the init netns */
void lxc_restore_phys_nics_to_netns(int netnsfd, struct lxc_conf *conf)
{
int ret;
int i, oldfd;
char ifname[IFNAMSIZ];
if (netnsfd < 0 || conf->num_savednics == 0)
return;
INFO("Trying to restore network device names in original namespace for "
"%d network devices", conf->num_savednics);
oldfd = lxc_preserve_ns(getpid(), "net");
if (oldfd < 0) {
SYSERROR("Failed to preserve network namespace");
return;
}
ret = setns(netnsfd, 0);
if (ret < 0) {
SYSERROR("Failed to enter network namespace");
close(oldfd);
return;
}
for (i = 0; i < conf->num_savednics; i++) {
struct saved_nic *s = &conf->saved_nics[i];
/* retrieve the name of the interface */
if (!if_indextoname(s->ifindex, ifname)) {
WARN("No interface corresponding to ifindex %d",
s->ifindex);
continue;
}
if (lxc_netdev_move_by_name(ifname, 1, s->orig_name))
WARN("Error moving network device \"%s\" back to "
"network namespace", ifname);
free(s->orig_name);
}
conf->num_savednics = 0;
ret = setns(oldfd, 0);
if (ret < 0)
SYSERROR("Failed to enter network namespace");
close(oldfd);
}
static int setup_hw_addr(char *hwaddr, const char *ifname)
{
struct sockaddr sockaddr;
struct ifreq ifr;
int ret, fd, saved_errno;
ret = lxc_convert_mac(hwaddr, &sockaddr);
if (ret) {
ERROR("Mac address \"%s\" conversion failed: %s", hwaddr,
strerror(-ret));
return -1;
}
memcpy(ifr.ifr_name, ifname, IFNAMSIZ);
ifr.ifr_name[IFNAMSIZ-1] = '\0';
memcpy((char *) &ifr.ifr_hwaddr, (char *) &sockaddr, sizeof(sockaddr));
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0)
return -1;
ret = ioctl(fd, SIOCSIFHWADDR, &ifr);
saved_errno = errno;
close(fd);
if (ret)
ERROR("Failed to perform ioctl: %s", strerror(saved_errno));
DEBUG("Mac address \"%s\" on \"%s\" has been setup", hwaddr,
ifr.ifr_name);
return ret;
}
static int setup_ipv4_addr(struct lxc_list *ip, int ifindex)
{
struct lxc_list *iterator;
int err;
lxc_list_for_each(iterator, ip) {
struct lxc_inetdev *inetdev = iterator->elem;
err = lxc_ipv4_addr_add(ifindex, &inetdev->addr,
&inetdev->bcast, inetdev->prefix);
if (err) {
ERROR("Failed to setup ipv4 address for network device "
"with eifindex %d: %s", ifindex, strerror(-err));
return -1;
}
}
return 0;
}
static int setup_ipv6_addr(struct lxc_list *ip, int ifindex)
{
struct lxc_list *iterator;
int err;
lxc_list_for_each(iterator, ip) {
struct lxc_inet6dev *inet6dev = iterator->elem;
err = lxc_ipv6_addr_add(ifindex, &inet6dev->addr,
&inet6dev->mcast, &inet6dev->acast,
inet6dev->prefix);
if (err) {
ERROR("Failed to setup ipv6 address for network device "
"with eifindex %d: %s", ifindex, strerror(-err));
return -1;
}
}
return 0;
}
static int lxc_setup_netdev_in_child_namespaces(struct lxc_netdev *netdev)
{
char ifname[IFNAMSIZ];
int err;
const char *net_type_name;
char *current_ifname = ifname;
/* empty network namespace */
if (!netdev->ifindex) {
if (netdev->flags & IFF_UP) {
err = lxc_netdev_up("lo");
if (err) {
ERROR("Failed to set the loopback network "
"device up: %s",
strerror(-err));
return -1;
}
}
if (netdev->type == LXC_NET_EMPTY)
return 0;
if (netdev->type == LXC_NET_NONE)
return 0;
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);
return -1;
}
netdev->ifindex = if_nametoindex(netdev->name);
}
/* get the new ifindex in case of physical netdev */
if (netdev->type == LXC_NET_PHYS) {
netdev->ifindex = if_nametoindex(netdev->link);
if (!netdev->ifindex) {
ERROR("Failed to get ifindex for network device \"%s\"",
netdev->link);
return -1;
}
}
/* retrieve the name of the interface */
if (!if_indextoname(netdev->ifindex, current_ifname)) {
ERROR("Failed get name for network device with ifindex %d",
netdev->ifindex);
return -1;
}
/* Default: let the system to choose one interface name.
* 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";
/* rename the interface name */
if (strcmp(ifname, netdev->name) != 0) {
err = lxc_netdev_rename_by_name(ifname, netdev->name);
if (err) {
ERROR("Failed to rename network device \"%s\" to "
"\"%s\": %s", ifname, netdev->name, strerror(-err));
return -1;
}
}
/* Re-read the name of the interface because its name has changed
* and would be automatically allocated by the system
*/
if (!if_indextoname(netdev->ifindex, current_ifname)) {
ERROR("Failed get name for network device with ifindex %d",
netdev->ifindex);
return -1;
}
/* set a mac address */
if (netdev->hwaddr) {
if (setup_hw_addr(netdev->hwaddr, current_ifname)) {
ERROR("Failed to setup hw address for network device \"%s\"",
current_ifname);
return -1;
}
}
/* setup ipv4 addresses on the interface */
if (setup_ipv4_addr(&netdev->ipv4, netdev->ifindex)) {
ERROR("Failed to setup ip addresses for network device \"%s\"",
ifname);
return -1;
}
/* setup ipv6 addresses on the interface */
if (setup_ipv6_addr(&netdev->ipv6, netdev->ifindex)) {
ERROR("Failed to setup ipv6 addresses for network device \"%s\"",
ifname);
return -1;
}
/* set the network device up */
if (netdev->flags & IFF_UP) {
int err;
err = lxc_netdev_up(current_ifname);
if (err) {
ERROR("Failed to set network device \"%s\" up: %s",
current_ifname, strerror(-err));
return -1;
}
/* the network is up, make the loopback up too */
err = lxc_netdev_up("lo");
if (err) {
ERROR("Failed to set the loopback network device up: %s",
strerror(-err));
return -1;
}
}
/* We can only set up the default routes after bringing
* up the interface, sine bringing up the interface adds
* the link-local routes and we can't add a default
* route if the gateway is not reachable. */
/* setup ipv4 gateway on the interface */
if (netdev->ipv4_gateway) {
if (!(netdev->flags & IFF_UP)) {
ERROR("Cannot add ipv4 gateway for network device "
"\"%s\" when not bringing up the interface", ifname);
return -1;
}
if (lxc_list_empty(&netdev->ipv4)) {
ERROR("Cannot add ipv4 gateway for network device "
"\"%s\" when not assigning an address", ifname);
return -1;
}
err = lxc_ipv4_gateway_add(netdev->ifindex, netdev->ipv4_gateway);
if (err) {
err = lxc_ipv4_dest_add(netdev->ifindex, netdev->ipv4_gateway);
if (err) {
ERROR("Failed to add ipv4 dest for network "
"device \"%s\": %s", ifname, strerror(-err));
}
err = lxc_ipv4_gateway_add(netdev->ifindex, netdev->ipv4_gateway);
if (err) {
ERROR("Failed to setup ipv4 gateway for "
"network device \"%s\": %s",
ifname, strerror(-err));
if (netdev->ipv4_gateway_auto) {
char buf[INET_ADDRSTRLEN];
inet_ntop(AF_INET, netdev->ipv4_gateway, buf, sizeof(buf));
ERROR("Fried to set autodetected ipv4 gateway \"%s\"", buf);
}
return -1;
}
}
}
/* setup ipv6 gateway on the interface */
if (netdev->ipv6_gateway) {
if (!(netdev->flags & IFF_UP)) {
ERROR("Cannot add ipv6 gateway for network device "
"\"%s\" when not bringing up the interface", ifname);
return -1;
}
if (lxc_list_empty(&netdev->ipv6) && !IN6_IS_ADDR_LINKLOCAL(netdev->ipv6_gateway)) {
ERROR("Cannot add ipv6 gateway for network device "
"\"%s\" when not assigning an address", ifname);
return -1;
}
err = lxc_ipv6_gateway_add(netdev->ifindex, netdev->ipv6_gateway);
if (err) {
err = lxc_ipv6_dest_add(netdev->ifindex, netdev->ipv6_gateway);
if (err) {
ERROR("Failed to add ipv6 dest for network "
"device \"%s\": %s", ifname, strerror(-err));
}
err = lxc_ipv6_gateway_add(netdev->ifindex, netdev->ipv6_gateway);
if (err) {
ERROR("Failed to setup ipv6 gateway for "
"network device \"%s\": %s", ifname,
strerror(-err));
if (netdev->ipv6_gateway_auto) {
char buf[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, netdev->ipv6_gateway, buf, sizeof(buf));
ERROR("Tried to set autodetected ipv6 "
"gateway for network device "
"\"%s\"", buf);
}
return -1;
}
}
}
DEBUG("Network devie \"%s\" has been setup", current_ifname);
return 0;
}
int lxc_setup_network_in_child_namespaces(const struct lxc_conf *conf,
struct lxc_list *network)
{
struct lxc_list *iterator;
struct lxc_netdev *netdev;
lxc_log_configured_netdevs(conf);
lxc_list_for_each(iterator, network) {
netdev = iterator->elem;
/* REMOVE in LXC 3.0 */
if (netdev->idx < 0) {
ERROR("WARNING: using \"lxc.network.*\" keys to define "
"networks is DEPRECATED, please switch to using "
"\"lxc.net.[i].* keys\"");
}
if (lxc_setup_netdev_in_child_namespaces(netdev)) {
ERROR("failed to setup netdev");
return -1;
}
}
if (!lxc_list_empty(network))
INFO("network has been setup");
return 0;
}
...@@ -23,9 +23,121 @@ ...@@ -23,9 +23,121 @@
#ifndef __LXC_NETWORK_H #ifndef __LXC_NETWORK_H
#define __LXC_NETWORK_H #define __LXC_NETWORK_H
#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include "list.h"
struct lxc_conf;
struct lxc_handler;
struct lxc_netdev;
enum {
LXC_NET_EMPTY,
LXC_NET_VETH,
LXC_NET_MACVLAN,
LXC_NET_PHYS,
LXC_NET_VLAN,
LXC_NET_NONE,
LXC_NET_MAXCONFTYPE,
};
/* /*
* Convert a string mac address to a socket structure * Defines the structure to configure an ipv4 address
* @address : ipv4 address
* @broadcast : ipv4 broadcast address
* @mask : network mask
*/ */
struct lxc_inetdev {
struct in_addr addr;
struct in_addr bcast;
unsigned int prefix;
};
struct lxc_route {
struct in_addr addr;
};
/*
* Defines the structure to configure an ipv6 address
* @flags : set the address up
* @address : ipv6 address
* @broadcast : ipv6 broadcast address
* @mask : network mask
*/
struct lxc_inet6dev {
struct in6_addr addr;
struct in6_addr mcast;
struct in6_addr acast;
unsigned int prefix;
};
struct lxc_route6 {
struct in6_addr addr;
};
struct ifla_veth {
char *pair; /* pair name */
char veth1[IFNAMSIZ]; /* needed for deconf */
};
struct ifla_vlan {
unsigned int flags;
unsigned int fmask;
unsigned short vid;
unsigned short pad;
};
struct ifla_macvlan {
int mode; /* private, vepa, bridge, passthru */
};
union netdev_p {
struct ifla_veth veth_attr;
struct ifla_vlan vlan_attr;
struct ifla_macvlan macvlan_attr;
};
/*
* Defines a structure to configure a network device
* @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
* @flags : flag of the network device (IFF_UP, ... )
* @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
* @upscript : a script filename to be executed during interface configuration
* @downscript : a script filename to be executed during interface destruction
* @idx : network counter
*/
struct lxc_netdev {
ssize_t idx;
int type;
int flags;
int ifindex;
char *link;
char *name;
char *hwaddr;
char *mtu;
union netdev_p priv;
struct lxc_list ipv4;
struct lxc_list ipv6;
struct in_addr *ipv4_gateway;
bool ipv4_gateway_auto;
struct in6_addr *ipv6_gateway;
bool ipv6_gateway_auto;
char *upscript;
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); extern int lxc_convert_mac(char *macaddr, struct sockaddr *sockaddr);
/* /*
...@@ -109,7 +221,10 @@ extern int lxc_ipv6_gateway_add(int ifindex, struct in6_addr *gw); ...@@ -109,7 +221,10 @@ extern int lxc_ipv6_gateway_add(int ifindex, struct in6_addr *gw);
/* /*
* Attach an interface to the bridge * Attach an interface to the bridge
*/ */
extern int lxc_bridge_attach(const char *lxcpath, const char *name, const char *bridge, const char *ifname); extern int lxc_bridge_attach(const char *bridge, const char *ifname);
extern int lxc_ovs_delete_port(const char *bridge, const char *nic);
extern bool is_ovs_bridge(const char *bridge);
/* /*
* Create default gateway * Create default gateway
...@@ -133,12 +248,22 @@ extern int lxc_neigh_proxy_on(const char *name, int family); ...@@ -133,12 +248,22 @@ extern int lxc_neigh_proxy_on(const char *name, int family);
*/ */
extern int lxc_neigh_proxy_off(const char *name, int family); extern int lxc_neigh_proxy_off(const char *name, int family);
/* /* Generate a new unique network interface name.
* Generate a new unique network interface name * Allocated memory must be freed by caller.
*/ */
extern char *lxc_mkifname(char *template); extern char *lxc_mkifname(const char *template);
extern const char *lxc_net_type_to_str(int type); 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);
#endif extern int lxc_create_network_priv(struct lxc_handler *handler);
extern bool lxc_delete_network(struct lxc_handler *handler);
extern int lxc_find_gateway_addresses(struct lxc_handler *handler);
extern int lxc_create_network(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_setup_network_in_child_namespaces(const struct lxc_conf *conf,
struct lxc_list *network);
#endif /* __LXC_NETWORK_H */
...@@ -70,6 +70,7 @@ ...@@ -70,6 +70,7 @@
#include "mainloop.h" #include "mainloop.h"
#include "monitor.h" #include "monitor.h"
#include "namespace.h" #include "namespace.h"
#include "network.h"
#include "start.h" #include "start.h"
#include "storage.h" #include "storage.h"
#include "storage_utils.h" #include "storage_utils.h"
...@@ -1311,7 +1312,7 @@ static int lxc_spawn(struct lxc_handler *handler) ...@@ -1311,7 +1312,7 @@ static int lxc_spawn(struct lxc_handler *handler)
/* That should be done before the clone because we will /* That should be done before the clone because we will
* fill the netdev index and use them in the child. * fill the netdev index and use them in the child.
*/ */
if (lxc_create_network(handler)) { if (lxc_create_network_priv(handler)) {
ERROR("Failed to create the network."); ERROR("Failed to create the network.");
lxc_sync_fini(handler); lxc_sync_fini(handler);
return -1; return -1;
...@@ -1429,7 +1430,7 @@ static int lxc_spawn(struct lxc_handler *handler) ...@@ -1429,7 +1430,7 @@ static int lxc_spawn(struct lxc_handler *handler)
/* Create the network configuration. */ /* Create the network configuration. */
if (handler->clone_flags & CLONE_NEWNET) { if (handler->clone_flags & CLONE_NEWNET) {
if (lxc_assign_network(handler->lxcpath, handler->name, if (lxc_create_network(handler->lxcpath, handler->name,
&handler->conf->network, handler->pid)) { &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;
......
...@@ -153,7 +153,7 @@ lxcpath=/home/usernic-user/.local/share/lxc ...@@ -153,7 +153,7 @@ lxcpath=/home/usernic-user/.local/share/lxc
lxcname=b1 lxcname=b1
# Assign one veth, should fail as no allowed entries yet # Assign one veth, should fail as no allowed entries yet
if run_cmd "$LXC_USER_NIC $lxcpath $lxcname $p1 veth usernic-br0 xx1"; then if run_cmd "$LXC_USER_NIC create $lxcpath $lxcname $p1 veth usernic-br0 xx1"; then
echo "FAIL: able to create nic with no entries" echo "FAIL: able to create nic with no entries"
exit 1 exit 1
fi fi
...@@ -164,24 +164,24 @@ sed -i '/^usernic-user/d' /etc/lxc/lxc-usernet ...@@ -164,24 +164,24 @@ sed -i '/^usernic-user/d' /etc/lxc/lxc-usernet
echo "usernic-user veth usernic-br0 2" >> /etc/lxc/lxc-usernet echo "usernic-user veth usernic-br0 2" >> /etc/lxc/lxc-usernet
# Assign one veth to second bridge, should fail # Assign one veth to second bridge, should fail
if run_cmd "$LXC_USER_NIC $lxcpath $lxcname $p1 veth usernic-br1 xx1"; then if run_cmd "$LXC_USER_NIC create $lxcpath $lxcname $p1 veth usernic-br1 xx1"; then
echo "FAIL: able to create nic with no entries" echo "FAIL: able to create nic with no entries"
exit 1 exit 1
fi fi
# Assign two veths, should succeed # Assign two veths, should succeed
if ! run_cmd "$LXC_USER_NIC $lxcpath $lxcname $p1 veth usernic-br0 xx2"; then if ! run_cmd "$LXC_USER_NIC create $lxcpath $lxcname $p1 veth usernic-br0 xx2"; then
echo "FAIL: unable to create first nic" echo "FAIL: unable to create first nic"
exit 1 exit 1
fi fi
if ! run_cmd "$LXC_USER_NIC $lxcpath $lxcname $p1 veth usernic-br0 xx3"; then if ! run_cmd "$LXC_USER_NIC create $lxcpath $lxcname $p1 veth usernic-br0 xx3"; then
echo "FAIL: unable to create second nic" echo "FAIL: unable to create second nic"
exit 1 exit 1
fi fi
# Assign one more veth, should fail. # Assign one more veth, should fail.
if run_cmd "$LXC_USER_NIC $lxcpath $lxcname $p1 veth usernic-br0 xx4"; then if run_cmd "$LXC_USER_NIC create $lxcpath $lxcname $p1 veth usernic-br0 xx4"; then
echo "FAIL: able to create third nic" echo "FAIL: able to create third nic"
exit 1 exit 1
fi fi
...@@ -191,7 +191,7 @@ run_cmd "lxc-stop -n b1 -k" ...@@ -191,7 +191,7 @@ run_cmd "lxc-stop -n b1 -k"
run_cmd "lxc-start -n b1 -d" run_cmd "lxc-start -n b1 -d"
p1=$(run_cmd "lxc-info -n b1 -p -H") p1=$(run_cmd "lxc-info -n b1 -p -H")
if ! run_cmd "$LXC_USER_NIC $lxcpath $lxcname $p1 veth usernic-br0 xx5"; then if ! run_cmd "$LXC_USER_NIC create $lxcpath $lxcname $p1 veth usernic-br0 xx5"; then
echo "FAIL: unable to create nic after destroying the old" echo "FAIL: unable to create nic after destroying the old"
cleanup 1 cleanup 1
fi fi
...@@ -204,7 +204,7 @@ lxc-start -n usernic-c1 -d ...@@ -204,7 +204,7 @@ lxc-start -n usernic-c1 -d
p2=$(lxc-info -n usernic-c1 -p -H) p2=$(lxc-info -n usernic-c1 -p -H)
# assign veth to it - should fail # assign veth to it - should fail
if run_cmd "$LXC_USER_NIC $lxcpath $lxcname $p2 veth usernic-br0 xx6"; then if run_cmd "$LXC_USER_NIC create $lxcpath $lxcname $p2 veth usernic-br0 xx6"; then
echo "FAIL: able to attach nic to root-owned container" echo "FAIL: able to attach nic to root-owned container"
cleanup 1 cleanup 1
fi fi
......
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