]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: l2tp: make Local= optionally take interface name
authorYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 25 Feb 2022 08:12:18 +0000 (17:12 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 27 Feb 2022 00:36:48 +0000 (09:36 +0900)
man/systemd.netdev.xml
src/network/netdev/l2tp-tunnel.c
src/network/netdev/l2tp-tunnel.h
src/network/netdev/netdev-gperf.gperf
test/test-network/conf/25-l2tp-ip.netdev
test/test-network/conf/25-l2tp-udp.netdev

index 1f94e3d599f4128f4e6dd8f311f6aa9c43636815..e23146f3ca8f24a78f22df637d78600e1024a5e3 100644 (file)
       <varlistentry>
         <term><varname>Local=</varname></term>
         <listitem>
-          <para>Specifies the IP address of the local interface. Takes an IP address, or the special values
-          <literal>auto</literal>, <literal>static</literal>, or <literal>dynamic</literal>. When an address
-          is set, then the local interface must have the address. If <literal>auto</literal>, then one of the
-          addresses on the local interface is used. Similarly, if <literal>static</literal> or
-          <literal>dynamic</literal> is set, then one of the static or dynamic addresses on the local
-          interface is used. Defaults to <literal>auto</literal>.</para>
+          <para>Specifies the IP address of a local interface. Takes an IP address, or the special
+          values <literal>auto</literal>, <literal>static</literal>, or <literal>dynamic</literal>.
+          Optionally a name of a local interface can be specified after <literal>@</literal>, e.g.
+          <literal>192.168.0.1@eth0</literal> or <literal>auto@eth0</literal>. When an address is
+          specified, then a local or specified interface must have the address, and the remote address
+          must be accessible through the local address. If <literal>auto</literal>, then one of the
+          addresses on a local or specified interface which is accessible to the remote address will be
+          used. Similarly, if <literal>static</literal> or <literal>dynamic</literal> is set, then one
+          of the static or dynamic addresses will be used. Defaults to <literal>auto</literal>.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
index 50f52f5d9b76c98b300d13910c0ee51469710abe..e94ca204079afaf919ddc27ba6b12eca126b6655 100644 (file)
@@ -10,6 +10,7 @@
 #include "netlink-util.h"
 #include "networkd-address.h"
 #include "networkd-manager.h"
+#include "networkd-route-util.h"
 #include "parse-util.h"
 #include "socket-util.h"
 #include "string-table.h"
@@ -245,44 +246,114 @@ static int netdev_l2tp_create_message_session(NetDev *netdev, L2tpSession *sessi
         return 0;
 }
 
-static int l2tp_acquire_local_address_one(L2tpTunnel *t, Address *a, union in_addr_union *ret) {
-        if (a->family != t->family)
-                return -EINVAL;
+static int link_get_l2tp_local_address(Link *link, L2tpTunnel *t, union in_addr_union *ret) {
+        Address *a;
 
-        if (in_addr_is_set(a->family, &a->in_addr_peer))
-                return -EINVAL;
+        assert(link);
+        assert(t);
 
-        if (t->local_address_type == NETDEV_L2TP_LOCAL_ADDRESS_STATIC &&
-            !FLAGS_SET(a->flags, IFA_F_PERMANENT))
-                return -EINVAL;
+        SET_FOREACH(a, link->addresses) {
+                if (!address_is_ready(a))
+                        continue;
 
-        if (t->local_address_type == NETDEV_L2TP_LOCAL_ADDRESS_DYNAMIC &&
-            FLAGS_SET(a->flags, IFA_F_PERMANENT))
-                return -EINVAL;
+                if (a->family != t->family)
+                        continue;
 
-        *ret = a->in_addr;
-        return 0;
+                if (in_addr_is_set(a->family, &a->in_addr_peer))
+                        continue;
+
+                if (t->local_address_type == NETDEV_L2TP_LOCAL_ADDRESS_STATIC &&
+                    !FLAGS_SET(a->flags, IFA_F_PERMANENT))
+                        continue;
+
+                if (t->local_address_type == NETDEV_L2TP_LOCAL_ADDRESS_DYNAMIC &&
+                    FLAGS_SET(a->flags, IFA_F_PERMANENT))
+                        continue;
+
+                if (ret)
+                        *ret = a->in_addr;
+        }
+
+        return -ENOENT;
 }
 
-static int l2tp_acquire_local_address(L2tpTunnel *t, Link *link, union in_addr_union *ret) {
+static int l2tp_get_local_address(NetDev *netdev, union in_addr_union *ret) {
+        Link *link = NULL;
+        L2tpTunnel *t;
         Address *a;
+        int r;
 
-        assert(t);
-        assert(link);
-        assert(ret);
-        assert(IN_SET(t->family, AF_INET, AF_INET6));
+        assert(netdev);
+        assert(netdev->manager);
+        assert_se(t = L2TP(netdev));
+
+        if (t->local_ifname) {
+                r = link_get_by_name(netdev->manager, t->local_ifname, &link);
+                if (r < 0)
+                        return r;
+
+                if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+                        return -EBUSY;
+        }
+
+        if (netdev->manager->manage_foreign_routes) {
+                /* First, check if the remote address is accessible. */
+                if (link)
+                        r = link_address_is_reachable(link, t->family, &t->remote, &t->local, &a);
+                else
+                        r = manager_address_is_reachable(netdev->manager, t->family, &t->remote, &t->local, &a);
+                if (r < 0)
+                        return r;
+        }
 
         if (in_addr_is_set(t->family, &t->local)) {
                 /* local address is explicitly specified. */
-                *ret = t->local;
+
+                if (!a) {
+                        if (link)
+                                r = link_get_address(link, t->family, &t->local, 0, &a);
+                        else
+                                r = manager_get_address(netdev->manager, t->family, &t->local, 0, &a);
+                        if (r < 0)
+                                return r;
+
+                        if (!address_is_ready(a))
+                                return -EBUSY;
+                }
+
+                if (ret)
+                        *ret = a->in_addr;
+
                 return 0;
         }
 
-        SET_FOREACH(a, link->addresses)
-                if (l2tp_acquire_local_address_one(t, a, ret) >= 0)
-                        return 1;
+        if (a) {
+                if (t->local_address_type == NETDEV_L2TP_LOCAL_ADDRESS_STATIC &&
+                    !FLAGS_SET(a->flags, IFA_F_PERMANENT))
+                        return -EINVAL;
+
+                if (t->local_address_type == NETDEV_L2TP_LOCAL_ADDRESS_DYNAMIC &&
+                    FLAGS_SET(a->flags, IFA_F_PERMANENT))
+                        return -EINVAL;
 
-        return -ENODATA;
+                if (ret)
+                        *ret = a->in_addr;
+
+                return 0;
+        }
+
+        if (link)
+                return link_get_l2tp_local_address(link, t, ret);
+
+        HASHMAP_FOREACH(link, netdev->manager->links_by_index) {
+                if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+                        continue;
+
+                if (link_get_l2tp_local_address(link, t, ret) >= 0)
+                        return 0;
+        }
+
+        return -ENOENT;
 }
 
 static void l2tp_session_destroy_callback(L2tpSession *session) {
@@ -370,11 +441,11 @@ static int l2tp_create_tunnel(NetDev *netdev, Link *link) {
         assert(netdev);
         assert_se(t = L2TP(netdev));
 
-        r = l2tp_acquire_local_address(t, link, &local_address);
+        r = l2tp_get_local_address(netdev, &local_address);
         if (r < 0)
                 return log_netdev_error_errno(netdev, r, "Could not find local address.");
 
-        if (r > 0 && DEBUG_LOGGING) {
+        if (t->local_address_type >= 0 && DEBUG_LOGGING) {
                 _cleanup_free_ char *str = NULL;
 
                 (void) in_addr_to_string(t->family, &local_address, &str);
@@ -395,7 +466,11 @@ static int l2tp_create_tunnel(NetDev *netdev, Link *link) {
         return 0;
 }
 
-int config_parse_l2tp_tunnel_address(
+static int netdev_l2tp_is_ready_to_create(NetDev *netdev, Link *link) {
+        return l2tp_get_local_address(netdev, NULL) >= 0;
+}
+
+int config_parse_l2tp_tunnel_local_address(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -407,42 +482,115 @@ int config_parse_l2tp_tunnel_address(
                 void *data,
                 void *userdata) {
 
+        _cleanup_free_ char *addr_or_type = NULL, *ifname = NULL;
+        L2tpLocalAddressType type;
         L2tpTunnel *t = userdata;
-        union in_addr_union *addr = data;
+        const char *p = rvalue;
         int r;
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
-        assert(data);
+        assert(t);
 
-        if (streq(lvalue, "Local")) {
-                L2tpLocalAddressType addr_type;
+        if (isempty(rvalue)) {
+                t->local_ifname = mfree(t->local_ifname);
+                t->local_address_type = NETDEV_L2TP_LOCAL_ADDRESS_AUTO;
+                t->local = IN_ADDR_NULL;
 
-                if (isempty(rvalue))
-                        addr_type = NETDEV_L2TP_LOCAL_ADDRESS_AUTO;
-                else
-                        addr_type = l2tp_local_address_type_from_string(rvalue);
+                if (!in_addr_is_set(t->family, &t->remote))
+                        /* If Remote= is not specified yet, then also clear family. */
+                        t->family = AF_UNSPEC;
 
-                if (addr_type >= 0) {
-                        if (!in_addr_is_set(t->family, &t->remote))
-                                /* If Remote= is not specified yet, then also clear family. */
-                                t->family = AF_UNSPEC;
+                return 0;
+        }
 
-                        t->local = IN_ADDR_NULL;
-                        t->local_address_type = addr_type;
+        r = extract_first_word(&p, &addr_or_type, "@", 0);
+        if (r < 0)
+                return log_oom();
+        if (r == 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "Invalid L2TP Tunnel address specified in %s=, ignoring assignment: %s", lvalue, rvalue);
+                return 0;
+        }
 
+        if (!isempty(p)) {
+                if (!ifname_valid_full(p, IFNAME_VALID_ALTERNATIVE)) {
+                        log_syntax(unit, LOG_WARNING, filename, line, 0,
+                                   "Invalid interface name specified in %s=, ignoring assignment: %s", lvalue, rvalue);
                         return 0;
                 }
+
+                ifname = strdup(p);
+                if (!ifname)
+                        return log_oom();
+        }
+
+        type = l2tp_local_address_type_from_string(rvalue);
+        if (type >= 0) {
+                free_and_replace(t->local_ifname, ifname);
+                t->local_address_type = type;
+                t->local = IN_ADDR_NULL;
+
+                if (!in_addr_is_set(t->family, &t->remote))
+                        /* If Remote= is not specified yet, then also clear family. */
+                        t->family = AF_UNSPEC;
+
+                return 0;
+        }
+
+        if (t->family == AF_UNSPEC)
+                r = in_addr_from_string_auto(rvalue, &t->family, &t->local);
+        else
+                r = in_addr_from_string(t->family, rvalue, &t->local);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Invalid L2TP Tunnel address specified in %s=, ignoring assignment: %s", lvalue, rvalue);
+                return 0;
+        }
+
+        free_and_replace(t->local_ifname, ifname);
+        t->local_address_type = _NETDEV_L2TP_LOCAL_ADDRESS_INVALID;
+        return 0;
+}
+
+int config_parse_l2tp_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) {
+
+        L2tpTunnel *t = userdata;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(t);
+
+        if (isempty(rvalue)) {
+                t->remote = IN_ADDR_NULL;
+
+                if (!in_addr_is_set(t->family, &t->local))
+                        /* If Local= is not specified yet, then also clear family. */
+                        t->family = AF_UNSPEC;
+
+                return 0;
         }
 
         if (t->family == AF_UNSPEC)
-                r = in_addr_from_string_auto(rvalue, &t->family, addr);
+                r = in_addr_from_string_auto(rvalue, &t->family, &t->remote);
         else
-                r = in_addr_from_string(t->family, rvalue, addr);
+                r = in_addr_from_string(t->family, rvalue, &t->remote);
         if (r < 0) {
                 log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Invalid L2TP Tunnel address specified in %s='%s', ignoring assignment: %m", lvalue, rvalue);
+                           "Invalid L2TP Tunnel address specified in %s=, ignoring assignment: %s", lvalue, rvalue);
                 return 0;
         }
 
@@ -699,6 +847,7 @@ static void l2tp_tunnel_done(NetDev *netdev) {
         assert(t);
 
         ordered_hashmap_free_with_destructor(t->sessions_by_section, l2tp_session_free);
+        free(t->local_ifname);
 }
 
 const NetDevVTable l2tptnl_vtable = {
@@ -708,5 +857,6 @@ const NetDevVTable l2tptnl_vtable = {
         .create_after_configured = l2tp_create_tunnel,
         .done = l2tp_tunnel_done,
         .create_type = NETDEV_CREATE_AFTER_CONFIGURED,
+        .is_ready_to_create = netdev_l2tp_is_ready_to_create,
         .config_verify = netdev_l2tp_tunnel_verify,
 };
index 236b78ce4b9678f75d79c8ace0b272480c6f6104..6028b35205774e19053414cf24b1ca4af4b9a21d 100644 (file)
@@ -58,6 +58,7 @@ struct L2tpTunnel {
         bool udp6_csum_rx;
         bool udp6_csum_tx;
 
+        char *local_ifname;
         L2tpLocalAddressType local_address_type;
         union in_addr_union local;
         union in_addr_union remote;
@@ -70,7 +71,8 @@ struct L2tpTunnel {
 DEFINE_NETDEV_CAST(L2TP, L2tpTunnel);
 extern const NetDevVTable l2tptnl_vtable;
 
-CONFIG_PARSER_PROTOTYPE(config_parse_l2tp_tunnel_address);
+CONFIG_PARSER_PROTOTYPE(config_parse_l2tp_tunnel_local_address);
+CONFIG_PARSER_PROTOTYPE(config_parse_l2tp_tunnel_remote_address);
 CONFIG_PARSER_PROTOTYPE(config_parse_l2tp_tunnel_id);
 CONFIG_PARSER_PROTOTYPE(config_parse_l2tp_encap_type);
 CONFIG_PARSER_PROTOTYPE(config_parse_l2tp_session_l2spec);
index 2fec1da06bdd5cae55fc81661fd88f9104e3409c..6dcc5a804f5710beea1ddee0c7c750cd7704341e 100644 (file)
@@ -103,8 +103,8 @@ L2TP.TunnelId,                            config_parse_l2tp_tunnel_id,
 L2TP.PeerTunnelId,                        config_parse_l2tp_tunnel_id,               0,                             offsetof(L2tpTunnel, peer_tunnel_id)
 L2TP.UDPSourcePort,                       config_parse_ip_port,                      0,                             offsetof(L2tpTunnel, l2tp_udp_sport)
 L2TP.UDPDestinationPort,                  config_parse_ip_port,                      0,                             offsetof(L2tpTunnel, l2tp_udp_dport)
-L2TP.Local,                               config_parse_l2tp_tunnel_address,          0,                             offsetof(L2tpTunnel, local)
-L2TP.Remote,                              config_parse_l2tp_tunnel_address,          0,                             offsetof(L2tpTunnel, remote)
+L2TP.Local,                               config_parse_l2tp_tunnel_local_address,    0,                             0
+L2TP.Remote,                              config_parse_l2tp_tunnel_remote_address,   0,                             0
 L2TP.EncapsulationType,                   config_parse_l2tp_encap_type,              0,                             offsetof(L2tpTunnel, l2tp_encap_type)
 L2TP.UDPCheckSum,                         config_parse_bool,                         0,                             offsetof(L2tpTunnel, udp_csum)
 L2TP.UDP6CheckSumRx,                      config_parse_bool,                         0,                             offsetof(L2tpTunnel, udp6_csum_rx)
index 1dd061894fd7ce7c187dc23112fece15cee5fc86..882c83ac04590ec00bf2e9f51ee236e740a279c2 100644 (file)
@@ -6,7 +6,7 @@ Name=l2tp99
 [L2TP]
 TunnelId=10
 PeerTunnelId=12
-Local=static
+Local=static@test1
 Remote=192.168.30.101
 EncapsulationType=ip
 
index 81d9ef51fcdeb7b909a3a46f93230ba55e49e826..79824df5d47c81623c3eb6daeabf039e3872222e 100644 (file)
@@ -8,7 +8,7 @@ TunnelId=10
 PeerTunnelId=11
 UDPSourcePort=3000
 UDPDestinationPort=4000
-Local=static
+Local=static@test1
 Remote=192.168.30.101
 EncapsulationType=udp
 UDPCheckSum=true