diff --git a/doc/lxc.container.conf.sgml.in b/doc/lxc.container.conf.sgml.in index 21d9cfcc1..8c14d1507 100644 --- a/doc/lxc.container.conf.sgml.in +++ b/doc/lxc.container.conf.sgml.in @@ -1389,6 +1389,34 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + Sysctl + + Configure kernel parameters for the container. + + + + + + + + + Specify the kernel parameters to be set. The parameters available + are those listed under /proc/sys/. + Note that not all sysctls are namespaced.Changing Non-namespaced + sysctls will cause the system-wide setting to be modified. + + sysctl + 8 + . + If used with no value, lxc will clear the parameters specified up + to this point. + + + + + + Apparmor profile diff --git a/src/lxc/conf.c b/src/lxc/conf.c index aaed68297..c59f28706 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -2366,6 +2366,38 @@ int setup_resource_limits(struct lxc_list *limits, pid_t pid) { return 0; } +int setup_sysctl_parameters(struct lxc_list *sysctls) +{ + struct lxc_list *it; + struct lxc_sysctl *elem; + char *tmp = NULL; + char filename[MAXPATHLEN] = {0}; + int ret = 0; + + lxc_list_for_each(it, sysctls) { + elem = it->elem; + tmp = lxc_string_replace(".", "/", elem->key); + if (!tmp) { + ERROR("Failed to replace key %s", elem->key); + return -1; + } + + ret = snprintf(filename, sizeof(filename), "/proc/sys/%s", tmp); + free(tmp); + if (ret < 0 || (size_t)ret >= sizeof(filename)) { + ERROR("Error setting up sysctl parameters path"); + return -1; + } + + ret = lxc_write_to_file(filename, elem->value, strlen(elem->value), false); + if (ret < 0) { + ERROR("Failed to setup sysctl parameters %s to %s", elem->key, elem->value); + return -1; + } + } + return 0; +} + static char *default_rootfs_mount = LXCROOTFSMOUNT; struct lxc_conf *lxc_conf_init(void) @@ -2416,6 +2448,7 @@ struct lxc_conf *lxc_conf_init(void) lxc_list_init(&new->aliens); lxc_list_init(&new->environment); lxc_list_init(&new->limits); + lxc_list_init(&new->sysctls); for (i = 0; i < NUM_LXC_HOOKS; i++) lxc_list_init(&new->hooks[i]); lxc_list_init(&new->groups); @@ -3164,6 +3197,15 @@ int lxc_setup(struct lxc_handler *handler) return -1; } + /* set sysctl value to a path under /proc/sys as determined from the key. + * For e.g. net.ipv4.ip_forward translated to /proc/sys/net/ipv4/ip_forward. + */ + if (!lxc_list_empty(&lxc_conf->sysctls)) { + ret = setup_sysctl_parameters(&lxc_conf->sysctls); + if (ret < 0) + return -1; + } + if (!lxc_list_empty(&lxc_conf->keepcaps)) { if (!lxc_list_empty(&lxc_conf->caps)) { ERROR("Container requests lxc.cap.drop and lxc.cap.keep: either use lxc.cap.drop or lxc.cap.keep, not both."); @@ -3315,6 +3357,32 @@ int lxc_clear_limits(struct lxc_conf *c, const char *key) return 0; } +int lxc_clear_sysctls(struct lxc_conf *c, const char *key) +{ + struct lxc_list *it, *next; + bool all = false; + const char *k = NULL; + + if (strcmp(key, "lxc.sysctl") == 0) + all = true; + else if (strncmp(key, "lxc.sysctl.", sizeof("lxc.sysctl.") - 1) == 0) + k = key + sizeof("lxc.sysctl.") - 1; + else + return -1; + + lxc_list_for_each_safe(it, &c->sysctls, next) { + struct lxc_sysctl *elem = it->elem; + if (!all && strcmp(elem->key, k) != 0) + continue; + lxc_list_del(it); + free(elem->key); + free(elem->value); + free(elem); + free(it); + } + return 0; +} + int lxc_clear_groups(struct lxc_conf *c) { struct lxc_list *it,*next; @@ -3454,6 +3522,7 @@ void lxc_conf_free(struct lxc_conf *conf) lxc_clear_aliens(conf); lxc_clear_environment(conf); lxc_clear_limits(conf, "lxc.prlimit"); + lxc_clear_sysctls(conf, "lxc.sysctl"); free(conf->cgroup_meta.dir); free(conf->cgroup_meta.controllers); free(conf); diff --git a/src/lxc/conf.h b/src/lxc/conf.h index 479f38d7e..e60a6151f 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -95,6 +95,16 @@ enum idtype { ID_TYPE_GID }; +/* + * Defines a structure to configure kernel parameters at runtime. + * @key : the kernel parameters will be configured without the "lxc.sysctl" prefix + * @value : the value to set + */ +struct lxc_sysctl { + char *key; + char *value; +}; + /* * id_map is an id map entry. Form in confile is: * lxc.idmap = u 0 9800 100 @@ -370,6 +380,9 @@ struct lxc_conf { /* A list of clients registered to be informed about a container state. */ struct lxc_list state_clients; + + /* sysctls */ + struct lxc_list sysctls; }; int write_id_mapping(enum idtype idtype, pid_t pid, const char *buf, @@ -428,5 +441,7 @@ extern unsigned long add_required_remount_flags(const char *s, const char *d, extern int run_script(const char *name, const char *section, const char *script, ...); extern int in_caplist(int cap, struct lxc_list *caps); +extern int setup_sysctl_parameters(struct lxc_list *sysctls); +extern int lxc_clear_sysctls(struct lxc_conf *c, const char *key); #endif /* __LXC_CONF_H */ diff --git a/src/lxc/confile.c b/src/lxc/confile.c index b4e3d1a1c..dba13d5ef 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -141,6 +141,7 @@ lxc_config_define(start); lxc_config_define(tty_max); lxc_config_define(tty_dir); lxc_config_define(uts_name); +lxc_config_define(sysctl); static struct lxc_config_t config[] = { /* REMOVE in LXC 3.0 */ @@ -241,6 +242,7 @@ static struct lxc_config_t config[] = { { "lxc.tty.dir", false, set_config_tty_dir, get_config_tty_dir, clr_config_tty_dir, }, { "lxc.tty.max", false, set_config_tty_max, get_config_tty_max, clr_config_tty_max, }, { "lxc.uts.name", false, set_config_uts_name, get_config_uts_name, clr_config_uts_name, }, + { "lxc.sysctl", false, set_config_sysctl, get_config_sysctl, clr_config_sysctl, }, /* [START]: REMOVE IN LXC 3.0 */ { "lxc.pts", true, set_config_pty_max, get_config_pty_max, clr_config_pty_max, }, @@ -1456,8 +1458,7 @@ static int set_config_prlimit(const char *key, const char *value, } /* find existing list element */ - lxc_list_for_each(iter, &lxc_conf->limits) - { + lxc_list_for_each(iter, &lxc_conf->limits) { limelem = iter->elem; if (!strcmp(key, limelem->resource)) { limelem->limit = limit; @@ -1468,25 +1469,25 @@ static int set_config_prlimit(const char *key, const char *value, /* allocate list element */ limlist = malloc(sizeof(*limlist)); if (!limlist) - goto out; + goto on_error; limelem = malloc(sizeof(*limelem)); if (!limelem) - goto out; + goto on_error; memset(limelem, 0, sizeof(*limelem)); limelem->resource = strdup(key); if (!limelem->resource) - goto out; + goto on_error; limelem->limit = limit; - limlist->elem = limelem; + lxc_list_add_elem(limlist, limelem);; lxc_list_add_tail(&lxc_conf->limits, limlist); return 0; -out: +on_error: free(limlist); if (limelem) { free(limelem->resource); @@ -1495,6 +1496,71 @@ out: return -1; } +static int set_config_sysctl(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) +{ + struct lxc_list *iter; + struct lxc_list *sysctl_list = NULL; + struct lxc_sysctl *sysctl_elem = NULL; + char *replace_value = NULL; + + if (lxc_config_value_empty(value)) + return lxc_clear_sysctls(lxc_conf, key); + + if (strncmp(key, "lxc.sysctl.", sizeof("lxc.sysctl.") - 1) != 0) + return -1; + + key += sizeof("lxc.sysctl.") - 1; + + /* find existing list element */ + lxc_list_for_each(iter, &lxc_conf->sysctls) { + sysctl_elem = iter->elem; + if (strcmp(key, sysctl_elem->key) == 0) { + replace_value = strdup(value); + if (!replace_value) + return -1; + free(sysctl_elem->value); + sysctl_elem->value = replace_value; + return 0; + } + } + + /* allocate list element */ + sysctl_list = malloc(sizeof(*sysctl_list)); + if (!sysctl_list) + goto on_error; + + sysctl_elem = malloc(sizeof(*sysctl_elem)); + if (!sysctl_elem) + goto on_error; + memset(sysctl_elem, 0, sizeof(*sysctl_elem)); + + sysctl_elem->key = strdup(key); + if (!sysctl_elem->key) + goto on_error; + + sysctl_elem->value = strdup(value); + if (!sysctl_elem->value) + goto on_error; + + lxc_list_add_elem(sysctl_list, sysctl_elem); + + lxc_list_add_tail(&lxc_conf->sysctls, sysctl_list); + + return 0; + +on_error: + free(sysctl_list); + if (sysctl_elem) { + free(sysctl_elem->key); + free(sysctl_elem->value); + free(sysctl_elem); + } + return -1; + + +} + static int set_config_idmaps(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { @@ -3310,6 +3376,43 @@ static int get_config_prlimit(const char *key, char *retv, int inlen, return fulllen; } +/* If you ask for a specific value, i.e. lxc.sysctl.net.ipv4.ip_forward, then just the value + * will be printed. If you ask for 'lxc.sysctl', then all sysctl entries will be + * printed, in 'lxc.sysctl.key = value' format. + */ +static int get_config_sysctl(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + int len; + struct lxc_list *it; + int fulllen = 0; + bool get_all = false; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + if (strcmp(key, "lxc.sysctl") == 0) + get_all = true; + else if (strncmp(key, "lxc.sysctl.", sizeof("lxc.sysctl.") - 1) == 0) + key += sizeof("lxc.sysctl.") - 1; + else + return -1; + + lxc_list_for_each(it, &c->sysctls) { + struct lxc_sysctl *elem = it->elem; + if (get_all) { + strprint(retv, inlen, "lxc.sysctl.%s = %s\n", + elem->key, elem->value); + } else if (strcmp(elem->key, key) == 0) { + strprint(retv, inlen, "%s", elem->value); + } + } + + return fulllen; +} + static int get_config_noop(const char *key, char *retv, int inlen, struct lxc_conf *c, void *data) { @@ -3687,6 +3790,12 @@ static inline int clr_config_prlimit(const char *key, struct lxc_conf *c, return lxc_clear_limits(c, key); } +static inline int clr_config_sysctl(const char *key, struct lxc_conf *c, + void *data) +{ + return lxc_clear_sysctls(c, key); +} + static inline int clr_config_includefiles(const char *key, struct lxc_conf *c, void *data) { diff --git a/src/tests/get_item.c b/src/tests/get_item.c index e0fd7b603..7a15c1c89 100644 --- a/src/tests/get_item.c +++ b/src/tests/get_item.c @@ -34,8 +34,9 @@ int main(int argc, char *argv[]) { - int ret = EXIT_FAILURE; + int ret; struct lxc_container *c; + int fret = EXIT_FAILURE; char v1[2], v2[256], v3[2048]; if ((c = lxc_container_new("testxyz", NULL)) == NULL) { @@ -243,6 +244,71 @@ int main(int argc, char *argv[]) } printf("lxc.limit returned %d %s\n", ret, v3); +#define SYSCTL_SOMAXCONN "lxc.sysctl.net.core.somaxconn = 256\n" +#define ALL_SYSCTLS "lxc.sysctl.net.ipv4.ip_forward = 1\n" SYSCTL_SOMAXCONN + + ret = c->get_config_item(c, "lxc.sysctl", v3, 2047); + if (ret != 0) { + fprintf(stderr, "%d: get_config_item(sysctl) returned %d\n", __LINE__, ret); + goto out; + } + + if (!c->set_config_item(c, "lxc.sysctl.net.ipv4.ip_forward", "1")) { + fprintf(stderr, "%d: failed to set lxc.sysctl.net.ipv4.ip_forward\n", __LINE__); + goto out; + } + ret = c->get_config_item(c, "lxc.sysctl.net.ipv4.ip_forward", v2, 255); + if (ret < 0) { + fprintf(stderr, "%d: get_config_item(lxc.sysctl.net.ipv4.ip_forward) returned %d\n", __LINE__, ret); + goto out; + } + if (strcmp(v2, "1")) { + fprintf(stderr, "%d: lxc.sysctl.net.ipv4.ip_forward returned wrong value: %d %s not 1\n", __LINE__, ret, v2); + goto out; + } + printf("lxc.sysctl.net.ipv4.ip_forward returned %d %s\n", ret, v2); + + if (!c->set_config_item(c, "lxc.sysctl.net.core.somaxconn", "256")) { + fprintf(stderr, "%d: failed to set lxc.sysctl.net.core.somaxconn\n", __LINE__); + goto out; + } + ret = c->get_config_item(c, "lxc.sysctl.net.core.somaxconn", v2, 255); + if (ret < 0) { + fprintf(stderr, "%d: get_config_item(lxc.sysctl.net.core.somaxconn) returned %d\n", __LINE__, ret); + goto out; + } + if (strcmp(v2, "256")) { + fprintf(stderr, "%d: lxc.sysctl.net.core.somaxconn returned wrong value: %d %s not 256\n", __LINE__, ret, v2); + goto out; + } + printf("lxc.sysctl.net.core.somaxconn returned %d %s\n", ret, v2); + + ret = c->get_config_item(c, "lxc.sysctl", v3, 2047); + if (ret != sizeof(ALL_SYSCTLS)-1) { + fprintf(stderr, "%d: get_config_item(sysctl) returned %d\n", __LINE__, ret); + goto out; + } + if (strcmp(v3, ALL_SYSCTLS)) { + fprintf(stderr, "%d: lxc.sysctl returned wrong value: %d %s not %d %s\n", __LINE__, ret, v3, (int)sizeof(ALL_SYSCTLS) - 1, ALL_SYSCTLS); + goto out; + } + printf("lxc.sysctl returned %d %s\n", ret, v3); + + if (!c->clear_config_item(c, "lxc.sysctl.net.ipv4.ip_forward")) { + fprintf(stderr, "%d: failed clearing lxc.sysctl.net.ipv4.ip_forward\n", __LINE__); + goto out; + } + ret = c->get_config_item(c, "lxc.sysctl", v3, 2047); + if (ret != sizeof(SYSCTL_SOMAXCONN) - 1) { + fprintf(stderr, "%d: get_config_item(sysctl) returned %d\n", __LINE__, ret); + goto out; + } + if (strcmp(v3, SYSCTL_SOMAXCONN)) { + fprintf(stderr, "%d: lxc.sysctl returned wrong value: %d %s not %d %s\n", __LINE__, ret, v3, (int)sizeof(SYSCTL_SOMAXCONN) - 1, SYSCTL_SOMAXCONN); + goto out; + } + printf("lxc.sysctl returned %d %s\n", ret, v3); + if (!c->set_config_item(c, "lxc.aa_profile", "unconfined")) { fprintf(stderr, "%d: failed to set aa_profile\n", __LINE__); goto out; @@ -424,9 +490,9 @@ int main(int argc, char *argv[]) } printf("All get_item tests passed\n"); - ret = EXIT_SUCCESS; + fret = EXIT_SUCCESS; out: c->destroy(c); lxc_container_put(c); - exit(ret); + exit(fret); } diff --git a/src/tests/parse_config_file.c b/src/tests/parse_config_file.c index c679acc28..556d6d034 100644 --- a/src/tests/parse_config_file.c +++ b/src/tests/parse_config_file.c @@ -922,6 +922,13 @@ int main(int argc, char *argv[]) goto non_test_error; } + /* lxc.sysctl */ + if (set_get_compare_clear_save_load(c, "lxc.sysctl.net.core.somaxconn", "256", tmpf, + true) < 0) { + lxc_error("%s\n", "lxc.sysctl.net.core.somaxconn"); + goto non_test_error; + } + /* REMOVE IN LXC 3.0 legacy lxc.limit.* key */