From 8b49ee2dcda04f8147650f7d9fb93662caf3ea2e Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 25 Feb 2022 17:12:18 +0900 Subject: network: l2tp: make Local= optionally take interface name --- man/systemd.netdev.xml | 15 +- src/network/netdev/l2tp-tunnel.c | 236 ++++++++++++++++++++++++------ src/network/netdev/l2tp-tunnel.h | 4 +- src/network/netdev/netdev-gperf.gperf | 4 +- test/test-network/conf/25-l2tp-ip.netdev | 2 +- test/test-network/conf/25-l2tp-udp.netdev | 2 +- 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 @@ Local= - Specifies the IP address of the local interface. Takes an IP address, or the special values - auto, static, or dynamic. When an address - is set, then the local interface must have the address. If auto, then one of the - addresses on the local interface is used. Similarly, if static or - dynamic is set, then one of the static or dynamic addresses on the local - interface is used. Defaults to auto. + Specifies the IP address of a local interface. Takes an IP address, or the special + values auto, static, or dynamic. + Optionally a name of a local interface can be specified after @, e.g. + 192.168.0.1@eth0 or auto@eth0. 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 auto, then one of the + addresses on a local or specified interface which is accessible to the remote address will be + used. Similarly, if static or dynamic is set, then one + of the static or dynamic addresses will be used. Defaults to auto. 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 -- cgit v1.2.3