summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYu Watanabe <watanabe.yu+github@gmail.com>2022-02-25 09:12:18 +0100
committerYu Watanabe <watanabe.yu+github@gmail.com>2022-02-27 01:36:48 +0100
commit8b49ee2dcda04f8147650f7d9fb93662caf3ea2e (patch)
treedfae2783fb079502633d556909243dd62a7c0ed0
parentnetwork: introduce {manager,link}_address_is_reachable() (diff)
downloadsystemd-8b49ee2dcda04f8147650f7d9fb93662caf3ea2e.tar.xz
systemd-8b49ee2dcda04f8147650f7d9fb93662caf3ea2e.zip
network: l2tp: make Local= optionally take interface name
-rw-r--r--man/systemd.netdev.xml15
-rw-r--r--src/network/netdev/l2tp-tunnel.c236
-rw-r--r--src/network/netdev/l2tp-tunnel.h4
-rw-r--r--src/network/netdev/netdev-gperf.gperf4
-rw-r--r--test/test-network/conf/25-l2tp-ip.netdev2
-rw-r--r--test/test-network/conf/25-l2tp-udp.netdev2
6 files changed, 209 insertions, 54 deletions
diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml
index 1f94e3d599..e23146f3ca 100644
--- a/man/systemd.netdev.xml
+++ b/man/systemd.netdev.xml
@@ -881,12 +881,15 @@
<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>
diff --git a/src/network/netdev/l2tp-tunnel.c b/src/network/netdev/l2tp-tunnel.c
index 50f52f5d9b..e94ca20407 100644
--- a/src/network/netdev/l2tp-tunnel.c
+++ b/src/network/netdev/l2tp-tunnel.c
@@ -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,
};
diff --git a/src/network/netdev/l2tp-tunnel.h b/src/network/netdev/l2tp-tunnel.h
index 236b78ce4b..6028b35205 100644
--- a/src/network/netdev/l2tp-tunnel.h
+++ b/src/network/netdev/l2tp-tunnel.h
@@ -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);
diff --git a/src/network/netdev/netdev-gperf.gperf b/src/network/netdev/netdev-gperf.gperf
index 2fec1da06b..6dcc5a804f 100644
--- a/src/network/netdev/netdev-gperf.gperf
+++ b/src/network/netdev/netdev-gperf.gperf
@@ -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)
diff --git a/test/test-network/conf/25-l2tp-ip.netdev b/test/test-network/conf/25-l2tp-ip.netdev
index 1dd061894f..882c83ac04 100644
--- a/test/test-network/conf/25-l2tp-ip.netdev
+++ b/test/test-network/conf/25-l2tp-ip.netdev
@@ -6,7 +6,7 @@ Name=l2tp99
[L2TP]
TunnelId=10
PeerTunnelId=12
-Local=static
+Local=static@test1
Remote=192.168.30.101
EncapsulationType=ip
diff --git a/test/test-network/conf/25-l2tp-udp.netdev b/test/test-network/conf/25-l2tp-udp.netdev
index 81d9ef51fc..79824df5d4 100644
--- a/test/test-network/conf/25-l2tp-udp.netdev
+++ b/test/test-network/conf/25-l2tp-udp.netdev
@@ -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