network: Adds support host side veth device static routes

Adds the following new config keys:

	lxc.net.[i].veth.ipv4.route
	lxc.net.[i].veth.ipv6.route
E.g.

	lxc.net.0.veth.ipv4.route = 192.0.2.1/32
	lxc.net.0.veth.ipv4.route = 192.0.3.0/24
	lxc.net.0.veth.ipv6.route = 2001:db8::1/128
	lxc.net.0.veth.ipv6.route = 2001:db8:2::/64

Signed-off-by: tomponline <thomas.parrott@canonical.com>
This commit is contained in:
tomponline 2019-04-25 12:47:17 +01:00 committed by tomponline
parent 41cd8a8d8c
commit d4a7da4632
8 changed files with 375 additions and 18 deletions

View File

@ -32,3 +32,9 @@ until a reboot succeeded. It takes a timeout argument. When set to `> 0`
This adds support for injecting and removing mounts into/from a running This adds support for injecting and removing mounts into/from a running
containers. Two new API functions `mount()` and `umount()` are added. They containers. Two new API functions `mount()` and `umount()` are added. They
mirror the current mount and umount API of the kernel. mirror the current mount and umount API of the kernel.
## network\_veth\_routes
This introduces the `lxc.net.[i].veth.ipv4.route` and `lxc.net.[i].veth.ipv6.route` properties
on `veth` type network interfaces. This allows adding static routes on host to the container's
network interface.

View File

@ -375,7 +375,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
<listitem> <listitem>
<para> <para>
The only allowed values are 0 and 1. Set this to 1 to destroy a The only allowed values are 0 and 1. Set this to 1 to destroy a
container on shutdown. container on shutdown.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
@ -459,6 +459,12 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
the <option>lxc.net.[i].veth.pair</option> option (except for the <option>lxc.net.[i].veth.pair</option> option (except for
unprivileged containers where this option is ignored for security unprivileged containers where this option is ignored for security
reasons). reasons).
Static routes can be added on the host pointing to the container using the
<option>lxc.net.[i].veth.ipv4.route</option> and
<option>lxc.net.[i].veth.ipv6.route</option> options.
Several lines specify several routes.
The route is in format x.y.z.t/m, eg. 192.168.1.0/24.
</para> </para>
<para> <para>
@ -855,7 +861,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
When manually specifying a size for the log file the value should When manually specifying a size for the log file the value should
be a power of 2 when converted to bytes. Valid size prefixes are be a power of 2 when converted to bytes. Valid size prefixes are
'KB', 'MB', 'GB'. (Note that all conversions are based on multiples 'KB', 'MB', 'GB'. (Note that all conversions are based on multiples
of 1024. That means 'KB' == 'KiB', 'MB' == 'MiB', 'GB' == 'GiB'. of 1024. That means 'KB' == 'KiB', 'MB' == 'MiB', 'GB' == 'GiB'.
Additionally, the case of the suffix is ignored, i.e. 'kB', 'KB' and Additionally, the case of the suffix is ignored, i.e. 'kB', 'KB' and
'Kb' are treated equally.) 'Kb' are treated equally.)
@ -1629,7 +1635,7 @@ dev/null proc/kcore none bind,relative 0 0
</para> </para>
<para> <para>
To inherit the namespace from another container set the To inherit the namespace from another container set the
<option>lxc.namespace.share.[namespace identifier]</option> to the name of <option>lxc.namespace.share.[namespace identifier]</option> to the name of
the container, e.g. <option>lxc.namespace.share.pid=c3</option>. the container, e.g. <option>lxc.namespace.share.pid=c3</option>.
</para> </para>
@ -1708,7 +1714,7 @@ dev/null proc/kcore none bind,relative 0 0
</term> </term>
<listitem> <listitem>
<para> <para>
Specify the kernel parameters to be set. The parameters available Specify the kernel parameters to be set. The parameters available
are those listed under /proc/sys/. are those listed under /proc/sys/.
Note that not all sysctls are namespaced. Changing Non-namespaced Note that not all sysctls are namespaced. Changing Non-namespaced
sysctls will cause the system-wide setting to be modified. sysctls will cause the system-wide setting to be modified.
@ -1716,7 +1722,7 @@ dev/null proc/kcore none bind,relative 0 0
<refentrytitle><command>sysctl</command></refentrytitle> <refentrytitle><command>sysctl</command></refentrytitle>
<manvolnum>8</manvolnum> <manvolnum>8</manvolnum>
</citerefentry>. </citerefentry>.
If used with no value, lxc will clear the parameters specified up If used with no value, lxc will clear the parameters specified up
to this point. to this point.
</para> </para>
</listitem> </listitem>

View File

@ -44,6 +44,7 @@ static char *api_extensions[] = {
"mount_injection_file", "mount_injection_file",
"seccomp_allow_nesting", "seccomp_allow_nesting",
"seccomp_notify", "seccomp_notify",
"network_veth_routes",
}; };
static size_t nr_api_extensions = sizeof(api_extensions) / sizeof(*api_extensions); static size_t nr_api_extensions = sizeof(api_extensions) / sizeof(*api_extensions);

View File

@ -137,6 +137,8 @@ lxc_config_define(net_script_down);
lxc_config_define(net_script_up); lxc_config_define(net_script_up);
lxc_config_define(net_type); lxc_config_define(net_type);
lxc_config_define(net_veth_pair); 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(net_vlan_id);
lxc_config_define(no_new_privs); lxc_config_define(no_new_privs);
lxc_config_define(personality); lxc_config_define(personality);
@ -226,6 +228,8 @@ static struct lxc_config_t config_jump_table[] = {
{ "lxc.net.type", set_config_net_type, get_config_net_type, clr_config_net_type, }, { "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.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.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_nic, get_config_net_nic, clr_config_net_nic, },
{ "lxc.net", set_config_net, get_config_net, clr_config_net, }, { "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.no_new_privs", set_config_no_new_privs, get_config_no_new_privs, clr_config_no_new_privs, },
@ -289,6 +293,8 @@ static int set_config_net_type(const char *key, const char *value,
if (!strcmp(value, "veth")) { if (!strcmp(value, "veth")) {
netdev->type = LXC_NET_VETH; netdev->type = LXC_NET_VETH;
lxc_list_init(&netdev->priv.veth_attr.ipv4_routes);
lxc_list_init(&netdev->priv.veth_attr.ipv6_routes);
} else if (!strcmp(value, "macvlan")) { } else if (!strcmp(value, "macvlan")) {
netdev->type = LXC_NET_MACVLAN; netdev->type = LXC_NET_MACVLAN;
lxc_macvlan_mode_to_flag(&netdev->priv.macvlan_attr.mode, lxc_macvlan_mode_to_flag(&netdev->priv.macvlan_attr.mode,
@ -629,6 +635,69 @@ static int set_config_net_ipv4_gateway(const char *key, const char *value,
return 0; 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, static int set_config_net_ipv6_address(const char *key, const char *value,
struct lxc_conf *lxc_conf, void *data) struct lxc_conf *lxc_conf, void *data)
{ {
@ -733,6 +802,69 @@ static int set_config_net_ipv6_gateway(const char *key, const char *value,
return 0; 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, static int set_config_net_script_up(const char *key, const char *value,
struct lxc_conf *lxc_conf, void *data) struct lxc_conf *lxc_conf, void *data)
{ {
@ -4898,6 +5030,24 @@ static int clr_config_net_ipv4_address(const char *key,
return 0; 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, static int clr_config_net_ipv6_gateway(const char *key,
struct lxc_conf *lxc_conf, void *data) struct lxc_conf *lxc_conf, void *data)
{ {
@ -4930,6 +5080,24 @@ static int clr_config_net_ipv6_address(const char *key,
return 0; 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, static int get_config_net_nic(const char *key, char *retv, int inlen,
struct lxc_conf *c, void *data) struct lxc_conf *c, void *data)
{ {
@ -5274,6 +5442,39 @@ static int get_config_net_ipv4_address(const char *key, char *retv, int inlen,
return fulllen; 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, static int get_config_net_ipv6_gateway(const char *key, char *retv, int inlen,
struct lxc_conf *c, void *data) struct lxc_conf *c, void *data)
{ {
@ -5330,6 +5531,39 @@ static int get_config_net_ipv6_address(const char *key, char *retv, int inlen,
return fulllen; 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) int lxc_list_config_items(char *retv, int inlen)
{ {
size_t i; size_t i;
@ -5463,6 +5697,8 @@ int lxc_list_net(struct lxc_conf *c, const char *key, char *retv, int inlen)
switch (netdev->type) { switch (netdev->type) {
case LXC_NET_VETH: case LXC_NET_VETH:
strprint(retv, inlen, "veth.pair\n"); strprint(retv, inlen, "veth.pair\n");
strprint(retv, inlen, "veth.ipv4.route\n");
strprint(retv, inlen, "veth.ipv6.route\n");
break; break;
case LXC_NET_MACVLAN: case LXC_NET_MACVLAN:
strprint(retv, inlen, "macvlan.mode\n"); strprint(retv, inlen, "macvlan.mode\n");

View File

@ -317,7 +317,7 @@ void lxc_log_configured_netdevs(const struct lxc_conf *conf)
TRACE("type: none"); TRACE("type: none");
break; break;
default: default:
ERROR("invalid network type %d", netdev->type); ERROR("Invalid network type %d", netdev->type);
return; return;
} }
@ -374,6 +374,28 @@ void lxc_log_configured_netdevs(const struct lxc_conf *conf)
sizeof(bufinet6)); sizeof(bufinet6));
TRACE("ipv6 addr: %s", bufinet6); TRACE("ipv6 addr: %s", bufinet6);
} }
if (netdev->type == LXC_NET_VETH) {
lxc_list_for_each_safe(cur, &netdev->priv.veth_attr.ipv4_routes, next) {
inet4dev = cur->elem;
if (!inet_ntop(AF_INET, &inet4dev->addr, bufinet4, sizeof(bufinet4))) {
ERROR("Invalid ipv4 veth route");
return;
}
TRACE("ipv4 veth route: %s/%u", bufinet4, inet4dev->prefix);
}
lxc_list_for_each_safe(cur, &netdev->priv.veth_attr.ipv6_routes, next) {
inet6dev = cur->elem;
if (!inet_ntop(AF_INET6, &inet6dev->addr, bufinet6, sizeof(bufinet6))) {
ERROR("Invalid ipv6 veth route");
return;
}
TRACE("ipv6 veth route: %s/%u", bufinet6, inet6dev->prefix);
}
}
} }
} }
} }
@ -401,6 +423,20 @@ static void lxc_free_netdev(struct lxc_netdev *netdev)
free(cur); free(cur);
} }
if (netdev->type == LXC_NET_VETH) {
lxc_list_for_each_safe(cur, &netdev->priv.veth_attr.ipv4_routes, next) {
lxc_list_del(cur);
free(cur->elem);
free(cur);
}
lxc_list_for_each_safe(cur, &netdev->priv.veth_attr.ipv6_routes, next) {
lxc_list_del(cur);
free(cur->elem);
free(cur);
}
}
free(netdev); free(netdev);
} }

View File

@ -69,6 +69,44 @@ lxc_log_define(network, lxc);
typedef int (*instantiate_cb)(struct lxc_handler *, struct lxc_netdev *); typedef int (*instantiate_cb)(struct lxc_handler *, struct lxc_netdev *);
static int lxc_setup_ipv4_routes(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_dest_add(ifindex, &inetdev->addr, inetdev->prefix);
if (err) {
SYSERROR("Failed to setup ipv4 route for network device "
"with ifindex %d", ifindex);
return minus_one_set_errno(-err);
}
}
return 0;
}
static int lxc_setup_ipv6_routes(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_dest_add(ifindex, &inet6dev->addr, inet6dev->prefix);
if (err) {
SYSERROR("Failed to setup ipv6 route for network device "
"with ifindex %d", ifindex);
return minus_one_set_errno(-err);
}
}
return 0;
}
static int instantiate_veth(struct lxc_handler *handler, struct lxc_netdev *netdev) static int instantiate_veth(struct lxc_handler *handler, struct lxc_netdev *netdev)
{ {
int bridge_index, err; int bridge_index, err;
@ -183,6 +221,18 @@ static int instantiate_veth(struct lxc_handler *handler, struct lxc_netdev *netd
goto out_delete; goto out_delete;
} }
/* setup ipv4 routes on the host interface */
if (lxc_setup_ipv4_routes(&netdev->priv.veth_attr.ipv4_routes, netdev->priv.veth_attr.ifindex)) {
ERROR("Failed to setup ipv4 routes for network device \"%s\"", veth1);
goto out_delete;
}
/* setup ipv6 routes on the host interface */
if (lxc_setup_ipv6_routes(&netdev->priv.veth_attr.ipv6_routes, netdev->priv.veth_attr.ifindex)) {
ERROR("Failed to setup ipv6 routes for network device \"%s\"", veth1);
goto out_delete;
}
if (netdev->upscript) { if (netdev->upscript) {
char *argv[] = { char *argv[] = {
"veth", "veth",
@ -1780,7 +1830,7 @@ int lxc_ipv6_gateway_add(int ifindex, struct in6_addr *gw)
return ip_gateway_add(AF_INET6, ifindex, gw); return ip_gateway_add(AF_INET6, ifindex, gw);
} }
static int ip_route_dest_add(int family, int ifindex, void *dest) static int ip_route_dest_add(int family, int ifindex, void *dest, unsigned int netmask)
{ {
int addrlen, err; int addrlen, err;
struct nl_handler nlh; struct nl_handler nlh;
@ -1815,7 +1865,7 @@ static int ip_route_dest_add(int family, int ifindex, void *dest)
rt->rtm_scope = RT_SCOPE_LINK; rt->rtm_scope = RT_SCOPE_LINK;
rt->rtm_protocol = RTPROT_BOOT; rt->rtm_protocol = RTPROT_BOOT;
rt->rtm_type = RTN_UNICAST; rt->rtm_type = RTN_UNICAST;
rt->rtm_dst_len = addrlen * 8; rt->rtm_dst_len = netmask;
err = -EINVAL; err = -EINVAL;
if (nla_put_buffer(nlmsg, RTA_DST, dest, addrlen)) if (nla_put_buffer(nlmsg, RTA_DST, dest, addrlen))
@ -1830,14 +1880,14 @@ out:
return err; return err;
} }
int lxc_ipv4_dest_add(int ifindex, struct in_addr *dest) int lxc_ipv4_dest_add(int ifindex, struct in_addr *dest, unsigned int netmask)
{ {
return ip_route_dest_add(AF_INET, ifindex, dest); return ip_route_dest_add(AF_INET, ifindex, dest, netmask);
} }
int lxc_ipv6_dest_add(int ifindex, struct in6_addr *dest) int lxc_ipv6_dest_add(int ifindex, struct in6_addr *dest, unsigned int netmask)
{ {
return ip_route_dest_add(AF_INET6, ifindex, dest); return ip_route_dest_add(AF_INET6, ifindex, dest, netmask);
} }
bool is_ovs_bridge(const char *bridge) bool is_ovs_bridge(const char *bridge)
@ -2807,7 +2857,7 @@ static int setup_ipv4_addr(struct lxc_list *ip, int ifindex)
if (err) { if (err) {
errno = -err; errno = -err;
SYSERROR("Failed to setup ipv4 address for network device " SYSERROR("Failed to setup ipv4 address for network device "
"with eifindex %d", ifindex); "with ifindex %d", ifindex);
return -1; return -1;
} }
} }
@ -2829,7 +2879,7 @@ static int setup_ipv6_addr(struct lxc_list *ip, int ifindex)
if (err) { if (err) {
errno = -err; errno = -err;
SYSERROR("Failed to setup ipv6 address for network device " SYSERROR("Failed to setup ipv6 address for network device "
"with eifindex %d", ifindex); "with ifindex %d", ifindex);
return -1; return -1;
} }
} }
@ -2988,7 +3038,7 @@ static int lxc_setup_netdev_in_child_namespaces(struct lxc_netdev *netdev)
err = lxc_ipv4_gateway_add(netdev->ifindex, netdev->ipv4_gateway); err = lxc_ipv4_gateway_add(netdev->ifindex, netdev->ipv4_gateway);
if (err) { if (err) {
err = lxc_ipv4_dest_add(netdev->ifindex, netdev->ipv4_gateway); err = lxc_ipv4_dest_add(netdev->ifindex, netdev->ipv4_gateway, 32);
if (err) { if (err) {
errno = -err; errno = -err;
SYSERROR("Failed to add ipv4 dest for network device \"%s\"", SYSERROR("Failed to add ipv4 dest for network device \"%s\"",
@ -3027,7 +3077,7 @@ static int lxc_setup_netdev_in_child_namespaces(struct lxc_netdev *netdev)
err = lxc_ipv6_gateway_add(netdev->ifindex, netdev->ipv6_gateway); err = lxc_ipv6_gateway_add(netdev->ifindex, netdev->ipv6_gateway);
if (err) { if (err) {
err = lxc_ipv6_dest_add(netdev->ifindex, netdev->ipv6_gateway); err = lxc_ipv6_dest_add(netdev->ifindex, netdev->ipv6_gateway, 128);
if (err) { if (err) {
errno = -err; errno = -err;
SYSERROR("Failed to add ipv6 dest for network device \"%s\"", SYSERROR("Failed to add ipv6 dest for network device \"%s\"",

View File

@ -95,6 +95,8 @@ struct ifla_veth {
char pair[IFNAMSIZ]; char pair[IFNAMSIZ];
char veth1[IFNAMSIZ]; char veth1[IFNAMSIZ];
int ifindex; int ifindex;
struct lxc_list ipv4_routes;
struct lxc_list ipv6_routes;
}; };
struct ifla_vlan { struct ifla_vlan {
@ -221,8 +223,8 @@ extern int lxc_ipv4_addr_get(int ifindex, struct in_addr **res);
extern int lxc_ipv6_addr_get(int ifindex, struct in6_addr **res); extern int lxc_ipv6_addr_get(int ifindex, struct in6_addr **res);
/* Set a destination route to an interface. */ /* Set a destination route to an interface. */
extern int lxc_ipv4_dest_add(int ifindex, struct in_addr *dest); extern int lxc_ipv4_dest_add(int ifindex, struct in_addr *dest, unsigned int netmask);
extern int lxc_ipv6_dest_add(int ifindex, struct in6_addr *dest); extern int lxc_ipv6_dest_add(int ifindex, struct in6_addr *dest, unsigned int netmask);
/* Set default route. */ /* Set default route. */
extern int lxc_ipv4_gateway_add(int ifindex, struct in_addr *gw); extern int lxc_ipv4_gateway_add(int ifindex, struct in_addr *gw);

View File

@ -134,6 +134,16 @@ static int set_and_clear_complete_netdev(struct lxc_container *c)
return -1; return -1;
} }
if (!c->set_config_item(c, "lxc.net.1.veth.ipv4.route", "192.0.2.1/32")) {
lxc_error("%s\n", "lxc.net.1.veth.ipv4.route");
return -1;
}
if (!c->set_config_item(c, "lxc.net.1.veth.ipv6.route", "2001:db8::1/128")) {
lxc_error("%s\n", "lxc.net.1.veth.ipv6.route");
return -1;
}
if (!c->set_config_item(c, "lxc.net.1.hwaddr", if (!c->set_config_item(c, "lxc.net.1.hwaddr",
"52:54:00:80:7a:5d")) { "52:54:00:80:7a:5d")) {
lxc_error("%s\n", "lxc.net.1.hwaddr"); lxc_error("%s\n", "lxc.net.1.hwaddr");
@ -695,6 +705,16 @@ int main(int argc, char *argv[])
goto non_test_error; goto non_test_error;
} }
if (set_get_compare_clear_save_load_network(c, "lxc.net.0.veth.ipv4.route", "192.0.2.1/32", tmpf, true, "veth")) {
lxc_error("%s\n", "lxc.net.0.veth.ipv4.route");
return -1;
}
if (set_get_compare_clear_save_load_network(c, "lxc.net.0.veth.ipv6.route", "2001:db8::1/128", tmpf, true, "veth")) {
lxc_error("%s\n", "lxc.net.0.veth.ipv6.route");
return -1;
}
if (set_get_compare_clear_save_load(c, "lxc.net.0.script.up", "/some/up/path", tmpf, true)) { if (set_get_compare_clear_save_load(c, "lxc.net.0.script.up", "/some/up/path", tmpf, true)) {
lxc_error("%s\n", "lxc.net.0.script.up"); lxc_error("%s\n", "lxc.net.0.script.up");
goto non_test_error; goto non_test_error;