]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: tunnel: support to set an address assigned on underlying interface as local...
authorYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 7 Dec 2021 14:36:36 +0000 (23:36 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 12 Dec 2021 04:42:45 +0000 (13:42 +0900)
Closes #18732.

man/systemd.netdev.xml
src/network/netdev/netdev-gperf.gperf
src/network/netdev/tunnel.c
src/network/netdev/tunnel.h

index 0aec58fc81a1db69c2bc48d9061102bafbb4062a..c26b60fb2465e8a29df148b87415846843b2b241 100644 (file)
       <varlistentry>
         <term><varname>Local=</varname></term>
         <listitem>
-          <para>A static local address for tunneled packets. It must be an address on another interface of
-          this host, or the special value <literal>any</literal>.</para>
+          <para>A static local address for tunneled packets. It must be an address on another interface
+          of this host, or one of the special values <literal>any</literal>,
+          <literal>ipv4_link_local</literal>, <literal>ipv6_link_local</literal>,
+          <literal>dhcp4</literal>, <literal>dhcp6</literal>, and <literal>slaac</literal>. If one
+          of the special values except for <literal>any</literal> is specified, an address which
+          matches the corresponding type on the underlying interface will be used. Defaults to
+          <literal>any</literal>.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
index a948ec2c8a0eed435e32162e623f1a36bc8aab68..9cb5db936635edbf0287e590ab5697b56dee9c98 100644 (file)
@@ -68,8 +68,8 @@ IPVLAN.Mode,                              config_parse_ipvlan_mode,
 IPVLAN.Flags,                             config_parse_ipvlan_flags,                 0,                             offsetof(IPVlan, flags)
 IPVTAP.Mode,                              config_parse_ipvlan_mode,                  0,                             offsetof(IPVlan, mode)
 IPVTAP.Flags,                             config_parse_ipvlan_flags,                 0,                             offsetof(IPVlan, flags)
-Tunnel.Local,                             config_parse_tunnel_address,               0,                             offsetof(Tunnel, local)
-Tunnel.Remote,                            config_parse_tunnel_address,               0,                             offsetof(Tunnel, remote)
+Tunnel.Local,                             config_parse_tunnel_local_address,         0,                             0
+Tunnel.Remote,                            config_parse_tunnel_remote_address,        0,                             0
 Tunnel.TOS,                               config_parse_unsigned,                     0,                             offsetof(Tunnel, tos)
 Tunnel.TTL,                               config_parse_unsigned,                     0,                             offsetof(Tunnel, ttl)
 Tunnel.Key,                               config_parse_tunnel_key,                   0,                             offsetof(Tunnel, key)
index 59da4d4376c80d4eb8f1dca887d904e4de7f1887..b4fdd9142a9e055725a9b533a2fa28dc2572da82 100644 (file)
@@ -172,7 +172,20 @@ int dhcp4_pd_create_6rd_tunnel(Link *link, link_netlink_message_handler_t callba
         return 0;
 }
 
+static int tunnel_get_local_address(Tunnel *t, Link *link, union in_addr_union *ret) {
+        assert(t);
+
+        if (t->local_type < 0) {
+                if (ret)
+                        *ret = t->local;
+                return 0;
+        }
+
+        return link_get_local_address(link, t->local_type, t->family, NULL, ret);
+}
+
 static int netdev_ipip_sit_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
+        union in_addr_union local;
         Tunnel *t;
         int r;
 
@@ -192,7 +205,11 @@ static int netdev_ipip_sit_fill_message_create(NetDev *netdev, Link *link, sd_ne
                         return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LINK attribute: %m");
         }
 
-        r = sd_netlink_message_append_in_addr(m, IFLA_IPTUN_LOCAL, &t->local.in);
+        r = tunnel_get_local_address(t, link, &local);
+        if (r < 0)
+                return log_netdev_error_errno(netdev, r, "Could not find local address: %m");
+
+        r = sd_netlink_message_append_in_addr(m, IFLA_IPTUN_LOCAL, &local.in);
         if (r < 0)
                 return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LOCAL attribute: %m");
 
@@ -251,6 +268,7 @@ static int netdev_ipip_sit_fill_message_create(NetDev *netdev, Link *link, sd_ne
 }
 
 static int netdev_gre_erspan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
+        union in_addr_union local;
         uint32_t ikey = 0;
         uint32_t okey = 0;
         uint16_t iflags = 0;
@@ -289,7 +307,11 @@ static int netdev_gre_erspan_fill_message_create(NetDev *netdev, Link *link, sd_
                         return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_ERSPAN_INDEX attribute: %m");
         }
 
-        r = sd_netlink_message_append_in_addr(m, IFLA_GRE_LOCAL, &t->local.in);
+        r = tunnel_get_local_address(t, link, &local);
+        if (r < 0)
+                return log_netdev_error_errno(netdev, r, "Could not find local address: %m");
+
+        r = sd_netlink_message_append_in_addr(m, IFLA_GRE_LOCAL, &local.in);
         if (r < 0)
                 return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_LOCAL attribute: %m");
 
@@ -367,6 +389,7 @@ static int netdev_gre_erspan_fill_message_create(NetDev *netdev, Link *link, sd_
 }
 
 static int netdev_ip6gre_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
+        union in_addr_union local;
         uint32_t ikey = 0;
         uint32_t okey = 0;
         uint16_t iflags = 0;
@@ -390,7 +413,11 @@ static int netdev_ip6gre_fill_message_create(NetDev *netdev, Link *link, sd_netl
                         return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_LINK attribute: %m");
         }
 
-        r = sd_netlink_message_append_in6_addr(m, IFLA_GRE_LOCAL, &t->local.in6);
+        r = tunnel_get_local_address(t, link, &local);
+        if (r < 0)
+                return log_netdev_error_errno(netdev, r, "Could not find local address: %m");
+
+        r = sd_netlink_message_append_in6_addr(m, IFLA_GRE_LOCAL, &local.in6);
         if (r < 0)
                 return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_LOCAL attribute: %m");
 
@@ -448,6 +475,7 @@ static int netdev_ip6gre_fill_message_create(NetDev *netdev, Link *link, sd_netl
 }
 
 static int netdev_vti_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
+        union in_addr_union local;
         uint32_t ikey, okey;
         Tunnel *t;
         int r;
@@ -483,7 +511,11 @@ static int netdev_vti_fill_message_create(NetDev *netdev, Link *link, sd_netlink
         if (r < 0)
                 return log_netdev_error_errno(netdev, r, "Could not append IFLA_VTI_OKEY attribute: %m");
 
-        r = netlink_message_append_in_addr_union(m, IFLA_VTI_LOCAL, t->family, &t->local);
+        r = tunnel_get_local_address(t, link, &local);
+        if (r < 0)
+                return log_netdev_error_errno(netdev, r, "Could not find local address: %m");
+
+        r = netlink_message_append_in_addr_union(m, IFLA_VTI_LOCAL, t->family, &local);
         if (r < 0)
                 return log_netdev_error_errno(netdev, r, "Could not append IFLA_VTI_LOCAL attribute: %m");
 
@@ -495,6 +527,7 @@ static int netdev_vti_fill_message_create(NetDev *netdev, Link *link, sd_netlink
 }
 
 static int netdev_ip6tnl_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
+        union in_addr_union local;
         uint8_t proto;
         Tunnel *t;
         int r;
@@ -512,7 +545,11 @@ static int netdev_ip6tnl_fill_message_create(NetDev *netdev, Link *link, sd_netl
                         return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LINK attribute: %m");
         }
 
-        r = sd_netlink_message_append_in6_addr(m, IFLA_IPTUN_LOCAL, &t->local.in6);
+        r = tunnel_get_local_address(t, link, &local);
+        if (r < 0)
+                return log_netdev_error_errno(netdev, r, "Could not find local address: %m");
+
+        r = sd_netlink_message_append_in6_addr(m, IFLA_IPTUN_LOCAL, &local.in6);
         if (r < 0)
                 return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LOCAL attribute: %m");
 
@@ -566,6 +603,19 @@ static int netdev_ip6tnl_fill_message_create(NetDev *netdev, Link *link, sd_netl
         return r;
 }
 
+static int netdev_tunnel_is_ready_to_create(NetDev *netdev, Link *link) {
+        Tunnel *t;
+
+        assert(netdev);
+        assert(link);
+
+        t = TUNNEL(netdev);
+
+        assert(t);
+
+        return tunnel_get_local_address(t, link, NULL) >= 0;
+}
+
 static int netdev_tunnel_verify(NetDev *netdev, const char *filename) {
         Tunnel *t;
 
@@ -615,10 +665,15 @@ static int netdev_tunnel_verify(NetDev *netdev, const char *filename) {
         if (t->assign_to_loopback)
                 t->independent = true;
 
+        if (t->independent && t->local_type >= 0)
+                return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
+                                              "The local address cannot be '%s' when Independent= or AssignToLoopback= is enabled, ignoring.",
+                                              strna(netdev_local_address_type_to_string(t->local_type)));
+
         return 0;
 }
 
-int config_parse_tunnel_address(
+int config_parse_tunnel_local_address(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -630,28 +685,82 @@ int config_parse_tunnel_address(
                 void *data,
                 void *userdata) {
 
+        union in_addr_union buffer = IN_ADDR_NULL;
+        NetDevLocalAddressType type;
         Tunnel *t = userdata;
-        union in_addr_union *addr = data, buffer;
         int r, f;
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
-        assert(data);
+        assert(userdata);
+
+        if (isempty(rvalue) || streq(rvalue, "any")) {
+                /* Unset the previous assignment. */
+                t->local = IN_ADDR_NULL;
+                t->local_type = _NETDEV_LOCAL_ADDRESS_TYPE_INVALID;
+
+                /* If the remote address is not specified, also clear the address family. */
+                if (!in_addr_is_set(t->family, &t->remote))
+                        t->family = AF_UNSPEC;
+                return 0;
+        }
+
+        type = netdev_local_address_type_from_string(rvalue);
+        if (IN_SET(type, NETDEV_LOCAL_ADDRESS_IPV4LL, NETDEV_LOCAL_ADDRESS_DHCP4))
+                f = AF_INET;
+        else if (IN_SET(type, NETDEV_LOCAL_ADDRESS_IPV6LL, NETDEV_LOCAL_ADDRESS_DHCP6, NETDEV_LOCAL_ADDRESS_SLAAC))
+                f = AF_INET6;
+        else {
+                type = _NETDEV_LOCAL_ADDRESS_TYPE_INVALID;
+                r = in_addr_from_string_auto(rvalue, &f, &buffer);
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Tunnel address \"%s\" invalid, ignoring assignment: %m", rvalue);
+                        return 0;
+                }
+        }
+
+        if (t->family != AF_UNSPEC && t->family != f) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "Address family does not match the previous assignment, ignoring assignment: %s", rvalue);
+                return 0;
+        }
 
-        /* This is used to parse addresses on both local and remote ends of the tunnel.
-         * Address families must match.
-         *
-         * "any" is a special value which means that the address is unspecified.
-         */
+        t->family = f;
+        t->local = buffer;
+        t->local_type = type;
+        return 0;
+}
+
+int config_parse_tunnel_remote_address(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        union in_addr_union buffer;
+        Tunnel *t = userdata;
+        int r, f;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(userdata);
 
-        if (streq(rvalue, "any")) {
-                *addr = IN_ADDR_NULL;
+        if (isempty(rvalue) || streq(rvalue, "any")) {
+                /* Unset the previous assignment. */
+                t->remote = IN_ADDR_NULL;
 
-                /* As a special case, if both the local and remote addresses are
-                 * unspecified, also clear the address family. */
-                if (!in_addr_is_set(t->family, &t->local) &&
-                    !in_addr_is_set(t->family, &t->remote))
+                /* If the local address is not specified, also clear the address family. */
+                if (t->local_type == _NETDEV_LOCAL_ADDRESS_TYPE_INVALID &&
+                    !in_addr_is_set(t->family, &t->local))
                         t->family = AF_UNSPEC;
                 return 0;
         }
@@ -665,12 +774,12 @@ int config_parse_tunnel_address(
 
         if (t->family != AF_UNSPEC && t->family != f) {
                 log_syntax(unit, LOG_WARNING, filename, line, 0,
-                           "Tunnel addresses incompatible, ignoring assignment: %s", rvalue);
+                           "Address family does not match the previous assignment, ignoring assignment: %s", rvalue);
                 return 0;
         }
 
         t->family = f;
-        *addr = buffer;
+        t->remote = buffer;
         return 0;
 }
 
@@ -844,6 +953,7 @@ static void netdev_tunnel_init(NetDev *netdev) {
 
         assert(t);
 
+        t->local_type = _NETDEV_LOCAL_ADDRESS_TYPE_INVALID;
         t->pmtudisc = true;
         t->fou_encap_type = NETDEV_FOO_OVER_UDP_ENCAP_DIRECT;
         t->isatap = -1;
@@ -863,6 +973,7 @@ const NetDevVTable ipip_vtable = {
         .sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
         .fill_message_create = netdev_ipip_sit_fill_message_create,
         .create_type = NETDEV_CREATE_STACKED,
+        .is_ready_to_create = netdev_tunnel_is_ready_to_create,
         .config_verify = netdev_tunnel_verify,
         .iftype = ARPHRD_TUNNEL,
 };
@@ -873,6 +984,7 @@ const NetDevVTable sit_vtable = {
         .sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
         .fill_message_create = netdev_ipip_sit_fill_message_create,
         .create_type = NETDEV_CREATE_STACKED,
+        .is_ready_to_create = netdev_tunnel_is_ready_to_create,
         .config_verify = netdev_tunnel_verify,
         .iftype = ARPHRD_SIT,
 };
@@ -883,6 +995,7 @@ const NetDevVTable vti_vtable = {
         .sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
         .fill_message_create = netdev_vti_fill_message_create,
         .create_type = NETDEV_CREATE_STACKED,
+        .is_ready_to_create = netdev_tunnel_is_ready_to_create,
         .config_verify = netdev_tunnel_verify,
         .iftype = ARPHRD_TUNNEL,
 };
@@ -893,6 +1006,7 @@ const NetDevVTable vti6_vtable = {
         .sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
         .fill_message_create = netdev_vti_fill_message_create,
         .create_type = NETDEV_CREATE_STACKED,
+        .is_ready_to_create = netdev_tunnel_is_ready_to_create,
         .config_verify = netdev_tunnel_verify,
         .iftype = ARPHRD_TUNNEL6,
 };
@@ -903,6 +1017,7 @@ const NetDevVTable gre_vtable = {
         .sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
         .fill_message_create = netdev_gre_erspan_fill_message_create,
         .create_type = NETDEV_CREATE_STACKED,
+        .is_ready_to_create = netdev_tunnel_is_ready_to_create,
         .config_verify = netdev_tunnel_verify,
         .iftype = ARPHRD_IPGRE,
 };
@@ -913,6 +1028,7 @@ const NetDevVTable gretap_vtable = {
         .sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
         .fill_message_create = netdev_gre_erspan_fill_message_create,
         .create_type = NETDEV_CREATE_STACKED,
+        .is_ready_to_create = netdev_tunnel_is_ready_to_create,
         .config_verify = netdev_tunnel_verify,
         .iftype = ARPHRD_ETHER,
         .generate_mac = true,
@@ -924,6 +1040,7 @@ const NetDevVTable ip6gre_vtable = {
         .sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
         .fill_message_create = netdev_ip6gre_fill_message_create,
         .create_type = NETDEV_CREATE_STACKED,
+        .is_ready_to_create = netdev_tunnel_is_ready_to_create,
         .config_verify = netdev_tunnel_verify,
         .iftype = ARPHRD_IP6GRE,
 };
@@ -934,6 +1051,7 @@ const NetDevVTable ip6gretap_vtable = {
         .sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
         .fill_message_create = netdev_ip6gre_fill_message_create,
         .create_type = NETDEV_CREATE_STACKED,
+        .is_ready_to_create = netdev_tunnel_is_ready_to_create,
         .config_verify = netdev_tunnel_verify,
         .iftype = ARPHRD_ETHER,
         .generate_mac = true,
@@ -945,6 +1063,7 @@ const NetDevVTable ip6tnl_vtable = {
         .sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
         .fill_message_create = netdev_ip6tnl_fill_message_create,
         .create_type = NETDEV_CREATE_STACKED,
+        .is_ready_to_create = netdev_tunnel_is_ready_to_create,
         .config_verify = netdev_tunnel_verify,
         .iftype = ARPHRD_TUNNEL6,
 };
@@ -955,6 +1074,7 @@ const NetDevVTable erspan_vtable = {
         .sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
         .fill_message_create = netdev_gre_erspan_fill_message_create,
         .create_type = NETDEV_CREATE_STACKED,
+        .is_ready_to_create = netdev_tunnel_is_ready_to_create,
         .config_verify = netdev_tunnel_verify,
         .iftype = ARPHRD_ETHER,
         .generate_mac = true,
index b9e68759fb4ef64fe3c68133cc5f9c63c3d78e5d..4d42ff9a154ccffcee8a6f702548f40b0c64c4d6 100644 (file)
@@ -5,6 +5,7 @@
 
 #include "conf-parser.h"
 #include "fou-tunnel.h"
+#include "netdev-util.h"
 #include "netdev.h"
 #include "networkd-link.h"
 
@@ -42,6 +43,7 @@ typedef struct Tunnel {
         uint32_t okey;
         uint32_t erspan_index;
 
+        NetDevLocalAddressType local_type;
         union in_addr_union local;
         union in_addr_union remote;
 
@@ -119,7 +121,8 @@ const char *ip6tnl_mode_to_string(Ip6TnlMode d) _const_;
 Ip6TnlMode ip6tnl_mode_from_string(const char *d) _pure_;
 
 CONFIG_PARSER_PROTOTYPE(config_parse_ip6tnl_mode);
-CONFIG_PARSER_PROTOTYPE(config_parse_tunnel_address);
+CONFIG_PARSER_PROTOTYPE(config_parse_tunnel_local_address);
+CONFIG_PARSER_PROTOTYPE(config_parse_tunnel_remote_address);
 CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_flowlabel);
 CONFIG_PARSER_PROTOTYPE(config_parse_encap_limit);
 CONFIG_PARSER_PROTOTYPE(config_parse_tunnel_key);