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
containers. Two new API functions `mount()` and `umount()` are added. They
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>
<para>
The only allowed values are 0 and 1. Set this to 1 to destroy a
container on shutdown.
container on shutdown.
</para>
</listitem>
</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
unprivileged containers where this option is ignored for security
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>
@ -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
be a power of 2 when converted to bytes. Valid size prefixes are
'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
'Kb' are treated equally.)
@ -1629,7 +1635,7 @@ dev/null proc/kcore none bind,relative 0 0
</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
the container, e.g. <option>lxc.namespace.share.pid=c3</option>.
</para>
@ -1708,7 +1714,7 @@ dev/null proc/kcore none bind,relative 0 0
</term>
<listitem>
<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/.
Note that not all sysctls are namespaced. Changing Non-namespaced
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>
<manvolnum>8</manvolnum>
</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.
</para>
</listitem>

View File

@ -44,6 +44,7 @@ static char *api_extensions[] = {
"mount_injection_file",
"seccomp_allow_nesting",
"seccomp_notify",
"network_veth_routes",
};
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_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);
@ -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.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, },
@ -289,6 +293,8 @@ static int set_config_net_type(const char *key, const char *value,
if (!strcmp(value, "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")) {
netdev->type = LXC_NET_MACVLAN;
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;
}
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)
{
@ -733,6 +802,69 @@ static int set_config_net_ipv6_gateway(const char *key, const char *value,
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)
{
@ -4898,6 +5030,24 @@ static int clr_config_net_ipv4_address(const char *key,
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)
{
@ -4930,6 +5080,24 @@ static int clr_config_net_ipv6_address(const char *key,
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)
{
@ -5274,6 +5442,39 @@ static int get_config_net_ipv4_address(const char *key, char *retv, int inlen,
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)
{
@ -5330,6 +5531,39 @@ static int get_config_net_ipv6_address(const char *key, char *retv, int inlen,
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;
@ -5463,6 +5697,8 @@ int lxc_list_net(struct lxc_conf *c, const char *key, char *retv, int inlen)
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");

View File

@ -317,7 +317,7 @@ void lxc_log_configured_netdevs(const struct lxc_conf *conf)
TRACE("type: none");
break;
default:
ERROR("invalid network type %d", netdev->type);
ERROR("Invalid network type %d", netdev->type);
return;
}
@ -374,6 +374,28 @@ void lxc_log_configured_netdevs(const struct lxc_conf *conf)
sizeof(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);
}
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);
}

View File

@ -69,6 +69,44 @@ lxc_log_define(network, lxc);
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)
{
int bridge_index, err;
@ -183,6 +221,18 @@ static int instantiate_veth(struct lxc_handler *handler, struct lxc_netdev *netd
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) {
char *argv[] = {
"veth",
@ -1780,7 +1830,7 @@ int lxc_ipv6_gateway_add(int ifindex, struct in6_addr *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;
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_protocol = RTPROT_BOOT;
rt->rtm_type = RTN_UNICAST;
rt->rtm_dst_len = addrlen * 8;
rt->rtm_dst_len = netmask;
err = -EINVAL;
if (nla_put_buffer(nlmsg, RTA_DST, dest, addrlen))
@ -1830,14 +1880,14 @@ out:
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)
@ -2807,7 +2857,7 @@ static int setup_ipv4_addr(struct lxc_list *ip, int ifindex)
if (err) {
errno = -err;
SYSERROR("Failed to setup ipv4 address for network device "
"with eifindex %d", ifindex);
"with ifindex %d", ifindex);
return -1;
}
}
@ -2829,7 +2879,7 @@ static int setup_ipv6_addr(struct lxc_list *ip, int ifindex)
if (err) {
errno = -err;
SYSERROR("Failed to setup ipv6 address for network device "
"with eifindex %d", ifindex);
"with ifindex %d", ifindex);
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);
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) {
errno = -err;
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);
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) {
errno = -err;
SYSERROR("Failed to add ipv6 dest for network device \"%s\"",

View File

@ -95,6 +95,8 @@ struct ifla_veth {
char pair[IFNAMSIZ];
char veth1[IFNAMSIZ];
int ifindex;
struct lxc_list ipv4_routes;
struct lxc_list ipv6_routes;
};
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);
/* Set a destination route to an interface. */
extern int lxc_ipv4_dest_add(int ifindex, struct in_addr *dest);
extern int lxc_ipv6_dest_add(int ifindex, struct in6_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, unsigned int netmask);
/* Set default route. */
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;
}
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",
"52:54:00:80:7a:5d")) {
lxc_error("%s\n", "lxc.net.1.hwaddr");
@ -695,6 +705,16 @@ int main(int argc, char *argv[])
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)) {
lxc_error("%s\n", "lxc.net.0.script.up");
goto non_test_error;