diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml
index bd493a8c53f..e4e7e611e77 100644
--- a/man/systemd.netdev.xml
+++ b/man/systemd.netdev.xml
@@ -1569,6 +1569,29 @@
Sets a firewall mark on outgoing WireGuard packets from this interface. Takes a number between 1 and 4294967295.
+
+ RouteTable=
+
+ The table identifier for the routes to the addresses specified in the
+ AllowedIPs=. Takes the special value off, one of the
+ predefined names default, main, and
+ local, names defined in RouteTable= in
+ networkd.conf5,
+ or a number in the range 1…4294967295. When off the routes to the
+ addresses specified in the AllowedIPs= setting will not be configured.
+ Defaults to main. This setting will be ignored when the same setting is
+ specified in the [WireGuardPeer] section.
+
+
+
+ RouteMetric=
+
+ The priority of the routes to the addresses specified in the
+ AllowedIPs=. Takes an integer in the range 0…4294967295. Defaults to 0
+ for IPv4 addresses, and 1024 for IPv6 addresses. This setting will be ignored when the same
+ setting is specified in the [WireGuardPeer] section.
+
+
@@ -1653,6 +1676,27 @@
Most users will not need this.
+
+ RouteTable=
+
+ The table identifier for the routes to the addresses specified in the
+ AllowedIPs=. Takes the special value off, one of the
+ predefined names default, main, and
+ local, names defined in RouteTable= in
+ networkd.conf5,
+ or a number in the range 1…4294967295. Defaults to unset, and the value specified in the
+ same setting in the [WireGuard] section will be used.
+
+
+
+ RouteMetric=
+
+ The priority of the routes to the addresses specified in the
+ AllowedIPs=. Takes an integer in the range 0…4294967295. Defaults to
+ unset, and the value specified in the same setting in the [WireGuard] section will be used.
+
+
+
diff --git a/src/network/netdev/netdev-gperf.gperf b/src/network/netdev/netdev-gperf.gperf
index 316592005f2..37a0d9fa5d5 100644
--- a/src/network/netdev/netdev-gperf.gperf
+++ b/src/network/netdev/netdev-gperf.gperf
@@ -229,12 +229,16 @@ WireGuard.FwMark, config_parse_unsigned,
WireGuard.ListenPort, config_parse_wireguard_listen_port, 0, offsetof(Wireguard, port)
WireGuard.PrivateKey, config_parse_wireguard_private_key, 0, 0
WireGuard.PrivateKeyFile, config_parse_wireguard_private_key_file, 0, 0
+WireGuard.RouteTable, config_parse_wireguard_route_table, 0, offsetof(Wireguard, route_table)
+WireGuard.RouteMetric, config_parse_wireguard_route_priority, 0, offsetof(Wireguard, route_priority)
WireGuardPeer.AllowedIPs, config_parse_wireguard_allowed_ips, 0, 0
WireGuardPeer.Endpoint, config_parse_wireguard_endpoint, 0, 0
WireGuardPeer.PublicKey, config_parse_wireguard_peer_key, 0, 0
WireGuardPeer.PresharedKey, config_parse_wireguard_peer_key, 0, 0
WireGuardPeer.PresharedKeyFile, config_parse_wireguard_preshared_key_file, 0, 0
WireGuardPeer.PersistentKeepalive, config_parse_wireguard_keepalive, 0, 0
+WireGuardPeer.RouteTable, config_parse_wireguard_peer_route_table, 0, 0
+WireGuardPeer.RouteMetric, config_parse_wireguard_peer_route_priority,0, 0
Xfrm.InterfaceId, config_parse_uint32, 0, offsetof(Xfrm, if_id)
Xfrm.Independent, config_parse_bool, 0, offsetof(Xfrm, independent)
BatmanAdvanced.Aggregation, config_parse_bool, 0, offsetof(BatmanAdvanced, aggregation)
diff --git a/src/network/netdev/wireguard.c b/src/network/netdev/wireguard.c
index 587e6db1c47..f254b05f86e 100644
--- a/src/network/netdev/wireguard.c
+++ b/src/network/netdev/wireguard.c
@@ -7,6 +7,7 @@
#include
#include
#include
+#include
#include "sd-resolve.h"
@@ -18,6 +19,8 @@
#include "memory-util.h"
#include "netlink-util.h"
#include "networkd-manager.h"
+#include "networkd-route-util.h"
+#include "networkd-route.h"
#include "networkd-util.h"
#include "parse-util.h"
#include "path-util.h"
@@ -827,6 +830,186 @@ int config_parse_wireguard_keepalive(
return 0;
}
+int config_parse_wireguard_route_table(
+ 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) {
+
+ NetDev *netdev = userdata;
+ uint32_t *table = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+ assert(userdata);
+
+ if (isempty(rvalue)) {
+ *table = RT_TABLE_MAIN;
+ return 0;
+ }
+
+ if (streq(rvalue, "off")) {
+ *table = 0;
+ return 0;
+ }
+
+ r = manager_get_route_table_from_string(netdev->manager, rvalue, table);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to parse %s=, ignoring assignment: %s",
+ lvalue, rvalue);
+ return 0;
+ }
+
+ return 0;
+}
+
+int config_parse_wireguard_peer_route_table(
+ 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_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
+ NetDev *netdev = userdata;
+ Wireguard *w;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(netdev);
+ assert(netdev->manager);
+
+ w = WIREGUARD(netdev);
+ assert(w);
+
+ r = wireguard_peer_new_static(w, filename, section_line, &peer);
+ if (r < 0)
+ return log_oom();
+
+ if (isempty(rvalue)) {
+ peer->route_table_set = false; /* Use the table specified in [WireGuard] section. */
+ TAKE_PTR(peer);
+ return 0;
+ }
+
+ if (streq(rvalue, "off")) {
+ peer->route_table = 0; /* Disabled. */
+ peer->route_table_set = true;
+ TAKE_PTR(peer);
+ return 0;
+ }
+
+ r = manager_get_route_table_from_string(netdev->manager, rvalue, &peer->route_table);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to parse %s=, ignoring assignment: %s",
+ lvalue, rvalue);
+ return 0;
+ }
+
+ peer->route_table_set = true;
+ TAKE_PTR(peer);
+ return 0;
+}
+
+int config_parse_wireguard_route_priority(
+ 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) {
+
+ uint32_t *priority = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (isempty(rvalue)) {
+ *priority = 0;
+ return 0;
+ }
+
+ r = safe_atou32(rvalue, priority);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Could not parse route priority \"%s\", ignoring assignment: %m", rvalue);
+ return 0;
+ }
+
+ return 0;
+}
+
+int config_parse_wireguard_peer_route_priority(
+ 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_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
+ Wireguard *w;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(userdata);
+
+ w = WIREGUARD(userdata);
+ assert(w);
+
+ r = wireguard_peer_new_static(w, filename, section_line, &peer);
+ if (r < 0)
+ return log_oom();
+
+ if (isempty(rvalue)) {
+ peer->route_priority_set = false; /* Use the priority specified in [WireGuard] section. */
+ TAKE_PTR(peer);
+ return 0;
+ }
+
+ r = safe_atou32(rvalue, &peer->route_priority);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Could not parse route priority \"%s\", ignoring assignment: %m", rvalue);
+ return 0;
+ }
+
+ peer->route_priority_set = true;
+ TAKE_PTR(peer);
+ return 0;
+}
+
static void wireguard_init(NetDev *netdev) {
Wireguard *w;
@@ -835,6 +1018,7 @@ static void wireguard_init(NetDev *netdev) {
assert(w);
w->flags = WGDEVICE_F_REPLACE_PEERS;
+ w->route_table = RT_TABLE_MAIN;
}
static void wireguard_done(NetDev *netdev) {
@@ -852,6 +1036,8 @@ static void wireguard_done(NetDev *netdev) {
hashmap_free_with_destructor(w->peers_by_section, wireguard_peer_free);
set_free(w->peers_with_unresolved_endpoint);
set_free(w->peers_with_failed_endpoint);
+
+ set_free(w->routes);
}
static int wireguard_read_key_file(const char *filename, uint8_t dest[static WG_KEY_LEN]) {
@@ -924,9 +1110,40 @@ static int wireguard_verify(NetDev *netdev, const char *filename) {
"%s: Missing PrivateKey= or PrivateKeyFile=, "
"Ignoring network device.", filename);
- LIST_FOREACH_SAFE(peers, peer, peer_next, w->peers)
- if (wireguard_peer_verify(peer) < 0)
+ LIST_FOREACH_SAFE(peers, peer, peer_next, w->peers) {
+ WireguardIPmask *ipmask;
+
+ if (wireguard_peer_verify(peer) < 0) {
wireguard_peer_free(peer);
+ continue;
+ }
+
+ if ((peer->route_table_set ? peer->route_table : w->route_table) == 0)
+ continue;
+
+ LIST_FOREACH(ipmasks, ipmask, peer->ipmasks) {
+ _cleanup_(route_freep) Route *route = NULL;
+
+ r = route_new(&route);
+ if (r < 0)
+ return log_oom();
+
+ route->family = ipmask->family;
+ route->dst = ipmask->ip;
+ route->dst_prefixlen = ipmask->cidr;
+ route->scope = RT_SCOPE_UNIVERSE;
+ route->protocol = RTPROT_STATIC;
+ route->table = peer->route_table_set ? peer->route_table : w->route_table;
+ route->priority = peer->route_priority_set ? peer->route_priority : w->route_priority;
+ if (route->priority == 0 && route->family == AF_INET6)
+ route->priority = IP6_RT_PRIO_USER;
+ route->source = NETWORK_CONFIG_SOURCE_STATIC;
+
+ r = set_ensure_consume(&w->routes, &route_hash_ops, TAKE_PTR(route));
+ if (r < 0)
+ return log_oom();
+ }
+ }
return 0;
}
diff --git a/src/network/netdev/wireguard.h b/src/network/netdev/wireguard.h
index b9b5ae9871d..5d4b6da45ec 100644
--- a/src/network/netdev/wireguard.h
+++ b/src/network/netdev/wireguard.h
@@ -33,6 +33,11 @@ typedef struct WireguardPeer {
char *endpoint_host;
char *endpoint_port;
+ uint32_t route_table;
+ uint32_t route_priority;
+ bool route_table_set;
+ bool route_priority_set;
+
LIST_HEAD(WireguardIPmask, ipmasks);
LIST_FIELDS(struct WireguardPeer, peers);
} WireguardPeer;
@@ -55,6 +60,10 @@ struct Wireguard {
unsigned n_retries;
sd_event_source *resolve_retry_event_source;
+
+ Set *routes;
+ uint32_t route_table;
+ uint32_t route_priority;
};
DEFINE_NETDEV_CAST(WIREGUARD, Wireguard);
@@ -68,3 +77,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_private_key);
CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_private_key_file);
CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_preshared_key_file);
CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_keepalive);
+CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_route_table);
+CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_peer_route_table);
+CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_route_priority);
+CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_peer_route_priority);
diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c
index 28ef058651b..20d6aa47f66 100644
--- a/src/network/networkd-route.c
+++ b/src/network/networkd-route.c
@@ -19,6 +19,7 @@
#include "string-util.h"
#include "strv.h"
#include "vrf.h"
+#include "wireguard.h"
int route_new(Route **ret) {
_cleanup_(route_freep) Route *route = NULL;
@@ -865,6 +866,28 @@ static bool route_by_kernel(const Route *route) {
return false;
}
+static void link_unmark_wireguard_routes(Link *link) {
+ Route *route, *existing;
+ NetDev *netdev;
+ Wireguard *w;
+
+ assert(link);
+
+ if (!streq_ptr(link->kind, "wireguard"))
+ return;
+
+ if (netdev_get(link->manager, link->ifname, &netdev) < 0)
+ return;
+
+ w = WIREGUARD(netdev);
+ if (!w)
+ return;
+
+ SET_FOREACH(route, w->routes)
+ if (route_get(NULL, link, route, &existing) >= 0)
+ route_unmark(existing);
+}
+
int link_drop_foreign_routes(Link *link) {
Route *route;
int k, r;
@@ -914,6 +937,8 @@ int link_drop_foreign_routes(Link *link) {
route_unmark(existing);
}
+ link_unmark_wireguard_routes(link);
+
r = 0;
SET_FOREACH(route, link->routes) {
if (!route_is_marked(route))
@@ -1342,6 +1367,36 @@ static int link_request_static_route(Link *link, Route *route) {
&link->static_route_messages, static_route_handler, NULL);
}
+static int link_request_wireguard_routes(Link *link, bool only_ipv4) {
+ NetDev *netdev;
+ Wireguard *w;
+ Route *route;
+ int r;
+
+ assert(link);
+
+ if (!streq_ptr(link->kind, "wireguard"))
+ return 0;
+
+ if (netdev_get(link->manager, link->ifname, &netdev) < 0)
+ return 0;
+
+ w = WIREGUARD(netdev);
+ if (!w)
+ return 0;
+
+ SET_FOREACH(route, w->routes) {
+ if (only_ipv4 && route->family != AF_INET)
+ continue;
+
+ r = link_request_static_route(link, route);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
int link_request_static_routes(Link *link, bool only_ipv4) {
Route *route;
int r;
@@ -1363,6 +1418,10 @@ int link_request_static_routes(Link *link, bool only_ipv4) {
return r;
}
+ r = link_request_wireguard_routes(link, only_ipv4);
+ if (r < 0)
+ return r;
+
if (link->static_route_messages == 0) {
link->static_routes_configured = true;
link_check_ready(link);
diff --git a/test/fuzz/fuzz-netdev-parser/directives.netdev b/test/fuzz/fuzz-netdev-parser/directives.netdev
index c7c3682eabd..e34d16af117 100644
--- a/test/fuzz/fuzz-netdev-parser/directives.netdev
+++ b/test/fuzz/fuzz-netdev-parser/directives.netdev
@@ -17,6 +17,8 @@ PrivateKey=
PrivateKeyFile=
FwMark=
FirewallMark=
+RouteTable=
+RouteMetric=
[MACVTAP]
Mode=
SourceMACAddress=
@@ -67,6 +69,8 @@ PresharedKeyFile=
PersistentKeepalive=
PublicKey=
AllowedIPs=
+RouteTable=
+RouteMetric=
[Tunnel]
FooOverUDP=
IPv6FlowLabel=