#include <time.h>
#include <unistd.h>
+#include "af_unix.h"
#include "conf.h"
#include "config.h"
#include "confile.h"
#include "../include/netns_ifaddrs.h"
#include "log.h"
#include "lxcseccomp.h"
+#include "memory_utils.h"
#include "network.h"
#include "parse.h"
#include "storage.h"
lxc_config_define(net_ipv6_address);
lxc_config_define(net_ipv6_gateway);
lxc_config_define(net_link);
+lxc_config_define(net_l2proxy);
lxc_config_define(net_macvlan_mode);
+lxc_config_define(net_ipvlan_mode);
+lxc_config_define(net_ipvlan_isolation);
lxc_config_define(net_mtu);
lxc_config_define(net_name);
lxc_config_define(net_nic);
lxc_config_define(net_script_up);
lxc_config_define(net_type);
lxc_config_define(net_veth_pair);
+lxc_config_define(net_veth_ipv4_route);
+lxc_config_define(net_veth_ipv6_route);
lxc_config_define(net_vlan_id);
lxc_config_define(no_new_privs);
lxc_config_define(personality);
lxc_config_define(rootfs_options);
lxc_config_define(rootfs_path);
lxc_config_define(seccomp_profile);
+lxc_config_define(seccomp_allow_nesting);
+lxc_config_define(seccomp_notify_cookie);
+lxc_config_define(seccomp_notify_proxy);
lxc_config_define(selinux_context);
lxc_config_define(signal_halt);
lxc_config_define(signal_reboot);
{ "lxc.net.ipv6.address", set_config_net_ipv6_address, get_config_net_ipv6_address, clr_config_net_ipv6_address, },
{ "lxc.net.ipv6.gateway", set_config_net_ipv6_gateway, get_config_net_ipv6_gateway, clr_config_net_ipv6_gateway, },
{ "lxc.net.link", set_config_net_link, get_config_net_link, clr_config_net_link, },
+ { "lxc.net.l2proxy", set_config_net_l2proxy, get_config_net_l2proxy, clr_config_net_l2proxy, },
{ "lxc.net.macvlan.mode", set_config_net_macvlan_mode, get_config_net_macvlan_mode, clr_config_net_macvlan_mode, },
+ { "lxc.net.ipvlan.mode", set_config_net_ipvlan_mode, get_config_net_ipvlan_mode, clr_config_net_ipvlan_mode, },
+ { "lxc.net.ipvlan.isolation", set_config_net_ipvlan_isolation, get_config_net_ipvlan_isolation, clr_config_net_ipvlan_isolation, },
{ "lxc.net.mtu", set_config_net_mtu, get_config_net_mtu, clr_config_net_mtu, },
{ "lxc.net.name", set_config_net_name, get_config_net_name, clr_config_net_name, },
{ "lxc.net.script.down", set_config_net_script_down, get_config_net_script_down, clr_config_net_script_down, },
{ "lxc.net.type", set_config_net_type, get_config_net_type, clr_config_net_type, },
{ "lxc.net.vlan.id", set_config_net_vlan_id, get_config_net_vlan_id, clr_config_net_vlan_id, },
{ "lxc.net.veth.pair", set_config_net_veth_pair, get_config_net_veth_pair, clr_config_net_veth_pair, },
+ { "lxc.net.veth.ipv4.route", set_config_net_veth_ipv4_route, get_config_net_veth_ipv4_route, clr_config_net_veth_ipv4_route, },
+ { "lxc.net.veth.ipv6.route", set_config_net_veth_ipv6_route, get_config_net_veth_ipv6_route, clr_config_net_veth_ipv6_route, },
{ "lxc.net.", set_config_net_nic, get_config_net_nic, clr_config_net_nic, },
{ "lxc.net", set_config_net, get_config_net, clr_config_net, },
{ "lxc.no_new_privs", set_config_no_new_privs, get_config_no_new_privs, clr_config_no_new_privs, },
{ "lxc.rootfs.mount", set_config_rootfs_mount, get_config_rootfs_mount, clr_config_rootfs_mount, },
{ "lxc.rootfs.options", set_config_rootfs_options, get_config_rootfs_options, clr_config_rootfs_options, },
{ "lxc.rootfs.path", set_config_rootfs_path, get_config_rootfs_path, clr_config_rootfs_path, },
+ { "lxc.seccomp.allow_nesting", set_config_seccomp_allow_nesting, get_config_seccomp_allow_nesting, clr_config_seccomp_allow_nesting, },
+ { "lxc.seccomp.notify.cookie", set_config_seccomp_notify_cookie, get_config_seccomp_notify_cookie, clr_config_seccomp_notify_cookie, },
+ { "lxc.seccomp.notify.proxy", set_config_seccomp_notify_proxy, get_config_seccomp_notify_proxy, clr_config_seccomp_notify_proxy, },
{ "lxc.seccomp.profile", set_config_seccomp_profile, get_config_seccomp_profile, clr_config_seccomp_profile, },
{ "lxc.selinux.context", set_config_selinux_context, get_config_selinux_context, clr_config_selinux_context, },
{ "lxc.signal.halt", set_config_signal_halt, get_config_signal_halt, clr_config_signal_halt, },
if (!netdev)
return -1;
- if (!strcmp(value, "veth")) {
+ if (strcmp(value, "veth") == 0) {
netdev->type = LXC_NET_VETH;
- } else if (!strcmp(value, "macvlan")) {
+ lxc_list_init(&netdev->priv.veth_attr.ipv4_routes);
+ lxc_list_init(&netdev->priv.veth_attr.ipv6_routes);
+ } else if (strcmp(value, "macvlan") == 0) {
netdev->type = LXC_NET_MACVLAN;
- lxc_macvlan_mode_to_flag(&netdev->priv.macvlan_attr.mode,
- "private");
- } else if (!strcmp(value, "vlan")) {
+ lxc_macvlan_mode_to_flag(&netdev->priv.macvlan_attr.mode, "private");
+ } else if (strcmp(value, "ipvlan") == 0) {
+ netdev->type = LXC_NET_IPVLAN;
+ lxc_ipvlan_mode_to_flag(&netdev->priv.ipvlan_attr.mode, "l3");
+ lxc_ipvlan_isolation_to_flag(&netdev->priv.ipvlan_attr.isolation, "bridge");
+ } else if (strcmp(value, "vlan") == 0) {
netdev->type = LXC_NET_VLAN;
- } else if (!strcmp(value, "phys")) {
+ } else if (strcmp(value, "phys") == 0) {
netdev->type = LXC_NET_PHYS;
- } else if (!strcmp(value, "empty")) {
+ } else if (strcmp(value, "empty") == 0) {
netdev->type = LXC_NET_EMPTY;
- } else if (!strcmp(value, "none")) {
+ } else if (strcmp(value, "none") == 0) {
netdev->type = LXC_NET_NONE;
} else {
ERROR("Invalid network type %s", value);
return ret;
}
+static int set_config_net_l2proxy(const char *key, const char *value,
+ struct lxc_conf *lxc_conf, void *data)
+{
+ struct lxc_netdev *netdev = data;
+ unsigned int val = 0;
+ int ret;
+
+ if (lxc_config_value_empty(value))
+ return clr_config_net_l2proxy(key, lxc_conf, data);
+
+ if (!netdev)
+ return minus_one_set_errno(EINVAL);
+
+ ret = lxc_safe_uint(value, &val);
+ if (ret < 0)
+ return minus_one_set_errno(-ret);
+
+ switch (val) {
+ case 0:
+ netdev->l2proxy = false;
+ return 0;
+ case 1:
+ netdev->l2proxy = true;
+ return 0;
+ }
+
+ return minus_one_set_errno(EINVAL);
+}
+
static int set_config_net_name(const char *key, const char *value,
struct lxc_conf *lxc_conf, void *data)
{
return lxc_macvlan_mode_to_flag(&netdev->priv.macvlan_attr.mode, value);
}
+static int set_config_net_ipvlan_mode(const char *key, const char *value,
+ struct lxc_conf *lxc_conf, void *data)
+{
+ struct lxc_netdev *netdev = data;
+
+ if (lxc_config_value_empty(value))
+ return clr_config_net_ipvlan_mode(key, lxc_conf, data);
+
+ if (!netdev)
+ return minus_one_set_errno(EINVAL);
+
+ if (netdev->type != LXC_NET_IPVLAN) {
+ SYSERROR("Invalid ipvlan mode \"%s\", can only be used with ipvlan network", value);
+ return minus_one_set_errno(EINVAL);
+ }
+
+ return lxc_ipvlan_mode_to_flag(&netdev->priv.ipvlan_attr.mode, value);
+}
+
+static int set_config_net_ipvlan_isolation(const char *key, const char *value,
+ struct lxc_conf *lxc_conf, void *data)
+{
+ struct lxc_netdev *netdev = data;
+
+ if (lxc_config_value_empty(value))
+ return clr_config_net_ipvlan_isolation(key, lxc_conf, data);
+
+ if (!netdev)
+ return minus_one_set_errno(EINVAL);
+
+ if (netdev->type != LXC_NET_IPVLAN) {
+ SYSERROR("Invalid ipvlan isolation \"%s\", can only be used with ipvlan network", value);
+ return minus_one_set_errno(EINVAL);
+ }
+
+ return lxc_ipvlan_isolation_to_flag(&netdev->priv.ipvlan_attr.isolation, value);
+}
+
static int set_config_net_hwaddr(const char *key, const char *value,
struct lxc_conf *lxc_conf, void *data)
{
free(netdev->ipv4_gateway);
- if (!strcmp(value, "auto")) {
+ if (strcmp(value, "auto") == 0) {
netdev->ipv4_gateway = NULL;
netdev->ipv4_gateway_auto = true;
+ } else if (strcmp(value, "dev") == 0) {
+ netdev->ipv4_gateway = NULL;
+ netdev->ipv4_gateway_auto = false;
+ netdev->ipv4_gateway_dev = true;
} else {
int ret;
struct in_addr *gw;
return 0;
}
+static int set_config_net_veth_ipv4_route(const char *key, const char *value,
+ struct lxc_conf *lxc_conf, void *data)
+{
+ __do_free char *valdup = NULL;
+ __do_free struct lxc_inetdev *inetdev = NULL;
+ __do_free struct lxc_list *list = NULL;
+ int ret;
+ char *netmask, *slash;
+ struct lxc_netdev *netdev = data;
+
+ if (lxc_config_value_empty(value))
+ return clr_config_net_veth_ipv4_route(key, lxc_conf, data);
+
+ if (!netdev)
+ return minus_one_set_errno(EINVAL);
+
+ if (netdev->type != LXC_NET_VETH) {
+ SYSERROR("Invalid ipv4 route \"%s\", can only be used with veth network", value);
+ return minus_one_set_errno(EINVAL);
+ }
+
+ inetdev = malloc(sizeof(*inetdev));
+ if (!inetdev)
+ return -1;
+ memset(inetdev, 0, sizeof(*inetdev));
+
+ list = malloc(sizeof(*list));
+ if (!list)
+ return -1;
+
+ lxc_list_init(list);
+ list->elem = inetdev;
+
+ valdup = strdup(value);
+ if (!valdup)
+ return -1;
+
+ slash = strchr(valdup, '/');
+ if (!slash)
+ return minus_one_set_errno(EINVAL);
+
+ *slash = '\0';
+ slash++;
+ if (*slash == '\0')
+ return minus_one_set_errno(EINVAL);
+
+ netmask = slash;
+
+ ret = lxc_safe_uint(netmask, &inetdev->prefix);
+ if (ret < 0 || inetdev->prefix > 32)
+ return minus_one_set_errno(EINVAL);
+
+ ret = inet_pton(AF_INET, valdup, &inetdev->addr);
+ if (!ret || ret < 0)
+ return minus_one_set_errno(EINVAL);
+
+ lxc_list_add_tail(&netdev->priv.veth_attr.ipv4_routes, list);
+ move_ptr(inetdev);
+ move_ptr(list);
+
+ return 0;
+}
+
static int set_config_net_ipv6_address(const char *key, const char *value,
struct lxc_conf *lxc_conf, void *data)
{
free(netdev->ipv6_gateway);
- if (!strcmp(value, "auto")) {
+ if (strcmp(value, "auto") == 0) {
netdev->ipv6_gateway = NULL;
netdev->ipv6_gateway_auto = true;
+ } else if (strcmp(value, "dev") == 0) {
+ netdev->ipv6_gateway = NULL;
+ netdev->ipv6_gateway_auto = false;
+ netdev->ipv6_gateway_dev = true;
} else {
int ret;
struct in6_addr *gw;
return 0;
}
+static int set_config_net_veth_ipv6_route(const char *key, const char *value,
+ struct lxc_conf *lxc_conf, void *data)
+{
+ __do_free char *valdup;
+ __do_free struct lxc_inet6dev *inet6dev;
+ __do_free struct lxc_list *list;
+ int ret;
+ char *netmask, *slash;
+ struct lxc_netdev *netdev = data;
+
+ if (lxc_config_value_empty(value))
+ return clr_config_net_veth_ipv6_route(key, lxc_conf, data);
+
+ if (!netdev)
+ return minus_one_set_errno(EINVAL);
+
+ if (netdev->type != LXC_NET_VETH) {
+ SYSERROR("Invalid ipv6 route \"%s\", can only be used with veth network", value);
+ return minus_one_set_errno(EINVAL);
+ }
+
+ inet6dev = malloc(sizeof(*inet6dev));
+ if (!inet6dev)
+ return -1;
+ memset(inet6dev, 0, sizeof(*inet6dev));
+
+ list = malloc(sizeof(*list));
+ if (!list)
+ return -1;
+
+ lxc_list_init(list);
+ list->elem = inet6dev;
+
+ valdup = strdup(value);
+ if (!valdup)
+ return -1;
+
+ slash = strchr(valdup, '/');
+ if (!slash)
+ return minus_one_set_errno(EINVAL);
+
+ *slash = '\0';
+ slash++;
+ if (*slash == '\0')
+ return minus_one_set_errno(EINVAL);
+
+ netmask = slash;
+
+ ret = lxc_safe_uint(netmask, &inet6dev->prefix);
+ if (ret < 0 || inet6dev->prefix > 128)
+ return minus_one_set_errno(EINVAL);
+
+ ret = inet_pton(AF_INET6, valdup, &inet6dev->addr);
+ if (!ret || ret < 0)
+ return minus_one_set_errno(EINVAL);
+
+ lxc_list_add_tail(&netdev->priv.veth_attr.ipv6_routes, list);
+ move_ptr(inet6dev);
+ move_ptr(list);
+
+ return 0;
+}
+
static int set_config_net_script_up(const char *key, const char *value,
struct lxc_conf *lxc_conf, void *data)
{
return 0;
}
+static int set_config_seccomp_allow_nesting(const char *key, const char *value,
+ struct lxc_conf *lxc_conf, void *data)
+{
+#ifdef HAVE_SECCOMP
+ if (lxc_config_value_empty(value))
+ return clr_config_seccomp_allow_nesting(key, lxc_conf, NULL);
+
+ if (lxc_safe_uint(value, &lxc_conf->seccomp.allow_nesting) < 0)
+ return -1;
+
+ if (lxc_conf->seccomp.allow_nesting > 1)
+ return minus_one_set_errno(EINVAL);
+
+ return 0;
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+static int set_config_seccomp_notify_cookie(const char *key, const char *value,
+ struct lxc_conf *lxc_conf, void *data)
+{
+#ifdef HAVE_SECCOMP_NOTIFY
+ return set_config_string_item(&lxc_conf->seccomp.notifier.cookie, value);
+#else
+ return minus_one_set_errno(ENOSYS);
+#endif
+}
+
+static int set_config_seccomp_notify_proxy(const char *key, const char *value,
+ struct lxc_conf *lxc_conf, void *data)
+{
+#ifdef HAVE_SECCOMP_NOTIFY
+ const char *offset;
+
+ if (lxc_config_value_empty(value))
+ return clr_config_seccomp_notify_proxy(key, lxc_conf, NULL);
+
+ if (strncmp(value, "unix:", 5) != 0)
+ return minus_one_set_errno(EINVAL);
+
+ offset = value + 5;
+ if (lxc_unix_sockaddr(&lxc_conf->seccomp.notifier.proxy_addr, offset) < 0)
+ return -1;
+
+ return 0;
+#else
+ return minus_one_set_errno(ENOSYS);
+#endif
+}
+
static int set_config_seccomp_profile(const char *key, const char *value,
struct lxc_conf *lxc_conf, void *data)
{
- return set_config_path_item(&lxc_conf->seccomp, value);
+ return set_config_path_item(&lxc_conf->seccomp.seccomp, value);
}
static int set_config_execute_cmd(const char *key, const char *value,
return -EINVAL;
}
+static bool parse_limit_value(const char **value, rlim_t *res)
+{
+ char *endptr = NULL;
+
+ if (strncmp(*value, "unlimited", STRLITERALLEN("unlimited")) == 0) {
+ *res = RLIM_INFINITY;
+ *value += STRLITERALLEN("unlimited");
+ return true;
+ }
+
+ errno = 0;
+ *res = strtoull(*value, &endptr, 10);
+ if (errno || !endptr)
+ return false;
+
+ *value = endptr;
+
+ return true;
+}
+
static int set_config_prlimit(const char *key, const char *value,
struct lxc_conf *lxc_conf, void *data)
{
return 0;
}
+/*
+ * If we find a lxc.net.[i].hwaddr or lxc.network.hwaddr in the original config
+ * file, we expand it in the unexpanded_config, so that after a save_config we
+ * store the hwaddr for re-use.
+ * This is only called when reading the config file, not when executing a
+ * lxc.include.
+ * 'x' and 'X' are substituted in-place.
+ */
+static void update_hwaddr(const char *line)
+{
+ char *p;
+
+ line += lxc_char_left_gc(line, strlen(line));
+ if (line[0] == '#')
+ return;
+
+ if (!lxc_config_net_is_hwaddr(line))
+ return;
+
+ /* Let config_net_hwaddr raise the error. */
+ p = strchr(line, '=');
+ if (!p)
+ return;
+ p++;
+
+ while (isblank(*p))
+ p++;
+
+ if (!*p)
+ return;
+
+ rand_complete_hwaddr(p);
+}
+
int append_unexp_config_line(const char *line, struct lxc_conf *conf)
{
size_t linelen;
bool do_append_unexp_config_line(struct lxc_conf *conf, const char *key,
const char *v)
{
+ __do_free char *tmp = NULL;
int ret;
size_t len;
- char *tmp;
len = strlen(key) + strlen(v) + 4;
- tmp = alloca(len);
+ tmp = must_realloc(NULL, len);
if (lxc_config_value_empty(v))
ret = snprintf(tmp, len, "%s =", key);
const char *newpath, const char *oldname,
const char *newname, const char *ovldir)
{
+ __do_free char *newdir = NULL,
+ *olddir = NULL;
int ret;
- char *lend, *newdir, *olddir, *p, *q;
+ char *lend, *p, *q;
size_t newdirlen, olddirlen;
char *lstart = conf->unexpanded_config;
const char *key = "lxc.mount.entry";
olddirlen = strlen(ovldir) + strlen(oldpath) + strlen(oldname) + 2;
- olddir = alloca(olddirlen + 1);
+ olddir = must_realloc(NULL, olddirlen + 1);
ret = snprintf(olddir, olddirlen + 1, "%s=%s/%s", ovldir, oldpath,
oldname);
if (ret < 0 || ret >= olddirlen + 1)
return false;
newdirlen = strlen(ovldir) + strlen(newpath) + strlen(newname) + 2;
- newdir = alloca(newdirlen + 1);
+ newdir = must_realloc(NULL, newdirlen + 1);
ret = snprintf(newdir, newdirlen + 1, "%s=%s/%s", ovldir, newpath,
newname);
if (ret < 0 || ret >= newdirlen + 1)
const char *newpath, const char *oldname,
const char *newname)
{
+ __do_free char *newdir = NULL,
+ *olddir = NULL;
int ret;
- char *lend, *newdir, *olddir, *p;
+ char *lend, *p;
char *lstart = conf->unexpanded_config;
size_t newdirlen, olddirlen;
const char *key = "lxc.hook";
olddirlen = strlen(oldpath) + strlen(oldname) + 1;
- olddir = alloca(olddirlen + 1);
+ olddir = must_realloc(NULL, olddirlen + 1);
ret = snprintf(olddir, olddirlen + 1, "%s/%s", oldpath, oldname);
if (ret < 0 || ret >= olddirlen + 1)
return false;
newdirlen = strlen(newpath) + strlen(newname) + 1;
- newdir = alloca(newdirlen + 1);
+ newdir = must_realloc(NULL, newdirlen + 1);
ret = snprintf(newdir, newdirlen + 1, "%s/%s", newpath, newname);
if (ret < 0 || ret >= newdirlen + 1)
return false;
else
lend++;
- if (!lxc_config_net_hwaddr(lstart)) {
+ if (!lxc_config_net_is_hwaddr(lstart)) {
lstart = lend;
continue;
}
return lxc_get_conf_uint64(c, retv, inlen, c->console.log_size);
}
+static int get_config_seccomp_allow_nesting(const char *key, char *retv,
+ int inlen, struct lxc_conf *c,
+ void *data)
+{
+#ifdef HAVE_SECCOMP
+ return lxc_get_conf_int(c, retv, inlen, c->seccomp.allow_nesting);
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+static int get_config_seccomp_notify_cookie(const char *key, char *retv, int inlen,
+ struct lxc_conf *c, void *data)
+{
+#ifdef HAVE_SECCOMP_NOTIFY
+ return lxc_get_conf_str(retv, inlen, c->seccomp.notifier.cookie);
+#else
+ return minus_one_set_errno(ENOSYS);
+#endif
+}
+
+static int get_config_seccomp_notify_proxy(const char *key, char *retv, int inlen,
+ struct lxc_conf *c, void *data)
+{
+#ifdef HAVE_SECCOMP_NOTIFY
+ return lxc_get_conf_str(retv, inlen,
+ (c->seccomp.notifier.proxy_addr.sun_path[0]) == '/'
+ ? &c->seccomp.notifier.proxy_addr.sun_path[0]
+ : &c->seccomp.notifier.proxy_addr.sun_path[1]);
+#else
+ return minus_one_set_errno(ENOSYS);
+#endif
+}
static int get_config_seccomp_profile(const char *key, char *retv, int inlen,
struct lxc_conf *c, void *data)
{
- return lxc_get_conf_str(retv, inlen, c->seccomp);
+ return lxc_get_conf_str(retv, inlen, c->seccomp.seccomp);
}
static int get_config_autodev(const char *key, char *retv, int inlen,
return 0;
}
+static inline int clr_config_seccomp_allow_nesting(const char *key,
+ struct lxc_conf *c, void *data)
+{
+#ifdef HAVE_SECCOMP
+ c->seccomp.allow_nesting = 0;
+ return 0;
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+static inline int clr_config_seccomp_notify_cookie(const char *key,
+ struct lxc_conf *c, void *data)
+{
+#ifdef HAVE_SECCOMP_NOTIFY
+ free(c->seccomp.notifier.cookie);
+ c->seccomp.notifier.cookie = NULL;
+ return 0;
+#else
+ return minus_one_set_errno(ENOSYS);
+#endif
+}
+
+static inline int clr_config_seccomp_notify_proxy(const char *key,
+ struct lxc_conf *c, void *data)
+{
+#ifdef HAVE_SECCOMP_NOTIFY
+ memset(&c->seccomp.notifier.proxy_addr, 0,
+ sizeof(c->seccomp.notifier.proxy_addr));
+ return 0;
+#else
+ return minus_one_set_errno(ENOSYS);
+#endif
+}
+
static inline int clr_config_seccomp_profile(const char *key,
struct lxc_conf *c, void *data)
{
- free(c->seccomp);
- c->seccomp = NULL;
+ free(c->seccomp.seccomp);
+ c->seccomp.seccomp = NULL;
return 0;
}
return 0;
}
+static int clr_config_net_l2proxy(const char *key, struct lxc_conf *lxc_conf,
+ void *data)
+{
+ struct lxc_netdev *netdev = data;
+
+ if (!netdev)
+ return minus_one_set_errno(EINVAL);
+
+ netdev->l2proxy = false;
+
+ return 0;
+}
+
static int clr_config_net_macvlan_mode(const char *key,
struct lxc_conf *lxc_conf, void *data)
{
return 0;
}
+static int clr_config_net_ipvlan_mode(const char *key,
+ struct lxc_conf *lxc_conf, void *data)
+{
+ struct lxc_netdev *netdev = data;
+
+ if (!netdev)
+ return minus_one_set_errno(EINVAL);
+
+ if (netdev->type != LXC_NET_IPVLAN)
+ return 0;
+
+ netdev->priv.ipvlan_attr.mode = -1;
+
+ return 0;
+}
+
+static int clr_config_net_ipvlan_isolation(const char *key,
+ struct lxc_conf *lxc_conf, void *data)
+{
+ struct lxc_netdev *netdev = data;
+
+ if (!netdev)
+ return minus_one_set_errno(EINVAL);
+
+ if (netdev->type != LXC_NET_IPVLAN)
+ return 0;
+
+ netdev->priv.ipvlan_attr.isolation = -1;
+
+ return 0;
+}
+
static int clr_config_net_veth_pair(const char *key, struct lxc_conf *lxc_conf,
void *data)
{
return 0;
}
+static int clr_config_net_veth_ipv4_route(const char *key,
+ struct lxc_conf *lxc_conf, void *data)
+{
+ struct lxc_netdev *netdev = data;
+ struct lxc_list *cur, *next;
+
+ if (!netdev)
+ return -1;
+
+ lxc_list_for_each_safe(cur, &netdev->priv.veth_attr.ipv4_routes, next) {
+ lxc_list_del(cur);
+ free(cur->elem);
+ free(cur);
+ }
+
+ return 0;
+}
+
static int clr_config_net_ipv6_gateway(const char *key,
struct lxc_conf *lxc_conf, void *data)
{
return 0;
}
+static int clr_config_net_veth_ipv6_route(const char *key,
+ struct lxc_conf *lxc_conf, void *data)
+{
+ struct lxc_netdev *netdev = data;
+ struct lxc_list *cur, *next;
+
+ if (!netdev)
+ return -1;
+
+ lxc_list_for_each_safe(cur, &netdev->priv.veth_attr.ipv6_routes, next) {
+ lxc_list_del(cur);
+ free(cur->elem);
+ free(cur);
+ }
+
+ return 0;
+}
+
static int get_config_net_nic(const char *key, char *retv, int inlen,
struct lxc_conf *c, void *data)
{
return fulllen;
}
+static int get_config_net_l2proxy(const char *key, char *retv, int inlen,
+ struct lxc_conf *c, void *data)
+{
+ struct lxc_netdev *netdev = data;
+ return lxc_get_conf_bool(c, retv, inlen, netdev->l2proxy);
+}
+
static int get_config_net_name(const char *key, char *retv, int inlen,
struct lxc_conf *c, void *data)
{
return fulllen;
}
+static int get_config_net_ipvlan_mode(const char *key, char *retv, int inlen,
+ struct lxc_conf *c, void *data)
+{
+ int len;
+ int fulllen = 0;
+ const char *mode;
+ struct lxc_netdev *netdev = data;
+
+ if (!retv)
+ inlen = 0;
+ else
+ memset(retv, 0, inlen);
+
+ if (!netdev)
+ return minus_one_set_errno(EINVAL);
+
+ if (netdev->type != LXC_NET_IPVLAN)
+ return 0;
+
+ switch (netdev->priv.ipvlan_attr.mode) {
+ case IPVLAN_MODE_L3:
+ mode = "l3";
+ break;
+ case IPVLAN_MODE_L3S:
+ mode = "l3s";
+ break;
+ case IPVLAN_MODE_L2:
+ mode = "l2";
+ break;
+ default:
+ mode = "(invalid)";
+ break;
+ }
+
+ strprint(retv, inlen, "%s", mode);
+
+ return fulllen;
+}
+
+static int get_config_net_ipvlan_isolation(const char *key, char *retv, int inlen,
+ struct lxc_conf *c, void *data)
+{
+ int len;
+ int fulllen = 0;
+ const char *mode;
+ struct lxc_netdev *netdev = data;
+
+ if (!retv)
+ inlen = 0;
+ else
+ memset(retv, 0, inlen);
+
+ if (!netdev)
+ return minus_one_set_errno(EINVAL);
+
+ if (netdev->type != LXC_NET_IPVLAN)
+ return 0;
+
+ switch (netdev->priv.ipvlan_attr.isolation) {
+ case IPVLAN_ISOLATION_BRIDGE:
+ mode = "bridge";
+ break;
+ case IPVLAN_ISOLATION_PRIVATE:
+ mode = "private";
+ break;
+ case IPVLAN_ISOLATION_VEPA:
+ mode = "vepa";
+ break;
+ default:
+ mode = "(invalid)";
+ break;
+ }
+
+ strprint(retv, inlen, "%s", mode);
+
+ return fulllen;
+}
+
static int get_config_net_veth_pair(const char *key, char *retv, int inlen,
struct lxc_conf *c, void *data)
{
if (netdev->ipv4_gateway_auto) {
strprint(retv, inlen, "auto");
+ } else if (netdev->ipv4_gateway_dev) {
+ strprint(retv, inlen, "dev");
} else if (netdev->ipv4_gateway) {
inet_ntop(AF_INET, netdev->ipv4_gateway, buf, sizeof(buf));
strprint(retv, inlen, "%s", buf);
return fulllen;
}
+static int get_config_net_veth_ipv4_route(const char *key, char *retv, int inlen,
+ struct lxc_conf *c, void *data)
+{
+ int len;
+ size_t listlen;
+ char buf[INET_ADDRSTRLEN];
+ struct lxc_list *it;
+ int fulllen = 0;
+ struct lxc_netdev *netdev = data;
+
+ if (!retv)
+ inlen = 0;
+ else
+ memset(retv, 0, inlen);
+
+ if (!netdev)
+ return minus_one_set_errno(EINVAL);
+
+ if (netdev->type != LXC_NET_VETH)
+ return 0;
+
+ listlen = lxc_list_len(&netdev->priv.veth_attr.ipv4_routes);
+
+ lxc_list_for_each(it, &netdev->priv.veth_attr.ipv4_routes) {
+ struct lxc_inetdev *i = it->elem;
+ inet_ntop(AF_INET, &i->addr, buf, sizeof(buf));
+ strprint(retv, inlen, "%s/%u%s", buf, i->prefix,
+ (listlen-- > 1) ? "\n" : "");
+ }
+
+ return fulllen;
+}
+
static int get_config_net_ipv6_gateway(const char *key, char *retv, int inlen,
struct lxc_conf *c, void *data)
{
if (netdev->ipv6_gateway_auto) {
strprint(retv, inlen, "auto");
+ } else if (netdev->ipv6_gateway_dev) {
+ strprint(retv, inlen, "dev");
} else if (netdev->ipv6_gateway) {
inet_ntop(AF_INET6, netdev->ipv6_gateway, buf, sizeof(buf));
strprint(retv, inlen, "%s", buf);
return fulllen;
}
+static int get_config_net_veth_ipv6_route(const char *key, char *retv, int inlen,
+ struct lxc_conf *c, void *data)
+{
+ int len;
+ size_t listlen;
+ char buf[INET6_ADDRSTRLEN];
+ struct lxc_list *it;
+ int fulllen = 0;
+ struct lxc_netdev *netdev = data;
+
+ if (!retv)
+ inlen = 0;
+ else
+ memset(retv, 0, inlen);
+
+ if (!netdev)
+ return minus_one_set_errno(EINVAL);
+
+ if (netdev->type != LXC_NET_VETH)
+ return 0;
+
+ listlen = lxc_list_len(&netdev->priv.veth_attr.ipv6_routes);
+
+ lxc_list_for_each(it, &netdev->priv.veth_attr.ipv6_routes) {
+ struct lxc_inet6dev *i = it->elem;
+ inet_ntop(AF_INET6, &i->addr, buf, sizeof(buf));
+ strprint(retv, inlen, "%s/%u%s", buf, i->prefix,
+ (listlen-- > 1) ? "\n" : "");
+ }
+
+ return fulllen;
+}
+
int lxc_list_config_items(char *retv, int inlen)
{
size_t i;
switch (netdev->type) {
case LXC_NET_VETH:
strprint(retv, inlen, "veth.pair\n");
+ strprint(retv, inlen, "veth.ipv4.route\n");
+ strprint(retv, inlen, "veth.ipv6.route\n");
break;
case LXC_NET_MACVLAN:
strprint(retv, inlen, "macvlan.mode\n");
break;
+ case LXC_NET_IPVLAN:
+ strprint(retv, inlen, "ipvlan.mode\n");
+ strprint(retv, inlen, "ipvlan.isolation\n");
+ break;
case LXC_NET_VLAN:
strprint(retv, inlen, "vlan.id\n");
break;