diff --git a/doc/api-extensions.md b/doc/api-extensions.md index b55acf0fb..a013935f7 100644 --- a/doc/api-extensions.md +++ b/doc/api-extensions.md @@ -84,3 +84,10 @@ For IPv6 addresses it will check the following sysctl values and fail with an er net.ipv6.conf.[link].proxy_ndp=1 net.ipv6.conf.[link].forwarding=1 ``` + +## network\_gateway\_device\_route + +This introduces the ability to specify `lxc.net.[i].ipv4.gateway` and/or +`lxc.net.[i].ipv6.gateway` with a value of `dev` which will cause the default gateway +inside the container to be created as a device route without destination gateway IP needed. +This is primarily intended for use with layer 3 networking devices, such as IPVLAN. diff --git a/doc/lxc.container.conf.sgml.in b/doc/lxc.container.conf.sgml.in index f8182567a..0af0456a5 100644 --- a/doc/lxc.container.conf.sgml.in +++ b/doc/lxc.container.conf.sgml.in @@ -665,6 +665,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA the gateway. is only available when using the , and network types. + Can also have the special value of , + which means to set the default gateway as a device route. + This is primarily for use with layer 3 network modes, such as IPVLAN. @@ -699,6 +702,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA the gateway. is only available when using the , and network types. + Can also have the special value of , + which means to set the default gateway as a device route. + This is primarily for use with layer 3 network modes, such as IPVLAN. diff --git a/src/lxc/api_extensions.h b/src/lxc/api_extensions.h index 1c748a1d4..44293db97 100644 --- a/src/lxc/api_extensions.h +++ b/src/lxc/api_extensions.h @@ -47,6 +47,7 @@ static char *api_extensions[] = { "network_veth_routes", "network_ipvlan", "network_l2proxy", + "network_gateway_device_route", }; static size_t nr_api_extensions = sizeof(api_extensions) / sizeof(*api_extensions); diff --git a/src/lxc/confile.c b/src/lxc/confile.c index 78cb88e4f..bb1edc7cb 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -686,9 +686,13 @@ static int set_config_net_ipv4_gateway(const char *key, const char *value, 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; @@ -853,9 +857,13 @@ static int set_config_net_ipv6_gateway(const char *key, const char *value, 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; @@ -5625,6 +5633,8 @@ static int get_config_net_ipv4_gateway(const char *key, char *retv, int inlen, 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); @@ -5714,6 +5724,8 @@ static int get_config_net_ipv6_gateway(const char *key, char *retv, int inlen, 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); diff --git a/src/lxc/confile_utils.c b/src/lxc/confile_utils.c index 07b023e4e..439445885 100644 --- a/src/lxc/confile_utils.c +++ b/src/lxc/confile_utils.c @@ -361,6 +361,9 @@ void lxc_log_configured_netdevs(const struct lxc_conf *conf) TRACE("ipv4 gateway auto: %s", netdev->ipv4_gateway_auto ? "true" : "false"); + TRACE("ipv4 gateway dev: %s", + netdev->ipv4_gateway_dev ? "true" : "false"); + if (netdev->ipv4_gateway) { inet_ntop(AF_INET, netdev->ipv4_gateway, bufinet4, sizeof(bufinet4)); @@ -377,6 +380,9 @@ void lxc_log_configured_netdevs(const struct lxc_conf *conf) TRACE("ipv6 gateway auto: %s", netdev->ipv6_gateway_auto ? "true" : "false"); + TRACE("ipv6 gateway dev: %s", + netdev->ipv6_gateway_dev ? "true" : "false"); + if (netdev->ipv6_gateway) { inet_ntop(AF_INET6, netdev->ipv6_gateway, bufinet6, sizeof(bufinet6)); diff --git a/src/lxc/network.c b/src/lxc/network.c index a71eb5ddf..2553c0c72 100644 --- a/src/lxc/network.c +++ b/src/lxc/network.c @@ -2063,8 +2063,12 @@ static int ip_gateway_add(int family, int ifindex, void *gw) rt->rtm_dst_len = 0; err = -EINVAL; - if (nla_put_buffer(nlmsg, RTA_GATEWAY, gw, addrlen)) - goto out; + + /* If gateway address not supplied, then a device route will be created instead */ + if (gw != NULL) { + if (nla_put_buffer(nlmsg, RTA_GATEWAY, gw, addrlen)) + goto out; + } /* Adding the interface index enables the use of link-local * addresses for the gateway. @@ -3381,12 +3385,12 @@ static int lxc_setup_netdev_in_child_namespaces(struct lxc_netdev *netdev) } /* We can only set up the default routes after bringing - * up the interface, sine bringing up the interface adds + * up the interface, since 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->ipv4_gateway || netdev->ipv4_gateway_dev) { if (!(netdev->flags & IFF_UP)) { ERROR("Cannot add ipv4 gateway for network device " "\"%s\" when not bringing up the interface", ifname); @@ -3399,33 +3403,43 @@ static int lxc_setup_netdev_in_child_namespaces(struct lxc_netdev *netdev) return -1; } - err = lxc_ipv4_gateway_add(netdev->ifindex, netdev->ipv4_gateway); - if (err) { - 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\"", + /* Setup device route if ipv4_gateway_dev is enabled */ + if (netdev->ipv4_gateway_dev) { + err = lxc_ipv4_gateway_add(netdev->ifindex, NULL); + if (err < 0) { + SYSERROR("Failed to setup ipv4 gateway to network device \"%s\"", ifname); + return minus_one_set_errno(-err); } - + } else { err = lxc_ipv4_gateway_add(netdev->ifindex, netdev->ipv4_gateway); if (err) { - errno = -err; - SYSERROR("Failed to setup ipv4 gateway for network device \"%s\"", - ifname); - - 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); + 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\"", + ifname); + } + + err = lxc_ipv4_gateway_add(netdev->ifindex, netdev->ipv4_gateway); + if (err) { + errno = -err; + SYSERROR("Failed to setup ipv4 gateway for network device \"%s\"", + ifname); + + 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; } - return -1; } } } /* setup ipv6 gateway on the interface */ - if (netdev->ipv6_gateway) { + if (netdev->ipv6_gateway || netdev->ipv6_gateway_dev) { if (!(netdev->flags & IFF_UP)) { ERROR("Cannot add ipv6 gateway for network device " "\"%s\" when not bringing up the interface", ifname); @@ -3438,29 +3452,39 @@ static int lxc_setup_netdev_in_child_namespaces(struct lxc_netdev *netdev) return -1; } - err = lxc_ipv6_gateway_add(netdev->ifindex, netdev->ipv6_gateway); - if (err) { - 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\"", + /* Setup device route if ipv6_gateway_dev is enabled */ + if (netdev->ipv6_gateway_dev) { + err = lxc_ipv6_gateway_add(netdev->ifindex, NULL); + if (err < 0) { + SYSERROR("Failed to setup ipv6 gateway to network device \"%s\"", ifname); + return minus_one_set_errno(-err); } - + } else { err = lxc_ipv6_gateway_add(netdev->ifindex, netdev->ipv6_gateway); if (err) { - errno = -err; - SYSERROR("Failed to setup ipv6 gateway for network device \"%s\"", - ifname); - - 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); + 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\"", + ifname); + } + + err = lxc_ipv6_gateway_add(netdev->ifindex, netdev->ipv6_gateway); + if (err) { + errno = -err; + SYSERROR("Failed to setup ipv6 gateway for network device \"%s\"", + ifname); + + 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; } - return -1; } } } diff --git a/src/lxc/network.h b/src/lxc/network.h index 468593f5e..d221b255f 100644 --- a/src/lxc/network.h +++ b/src/lxc/network.h @@ -156,9 +156,11 @@ union netdev_p { * @ipv6 : a list of ipv6 addresses to be set on the network device * @ipv4_gateway_auto : whether the ipv4 gateway is to be automatically gathered * from the associated @link + * @ipv4_gateway_dev : whether the ipv4 gateway is to be set as a device route * @ipv4_gateway : ipv4 gateway * @ipv6_gateway_auto : whether the ipv6 gateway is to be automatically gathered * from the associated @link + * @ipv6_gateway_dev : whether the ipv6 gateway is to be set as a device route * @ipv6_gateway : ipv6 gateway * @upscript : a script filename to be executed during interface * configuration @@ -179,8 +181,10 @@ struct lxc_netdev { struct lxc_list ipv4; struct lxc_list ipv6; bool ipv4_gateway_auto; + bool ipv4_gateway_dev; struct in_addr *ipv4_gateway; bool ipv6_gateway_auto; + bool ipv6_gateway_dev; struct in6_addr *ipv6_gateway; char *upscript; char *downscript; diff --git a/src/tests/parse_config_file.c b/src/tests/parse_config_file.c index ad17867b4..bc68ae24c 100644 --- a/src/tests/parse_config_file.c +++ b/src/tests/parse_config_file.c @@ -108,6 +108,16 @@ static int set_and_clear_complete_netdev(struct lxc_container *c) return -1; } + if (!c->set_config_item(c, "lxc.net.1.ipv4.gateway", "auto")) { + lxc_error("%s\n", "lxc.net.1.ipv4.gateway"); + return -1; + } + + if (!c->set_config_item(c, "lxc.net.1.ipv4.gateway", "dev")) { + lxc_error("%s\n", "lxc.net.1.ipv4.gateway"); + return -1; + } + if (!c->set_config_item(c, "lxc.net.1.ipv6.address", "2003:db8:1:0:214:1234:fe0b:3596/64")) { lxc_error("%s\n", "lxc.net.1.ipv6.address"); @@ -120,6 +130,16 @@ static int set_and_clear_complete_netdev(struct lxc_container *c) return -1; } + if (!c->set_config_item(c, "lxc.net.1.ipv6.gateway", "auto")) { + lxc_error("%s\n", "lxc.net.1.ipv6.gateway"); + return -1; + } + + if (!c->set_config_item(c, "lxc.net.1.ipv6.gateway", "dev")) { + lxc_error("%s\n", "lxc.net.1.ipv6.gateway"); + return -1; + } + if (!c->set_config_item(c, "lxc.net.1.flags", "up")) { lxc_error("%s\n", "lxc.net.1.flags"); return -1; @@ -781,11 +801,31 @@ int main(int argc, char *argv[]) goto non_test_error; } + if (set_get_compare_clear_save_load(c, "lxc.net.0.ipv4.gateway", "auto", tmpf, true)) { + lxc_error("%s\n", "lxc.net.0.ipv4.gateway"); + goto non_test_error; + } + + if (set_get_compare_clear_save_load(c, "lxc.net.0.ipv4.gateway", "dev", tmpf, true)) { + lxc_error("%s\n", "lxc.net.0.ipv4.gateway"); + goto non_test_error; + } + if (set_get_compare_clear_save_load(c, "lxc.net.0.ipv6.gateway", "2003:db8:1::1", tmpf, true)) { lxc_error("%s\n", "lxc.net.0.ipv6.gateway"); goto non_test_error; } + if (set_get_compare_clear_save_load(c, "lxc.net.0.ipv6.gateway", "auto", tmpf, true)) { + lxc_error("%s\n", "lxc.net.0.ipv6.gateway"); + goto non_test_error; + } + + if (set_get_compare_clear_save_load(c, "lxc.net.0.ipv6.gateway", "dev", tmpf, true)) { + lxc_error("%s\n", "lxc.net.0.ipv6.gateway"); + goto non_test_error; + } + if (set_get_compare_clear_save_load(c, "lxc.net.0.ipv4.address", "10.0.2.3/24", tmpf, true)) { lxc_error("%s\n", "lxc.net.0.ipv4.address"); goto non_test_error;