summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--man/custom-html.xsl12
-rw-r--r--man/systemd.netdev.xml115
-rw-r--r--src/libsystemd/sd-netlink/generic-netlink.c1
-rw-r--r--src/libsystemd/sd-netlink/netlink-types.c52
-rw-r--r--src/libsystemd/sd-netlink/netlink-types.h1
-rw-r--r--src/network/meson.build2
-rw-r--r--src/network/netdev/netdev-gperf.gperf237
-rw-r--r--src/network/netdev/netdev.c3
-rw-r--r--src/network/netdev/netdev.h1
-rw-r--r--src/network/netdev/wireguard.c721
-rw-r--r--src/network/netdev/wireguard.h98
-rw-r--r--src/shared/meson.build1
-rw-r--r--src/shared/wireguard-netlink.h179
-rw-r--r--src/systemd/sd-netlink.h2
14 files changed, 1309 insertions, 116 deletions
diff --git a/man/custom-html.xsl b/man/custom-html.xsl
index 47ce6abfee..e8a7404df3 100644
--- a/man/custom-html.xsl
+++ b/man/custom-html.xsl
@@ -73,6 +73,18 @@
</a>
</xsl:template>
+<xsl:template match="citerefentry[@project='wireguard']">
+ <a>
+ <xsl:attribute name="href">
+ <xsl:text>https://git.zx2c4.com/WireGuard/about/src/tools/</xsl:text>
+ <xsl:value-of select="refentrytitle"/>
+ <xsl:text>.</xsl:text>
+ <xsl:value-of select="manvolnum"/>
+ </xsl:attribute>
+ <xsl:call-template name="inline.charseq"/>
+ </a>
+</xsl:template>
+
<xsl:template match="citerefentry[@project='mankier']">
<a>
<xsl:attribute name="href">
diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml
index 8f8d54a8eb..eb86db9792 100644
--- a/man/systemd.netdev.xml
+++ b/man/systemd.netdev.xml
@@ -184,6 +184,9 @@
<entry>The virtual CAN tunnel driver (vxcan). Similar to the virtual ethernet driver veth, vxcan implements a local CAN traffic tunnel between two virtual CAN network devices. When creating a vxcan, two vxcan devices are created as pair. When one end receives the packet it appears on its pair and vice versa. The vxcan can be used for cross namespace communication.
</entry></row>
+ <row><entry><varname>wireguard</varname></entry>
+ <entry>WireGuard Secure Network Tunnel.</entry></row>
+
</tbody>
</tgroup>
</table>
@@ -1010,6 +1013,103 @@
</refsect1>
<refsect1>
+ <title>[WireGuard] Section Options</title>
+
+ <para>The <literal>[WireGuard]</literal> section accepts the following
+ keys:</para>
+
+ <variablelist class='network-directives'>
+ <varlistentry>
+ <term><varname>PrivateKey=</varname></term>
+ <listitem>
+ <para>The Base64 encoded private key for the interface. It can be
+ generated using the <command>wg genkey</command> command
+ (see <citerefentry project="wireguard"><refentrytitle>wg</refentrytitle><manvolnum>8</manvolnum></citerefentry>).
+ This option is mandatory to use wireguard.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>ListenPort=</varname></term>
+ <listitem>
+ <para>Sets UDP port for listening. Takes either value between 1 and 65535
+ or <literal>auto</literal>. If <literal>auto</literal> is specified,
+ the port is automatically generated based on interface name.
+ Defaults to <literal>auto</literal>.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>FwMark=</varname></term>
+ <listitem>
+ <para>Sets a firewall mark on outgoing wireguard packets from this interface.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>[WireGuardPeer] Section Options</title>
+
+ <para>The <literal>[WireGuardPeer]</literal> section accepts the following
+ keys:</para>
+
+ <variablelist class='network-directives'>
+ <varlistentry>
+ <term><varname>PublicKey=</varname></term>
+ <listitem>
+ <para>Sets a Base64 encoded public key calculated by <command>wg pubkey</command>
+ (see <citerefentry project="wireguard"><refentrytitle>wg</refentrytitle><manvolnum>8</manvolnum></citerefentry>)
+ from a private key, and usually transmitted out of band to the
+ author of the configuration file. This option is mandatory for this
+ section.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>PresharedKey=</varname></term>
+ <listitem>
+ <para>Optional preshared key for the interface. It can be generated
+ by the <command>wg genpsk</command> command. This option adds an
+ additional layer of symmetric-key cryptography to be mixed into the
+ already existing public-key cryptography, for post-quantum
+ resistance.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>AllowedIPs=</varname></term>
+ <listitem>
+ <para>Sets a comma-separated list of IP (v4 or v6) addresses with CIDR masks
+ from which this peer is allowed to send incoming traffic and to
+ which outgoing traffic for this peer is directed. The catch-all
+ 0.0.0.0/0 may be specified for matching all IPv4 addresses, and
+ ::/0 may be specified for matching all IPv6 addresses. </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>Endpoint=</varname></term>
+ <listitem>
+ <para>Sets an endpoint IP address or hostname, followed by a colon, and then
+ a port number. This endpoint will be updated automatically once to
+ the most recent source IP address and port of correctly
+ authenticated packets from the peer at configuration time.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>PersistentKeepalive=</varname></term>
+ <listitem>
+ <para>Sets a seconds interval, between 1 and 65535 inclusive, of how often
+ to send an authenticated empty packet to the peer for the purpose
+ of keeping a stateful firewall or NAT mapping valid persistently.
+ For example, if the interface very rarely sends traffic, but it
+ might at anytime receive traffic from a peer, and it is behind NAT,
+ the interface might benefit from having a persistent keepalive
+ interval of 25 seconds. If set to 0 or "off", this option is
+ disabled. By default or when unspecified, this option is off.
+ Most users will not need this.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
<title>[Bond] Section Options</title>
<para>The <literal>[Bond]</literal> section accepts the following
@@ -1391,6 +1491,21 @@ Name=macvtap-test
Kind=macvtap
</programlisting>
</example>
+ <example>
+ <title>/etc/systemd/network/25-wireguard.netdev</title>
+ <programlisting>[NetDev]
+Name=wg0
+Kind=wireguard
+
+[WireGuard]
+PrivateKey=EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=
+ListenPort=51820
+
+[WireGuardPeer]
+PublicKey=RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=
+AllowedIPs=fd31:bf08:57cb::/48,192.168.26.0/24
+Endpoint=wireguard.example.com:51820</programlisting>
+ </example>
</refsect1>
<refsect1>
<title>See Also</title>
diff --git a/src/libsystemd/sd-netlink/generic-netlink.c b/src/libsystemd/sd-netlink/generic-netlink.c
index e6e0f958ba..771658d9ae 100644
--- a/src/libsystemd/sd-netlink/generic-netlink.c
+++ b/src/libsystemd/sd-netlink/generic-netlink.c
@@ -11,6 +11,7 @@ typedef struct {
static const genl_family genl_families[] = {
[SD_GENL_ID_CTRL] = { .name = "", .version = 1 },
+ [SD_GENL_WIREGUARD] = { .name = "wireguard", .version = 1 },
};
int sd_genl_socket_open(sd_netlink **ret) {
diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c
index 6dea62e313..718aa74f15 100644
--- a/src/libsystemd/sd-netlink/netlink-types.c
+++ b/src/libsystemd/sd-netlink/netlink-types.c
@@ -47,6 +47,7 @@
#include "netlink-types.h"
#include "string-table.h"
#include "util.h"
+#include "wireguard-netlink.h"
#include "sd-netlink.h"
/* Maximum ARP IP target defined in kernel */
@@ -340,7 +341,7 @@ static const char* const nl_union_link_info_data_table[] = {
[NL_UNION_LINK_INFO_DATA_VCAN] = "vcan",
[NL_UNION_LINK_INFO_DATA_GENEVE] = "geneve",
[NL_UNION_LINK_INFO_DATA_VXCAN] = "vxcan",
-
+ [NL_UNION_LINK_INFO_DATA_WIREGUARD] = "wireguard",
};
DEFINE_STRING_TABLE_LOOKUP(nl_union_link_info_data, NLUnionLinkInfoData);
@@ -672,6 +673,54 @@ const NLTypeSystem rtnl_type_system_root = {
.types = rtnl_types,
};
+static const NLType genl_wireguard_allowedip_types[] = {
+ [WGALLOWEDIP_A_FAMILY] = { .type = NETLINK_TYPE_U16 },
+ [WGALLOWEDIP_A_IPADDR] = { .type = NETLINK_TYPE_IN_ADDR },
+ [WGALLOWEDIP_A_CIDR_MASK] = { .type = NETLINK_TYPE_U8 },
+};
+
+static const NLTypeSystem genl_wireguard_allowedip_type_system = {
+ .count = ELEMENTSOF(genl_wireguard_allowedip_types),
+ .types = genl_wireguard_allowedip_types,
+};
+
+static const NLType genl_wireguard_peer_types[] = {
+ [WGPEER_A_PUBLIC_KEY] = { .size = WG_KEY_LEN },
+ [WGPEER_A_FLAGS] = { .type = NETLINK_TYPE_U32 },
+ [WGPEER_A_PRESHARED_KEY] = { .size = WG_KEY_LEN },
+ [WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL] = { .type = NETLINK_TYPE_U32 },
+ [WGPEER_A_ENDPOINT] = { /* either size of sockaddr_in or sockaddr_in6 depending on address family */ },
+ [WGPEER_A_ALLOWEDIPS] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_wireguard_allowedip_type_system },
+};
+
+static const NLTypeSystem genl_wireguard_peer_type_system = {
+ .count = ELEMENTSOF(genl_wireguard_peer_types),
+ .types = genl_wireguard_peer_types,
+};
+
+static const NLType genl_wireguard_set_device_types[] = {
+ [WGDEVICE_A_IFINDEX] = { .type = NETLINK_TYPE_U32 },
+ [WGDEVICE_A_IFNAME] = { .type = NETLINK_TYPE_STRING },
+ [WGDEVICE_A_FLAGS] = { .type = NETLINK_TYPE_U32 },
+ [WGDEVICE_A_PRIVATE_KEY] = { .size = WG_KEY_LEN },
+ [WGDEVICE_A_LISTEN_PORT] = { .type = NETLINK_TYPE_U16 },
+ [WGDEVICE_A_FWMARK] = { .type = NETLINK_TYPE_U32 },
+ [WGDEVICE_A_PEERS] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_wireguard_peer_type_system },
+};
+
+static const NLTypeSystem genl_wireguard_set_device_type_system = {
+ .count = ELEMENTSOF(genl_wireguard_set_device_types),
+ .types = genl_wireguard_set_device_types,
+};
+
+static const NLType genl_wireguard_cmds[] = {
+ [WG_CMD_SET_DEVICE] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_wireguard_set_device_type_system },
+};
+
+static const NLTypeSystem genl_wireguard_type_system = {
+ .count = ELEMENTSOF(genl_wireguard_cmds),
+ .types = genl_wireguard_cmds,
+};
static const NLType genl_get_family_types[] = {
[CTRL_ATTR_FAMILY_NAME] = { .type = NETLINK_TYPE_STRING },
@@ -694,6 +743,7 @@ static const NLTypeSystem genl_ctrl_id_ctrl_type_system = {
static const NLType genl_families[] = {
[SD_GENL_ID_CTRL] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_ctrl_id_ctrl_type_system },
+ [SD_GENL_WIREGUARD] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_wireguard_type_system },
};
const NLTypeSystem genl_family_type_system_root = {
diff --git a/src/libsystemd/sd-netlink/netlink-types.h b/src/libsystemd/sd-netlink/netlink-types.h
index 2012319e8a..ea7f8d5e6c 100644
--- a/src/libsystemd/sd-netlink/netlink-types.h
+++ b/src/libsystemd/sd-netlink/netlink-types.h
@@ -94,6 +94,7 @@ typedef enum NLUnionLinkInfoData {
NL_UNION_LINK_INFO_DATA_VCAN,
NL_UNION_LINK_INFO_DATA_GENEVE,
NL_UNION_LINK_INFO_DATA_VXCAN,
+ NL_UNION_LINK_INFO_DATA_WIREGUARD,
_NL_UNION_LINK_INFO_DATA_MAX,
_NL_UNION_LINK_INFO_DATA_INVALID = -1
} NLUnionLinkInfoData;
diff --git a/src/network/meson.build b/src/network/meson.build
index f97484eb26..d777b5f8c2 100644
--- a/src/network/meson.build
+++ b/src/network/meson.build
@@ -46,6 +46,8 @@ sources = files('''
netdev/geneve.h
netdev/vxcan.c
netdev/vxcan.h
+ netdev/wireguard.c
+ netdev/wireguard.h
networkd-address-label.c
networkd-address-label.h
networkd-address-pool.c
diff --git a/src/network/netdev/netdev-gperf.gperf b/src/network/netdev/netdev-gperf.gperf
index 1b4cb5a60c..ba6268fa66 100644
--- a/src/network/netdev/netdev-gperf.gperf
+++ b/src/network/netdev/netdev-gperf.gperf
@@ -18,6 +18,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
#include "netdev/vrf.h"
#include "netdev/netdev.h"
#include "netdev/vxcan.h"
+#include "netdev/wireguard.h"
#include "vlan-util.h"
%}
struct ConfigPerfItem;
@@ -31,117 +32,125 @@ struct ConfigPerfItem;
%struct-type
%includes
%%
-Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(NetDev, match_host)
-Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(NetDev, match_virt)
-Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(NetDev, match_kernel_cmdline)
-Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(NetDev, match_kernel_version)
-Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(NetDev, match_arch)
-NetDev.Description, config_parse_string, 0, offsetof(NetDev, description)
-NetDev.Name, config_parse_ifname, 0, offsetof(NetDev, ifname)
-NetDev.Kind, config_parse_netdev_kind, 0, offsetof(NetDev, kind)
-NetDev.MTUBytes, config_parse_iec_size, 0, offsetof(NetDev, mtu)
-NetDev.MACAddress, config_parse_hwaddr, 0, offsetof(NetDev, mac)
-VLAN.Id, config_parse_vlanid, 0, offsetof(VLan, id)
-VLAN.GVRP, config_parse_tristate, 0, offsetof(VLan, gvrp)
-VLAN.MVRP, config_parse_tristate, 0, offsetof(VLan, mvrp)
-VLAN.LooseBinding, config_parse_tristate, 0, offsetof(VLan, loose_binding)
-VLAN.ReorderHeader, config_parse_tristate, 0, offsetof(VLan, reorder_hdr)
-MACVLAN.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode)
-MACVTAP.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode)
-IPVLAN.Mode, config_parse_ipvlan_mode, 0, offsetof(IPVlan, mode)
-IPVLAN.Flags, config_parse_ipvlan_flags, 0, offsetof(IPVlan, flags)
-Tunnel.Local, config_parse_tunnel_address, 0, offsetof(Tunnel, local)
-Tunnel.Remote, config_parse_tunnel_address, 0, offsetof(Tunnel, remote)
-Tunnel.TOS, config_parse_unsigned, 0, offsetof(Tunnel, tos)
-Tunnel.TTL, config_parse_unsigned, 0, offsetof(Tunnel, ttl)
-Tunnel.Key, config_parse_tunnel_key, 0, offsetof(Tunnel, key)
-Tunnel.InputKey, config_parse_tunnel_key, 0, offsetof(Tunnel, ikey)
-Tunnel.OutputKey, config_parse_tunnel_key, 0, offsetof(Tunnel, okey)
-Tunnel.DiscoverPathMTU, config_parse_bool, 0, offsetof(Tunnel, pmtudisc)
-Tunnel.Mode, config_parse_ip6tnl_mode, 0, offsetof(Tunnel, ip6tnl_mode)
-Tunnel.IPv6FlowLabel, config_parse_ipv6_flowlabel, 0, offsetof(Tunnel, ipv6_flowlabel)
-Tunnel.CopyDSCP, config_parse_bool, 0, offsetof(Tunnel, copy_dscp)
-Tunnel.EncapsulationLimit, config_parse_encap_limit, 0, offsetof(Tunnel, encap_limit)
-Tunnel.Independent, config_parse_bool, 0, offsetof(Tunnel, independent)
-Tunnel.AllowLocalRemote, config_parse_tristate, 0, offsetof(Tunnel, allow_localremote)
-Peer.Name, config_parse_ifname, 0, offsetof(Veth, ifname_peer)
-Peer.MACAddress, config_parse_hwaddr, 0, offsetof(Veth, mac_peer)
-VXCAN.Peer, config_parse_ifname, 0, offsetof(VxCan, ifname_peer)
-VXLAN.Id, config_parse_uint64, 0, offsetof(VxLan, id)
-VXLAN.Group, config_parse_vxlan_address, 0, offsetof(VxLan, remote)
-VXLAN.Local, config_parse_vxlan_address, 0, offsetof(VxLan, local)
-VXLAN.Remote, config_parse_vxlan_address, 0, offsetof(VxLan, remote)
-VXLAN.TOS, config_parse_unsigned, 0, offsetof(VxLan, tos)
-VXLAN.TTL, config_parse_unsigned, 0, offsetof(VxLan, ttl)
-VXLAN.MacLearning, config_parse_bool, 0, offsetof(VxLan, learning)
-VXLAN.ARPProxy, config_parse_bool, 0, offsetof(VxLan, arp_proxy)
-VXLAN.ReduceARPProxy, config_parse_bool, 0, offsetof(VxLan, arp_proxy)
-VXLAN.L2MissNotification, config_parse_bool, 0, offsetof(VxLan, l2miss)
-VXLAN.L3MissNotification, config_parse_bool, 0, offsetof(VxLan, l3miss)
-VXLAN.RouteShortCircuit, config_parse_bool, 0, offsetof(VxLan, route_short_circuit)
-VXLAN.UDPCheckSum, config_parse_bool, 0, offsetof(VxLan, udpcsum)
-VXLAN.UDPChecksum, config_parse_bool, 0, offsetof(VxLan, udpcsum)
-VXLAN.UDP6ZeroCheckSumRx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumrx)
-VXLAN.UDP6ZeroChecksumRx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumrx)
-VXLAN.UDP6ZeroCheckSumTx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumtx)
-VXLAN.UDP6ZeroChecksumTx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumtx)
-VXLAN.RemoteChecksumTx, config_parse_bool, 0, offsetof(VxLan, remote_csum_tx)
-VXLAN.RemoteChecksumRx, config_parse_bool, 0, offsetof(VxLan, remote_csum_rx)
-VXLAN.FDBAgeingSec, config_parse_sec, 0, offsetof(VxLan, fdb_ageing)
-VXLAN.GroupPolicyExtension, config_parse_bool, 0, offsetof(VxLan, group_policy)
-VXLAN.MaximumFDBEntries, config_parse_unsigned, 0, offsetof(VxLan, max_fdb)
-VXLAN.PortRange, config_parse_port_range, 0, 0
-VXLAN.DestinationPort, config_parse_ip_port, 0, offsetof(VxLan, dest_port)
-VXLAN.FlowLabel, config_parse_flow_label, 0, 0
-GENEVE.Id, config_parse_geneve_vni, 0, offsetof(Geneve, id)
-GENEVE.Remote, config_parse_geneve_address, 0, offsetof(Geneve, remote)
-GENEVE.TOS, config_parse_uint8, 0, offsetof(Geneve, tos)
-GENEVE.TTL, config_parse_uint8, 0, offsetof(Geneve, ttl)
-GENEVE.UDPChecksum, config_parse_bool, 0, offsetof(Geneve, udpcsum)
-GENEVE.UDP6ZeroCheckSumRx, config_parse_bool, 0, offsetof(Geneve, udp6zerocsumrx)
-GENEVE.UDP6ZeroCheckSumTx, config_parse_bool, 0, offsetof(Geneve, udp6zerocsumtx)
-GENEVE.DestinationPort, config_parse_ip_port, 0, offsetof(Geneve, dest_port)
-GENEVE.FlowLabel, config_parse_geneve_flow_label, 0, 0
-Tun.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue)
-Tun.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue)
-Tun.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info)
-Tun.User, config_parse_string, 0, offsetof(TunTap, user_name)
-Tun.Group, config_parse_string, 0, offsetof(TunTap, group_name)
-Tap.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue)
-Tap.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue)
-Tap.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info)
-Tap.VNetHeader, config_parse_bool, 0, offsetof(TunTap, vnet_hdr)
-Tap.User, config_parse_string, 0, offsetof(TunTap, user_name)
-Tap.Group, config_parse_string, 0, offsetof(TunTap, group_name)
-Bond.Mode, config_parse_bond_mode, 0, offsetof(Bond, mode)
-Bond.TransmitHashPolicy, config_parse_bond_xmit_hash_policy, 0, offsetof(Bond, xmit_hash_policy)
-Bond.LACPTransmitRate, config_parse_bond_lacp_rate, 0, offsetof(Bond, lacp_rate)
-Bond.AdSelect, config_parse_bond_ad_select, 0, offsetof(Bond, ad_select)
-Bond.FailOverMACPolicy, config_parse_bond_fail_over_mac, 0, offsetof(Bond, fail_over_mac)
-Bond.ARPIPTargets, config_parse_arp_ip_target_address, 0, 0
-Bond.ARPValidate, config_parse_bond_arp_validate, 0, offsetof(Bond, arp_validate)
-Bond.ARPAllTargets, config_parse_bond_arp_all_targets, 0, offsetof(Bond, arp_all_targets)
-Bond.PrimaryReselectPolicy, config_parse_bond_primary_reselect, 0, offsetof(Bond, primary_reselect)
-Bond.ResendIGMP, config_parse_unsigned, 0, offsetof(Bond, resend_igmp)
-Bond.PacketsPerSlave, config_parse_unsigned, 0, offsetof(Bond, packets_per_slave)
-Bond.GratuitousARP, config_parse_unsigned, 0, offsetof(Bond, num_grat_arp)
-Bond.AllSlavesActive, config_parse_unsigned, 0, offsetof(Bond, all_slaves_active)
-Bond.MinLinks, config_parse_unsigned, 0, offsetof(Bond, min_links)
-Bond.MIIMonitorSec, config_parse_sec, 0, offsetof(Bond, miimon)
-Bond.UpDelaySec, config_parse_sec, 0, offsetof(Bond, updelay)
-Bond.DownDelaySec, config_parse_sec, 0, offsetof(Bond, downdelay)
-Bond.ARPIntervalSec, config_parse_sec, 0, offsetof(Bond, arp_interval)
-Bond.LearnPacketIntervalSec, config_parse_sec, 0, offsetof(Bond, lp_interval)
-Bridge.HelloTimeSec, config_parse_sec, 0, offsetof(Bridge, hello_time)
-Bridge.MaxAgeSec, config_parse_sec, 0, offsetof(Bridge, max_age)
-Bridge.AgeingTimeSec, config_parse_sec, 0, offsetof(Bridge, ageing_time)
-Bridge.ForwardDelaySec, config_parse_sec, 0, offsetof(Bridge, forward_delay)
-Bridge.Priority, config_parse_uint16, 0, offsetof(Bridge, priority)
-Bridge.GroupForwardMask, config_parse_uint16, 0, offsetof(Bridge, group_fwd_mask)
-Bridge.DefaultPVID, config_parse_default_port_vlanid, 0, offsetof(Bridge, default_pvid)
-Bridge.MulticastQuerier, config_parse_tristate, 0, offsetof(Bridge, mcast_querier)
-Bridge.MulticastSnooping, config_parse_tristate, 0, offsetof(Bridge, mcast_snooping)
-Bridge.VLANFiltering, config_parse_tristate, 0, offsetof(Bridge, vlan_filtering)
-Bridge.STP, config_parse_tristate, 0, offsetof(Bridge, stp)
-VRF.TableId, config_parse_uint32, 0, offsetof(Vrf, table) /* deprecated */
-VRF.Table, config_parse_route_table, 0, offsetof(Vrf, table)
+Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(NetDev, match_host)
+Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(NetDev, match_virt)
+Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(NetDev, match_kernel_cmdline)
+Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(NetDev, match_kernel_version)
+Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(NetDev, match_arch)
+NetDev.Description, config_parse_string, 0, offsetof(NetDev, description)
+NetDev.Name, config_parse_ifname, 0, offsetof(NetDev, ifname)
+NetDev.Kind, config_parse_netdev_kind, 0, offsetof(NetDev, kind)
+NetDev.MTUBytes, config_parse_iec_size, 0, offsetof(NetDev, mtu)
+NetDev.MACAddress, config_parse_hwaddr, 0, offsetof(NetDev, mac)
+VLAN.Id, config_parse_vlanid, 0, offsetof(VLan, id)
+VLAN.GVRP, config_parse_tristate, 0, offsetof(VLan, gvrp)
+VLAN.MVRP, config_parse_tristate, 0, offsetof(VLan, mvrp)
+VLAN.LooseBinding, config_parse_tristate, 0, offsetof(VLan, loose_binding)
+VLAN.ReorderHeader, config_parse_tristate, 0, offsetof(VLan, reorder_hdr)
+MACVLAN.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode)
+MACVTAP.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode)
+IPVLAN.Mode, config_parse_ipvlan_mode, 0, offsetof(IPVlan, mode)
+IPVLAN.Flags, config_parse_ipvlan_flags, 0, offsetof(IPVlan, flags)
+Tunnel.Local, config_parse_tunnel_address, 0, offsetof(Tunnel, local)
+Tunnel.Remote, config_parse_tunnel_address, 0, offsetof(Tunnel, remote)
+Tunnel.TOS, config_parse_unsigned, 0, offsetof(Tunnel, tos)
+Tunnel.TTL, config_parse_unsigned, 0, offsetof(Tunnel, ttl)
+Tunnel.Key, config_parse_tunnel_key, 0, offsetof(Tunnel, key)
+Tunnel.InputKey, config_parse_tunnel_key, 0, offsetof(Tunnel, ikey)
+Tunnel.OutputKey, config_parse_tunnel_key, 0, offsetof(Tunnel, okey)
+Tunnel.DiscoverPathMTU, config_parse_bool, 0, offsetof(Tunnel, pmtudisc)
+Tunnel.Mode, config_parse_ip6tnl_mode, 0, offsetof(Tunnel, ip6tnl_mode)
+Tunnel.IPv6FlowLabel, config_parse_ipv6_flowlabel, 0, offsetof(Tunnel, ipv6_flowlabel)
+Tunnel.CopyDSCP, config_parse_bool, 0, offsetof(Tunnel, copy_dscp)
+Tunnel.EncapsulationLimit, config_parse_encap_limit, 0, offsetof(Tunnel, encap_limit)
+Tunnel.Independent, config_parse_bool, 0, offsetof(Tunnel, independent)
+Tunnel.AllowLocalRemote, config_parse_tristate, 0, offsetof(Tunnel, allow_localremote)
+Peer.Name, config_parse_ifname, 0, offsetof(Veth, ifname_peer)
+Peer.MACAddress, config_parse_hwaddr, 0, offsetof(Veth, mac_peer)
+VXCAN.Peer, config_parse_ifname, 0, offsetof(VxCan, ifname_peer)
+VXLAN.Id, config_parse_uint64, 0, offsetof(VxLan, id)
+VXLAN.Group, config_parse_vxlan_address, 0, offsetof(VxLan, remote)
+VXLAN.Local, config_parse_vxlan_address, 0, offsetof(VxLan, local)
+VXLAN.Remote, config_parse_vxlan_address, 0, offsetof(VxLan, remote)
+VXLAN.TOS, config_parse_unsigned, 0, offsetof(VxLan, tos)
+VXLAN.TTL, config_parse_unsigned, 0, offsetof(VxLan, ttl)
+VXLAN.MacLearning, config_parse_bool, 0, offsetof(VxLan, learning)
+VXLAN.ARPProxy, config_parse_bool, 0, offsetof(VxLan, arp_proxy)
+VXLAN.ReduceARPProxy, config_parse_bool, 0, offsetof(VxLan, arp_proxy)
+VXLAN.L2MissNotification, config_parse_bool, 0, offsetof(VxLan, l2miss)
+VXLAN.L3MissNotification, config_parse_bool, 0, offsetof(VxLan, l3miss)
+VXLAN.RouteShortCircuit, config_parse_bool, 0, offsetof(VxLan, route_short_circuit)
+VXLAN.UDPCheckSum, config_parse_bool, 0, offsetof(VxLan, udpcsum)
+VXLAN.UDPChecksum, config_parse_bool, 0, offsetof(VxLan, udpcsum)
+VXLAN.UDP6ZeroCheckSumRx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumrx)
+VXLAN.UDP6ZeroChecksumRx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumrx)
+VXLAN.UDP6ZeroCheckSumTx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumtx)
+VXLAN.UDP6ZeroChecksumTx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumtx)
+VXLAN.RemoteChecksumTx, config_parse_bool, 0, offsetof(VxLan, remote_csum_tx)
+VXLAN.RemoteChecksumRx, config_parse_bool, 0, offsetof(VxLan, remote_csum_rx)
+VXLAN.FDBAgeingSec, config_parse_sec, 0, offsetof(VxLan, fdb_ageing)
+VXLAN.GroupPolicyExtension, config_parse_bool, 0, offsetof(VxLan, group_policy)
+VXLAN.MaximumFDBEntries, config_parse_unsigned, 0, offsetof(VxLan, max_fdb)
+VXLAN.PortRange, config_parse_port_range, 0, 0
+VXLAN.DestinationPort, config_parse_ip_port, 0, offsetof(VxLan, dest_port)
+VXLAN.FlowLabel, config_parse_flow_label, 0, 0
+GENEVE.Id, config_parse_geneve_vni, 0, offsetof(Geneve, id)
+GENEVE.Remote, config_parse_geneve_address, 0, offsetof(Geneve, remote)
+GENEVE.TOS, config_parse_uint8, 0, offsetof(Geneve, tos)
+GENEVE.TTL, config_parse_uint8, 0, offsetof(Geneve, ttl)
+GENEVE.UDPChecksum, config_parse_bool, 0, offsetof(Geneve, udpcsum)
+GENEVE.UDP6ZeroCheckSumRx, config_parse_bool, 0, offsetof(Geneve, udp6zerocsumrx)
+GENEVE.UDP6ZeroCheckSumTx, config_parse_bool, 0, offsetof(Geneve, udp6zerocsumtx)
+GENEVE.DestinationPort, config_parse_ip_port, 0, offsetof(Geneve, dest_port)
+GENEVE.FlowLabel, config_parse_geneve_flow_label, 0, 0
+Tun.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue)
+Tun.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue)
+Tun.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info)
+Tun.User, config_parse_string, 0, offsetof(TunTap, user_name)
+Tun.Group, config_parse_string, 0, offsetof(TunTap, group_name)
+Tap.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue)
+Tap.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue)
+Tap.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info)
+Tap.VNetHeader, config_parse_bool, 0, offsetof(TunTap, vnet_hdr)
+Tap.User, config_parse_string, 0, offsetof(TunTap, user_name)
+Tap.Group, config_parse_string, 0, offsetof(TunTap, group_name)
+Bond.Mode, config_parse_bond_mode, 0, offsetof(Bond, mode)
+Bond.TransmitHashPolicy, config_parse_bond_xmit_hash_policy, 0, offsetof(Bond, xmit_hash_policy)
+Bond.LACPTransmitRate, config_parse_bond_lacp_rate, 0, offsetof(Bond, lacp_rate)
+Bond.AdSelect, config_parse_bond_ad_select, 0, offsetof(Bond, ad_select)
+Bond.FailOverMACPolicy, config_parse_bond_fail_over_mac, 0, offsetof(Bond, fail_over_mac)
+Bond.ARPIPTargets, config_parse_arp_ip_target_address, 0, 0
+Bond.ARPValidate, config_parse_bond_arp_validate, 0, offsetof(Bond, arp_validate)
+Bond.ARPAllTargets, config_parse_bond_arp_all_targets, 0, offsetof(Bond, arp_all_targets)
+Bond.PrimaryReselectPolicy, config_parse_bond_primary_reselect, 0, offsetof(Bond, primary_reselect)
+Bond.ResendIGMP, config_parse_unsigned, 0, offsetof(Bond, resend_igmp)
+Bond.PacketsPerSlave, config_parse_unsigned, 0, offsetof(Bond, packets_per_slave)
+Bond.GratuitousARP, config_parse_unsigned, 0, offsetof(Bond, num_grat_arp)
+Bond.AllSlavesActive, config_parse_unsigned, 0, offsetof(Bond, all_slaves_active)
+Bond.MinLinks, config_parse_unsigned, 0, offsetof(Bond, min_links)
+Bond.MIIMonitorSec, config_parse_sec, 0, offsetof(Bond, miimon)
+Bond.UpDelaySec, config_parse_sec, 0, offsetof(Bond, updelay)
+Bond.DownDelaySec, config_parse_sec, 0, offsetof(Bond, downdelay)
+Bond.ARPIntervalSec, config_parse_sec, 0, offsetof(Bond, arp_interval)
+Bond.LearnPacketIntervalSec, config_parse_sec, 0, offsetof(Bond, lp_interval)
+Bridge.HelloTimeSec, config_parse_sec, 0, offsetof(Bridge, hello_time)
+Bridge.MaxAgeSec, config_parse_sec, 0, offsetof(Bridge, max_age)
+Bridge.AgeingTimeSec, config_parse_sec, 0, offsetof(Bridge, ageing_time)
+Bridge.ForwardDelaySec, config_parse_sec, 0, offsetof(Bridge, forward_delay)
+Bridge.Priority, config_parse_uint16, 0, offsetof(Bridge, priority)
+Bridge.GroupForwardMask, config_parse_uint16, 0, offsetof(Bridge, group_fwd_mask)
+Bridge.DefaultPVID, config_parse_default_port_vlanid, 0, offsetof(Bridge, default_pvid)
+Bridge.MulticastQuerier, config_parse_tristate, 0, offsetof(Bridge, mcast_querier)
+Bridge.MulticastSnooping, config_parse_tristate, 0, offsetof(Bridge, mcast_snooping)
+Bridge.VLANFiltering, config_parse_tristate, 0, offsetof(Bridge, vlan_filtering)
+Bridge.STP, config_parse_tristate, 0, offsetof(Bridge, stp)
+VRF.TableId, config_parse_uint32, 0, offsetof(Vrf, table) /* deprecated */
+VRF.Table, config_parse_route_table, 0, offsetof(Vrf, table)
+WireGuard.FwMark, config_parse_unsigned, 0, offsetof(Wireguard, fwmark)
+WireGuard.ListenPort, config_parse_wireguard_listen_port, 0, offsetof(Wireguard, port)
+WireGuard.PrivateKey, config_parse_wireguard_private_key, 0, 0
+WireGuardPeer.AllowedIPs, config_parse_wireguard_allowed_ips, 0, 0
+WireGuardPeer.Endpoint, config_parse_wireguard_endpoint, 0, 0
+WireGuardPeer.PublicKey, config_parse_wireguard_public_key, 0, 0
+WireGuardPeer.PresharedKey, config_parse_wireguard_preshared_key, 0, 0
+WireGuardPeer.PersistentKeepalive, config_parse_wireguard_keepalive, 0, 0
diff --git a/src/network/netdev/netdev.c b/src/network/netdev/netdev.c
index 4adaac26b8..d609b0fb72 100644
--- a/src/network/netdev/netdev.c
+++ b/src/network/netdev/netdev.c
@@ -49,6 +49,7 @@
#include "netdev/vrf.h"
#include "netdev/vcan.h"
#include "netdev/vxcan.h"
+#include "netdev/wireguard.h"
const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = {
[NETDEV_KIND_BRIDGE] = &bridge_vtable,
@@ -75,6 +76,7 @@ const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = {
[NETDEV_KIND_VCAN] = &vcan_vtable,
[NETDEV_KIND_GENEVE] = &geneve_vtable,
[NETDEV_KIND_VXCAN] = &vxcan_vtable,
+ [NETDEV_KIND_WIREGUARD] = &wireguard_vtable,
};
static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
@@ -102,6 +104,7 @@ static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
[NETDEV_KIND_VCAN] = "vcan",
[NETDEV_KIND_GENEVE] = "geneve",
[NETDEV_KIND_VXCAN] = "vxcan",
+ [NETDEV_KIND_WIREGUARD] = "wireguard",
};
DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind);
diff --git a/src/network/netdev/netdev.h b/src/network/netdev/netdev.h
index 507b86a078..bd1a94c9be 100644
--- a/src/network/netdev/netdev.h
+++ b/src/network/netdev/netdev.h
@@ -60,6 +60,7 @@ typedef enum NetDevKind {
NETDEV_KIND_VCAN,
NETDEV_KIND_GENEVE,
NETDEV_KIND_VXCAN,
+ NETDEV_KIND_WIREGUARD,
_NETDEV_KIND_MAX,
_NETDEV_KIND_INVALID = -1
} NetDevKind;
diff --git a/src/network/netdev/wireguard.c b/src/network/netdev/wireguard.c
new file mode 100644
index 0000000000..f1f4bab475
--- /dev/null
+++ b/src/network/netdev/wireguard.c
@@ -0,0 +1,721 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016-2017 Jörg Thalheim <joerg@thalheim.io>
+ Copyright 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/ioctl.h>
+#include <net/if.h>
+
+#include "alloc-util.h"
+#include "parse-util.h"
+#include "fd-util.h"
+#include "strv.h"
+#include "hexdecoct.h"
+#include "string-util.h"
+#include "wireguard.h"
+#include "networkd-link.h"
+#include "networkd-util.h"
+#include "networkd-manager.h"
+#include "wireguard-netlink.h"
+
+static void resolve_endpoints(NetDev *netdev);
+
+static WireguardPeer *wireguard_peer_new(Wireguard *w, unsigned section) {
+ WireguardPeer *peer;
+
+ assert(w);
+
+ if (w->last_peer_section == section && w->peers)
+ return w->peers;
+
+ peer = new0(WireguardPeer, 1);
+ if (!peer)
+ return NULL;
+ peer->flags = WGPEER_F_REPLACE_ALLOWEDIPS;
+
+ LIST_PREPEND(peers, w->peers, peer);
+ w->last_peer_section = section;
+
+ return peer;
+}
+
+static int set_wireguard_interface(NetDev *netdev) {
+ int r;
+ unsigned int i, j;
+ WireguardPeer *peer, *peer_start;
+ WireguardIPmask *mask, *mask_start = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
+ Wireguard *w;
+ uint32_t serial;
+
+ assert(netdev);
+ w = WIREGUARD(netdev);
+ assert(w);
+
+ peer_start = w->peers;
+
+ do {
+ message = sd_netlink_message_unref(message);
+
+ r = sd_genl_message_new(netdev->manager->genl, SD_GENL_WIREGUARD, WG_CMD_SET_DEVICE, &message);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Failed to allocate generic netlink message: %m");
+
+ r = sd_netlink_message_append_string(message, WGDEVICE_A_IFNAME, netdev->ifname);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append wireguard interface name: %m");
+
+ if (peer_start == w->peers) {
+ r = sd_netlink_message_append_data(message, WGDEVICE_A_PRIVATE_KEY, &w->private_key, WG_KEY_LEN);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append wireguard private key: %m");
+
+ r = sd_netlink_message_append_u16(message, WGDEVICE_A_LISTEN_PORT, w->port);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append wireguard port: %m");
+
+ r = sd_netlink_message_append_u32(message, WGDEVICE_A_FWMARK, w->fwmark);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append wireguard fwmark: %m");
+
+ r = sd_netlink_message_append_u32(message, WGDEVICE_A_FLAGS, w->flags);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append wireguard flags: %m");
+ }
+
+ r = sd_netlink_message_open_container(message, WGDEVICE_A_PEERS);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append wireguard peer attributes: %m");
+
+ i = 0;
+
+ LIST_FOREACH(peers, peer, peer_start) {
+ r = sd_netlink_message_open_array(message, ++i);
+ if (r < 0)
+ break;
+
+ r = sd_netlink_message_append_data(message, WGPEER_A_PUBLIC_KEY, &peer->public_key, sizeof(peer->public_key));
+ if (r < 0)
+ break;
+
+ if (!mask_start) {
+ r = sd_netlink_message_append_data(message, WGPEER_A_PRESHARED_KEY, &peer->preshared_key, WG_KEY_LEN);
+ if (r < 0)
+ break;
+
+ r = sd_netlink_message_append_u32(message, WGPEER_A_FLAGS, peer->flags);
+ if (r < 0)
+ break;
+
+ r = sd_netlink_message_append_u32(message, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval);
+ if (r < 0)
+ break;
+
+ if (peer->endpoint.sa.sa_family == AF_INET) {
+ r = sd_netlink_message_append_data(message, WGPEER_A_ENDPOINT, &peer->endpoint.in, sizeof(peer->endpoint.in));
+ if (r < 0)
+ break;
+ } else if (peer->endpoint.sa.sa_family == AF_INET6) {
+ r = sd_netlink_message_append_data(message, WGPEER_A_ENDPOINT, &peer->endpoint.in6, sizeof(peer->endpoint.in6));
+ if (r < 0)
+ break;
+ }
+
+ mask_start = peer->ipmasks;
+ }
+
+ r = sd_netlink_message_open_container(message, WGPEER_A_ALLOWEDIPS);
+ if (r < 0) {
+ mask_start = NULL;
+ break;
+ }
+ j = 0;
+ LIST_FOREACH(ipmasks, mask, mask_start) {
+ r = sd_netlink_message_open_array(message, ++j);
+ if (r < 0)
+ break;
+
+ r = sd_netlink_message_append_u16(message, WGALLOWEDIP_A_FAMILY, mask->family);
+ if (r < 0)
+ break;
+
+ if (mask->family == AF_INET) {
+ r = sd_netlink_message_append_in_addr(message, WGALLOWEDIP_A_IPADDR, &mask->ip.in);
+ if (r < 0)
+ break;
+ } else if (mask->family == AF_INET6) {
+ r = sd_netlink_message_append_in6_addr(message, WGALLOWEDIP_A_IPADDR, &mask->ip.in6);
+ if (r < 0)
+ break;
+ }
+
+ r = sd_netlink_message_append_u8(message, WGALLOWEDIP_A_CIDR_MASK, mask->cidr);
+ if (r < 0)
+ break;
+
+ r = sd_netlink_message_close_container(message);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not add wireguard allowed ip: %m");
+ }
+ mask_start = mask;
+ if (mask_start) {
+ r = sd_netlink_message_cancel_array(message);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not cancel wireguard allowed ip message attribute: %m");
+ }
+ r = sd_netlink_message_close_container(message);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not add wireguard allowed ip: %m");
+
+ r = sd_netlink_message_close_container(message);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not add wireguard peer: %m");
+ }
+
+ peer_start = peer;
+ if (peer_start && !mask_start) {
+ r = sd_netlink_message_cancel_array(message);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not cancel wireguard peers: %m");
+ }
+
+ r = sd_netlink_message_close_container(message);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not close wireguard container: %m");
+
+ r = sd_netlink_send(netdev->manager->genl, message, &serial);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not set wireguard device: %m");
+
+ } while (peer || mask_start);
+
+ return 0;
+}
+
+static WireguardEndpoint* wireguard_endpoint_free(WireguardEndpoint *e) {
+ if (!e)
+ return NULL;
+ netdev_unref(e->netdev);
+ e->host = mfree(e->host);
+ e->port = mfree(e->port);
+ return mfree(e);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(WireguardEndpoint*, wireguard_endpoint_free);
+
+static int on_resolve_retry(sd_event_source *s, usec_t usec, void *userdata) {
+ NetDev *netdev = userdata;
+ Wireguard *w;
+
+ assert(netdev);
+ w = WIREGUARD(netdev);
+ assert(w);
+
+ w->resolve_retry_event_source = sd_event_source_unref(w->resolve_retry_event_source);
+
+ w->unresolved_endpoints = w->failed_endpoints;
+ w->failed_endpoints = NULL;
+
+ resolve_endpoints(netdev);
+
+ return 0;
+}
+
+/*
+ * Given the number of retries this function will return will an exponential
+ * increasing time in milliseconds to wait starting at 200ms and capped at 25 seconds.
+ */
+static int exponential_backoff_milliseconds(unsigned n_retries) {
+ return (2 << MAX(n_retries, 7U)) * 100 * USEC_PER_MSEC;
+}
+
+static int wireguard_resolve_handler(sd_resolve_query *q,
+ int ret,
+ const struct addrinfo *ai,
+ void *userdata) {
+ NetDev *netdev;
+ Wireguard *w;
+ _cleanup_(wireguard_endpoint_freep) WireguardEndpoint *e;
+ int r;
+
+ assert(userdata);
+ e = userdata;
+ netdev = e->netdev;
+
+ assert(netdev);
+ w = WIREGUARD(netdev);
+ assert(w);
+
+ w->resolve_query = sd_resolve_query_unref(w->resolve_query);
+
+ if (ret != 0) {
+ log_netdev_error(netdev, "Failed to resolve host '%s:%s': %s", e->host, e->port, gai_strerror(ret));
+ LIST_PREPEND(endpoints, w->failed_endpoints, e);
+ e = NULL;
+ } else if ((ai->ai_family == AF_INET && ai->ai_addrlen == sizeof(struct sockaddr_in)) ||
+ (ai->ai_family == AF_INET6 && ai->ai_addrlen == sizeof(struct sockaddr_in6)))
+ memcpy(&e->peer->endpoint, ai->ai_addr, ai->ai_addrlen);
+ else
+ log_netdev_error(netdev, "Neither IPv4 nor IPv6 address found for peer endpoint: %s:%s", e->host, e->port);
+
+ if (w->unresolved_endpoints) {
+ resolve_endpoints(netdev);
+ return 0;
+ }
+
+ set_wireguard_interface(netdev);
+ if (w->failed_endpoints) {
+ w->n_retries++;
+ r = sd_event_add_time(netdev->manager->event,
+ &w->resolve_retry_event_source,
+ CLOCK_MONOTONIC,
+ now(CLOCK_MONOTONIC) + exponential_backoff_milliseconds(w->n_retries),
+ 0,
+ on_resolve_retry,
+ netdev);
+ if (r < 0)
+ log_netdev_warning_errno(netdev, r, "Could not arm resolve retry handler: %m");
+ }
+
+ return 0;
+}
+
+static void resolve_endpoints(NetDev *netdev) {
+ int r = 0;
+ Wireguard *w;
+ WireguardEndpoint *endpoint;
+ static const struct addrinfo hints = {
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_DGRAM,
+ .ai_protocol = IPPROTO_UDP
+ };
+
+ assert(netdev);
+ w = WIREGUARD(netdev);
+ assert(w);
+
+ LIST_FOREACH(endpoints, endpoint, w->unresolved_endpoints) {
+ r = sd_resolve_getaddrinfo(netdev->manager->resolve,
+ &w->resolve_query,
+ endpoint->host,
+ endpoint->port,
+ &hints,
+ wireguard_resolve_handler,
+ endpoint);
+
+ if (r == -ENOBUFS)
+ break;
+
+ LIST_REMOVE(endpoints, w->unresolved_endpoints, endpoint);
+
+ if (r < 0)
+ log_netdev_error_errno(netdev, r, "Failed create resolver: %m");
+ }
+}
+
+
+static int netdev_wireguard_post_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
+ Wireguard *w;
+
+ assert(netdev);
+ w = WIREGUARD(netdev);
+ assert(w);
+
+ set_wireguard_interface(netdev);
+ resolve_endpoints(netdev);
+ return 0;
+}
+
+int config_parse_wireguard_listen_port(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) {
+ uint16_t *s = data;
+ uint16_t port = 0;
+ int r;
+
+ assert(rvalue);
+ assert(data);
+
+ if (!streq(rvalue, "auto")) {
+ r = parse_ip_port(rvalue, &port);
+ if (r < 0)
+ log_syntax(unit, LOG_ERR, filename, line, r, "Invalid port specification, ignoring assignment: %s", rvalue);
+ }
+
+ *s = port;
+
+ return 0;
+}
+
+static int parse_wireguard_key(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_ void *key = NULL;
+ size_t len;
+ int r;
+
+ assert(filename);
+ assert(rvalue);
+ assert(userdata);
+
+ r = unbase64mem(rvalue, strlen(rvalue), &key, &len);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Could not parse wireguard key \"%s\", ignoring assignment: %m", rvalue);
+ return 0;
+ }
+ if (len != WG_KEY_LEN) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Wireguard key is too short, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+
+ memcpy(userdata, key, WG_KEY_LEN);
+ return true;
+}
+
+int config_parse_wireguard_private_key(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) {
+ Wireguard *w;
+
+ assert(data);
+
+ w = WIREGUARD(data);
+
+ assert(w);
+
+ return parse_wireguard_key(unit,
+ filename,
+ line,
+ section,
+ section_line,
+ lvalue,
+ ltype,
+ rvalue,
+ data,
+ &w->private_key);
+
+}
+
+int config_parse_wireguard_preshared_key(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) {
+ Wireguard *w;
+ WireguardPeer *peer;
+
+ assert(data);
+
+ w = WIREGUARD(data);
+
+ assert(w);
+
+ peer = wireguard_peer_new(w, section_line);
+ if (!peer)
+ return log_oom();
+
+ return parse_wireguard_key(unit,
+ filename,
+ line,
+ section,
+ section_line,
+ lvalue,
+ ltype,
+ rvalue,
+ data,
+ peer->preshared_key);
+}
+
+
+int config_parse_wireguard_public_key(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) {
+ Wireguard *w;
+ WireguardPeer *peer;
+
+ assert(data);
+
+ w = WIREGUARD(data);
+
+ assert(w);
+
+ peer = wireguard_peer_new(w, section_line);
+ if (!peer)
+ return log_oom();
+
+ return parse_wireguard_key(unit,
+ filename,
+ line,
+ section,
+ section_line,
+ lvalue,
+ ltype,
+ rvalue,
+ data,
+ peer->public_key);
+}
+
+int config_parse_wireguard_allowed_ips(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) {
+ union in_addr_union addr;
+ unsigned char prefixlen;
+ int r, family;
+ Wireguard *w;
+ WireguardPeer *peer;
+ WireguardIPmask *ipmask;
+
+ assert(rvalue);
+ assert(data);
+
+ w = WIREGUARD(data);
+
+ peer = wireguard_peer_new(w, section_line);
+ if (!peer)
+ return log_oom();
+
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
+
+ r = extract_first_word(&rvalue, &word, "," WHITESPACE, 0);
+ if (r == 0)
+ break;
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to split allowed ips \"%s\" option: %m", rvalue);
+ break;
+ }
+
+ r = in_addr_prefix_from_string_auto(word, &family, &addr, &prefixlen);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Network address is invalid, ignoring assignment: %s", word);
+ return 0;
+ }
+
+ ipmask = new0(WireguardIPmask, 1);
+ if (!ipmask)
+ return log_oom();
+ ipmask->family = family;
+ ipmask->ip.in6 = addr.in6;
+ ipmask->cidr = prefixlen;
+
+ LIST_PREPEND(ipmasks, peer->ipmasks, ipmask);
+ }
+
+ return 0;
+}
+
+int config_parse_wireguard_endpoint(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) {
+ Wireguard *w;
+ WireguardPeer *peer;
+ size_t len;
+ const char *begin, *end = NULL;
+ _cleanup_free_ char *host = NULL, *port = NULL;
+ _cleanup_(wireguard_endpoint_freep) WireguardEndpoint *endpoint = NULL;
+
+ assert(data);
+ assert(rvalue);
+
+ w = WIREGUARD(data);
+
+ assert(w);
+
+ peer = wireguard_peer_new(w, section_line);
+ if (!peer)
+ return log_oom();
+
+ endpoint = new0(WireguardEndpoint, 1);
+ if (!endpoint)
+ return log_oom();
+
+ if (rvalue[0] == '[') {
+ begin = &rvalue[1];
+ end = strchr(rvalue, ']');
+ if (!end) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Unable to find matching brace of endpoint, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+ len = end - begin;
+ ++end;
+ if (*end != ':' || !*(end + 1)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Unable to find port of endpoint: %s", rvalue);
+ return 0;
+ }
+ ++end;
+ } else {
+ begin = rvalue;
+ end = strrchr(rvalue, ':');
+ if (!end || !*(end + 1)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Unable to find port of endpoint: %s", rvalue);
+ return 0;
+ }
+ len = end - begin;
+ ++end;
+ }
+
+ host = strndup(begin, len);
+ if (!host)
+ return log_oom();
+
+ port = strdup(end);
+ if (!port)
+ return log_oom();
+
+ endpoint->peer = peer;
+ endpoint->host = host;
+ endpoint->port = port;
+ endpoint->netdev = netdev_ref(data);
+ LIST_PREPEND(endpoints, w->unresolved_endpoints, endpoint);
+
+ peer = NULL;
+ host = NULL;
+ port = NULL;
+ endpoint = NULL;
+
+ return 0;
+}
+
+int config_parse_wireguard_keepalive(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) {
+ int r;
+ uint16_t keepalive = 0;
+ Wireguard *w;
+ WireguardPeer *peer;
+
+ assert(rvalue);
+ assert(data);
+
+ w = WIREGUARD(data);
+
+ assert(w);
+
+ peer = wireguard_peer_new(w, section_line);
+ if (!peer)
+ return log_oom();
+
+ if (streq(rvalue, "off"))
+ keepalive = 0;
+ else {
+ r = safe_atou16(rvalue, &keepalive);
+ if (r < 0)
+ log_syntax(unit, LOG_ERR, filename, line, r, "The persistent keepalive interval must be 0-65535. Ignore assignment: %s", rvalue);
+ }
+
+ peer->persistent_keepalive_interval = keepalive;
+ return 0;
+}
+
+static void wireguard_init(NetDev *netdev) {
+ Wireguard *w;
+
+ assert(netdev);
+
+ w = WIREGUARD(netdev);
+
+ assert(w);
+
+ w->flags = WGDEVICE_F_REPLACE_PEERS;
+}
+
+static void wireguard_done(NetDev *netdev) {
+ Wireguard *w;
+ WireguardPeer *peer;
+ WireguardIPmask *mask;
+
+ assert(netdev);
+ w = WIREGUARD(netdev);
+ assert(!w->unresolved_endpoints);
+ w->resolve_retry_event_source = sd_event_source_unref(w->resolve_retry_event_source);
+
+ while ((peer = w->peers)) {
+ LIST_REMOVE(peers, w->peers, peer);
+ while ((mask = peer->ipmasks)) {
+ LIST_REMOVE(ipmasks, peer->ipmasks, mask);
+ free(mask);
+ }
+ free(peer);
+ }
+}
+
+const NetDevVTable wireguard_vtable = {
+ .object_size = sizeof(Wireguard),
+ .sections = "Match\0NetDev\0WireGuard\0WireGuardPeer\0",
+ .post_create = netdev_wireguard_post_create,
+ .init = wireguard_init,
+ .done = wireguard_done,
+ .create_type = NETDEV_CREATE_INDEPENDENT,
+};
diff --git a/src/network/netdev/wireguard.h b/src/network/netdev/wireguard.h
new file mode 100644
index 0000000000..f788fa4221
--- /dev/null
+++ b/src/network/netdev/wireguard.h
@@ -0,0 +1,98 @@
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Jörg Thalheim <joerg@thalheim.io>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct Wireguard Wireguard;
+
+#include "netdev.h"
+#include "sd-resolve.h"
+#include "wireguard-netlink.h"
+#include "socket-util.h"
+#include "in-addr-util.h"
+
+#ifndef IFNAMSIZ
+#define IFNAMSIZ 16
+#endif
+
+typedef struct WireguardIPmask {
+ uint16_t family;
+ union in_addr_union ip;
+ uint8_t cidr;
+
+ LIST_FIELDS(struct WireguardIPmask, ipmasks);
+} WireguardIPmask;
+
+typedef struct WireguardPeer {
+ uint8_t public_key[WG_KEY_LEN];
+ uint8_t preshared_key[WG_KEY_LEN];
+ uint32_t flags;
+
+ union sockaddr_union endpoint;
+
+ uint16_t persistent_keepalive_interval;
+
+ LIST_HEAD(WireguardIPmask, ipmasks);
+ LIST_FIELDS(struct WireguardPeer, peers);
+} WireguardPeer;
+
+typedef struct WireguardEndpoint {
+ char *host;
+ char *port;
+
+ NetDev *netdev;
+ WireguardPeer *peer;
+
+ LIST_FIELDS(struct WireguardEndpoint, endpoints);
+} WireguardEndpoint;
+
+struct Wireguard {
+ NetDev meta;
+ unsigned last_peer_section;
+
+ char interface[IFNAMSIZ];
+ uint32_t flags;
+
+ uint8_t public_key[WG_KEY_LEN];
+ uint8_t private_key[WG_KEY_LEN];
+ uint32_t fwmark;
+
+ uint16_t port;
+
+ LIST_HEAD(WireguardPeer, peers);
+ size_t allocation_size;
+ sd_event_source *resolve_retry_event_source;
+
+ LIST_HEAD(WireguardEndpoint, unresolved_endpoints);
+ LIST_HEAD(WireguardEndpoint, failed_endpoints);
+ unsigned n_retries;
+ sd_resolve_query *resolve_query;
+};
+
+DEFINE_NETDEV_CAST(WIREGUARD, Wireguard);
+extern const NetDevVTable wireguard_vtable;
+
+int config_parse_wireguard_allowed_ips(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);
+int config_parse_wireguard_endpoint(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);
+int config_parse_wireguard_listen_port(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);
+
+int config_parse_wireguard_public_key(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);
+int config_parse_wireguard_private_key(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);
+int config_parse_wireguard_preshared_key(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);
+int config_parse_wireguard_keepalive(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);
diff --git a/src/shared/meson.build b/src/shared/meson.build
index 06a944c49d..dbf6cca321 100644
--- a/src/shared/meson.build
+++ b/src/shared/meson.build
@@ -120,6 +120,7 @@ shared_sources = '''
volatile-util.h
watchdog.c
watchdog.h
+ wireguard-netlink.h
'''.split()
test_tables_h = files('test-tables.h')
diff --git a/src/shared/wireguard-netlink.h b/src/shared/wireguard-netlink.h
new file mode 100644
index 0000000000..eb170915a6
--- /dev/null
+++ b/src/shared/wireguard-netlink.h
@@ -0,0 +1,179 @@
+#pragma once
+
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR MIT)
+ *
+ * Copyright (C) 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ *
+ * Documentation
+ * =============
+ *
+ * The below enums and macros are for interfacing with WireGuard, using generic
+ * netlink, with family WG_GENL_NAME and version WG_GENL_VERSION. It defines two
+ * methods: get and set. Note that while they share many common attributes, these
+ * two functions actually accept a slightly different set of inputs and outputs.
+ *
+ * WG_CMD_GET_DEVICE
+ * -----------------
+ *
+ * May only be called via NLM_F_REQUEST | NLM_F_DUMP. The command should contain
+ * one but not both of:
+ *
+ * WGDEVICE_A_IFINDEX: NLA_U32
+ * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMESIZ - 1
+ *
+ * The kernel will then return several messages (NLM_F_MULTI) containing the following
+ * tree of nested items:
+ *
+ * WGDEVICE_A_IFINDEX: NLA_U32
+ * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMESIZ - 1
+ * WGDEVICE_A_PRIVATE_KEY: len WG_KEY_LEN
+ * WGDEVICE_A_PUBLIC_KEY: len WG_KEY_LEN
+ * WGDEVICE_A_LISTEN_PORT: NLA_U16
+ * WGDEVICE_A_FWMARK: NLA_U32
+ * WGDEVICE_A_PEERS: NLA_NESTED
+ * 0: NLA_NESTED
+ * WGPEER_A_PUBLIC_KEY: len WG_KEY_LEN
+ * WGPEER_A_PRESHARED_KEY: len WG_KEY_LEN
+ * WGPEER_A_ENDPOINT: struct sockaddr_in or struct sockaddr_in6
+ * WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL: NLA_U16
+ * WGPEER_A_LAST_HANDSHAKE_TIME: struct timespec
+ * WGPEER_A_RX_BYTES: NLA_U64
+ * WGPEER_A_TX_BYTES: NLA_U64
+ * WGPEER_A_ALLOWEDIPS: NLA_NESTED
+ * 0: NLA_NESTED
+ * WGALLOWEDIP_A_FAMILY: NLA_U16
+ * WGALLOWEDIP_A_IPADDR: struct in_addr or struct in6_addr
+ * WGALLOWEDIP_A_CIDR_MASK: NLA_U8
+ * 1: NLA_NESTED
+ * ...
+ * 2: NLA_NESTED
+ * ...
+ * ...
+ * 1: NLA_NESTED
+ * ...
+ * ...
+ *
+ * It is possible that all of the allowed IPs of a single peer will not
+ * fit within a single netlink message. In that case, the same peer will
+ * be written in the following message, except it will only contain
+ * WGPEER_A_PUBLIC_KEY and WGPEER_A_ALLOWEDIPS. This may occur several
+ * times in a row for the same peer. It is then up to the receiver to
+ * coalesce adjacent peers. Likewise, it is possible that all peers will
+ * not fit within a single message. So, subsequent peers will be sent
+ * in following messages, except those will only contain WGDEVICE_A_IFNAME
+ * and WGDEVICE_A_PEERS. It is then up to the receiver to coalesce these
+ * messages to form the complete list of peers.
+ *
+ * Since this is an NLA_F_DUMP command, the final message will always be
+ * NLMSG_DONE, even if an error occurs. However, this NLMSG_DONE message
+ * contains an integer error code. It is either zero or a negative error
+ * code corresponding to the errno.
+ *
+ * WG_CMD_SET_DEVICE
+ * -----------------
+ *
+ * May only be called via NLM_F_REQUEST. The command should contain the following
+ * tree of nested items, containing one but not both of WGDEVICE_A_IFINDEX
+ * and WGDEVICE_A_IFNAME:
+ *
+ * WGDEVICE_A_IFINDEX: NLA_U32
+ * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMESIZ - 1
+ * WGDEVICE_A_FLAGS: NLA_U32, 0 or WGDEVICE_F_REPLACE_PEERS if all current
+ * peers should be removed prior to adding the list below.
+ * WGDEVICE_A_PRIVATE_KEY: len WG_KEY_LEN, all zeros to remove
+ * WGDEVICE_A_LISTEN_PORT: NLA_U16, 0 to choose randomly
+ * WGDEVICE_A_FWMARK: NLA_U32, 0 to disable
+ * WGDEVICE_A_PEERS: NLA_NESTED
+ * 0: NLA_NESTED
+ * WGPEER_A_PUBLIC_KEY: len WG_KEY_LEN
+ * WGPEER_A_FLAGS: NLA_U32, 0 and/or WGPEER_F_REMOVE_ME if the specified peer
+ * should be removed rather than added/updated and/or
+ * WGPEER_F_REPLACE_ALLOWEDIPS if all current allowed IPs of
+ * this peer should be removed prior to adding the list below.
+ * WGPEER_A_PRESHARED_KEY: len WG_KEY_LEN, all zeros to remove
+ * WGPEER_A_ENDPOINT: struct sockaddr_in or struct sockaddr_in6
+ * WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL: NLA_U16, 0 to disable
+ * WGPEER_A_ALLOWEDIPS: NLA_NESTED
+ * 0: NLA_NESTED
+ * WGALLOWEDIP_A_FAMILY: NLA_U16
+ * WGALLOWEDIP_A_IPADDR: struct in_addr or struct in6_addr
+ * WGALLOWEDIP_A_CIDR_MASK: NLA_U8
+ * 1: NLA_NESTED
+ * ...
+ * 2: NLA_NESTED
+ * ...
+ * ...
+ * 1: NLA_NESTED
+ * ...
+ * ...
+ *
+ * It is possible that the amount of configuration data exceeds that of
+ * the maximum message length accepted by the kernel. In that case,
+ * several messages should be sent one after another, with each
+ * successive one filling in information not contained in the prior. Note
+ * that if WGDEVICE_F_REPLACE_PEERS is specified in the first message, it
+ * probably should not be specified in fragments that come after, so that
+ * the list of peers is only cleared the first time but appened after.
+ * Likewise for peers, if WGPEER_F_REPLACE_ALLOWEDIPS is specified in the
+ * first message of a peer, it likely should not be specified in subsequent
+ * fragments.
+ *
+ * If an error occurs, NLMSG_ERROR will reply containing an errno.
+ */
+
+#define WG_GENL_NAME "wireguard"
+#define WG_GENL_VERSION 1
+
+#define WG_KEY_LEN 32
+
+enum wg_cmd {
+ WG_CMD_GET_DEVICE,
+ WG_CMD_SET_DEVICE,
+ __WG_CMD_MAX
+};
+#define WG_CMD_MAX (__WG_CMD_MAX - 1)
+
+enum wgdevice_flag {
+ WGDEVICE_F_REPLACE_PEERS = 1U << 0
+};
+enum wgdevice_attribute {
+ WGDEVICE_A_UNSPEC,
+ WGDEVICE_A_IFINDEX,
+ WGDEVICE_A_IFNAME,
+ WGDEVICE_A_PRIVATE_KEY,
+ WGDEVICE_A_PUBLIC_KEY,
+ WGDEVICE_A_FLAGS,
+ WGDEVICE_A_LISTEN_PORT,
+ WGDEVICE_A_FWMARK,
+ WGDEVICE_A_PEERS,
+ __WGDEVICE_A_LAST
+};
+#define WGDEVICE_A_MAX (__WGDEVICE_A_LAST - 1)
+
+enum wgpeer_flag {
+ WGPEER_F_REMOVE_ME = 1U << 0,
+ WGPEER_F_REPLACE_ALLOWEDIPS = 1U << 1
+};
+enum wgpeer_attribute {
+ WGPEER_A_UNSPEC,
+ WGPEER_A_PUBLIC_KEY,
+ WGPEER_A_PRESHARED_KEY,
+ WGPEER_A_FLAGS,
+ WGPEER_A_ENDPOINT,
+ WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL,
+ WGPEER_A_LAST_HANDSHAKE_TIME,
+ WGPEER_A_RX_BYTES,
+ WGPEER_A_TX_BYTES,
+ WGPEER_A_ALLOWEDIPS,
+ __WGPEER_A_LAST
+};
+#define WGPEER_A_MAX (__WGPEER_A_LAST - 1)
+
+enum wgallowedip_attribute {
+ WGALLOWEDIP_A_UNSPEC,
+ WGALLOWEDIP_A_FAMILY,
+ WGALLOWEDIP_A_IPADDR,
+ WGALLOWEDIP_A_CIDR_MASK,
+ __WGALLOWEDIP_A_LAST
+};
+#define WGALLOWEDIP_A_MAX (__WGALLOWEDIP_A_LAST - 1)
diff --git a/src/systemd/sd-netlink.h b/src/systemd/sd-netlink.h
index 7c4d51ea79..e742807e92 100644
--- a/src/systemd/sd-netlink.h
+++ b/src/systemd/sd-netlink.h
@@ -36,7 +36,7 @@ _SD_BEGIN_DECLARATIONS;
typedef struct sd_netlink sd_netlink;
typedef struct sd_genl_socket sd_genl_socket;
typedef struct sd_netlink_message sd_netlink_message;
-typedef enum {SD_GENL_ID_CTRL} sd_genl_family;
+typedef enum {SD_GENL_ID_CTRL, SD_GENL_WIREGUARD} sd_genl_family;
/* callback */