1
0
mirror of https://github.com/systemd/systemd.git synced 2024-12-22 17:35:35 +03:00

Merge pull request #22627 from yuwata/network-l2tp-cleanups

network: cleanups for L2TP tunnel
This commit is contained in:
Luca Boccassi 2022-02-27 18:01:14 +00:00 committed by GitHub
commit ba119ffcf9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 416 additions and 139 deletions

View File

@ -881,12 +881,15 @@
<varlistentry> <varlistentry>
<term><varname>Local=</varname></term> <term><varname>Local=</varname></term>
<listitem> <listitem>
<para>Specifies the IP address of the local interface. Takes an IP address, or the special values <para>Specifies the IP address of a local interface. Takes an IP address, or the special
<literal>auto</literal>, <literal>static</literal>, or <literal>dynamic</literal>. When an address values <literal>auto</literal>, <literal>static</literal>, or <literal>dynamic</literal>.
is set, then the local interface must have the address. If <literal>auto</literal>, then one of the Optionally a name of a local interface can be specified after <literal>@</literal>, e.g.
addresses on the local interface is used. Similarly, if <literal>static</literal> or <literal>192.168.0.1@eth0</literal> or <literal>auto@eth0</literal>. When an address is
<literal>dynamic</literal> is set, then one of the static or dynamic addresses on the local specified, then a local or specified interface must have the address, and the remote address
interface is used. Defaults to <literal>auto</literal>.</para> 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> </listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>

View File

@ -869,7 +869,6 @@ Table=1234</programlisting></para>
<term><varname>IPoIB=</varname></term> <term><varname>IPoIB=</varname></term>
<term><varname>IPVLAN=</varname></term> <term><varname>IPVLAN=</varname></term>
<term><varname>IPVTAP=</varname></term> <term><varname>IPVTAP=</varname></term>
<term><varname>L2TP=</varname></term>
<term><varname>MACsec=</varname></term> <term><varname>MACsec=</varname></term>
<term><varname>MACVLAN=</varname></term> <term><varname>MACVLAN=</varname></term>
<term><varname>MACVTAP=</varname></term> <term><varname>MACVTAP=</varname></term>
@ -878,7 +877,7 @@ Table=1234</programlisting></para>
<term><varname>VXLAN=</varname></term> <term><varname>VXLAN=</varname></term>
<term><varname>Xfrm=</varname></term> <term><varname>Xfrm=</varname></term>
<listitem> <listitem>
<para>The name of an IPoIB, IPVLAN, IPVTAP, L2TP, MACsec, MACVLAN, MACVTAP, tunnel, VLAN, <para>The name of an IPoIB, IPVLAN, IPVTAP, MACsec, MACVLAN, MACVTAP, tunnel, VLAN,
VXLAN, or Xfrm to be created on the link. See VXLAN, or Xfrm to be created on the link. See
<citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>. <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
This option may be specified more than once.</para> This option may be specified more than once.</para>

View File

@ -10,6 +10,7 @@
#include "netlink-util.h" #include "netlink-util.h"
#include "networkd-address.h" #include "networkd-address.h"
#include "networkd-manager.h" #include "networkd-manager.h"
#include "networkd-route-util.h"
#include "parse-util.h" #include "parse-util.h"
#include "socket-util.h" #include "socket-util.h"
#include "string-table.h" #include "string-table.h"
@ -245,44 +246,114 @@ static int netdev_l2tp_create_message_session(NetDev *netdev, L2tpSession *sessi
return 0; return 0;
} }
static int l2tp_acquire_local_address_one(L2tpTunnel *t, Address *a, union in_addr_union *ret) { static int link_get_l2tp_local_address(Link *link, L2tpTunnel *t, union in_addr_union *ret) {
if (a->family != t->family)
return -EINVAL;
if (in_addr_is_set(a->family, &a->in_addr_peer))
return -EINVAL;
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;
*ret = a->in_addr;
return 0;
}
static int l2tp_acquire_local_address(L2tpTunnel *t, Link *link, union in_addr_union *ret) {
Address *a; Address *a;
assert(t);
assert(link); assert(link);
assert(ret); assert(t);
assert(IN_SET(t->family, AF_INET, AF_INET6));
SET_FOREACH(a, link->addresses) {
if (!address_is_ready(a))
continue;
if (a->family != t->family)
continue;
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_get_local_address(NetDev *netdev, union in_addr_union *ret) {
Link *link = NULL;
L2tpTunnel *t;
Address *a;
int r;
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)) { if (in_addr_is_set(t->family, &t->local)) {
/* local address is explicitly specified. */ /* 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; return 0;
} }
SET_FOREACH(a, link->addresses) if (a) {
if (l2tp_acquire_local_address_one(t, a, ret) >= 0) if (t->local_address_type == NETDEV_L2TP_LOCAL_ADDRESS_STATIC &&
return 1; !FLAGS_SET(a->flags, IFA_F_PERMANENT))
return -EINVAL;
return -ENODATA; if (t->local_address_type == NETDEV_L2TP_LOCAL_ADDRESS_DYNAMIC &&
FLAGS_SET(a->flags, IFA_F_PERMANENT))
return -EINVAL;
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) { static void l2tp_session_destroy_callback(L2tpSession *session) {
@ -361,7 +432,7 @@ static int l2tp_create_tunnel_handler(sd_netlink *rtnl, sd_netlink_message *m, N
return 1; return 1;
} }
static int l2tp_create_tunnel(NetDev *netdev, Link *link) { static int l2tp_create_tunnel(NetDev *netdev) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
union in_addr_union local_address; union in_addr_union local_address;
L2tpTunnel *t; L2tpTunnel *t;
@ -370,11 +441,11 @@ static int l2tp_create_tunnel(NetDev *netdev, Link *link) {
assert(netdev); assert(netdev);
assert_se(t = L2TP(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) if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not find local address."); 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; _cleanup_free_ char *str = NULL;
(void) in_addr_to_string(t->family, &local_address, &str); (void) in_addr_to_string(t->family, &local_address, &str);
@ -395,7 +466,95 @@ static int l2tp_create_tunnel(NetDev *netdev, Link *link) {
return 0; 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,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
_cleanup_free_ char *addr_or_type = NULL, *ifname = NULL;
L2tpLocalAddressType type;
L2tpTunnel *t = userdata;
const char *p = rvalue;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(t);
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 (!in_addr_is_set(t->family, &t->remote))
/* If Remote= is not specified yet, then also clear family. */
t->family = AF_UNSPEC;
return 0;
}
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 *unit,
const char *filename, const char *filename,
unsigned line, unsigned line,
@ -408,41 +567,30 @@ int config_parse_l2tp_tunnel_address(
void *userdata) { void *userdata) {
L2tpTunnel *t = userdata; L2tpTunnel *t = userdata;
union in_addr_union *addr = data;
int r; int r;
assert(filename); assert(filename);
assert(lvalue); assert(lvalue);
assert(rvalue); assert(rvalue);
assert(data); assert(t);
if (streq(lvalue, "Local")) { if (isempty(rvalue)) {
L2tpLocalAddressType addr_type; t->remote = IN_ADDR_NULL;
if (isempty(rvalue)) if (!in_addr_is_set(t->family, &t->local))
addr_type = NETDEV_L2TP_LOCAL_ADDRESS_AUTO; /* If Local= is not specified yet, then also clear family. */
else t->family = AF_UNSPEC;
addr_type = l2tp_local_address_type_from_string(rvalue);
if (addr_type >= 0) { return 0;
if (!in_addr_is_set(t->family, &t->remote))
/* If Remote= is not specified yet, then also clear family. */
t->family = AF_UNSPEC;
t->local = IN_ADDR_NULL;
t->local_address_type = addr_type;
return 0;
}
} }
if (t->family == AF_UNSPEC) 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 else
r = in_addr_from_string(t->family, rvalue, addr); r = in_addr_from_string(t->family, rvalue, &t->remote);
if (r < 0) { if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r, 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; return 0;
} }
@ -699,14 +847,16 @@ static void l2tp_tunnel_done(NetDev *netdev) {
assert(t); assert(t);
ordered_hashmap_free_with_destructor(t->sessions_by_section, l2tp_session_free); ordered_hashmap_free_with_destructor(t->sessions_by_section, l2tp_session_free);
free(t->local_ifname);
} }
const NetDevVTable l2tptnl_vtable = { const NetDevVTable l2tptnl_vtable = {
.object_size = sizeof(L2tpTunnel), .object_size = sizeof(L2tpTunnel),
.init = l2tp_tunnel_init, .init = l2tp_tunnel_init,
.sections = NETDEV_COMMON_SECTIONS "L2TP\0L2TPSession\0", .sections = NETDEV_COMMON_SECTIONS "L2TP\0L2TPSession\0",
.create_after_configured = l2tp_create_tunnel, .create = l2tp_create_tunnel,
.done = l2tp_tunnel_done, .done = l2tp_tunnel_done,
.create_type = NETDEV_CREATE_AFTER_CONFIGURED, .create_type = NETDEV_CREATE_INDEPENDENT,
.is_ready_to_create = netdev_l2tp_is_ready_to_create,
.config_verify = netdev_l2tp_tunnel_verify, .config_verify = netdev_l2tp_tunnel_verify,
}; };

View File

@ -58,6 +58,7 @@ struct L2tpTunnel {
bool udp6_csum_rx; bool udp6_csum_rx;
bool udp6_csum_tx; bool udp6_csum_tx;
char *local_ifname;
L2tpLocalAddressType local_address_type; L2tpLocalAddressType local_address_type;
union in_addr_union local; union in_addr_union local;
union in_addr_union remote; union in_addr_union remote;
@ -70,7 +71,8 @@ struct L2tpTunnel {
DEFINE_NETDEV_CAST(L2TP, L2tpTunnel); DEFINE_NETDEV_CAST(L2TP, L2tpTunnel);
extern const NetDevVTable l2tptnl_vtable; 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_tunnel_id);
CONFIG_PARSER_PROTOTYPE(config_parse_l2tp_encap_type); CONFIG_PARSER_PROTOTYPE(config_parse_l2tp_encap_type);
CONFIG_PARSER_PROTOTYPE(config_parse_l2tp_session_l2spec); CONFIG_PARSER_PROTOTYPE(config_parse_l2tp_session_l2spec);

View 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.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.UDPSourcePort, config_parse_ip_port, 0, offsetof(L2tpTunnel, l2tp_udp_sport)
L2TP.UDPDestinationPort, config_parse_ip_port, 0, offsetof(L2tpTunnel, l2tp_udp_dport) L2TP.UDPDestinationPort, config_parse_ip_port, 0, offsetof(L2tpTunnel, l2tp_udp_dport)
L2TP.Local, config_parse_l2tp_tunnel_address, 0, offsetof(L2tpTunnel, local) L2TP.Local, config_parse_l2tp_tunnel_local_address, 0, 0
L2TP.Remote, config_parse_l2tp_tunnel_address, 0, offsetof(L2tpTunnel, remote) L2TP.Remote, config_parse_l2tp_tunnel_remote_address, 0, 0
L2TP.EncapsulationType, config_parse_l2tp_encap_type, 0, offsetof(L2tpTunnel, l2tp_encap_type) L2TP.EncapsulationType, config_parse_l2tp_encap_type, 0, offsetof(L2tpTunnel, l2tp_encap_type)
L2TP.UDPCheckSum, config_parse_bool, 0, offsetof(L2tpTunnel, udp_csum) L2TP.UDPCheckSum, config_parse_bool, 0, offsetof(L2tpTunnel, udp_csum)
L2TP.UDP6CheckSumRx, config_parse_bool, 0, offsetof(L2tpTunnel, udp6_csum_rx) L2TP.UDP6CheckSumRx, config_parse_bool, 0, offsetof(L2tpTunnel, udp6_csum_rx)

View File

@ -144,7 +144,7 @@ bool netdev_is_managed(NetDev *netdev) {
static bool netdev_is_stacked_and_independent(NetDev *netdev) { static bool netdev_is_stacked_and_independent(NetDev *netdev) {
assert(netdev); assert(netdev);
if (!IN_SET(netdev_get_create_type(netdev), NETDEV_CREATE_STACKED, NETDEV_CREATE_AFTER_CONFIGURED)) if (netdev_get_create_type(netdev) != NETDEV_CREATE_STACKED)
return false; return false;
switch (netdev->kind) { switch (netdev->kind) {
@ -180,7 +180,7 @@ static bool netdev_is_stacked_and_independent(NetDev *netdev) {
static bool netdev_is_stacked(NetDev *netdev) { static bool netdev_is_stacked(NetDev *netdev) {
assert(netdev); assert(netdev);
if (!IN_SET(netdev_get_create_type(netdev), NETDEV_CREATE_STACKED, NETDEV_CREATE_AFTER_CONFIGURED)) if (netdev_get_create_type(netdev) != NETDEV_CREATE_STACKED)
return false; return false;
if (netdev_is_stacked_and_independent(netdev)) if (netdev_is_stacked_and_independent(netdev))
@ -617,10 +617,6 @@ static int netdev_is_ready_to_create(NetDev *netdev, Link *link) {
if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
return false; return false;
if (netdev_get_create_type(netdev) == NETDEV_CREATE_AFTER_CONFIGURED &&
link->state != LINK_STATE_CONFIGURED)
return false;
if (link->set_link_messages > 0) if (link->set_link_messages > 0)
return false; return false;
@ -652,28 +648,21 @@ int request_process_stacked_netdev(Request *req) {
if (r <= 0) if (r <= 0)
return r; return r;
switch (netdev_get_create_type(netdev)) { r = stacked_netdev_create(netdev, link, req->netlink_handler);
case NETDEV_CREATE_STACKED:
r = stacked_netdev_create(netdev, link, req->netlink_handler);
break;
case NETDEV_CREATE_AFTER_CONFIGURED:
assert(NETDEV_VTABLE(netdev)->create_after_configured);
r = NETDEV_VTABLE(netdev)->create_after_configured(netdev, link);
break;
default:
assert_not_reached();
}
if (r < 0) if (r < 0)
return log_netdev_warning_errno(netdev, r, "Failed to create netdev: %m"); return log_netdev_warning_errno(netdev, r, "Failed to create netdev: %m");
return 1; return 1;
} }
static int link_create_stacked_netdev_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { static int link_create_stacked_netdev_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r; int r;
assert(m); assert(m);
assert(link); assert(link);
assert(link->create_stacked_netdev_messages > 0);
link->create_stacked_netdev_messages--;
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
return 0; return 0;
@ -685,18 +674,6 @@ static int link_create_stacked_netdev_handler_internal(sd_netlink *rtnl, sd_netl
return 0; return 0;
} }
return 1;
}
static int link_create_stacked_netdev_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
assert(link);
assert(link->create_stacked_netdev_messages > 0);
link->create_stacked_netdev_messages--;
if (link_create_stacked_netdev_handler_internal(rtnl, m, link) <= 0)
return 0;
if (link->create_stacked_netdev_messages == 0) { if (link->create_stacked_netdev_messages == 0) {
link->stacked_netdevs_created = true; link->stacked_netdevs_created = true;
log_link_debug(link, "Stacked netdevs created."); log_link_debug(link, "Stacked netdevs created.");
@ -706,10 +683,6 @@ static int link_create_stacked_netdev_handler(sd_netlink *rtnl, sd_netlink_messa
return 0; return 0;
} }
static int link_create_stacked_netdev_after_configured_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
return 0;
}
int link_request_stacked_netdev(Link *link, NetDev *netdev) { int link_request_stacked_netdev(Link *link, NetDev *netdev) {
int r; int r;
@ -722,15 +695,11 @@ int link_request_stacked_netdev(Link *link, NetDev *netdev) {
if (!IN_SET(netdev->state, NETDEV_STATE_LOADING, NETDEV_STATE_FAILED) || netdev->ifindex > 0) if (!IN_SET(netdev->state, NETDEV_STATE_LOADING, NETDEV_STATE_FAILED) || netdev->ifindex > 0)
return 0; /* Already created. */ return 0; /* Already created. */
if (netdev_get_create_type(netdev) == NETDEV_CREATE_STACKED) { link->stacked_netdevs_created = false;
link->stacked_netdevs_created = false; r = link_queue_request(link, REQUEST_TYPE_NETDEV_STACKED, netdev_ref(netdev), true,
r = link_queue_request(link, REQUEST_TYPE_NETDEV_STACKED, netdev_ref(netdev), true, &link->create_stacked_netdev_messages,
&link->create_stacked_netdev_messages, link_create_stacked_netdev_handler,
link_create_stacked_netdev_handler, NULL);
NULL);
} else
r = link_queue_request(link, REQUEST_TYPE_NETDEV_STACKED, netdev_ref(netdev), true,
NULL, link_create_stacked_netdev_after_configured_handler, NULL);
if (r < 0) if (r < 0)
return log_link_error_errno(link, r, "Failed to request stacked netdev '%s': %m", return log_link_error_errno(link, r, "Failed to request stacked netdev '%s': %m",
netdev->ifname); netdev->ifname);

View File

@ -103,7 +103,6 @@ typedef enum NetDevState {
typedef enum NetDevCreateType { typedef enum NetDevCreateType {
NETDEV_CREATE_INDEPENDENT, NETDEV_CREATE_INDEPENDENT,
NETDEV_CREATE_STACKED, NETDEV_CREATE_STACKED,
NETDEV_CREATE_AFTER_CONFIGURED,
_NETDEV_CREATE_MAX, _NETDEV_CREATE_MAX,
_NETDEV_CREATE_INVALID = -EINVAL, _NETDEV_CREATE_INVALID = -EINVAL,
} NetDevCreateType; } NetDevCreateType;
@ -160,9 +159,6 @@ typedef struct NetDevVTable {
/* create netdev, if not done via rtnl */ /* create netdev, if not done via rtnl */
int (*create)(NetDev *netdev); int (*create)(NetDev *netdev);
/* create netdev after link is fully configured */
int (*create_after_configured)(NetDev *netdev, Link *link);
/* perform additional configuration after netdev has been createad */ /* perform additional configuration after netdev has been createad */
int (*post_create)(NetDev *netdev, Link *link); int (*post_create)(NetDev *netdev, Link *link);

View File

@ -602,19 +602,35 @@ int link_get_address(Link *link, int family, const union in_addr_union *address,
return -ENOENT; return -ENOENT;
} }
int manager_has_address(Manager *manager, int family, const union in_addr_union *address, bool check_ready) { int manager_get_address(Manager *manager, int family, const union in_addr_union *address, unsigned char prefixlen, Address **ret) {
Address *a;
Link *link; Link *link;
assert(manager); assert(manager);
assert(IN_SET(family, AF_INET, AF_INET6)); assert(IN_SET(family, AF_INET, AF_INET6));
assert(address); assert(address);
HASHMAP_FOREACH(link, manager->links_by_index) HASHMAP_FOREACH(link, manager->links_by_index) {
if (link_get_address(link, family, address, 0, &a) >= 0) if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
return check_ready ? address_is_ready(a) : address_exists(a); continue;
return false; if (link_get_address(link, family, address, prefixlen, ret) >= 0)
return 0;
}
return -ENOENT;
}
bool manager_has_address(Manager *manager, int family, const union in_addr_union *address, bool check_ready) {
Address *a;
assert(manager);
assert(IN_SET(family, AF_INET, AF_INET6));
assert(address);
if (manager_get_address(manager, family, address, 0, &a) < 0)
return false;
return check_ready ? address_is_ready(a) : address_exists(a);
} }
const char* format_lifetime(char *buf, size_t l, usec_t lifetime_usec) { const char* format_lifetime(char *buf, size_t l, usec_t lifetime_usec) {

View File

@ -88,7 +88,8 @@ static inline int link_get_ipv4_address(Link *link, const struct in_addr *addres
assert(address); assert(address);
return link_get_address(link, AF_INET, &(union in_addr_union) { .in = *address }, prefixlen, ret); return link_get_address(link, AF_INET, &(union in_addr_union) { .in = *address }, prefixlen, ret);
} }
int manager_has_address(Manager *manager, int family, const union in_addr_union *address, bool check_ready); int manager_get_address(Manager *manager, int family, const union in_addr_union *address, unsigned char prefixlen, Address **ret);
bool manager_has_address(Manager *manager, int family, const union in_addr_union *address, bool check_ready);
void address_cancel_request(Address *address); void address_cancel_request(Address *address);
int link_request_address( int link_request_address(

View File

@ -93,7 +93,7 @@ Network.VRF, config_parse_ifname,
Network.IPoIB, config_parse_stacked_netdev, NETDEV_KIND_IPOIB, offsetof(Network, stacked_netdev_names) Network.IPoIB, config_parse_stacked_netdev, NETDEV_KIND_IPOIB, offsetof(Network, stacked_netdev_names)
Network.IPVLAN, config_parse_stacked_netdev, NETDEV_KIND_IPVLAN, offsetof(Network, stacked_netdev_names) Network.IPVLAN, config_parse_stacked_netdev, NETDEV_KIND_IPVLAN, offsetof(Network, stacked_netdev_names)
Network.IPVTAP, config_parse_stacked_netdev, NETDEV_KIND_IPVTAP, offsetof(Network, stacked_netdev_names) Network.IPVTAP, config_parse_stacked_netdev, NETDEV_KIND_IPVTAP, offsetof(Network, stacked_netdev_names)
Network.L2TP, config_parse_stacked_netdev, NETDEV_KIND_L2TP, offsetof(Network, stacked_netdev_names) Network.L2TP, config_parse_warn_compat, DISABLED_LEGACY, 0
Network.MACsec, config_parse_stacked_netdev, NETDEV_KIND_MACSEC, offsetof(Network, stacked_netdev_names) Network.MACsec, config_parse_stacked_netdev, NETDEV_KIND_MACSEC, offsetof(Network, stacked_netdev_names)
Network.MACVLAN, config_parse_stacked_netdev, NETDEV_KIND_MACVLAN, offsetof(Network, stacked_netdev_names) Network.MACVLAN, config_parse_stacked_netdev, NETDEV_KIND_MACVLAN, offsetof(Network, stacked_netdev_names)
Network.MACVTAP, config_parse_stacked_netdev, NETDEV_KIND_MACVTAP, offsetof(Network, stacked_netdev_names) Network.MACVTAP, config_parse_stacked_netdev, NETDEV_KIND_MACVTAP, offsetof(Network, stacked_netdev_names)

View File

@ -871,7 +871,6 @@ int config_parse_stacked_netdev(
NETDEV_KIND_IPOIB, NETDEV_KIND_IPOIB,
NETDEV_KIND_IPVLAN, NETDEV_KIND_IPVLAN,
NETDEV_KIND_IPVTAP, NETDEV_KIND_IPVTAP,
NETDEV_KIND_L2TP,
NETDEV_KIND_MACSEC, NETDEV_KIND_MACSEC,
NETDEV_KIND_MACVLAN, NETDEV_KIND_MACVLAN,
NETDEV_KIND_MACVTAP, NETDEV_KIND_MACVTAP,

View File

@ -102,14 +102,21 @@ int manager_find_uplink(Manager *m, int family, Link *exclude, Link **ret) {
return 0; return 0;
} }
static bool link_address_is_reachable(Link *link, int family, const union in_addr_union *address) { bool gateway_is_ready(Link *link, bool onlink, int family, const union in_addr_union *gw) {
Route *route; Route *route;
Address *a; Address *a;
assert(link); assert(link);
assert(link->manager); assert(link->manager);
assert(IN_SET(family, AF_INET, AF_INET6));
assert(address); if (onlink)
return true;
if (!gw || !in_addr_is_set(family, gw))
return true;
if (family == AF_INET6 && in6_addr_is_link_local(&gw->in6))
return true;
SET_FOREACH(route, link->routes) { SET_FOREACH(route, link->routes) {
if (!route_exists(route)) if (!route_exists(route))
@ -118,7 +125,7 @@ static bool link_address_is_reachable(Link *link, int family, const union in_add
continue; continue;
if (!in_addr_is_set(route->family, &route->dst) && route->dst_prefixlen == 0) if (!in_addr_is_set(route->family, &route->dst) && route->dst_prefixlen == 0)
continue; continue;
if (in_addr_prefix_covers(family, &route->dst, route->dst_prefixlen, address) > 0) if (in_addr_prefix_covers(family, &route->dst, route->dst_prefixlen, gw) > 0)
return true; return true;
} }
@ -136,27 +143,148 @@ static bool link_address_is_reachable(Link *link, int family, const union in_add
continue; continue;
if (in_addr_is_set(a->family, &a->in_addr_peer)) if (in_addr_is_set(a->family, &a->in_addr_peer))
continue; continue;
if (in_addr_prefix_covers(family, &a->in_addr, a->prefixlen, address) > 0) if (in_addr_prefix_covers(family, &a->in_addr, a->prefixlen, gw) > 0)
return true; return true;
} }
return false; return false;
} }
bool gateway_is_ready(Link *link, bool onlink, int family, const union in_addr_union *gw) { static int link_address_is_reachable_internal(
Link *link,
int family,
const union in_addr_union *address,
const union in_addr_union *prefsrc, /* optional */
Route **ret) {
Route *route, *found = NULL;
assert(link); assert(link);
assert(gw); assert(IN_SET(family, AF_INET, AF_INET6));
assert(address);
if (onlink) SET_FOREACH(route, link->routes) {
return true; if (!route_exists(route))
continue;
if (!in_addr_is_set(family, gw)) if (route->type != RTN_UNICAST)
return true; continue;
if (family == AF_INET6 && in6_addr_is_link_local(&gw->in6)) if (route->family != family)
return true; continue;
return link_address_is_reachable(link, family, gw); if (in_addr_prefix_covers(family, &route->dst, route->dst_prefixlen, address) <= 0)
continue;
if (prefsrc &&
in_addr_is_set(family, prefsrc) &&
in_addr_is_set(family, &route->prefsrc) &&
!in_addr_equal(family, prefsrc, &route->prefsrc))
continue;
if (found && found->priority <= route->priority)
continue;
found = route;
}
if (!found)
return -ENOENT;
if (ret)
*ret = found;
return 0;
}
int link_address_is_reachable(
Link *link,
int family,
const union in_addr_union *address,
const union in_addr_union *prefsrc, /* optional */
Address **ret) {
Route *route;
Address *a;
int r;
assert(link);
assert(IN_SET(family, AF_INET, AF_INET6));
assert(address);
/* This checks if the address is reachable, and optionally return the Address object of the
* preferred source to access the address. */
r = link_address_is_reachable_internal(link, family, address, prefsrc, &route);
if (r < 0)
return r;
if (!in_addr_is_set(route->family, &route->prefsrc)) {
if (ret)
*ret = NULL;
return 0;
}
r = link_get_address(link, route->family, &route->prefsrc, 0, &a);
if (r < 0)
return r;
if (!address_is_ready(a))
return -EBUSY;
if (ret)
*ret = a;
return 0;
}
int manager_address_is_reachable(
Manager *manager,
int family,
const union in_addr_union *address,
const union in_addr_union *prefsrc, /* optional */
Address **ret) {
Route *route, *found = NULL;
Address *a;
Link *link;
int r;
assert(manager);
HASHMAP_FOREACH(link, manager->links_by_index) {
if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
continue;
if (link_address_is_reachable_internal(link, family, address, prefsrc, &route) < 0)
continue;
if (found && found->priority <= route->priority)
continue;
found = route;
}
if (!found)
return -ENOENT;
if (!in_addr_is_set(found->family, &found->prefsrc)) {
if (ret)
*ret = NULL;
return 0;
}
r = link_get_address(found->link, found->family, &found->prefsrc, 0, &a);
if (r < 0)
return r;
if (!address_is_ready(a))
return -EBUSY;
if (ret)
*ret = a;
return 0;
} }
static const char * const route_type_table[__RTN_MAX] = { static const char * const route_type_table[__RTN_MAX] = {

View File

@ -8,6 +8,7 @@
typedef struct Link Link; typedef struct Link Link;
typedef struct Manager Manager; typedef struct Manager Manager;
typedef struct Address Address;
unsigned routes_max(void); unsigned routes_max(void);
@ -15,6 +16,20 @@ int manager_find_uplink(Manager *m, int family, Link *exclude, Link **ret);
bool gateway_is_ready(Link *link, bool onlink, int family, const union in_addr_union *gw); bool gateway_is_ready(Link *link, bool onlink, int family, const union in_addr_union *gw);
int link_address_is_reachable(
Link *link,
int family,
const union in_addr_union *address,
const union in_addr_union *prefsrc, /* optional */
Address **ret);
int manager_address_is_reachable(
Manager *manager,
int family,
const union in_addr_union *address,
const union in_addr_union *prefsrc, /* optional */
Address **ret);
int route_type_from_string(const char *s) _pure_; int route_type_from_string(const char *s) _pure_;
const char *route_type_to_string(int t) _const_; const char *route_type_to_string(int t) _const_;

View File

@ -5,4 +5,3 @@ Name=test1
[Network] [Network]
Address=192.168.30.100/24 Address=192.168.30.100/24
IPv6AcceptRA=false IPv6AcceptRA=false
L2TP=l2tp99

View File

@ -6,7 +6,7 @@ Name=l2tp99
[L2TP] [L2TP]
TunnelId=10 TunnelId=10
PeerTunnelId=12 PeerTunnelId=12
Local=static Local=static@test1
Remote=192.168.30.101 Remote=192.168.30.101
EncapsulationType=ip EncapsulationType=ip

View File

@ -8,7 +8,7 @@ TunnelId=10
PeerTunnelId=11 PeerTunnelId=11
UDPSourcePort=3000 UDPSourcePort=3000
UDPDestinationPort=4000 UDPDestinationPort=4000
Local=static Local=static@test1
Remote=192.168.30.101 Remote=192.168.30.101
EncapsulationType=udp EncapsulationType=udp
UDPCheckSum=true UDPCheckSum=true