mirror of
https://github.com/systemd/systemd-stable.git
synced 2024-12-22 13:33:56 +03:00
Merge pull request #7191 from Mic92/systemd
The change in netdev.c done in the merge is necessary to avoid crashing in
cleanup. This is a follow-up for f3c33b234d
.
This commit is contained in:
commit
2269954112
@ -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">
|
||||
|
@ -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>
|
||||
@ -1009,6 +1012,103 @@
|
||||
as the <literal>[Tun]</literal> section.</para>
|
||||
</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>
|
||||
|
||||
@ -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>
|
||||
|
@ -74,6 +74,7 @@ libsystemd_sources = files('''
|
||||
sd-id128/id128-util.c
|
||||
sd-id128/id128-util.h
|
||||
sd-id128/sd-id128.c
|
||||
sd-netlink/generic-netlink.c
|
||||
sd-netlink/local-addresses.c
|
||||
sd-netlink/local-addresses.h
|
||||
sd-netlink/netlink-internal.h
|
||||
|
97
src/libsystemd/sd-netlink/generic-netlink.c
Normal file
97
src/libsystemd/sd-netlink/generic-netlink.c
Normal file
@ -0,0 +1,97 @@
|
||||
#include <linux/genetlink.h>
|
||||
|
||||
#include "sd-netlink.h"
|
||||
#include "netlink-internal.h"
|
||||
#include "alloc-util.h"
|
||||
|
||||
typedef struct {
|
||||
const char* name;
|
||||
uint8_t version;
|
||||
} genl_family;
|
||||
|
||||
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) {
|
||||
return netlink_open_family(ret, NETLINK_GENERIC);
|
||||
}
|
||||
static int lookup_id(sd_netlink *nl, sd_genl_family family, uint16_t *id);
|
||||
|
||||
static int genl_message_new(sd_netlink *nl, sd_genl_family family, uint16_t nlmsg_type, uint8_t cmd, sd_netlink_message **ret) {
|
||||
int r;
|
||||
struct genlmsghdr *genl;
|
||||
const NLType *genl_cmd_type, *nl_type;
|
||||
const NLTypeSystem *type_system;
|
||||
size_t size;
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
|
||||
|
||||
assert_return(nl->protocol == NETLINK_GENERIC, -EINVAL);
|
||||
|
||||
r = type_system_get_type(&genl_family_type_system_root, &genl_cmd_type, family);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = message_new_empty(nl, &m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
size = NLMSG_SPACE(sizeof(struct genlmsghdr));
|
||||
m->hdr = malloc0(size);
|
||||
if (!m->hdr)
|
||||
return -ENOMEM;
|
||||
|
||||
m->hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
|
||||
|
||||
type_get_type_system(genl_cmd_type, &type_system);
|
||||
|
||||
r = type_system_get_type(type_system, &nl_type, cmd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
m->hdr->nlmsg_len = size;
|
||||
m->hdr->nlmsg_type = nlmsg_type;
|
||||
|
||||
type_get_type_system(nl_type, &m->containers[0].type_system);
|
||||
genl = NLMSG_DATA(m->hdr);
|
||||
genl->cmd = cmd;
|
||||
genl->version = genl_families[family].version;
|
||||
|
||||
*ret = m;
|
||||
m = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_genl_message_new(sd_netlink *nl, sd_genl_family family, uint8_t cmd, sd_netlink_message **ret) {
|
||||
int r;
|
||||
uint16_t id = GENL_ID_CTRL;
|
||||
|
||||
if (family != SD_GENL_ID_CTRL) {
|
||||
r = lookup_id(nl, family, &id);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return genl_message_new(nl, family, id, cmd, ret);
|
||||
}
|
||||
|
||||
static int lookup_id(sd_netlink *nl, sd_genl_family family, uint16_t *id) {
|
||||
int r;
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
|
||||
|
||||
r = sd_genl_message_new(nl, SD_GENL_ID_CTRL, CTRL_CMD_GETFAMILY, &req);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_string(req, CTRL_ATTR_FAMILY_NAME, genl_families[family].name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_call(nl, req, 0, &reply);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return sd_netlink_message_read_u16(reply, CTRL_ATTR_FAMILY_ID, id);
|
||||
}
|
@ -62,6 +62,8 @@ struct sd_netlink {
|
||||
struct sockaddr_nl nl;
|
||||
} sockaddr;
|
||||
|
||||
int protocol;
|
||||
|
||||
Hashmap *broadcast_group_refs;
|
||||
bool broadcast_group_dont_leave:1; /* until we can rely on 4.2 */
|
||||
|
||||
@ -111,6 +113,8 @@ struct sd_netlink_message {
|
||||
|
||||
sd_netlink *rtnl;
|
||||
|
||||
int protocol;
|
||||
|
||||
struct nlmsghdr *hdr;
|
||||
struct netlink_container containers[RTNL_CONTAINER_DEPTH];
|
||||
unsigned n_containers; /* number of containers */
|
||||
@ -123,6 +127,8 @@ struct sd_netlink_message {
|
||||
int message_new(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t type);
|
||||
int message_new_empty(sd_netlink *rtnl, sd_netlink_message **ret);
|
||||
|
||||
int netlink_open_family(sd_netlink **ret, int family);
|
||||
|
||||
int socket_open(int family);
|
||||
int socket_bind(sd_netlink *nl);
|
||||
int socket_broadcast_group_ref(sd_netlink *nl, unsigned group);
|
||||
|
@ -55,7 +55,7 @@ int message_new_empty(sd_netlink *rtnl, sd_netlink_message **ret) {
|
||||
return -ENOMEM;
|
||||
|
||||
m->n_ref = REFCNT_INIT;
|
||||
|
||||
m->protocol = rtnl->protocol;
|
||||
m->sealed = false;
|
||||
|
||||
*ret = m;
|
||||
@ -66,10 +66,15 @@ int message_new_empty(sd_netlink *rtnl, sd_netlink_message **ret) {
|
||||
int message_new(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t type) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
|
||||
const NLType *nl_type;
|
||||
const NLTypeSystem *type_system_root;
|
||||
size_t size;
|
||||
int r;
|
||||
|
||||
r = type_system_get_type(&type_system_root, &nl_type, type);
|
||||
assert_return(rtnl, -EINVAL);
|
||||
|
||||
type_system_root = type_system_get_root(rtnl->protocol);
|
||||
|
||||
r = type_system_get_type(type_system_root, &nl_type, type);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -186,6 +191,10 @@ static int add_rtattr(sd_netlink_message *m, unsigned short type, const void *da
|
||||
/* get the new message size (with padding at the end) */
|
||||
message_length = offset + RTA_ALIGN(rta_length);
|
||||
|
||||
/* buffer should be smaller than both one page or 8K to be accepted by the kernel */
|
||||
if (message_length > MIN(page_size(), 8192UL))
|
||||
return -ENOBUFS;
|
||||
|
||||
/* realloc to fit the new attribute */
|
||||
new_hdr = realloc(m->hdr, message_length);
|
||||
if (!new_hdr)
|
||||
@ -490,7 +499,7 @@ int sd_netlink_message_open_container_union(sd_netlink_message *m, unsigned shor
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* do we evere need non-null size */
|
||||
/* do we ever need non-null size */
|
||||
r = add_rtattr(m, type | NLA_F_NESTED, NULL, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -500,18 +509,57 @@ int sd_netlink_message_open_container_union(sd_netlink_message *m, unsigned shor
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int sd_netlink_message_close_container(sd_netlink_message *m) {
|
||||
assert_return(m, -EINVAL);
|
||||
assert_return(!m->sealed, -EPERM);
|
||||
assert_return(m->n_containers > 0, -EINVAL);
|
||||
|
||||
m->containers[m->n_containers].type_system = NULL;
|
||||
m->containers[m->n_containers].offset = 0;
|
||||
m->n_containers--;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_netlink_message_open_array(sd_netlink_message *m, uint16_t type) {
|
||||
int r;
|
||||
|
||||
assert_return(m, -EINVAL);
|
||||
assert_return(!m->sealed, -EPERM);
|
||||
assert_return(m->n_containers > 0, -EINVAL);
|
||||
|
||||
r = add_rtattr(m, type | NLA_F_NESTED, NULL, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
m->containers[m->n_containers].offset = r;
|
||||
m->n_containers++;
|
||||
m->containers[m->n_containers].type_system = m->containers[m->n_containers - 1].type_system;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_netlink_message_cancel_array(sd_netlink_message *m) {
|
||||
unsigned i;
|
||||
uint32_t rta_len;
|
||||
|
||||
assert_return(m, -EINVAL);
|
||||
assert_return(!m->sealed, -EPERM);
|
||||
assert_return(m->n_containers > 1, -EINVAL);
|
||||
|
||||
rta_len = GET_CONTAINER(m, (m->n_containers - 1))->rta_len;
|
||||
|
||||
for (i = 0; i < m->n_containers; i++)
|
||||
GET_CONTAINER(m, i)->rta_len -= rta_len;
|
||||
|
||||
m->hdr->nlmsg_len -= rta_len;
|
||||
|
||||
m->n_containers--;
|
||||
m->containers[m->n_containers].type_system = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int netlink_message_read_internal(sd_netlink_message *m, unsigned short type, void **data, bool *net_byteorder) {
|
||||
struct netlink_attribute *attribute;
|
||||
struct rtattr *rta;
|
||||
@ -899,6 +947,7 @@ int sd_netlink_message_get_errno(sd_netlink_message *m) {
|
||||
|
||||
int sd_netlink_message_rewind(sd_netlink_message *m) {
|
||||
const NLType *nl_type;
|
||||
const NLTypeSystem *type_system_root;
|
||||
uint16_t type;
|
||||
size_t size;
|
||||
unsigned i;
|
||||
@ -910,6 +959,8 @@ int sd_netlink_message_rewind(sd_netlink_message *m) {
|
||||
if (!m->sealed)
|
||||
rtnl_message_seal(m);
|
||||
|
||||
type_system_root = type_system_get_root(m->protocol);
|
||||
|
||||
for (i = 1; i <= m->n_containers; i++)
|
||||
m->containers[i].attributes = mfree(m->containers[i].attributes);
|
||||
|
||||
@ -921,7 +972,7 @@ int sd_netlink_message_rewind(sd_netlink_message *m) {
|
||||
|
||||
assert(m->hdr);
|
||||
|
||||
r = type_system_get_type(&type_system_root, &nl_type, m->hdr->nlmsg_type);
|
||||
r = type_system_get_type(type_system_root, &nl_type, m->hdr->nlmsg_type);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -330,11 +330,14 @@ int socket_read_message(sd_netlink *rtnl) {
|
||||
size_t len;
|
||||
int r;
|
||||
unsigned i = 0;
|
||||
const NLTypeSystem *type_system_root;
|
||||
|
||||
assert(rtnl);
|
||||
assert(rtnl->rbuffer);
|
||||
assert(rtnl->rbuffer_allocated >= sizeof(struct nlmsghdr));
|
||||
|
||||
type_system_root = type_system_get_root(rtnl->protocol);
|
||||
|
||||
/* read nothing, just get the pending message size */
|
||||
r = socket_recv_message(rtnl->fd, &iov, NULL, true);
|
||||
if (r <= 0)
|
||||
@ -396,7 +399,8 @@ int socket_read_message(sd_netlink *rtnl) {
|
||||
}
|
||||
|
||||
/* check that we support this message type */
|
||||
r = type_system_get_type(&type_system_root, &nl_type, new_msg->nlmsg_type);
|
||||
r = type_system_get_type(type_system_root, &nl_type, new_msg->nlmsg_type);
|
||||
|
||||
if (r < 0) {
|
||||
if (r == -EOPNOTSUPP)
|
||||
log_debug("sd-netlink: ignored message with unknown type: %i",
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include <linux/if_link.h>
|
||||
#include <linux/if_tunnel.h>
|
||||
#include <linux/fib_rules.h>
|
||||
#include <linux/genetlink.h>
|
||||
|
||||
#if HAVE_VXCAN_INFO_PEER
|
||||
#include <linux/can/vxcan.h>
|
||||
@ -46,6 +47,8 @@
|
||||
#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 */
|
||||
#define BOND_MAX_ARP_TARGETS 16
|
||||
@ -338,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);
|
||||
@ -665,11 +668,98 @@ static const NLType rtnl_types[] = {
|
||||
[RTM_GETRULE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct rtmsg) },
|
||||
};
|
||||
|
||||
const NLTypeSystem type_system_root = {
|
||||
const NLTypeSystem rtnl_type_system_root = {
|
||||
.count = ELEMENTSOF(rtnl_types),
|
||||
.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 },
|
||||
[CTRL_ATTR_FAMILY_ID] = { .type = NETLINK_TYPE_U16 },
|
||||
};
|
||||
|
||||
static const NLTypeSystem genl_get_family_type_system = {
|
||||
.count = ELEMENTSOF(genl_get_family_types),
|
||||
.types = genl_get_family_types,
|
||||
};
|
||||
|
||||
static const NLType genl_ctrl_id_ctrl_cmds[] = {
|
||||
[CTRL_CMD_GETFAMILY] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_get_family_type_system },
|
||||
};
|
||||
|
||||
static const NLTypeSystem genl_ctrl_id_ctrl_type_system = {
|
||||
.count = ELEMENTSOF(genl_ctrl_id_ctrl_cmds),
|
||||
.types = genl_ctrl_id_ctrl_cmds,
|
||||
};
|
||||
|
||||
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 = {
|
||||
.count = ELEMENTSOF(genl_families),
|
||||
.types = genl_families,
|
||||
};
|
||||
|
||||
static const NLType genl_types[] = {
|
||||
[GENL_ID_CTRL] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_get_family_type_system, .size = sizeof(struct genlmsghdr) },
|
||||
};
|
||||
|
||||
const NLTypeSystem genl_type_system_root = {
|
||||
.count = ELEMENTSOF(genl_types),
|
||||
.types = genl_types,
|
||||
};
|
||||
|
||||
uint16_t type_get_type(const NLType *type) {
|
||||
assert(type);
|
||||
return type->type;
|
||||
@ -703,6 +793,15 @@ uint16_t type_system_get_count(const NLTypeSystem *type_system) {
|
||||
return type_system->count;
|
||||
}
|
||||
|
||||
const NLTypeSystem *type_system_get_root(int protocol) {
|
||||
switch (protocol) {
|
||||
case NETLINK_GENERIC:
|
||||
return &genl_type_system_root;
|
||||
default: /* NETLINK_ROUTE: */
|
||||
return &rtnl_type_system_root;
|
||||
}
|
||||
}
|
||||
|
||||
int type_system_get_type(const NLTypeSystem *type_system, const NLType **ret, uint16_t type) {
|
||||
const NLType *nl_type;
|
||||
|
||||
|
@ -54,13 +54,16 @@ struct NLTypeSystemUnion {
|
||||
const NLTypeSystem *type_systems;
|
||||
};
|
||||
|
||||
extern const NLTypeSystem type_system_root;
|
||||
extern const NLTypeSystem rtnl_type_system_root;
|
||||
extern const NLTypeSystem genl_type_system_root;
|
||||
extern const NLTypeSystem genl_family_type_system_root;
|
||||
|
||||
uint16_t type_get_type(const NLType *type);
|
||||
size_t type_get_size(const NLType *type);
|
||||
void type_get_type_system(const NLType *type, const NLTypeSystem **ret);
|
||||
void type_get_type_system_union(const NLType *type, const NLTypeSystemUnion **ret);
|
||||
|
||||
const NLTypeSystem* type_system_get_root(int protocol);
|
||||
uint16_t type_system_get_count(const NLTypeSystem *type_system);
|
||||
int type_system_get_type(const NLTypeSystem *type_system, const NLType **ret, uint16_t type);
|
||||
int type_system_get_type_system(const NLTypeSystem *type_system, const NLTypeSystem **ret, uint16_t type);
|
||||
@ -91,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;
|
||||
|
@ -98,13 +98,13 @@ int rtnl_set_link_properties(sd_netlink **rtnl, int ifindex, const char *alias,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rtnl_message_new_synthetic_error(int error, uint32_t serial, sd_netlink_message **ret) {
|
||||
int rtnl_message_new_synthetic_error(sd_netlink *rtnl, int error, uint32_t serial, sd_netlink_message **ret) {
|
||||
struct nlmsgerr *err;
|
||||
int r;
|
||||
|
||||
assert(error <= 0);
|
||||
|
||||
r = message_new(NULL, ret, NLMSG_ERROR);
|
||||
r = message_new(rtnl, ret, NLMSG_ERROR);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -24,7 +24,7 @@
|
||||
|
||||
#include "util.h"
|
||||
|
||||
int rtnl_message_new_synthetic_error(int error, uint32_t serial, sd_netlink_message **ret);
|
||||
int rtnl_message_new_synthetic_error(sd_netlink *rtnl, int error, uint32_t serial, sd_netlink_message **ret);
|
||||
uint32_t rtnl_message_get_serial(sd_netlink_message *m);
|
||||
void rtnl_message_seal(sd_netlink_message *m);
|
||||
|
||||
|
@ -46,6 +46,7 @@ static int sd_netlink_new(sd_netlink **ret) {
|
||||
rtnl->fd = -1;
|
||||
rtnl->sockaddr.nl.nl_family = AF_NETLINK;
|
||||
rtnl->original_pid = getpid_cached();
|
||||
rtnl->protocol = -1;
|
||||
|
||||
LIST_HEAD_INIT(rtnl->match_callbacks);
|
||||
|
||||
@ -106,6 +107,8 @@ static bool rtnl_pid_changed(sd_netlink *rtnl) {
|
||||
int sd_netlink_open_fd(sd_netlink **ret, int fd) {
|
||||
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
|
||||
int r;
|
||||
int protocol;
|
||||
socklen_t l;
|
||||
|
||||
assert_return(ret, -EINVAL);
|
||||
assert_return(fd >= 0, -EBADF);
|
||||
@ -114,11 +117,18 @@ int sd_netlink_open_fd(sd_netlink **ret, int fd) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
l = sizeof(protocol);
|
||||
r = getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &protocol, &l);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
rtnl->fd = fd;
|
||||
rtnl->protocol = protocol;
|
||||
|
||||
r = socket_bind(rtnl);
|
||||
if (r < 0) {
|
||||
rtnl->fd = -1; /* on failure, the caller remains owner of the fd, hence don't close it here */
|
||||
rtnl->protocol = -1;
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -128,11 +138,11 @@ int sd_netlink_open_fd(sd_netlink **ret, int fd) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_netlink_open(sd_netlink **ret) {
|
||||
int netlink_open_family(sd_netlink **ret, int family) {
|
||||
_cleanup_close_ int fd = -1;
|
||||
int r;
|
||||
|
||||
fd = socket_open(NETLINK_ROUTE);
|
||||
fd = socket_open(family);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
@ -145,6 +155,10 @@ int sd_netlink_open(sd_netlink **ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_netlink_open(sd_netlink **ret) {
|
||||
return netlink_open_family(ret, NETLINK_ROUTE);
|
||||
}
|
||||
|
||||
int sd_netlink_inc_rcvbuf(sd_netlink *rtnl, size_t size) {
|
||||
assert_return(rtnl, -EINVAL);
|
||||
assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
|
||||
@ -309,7 +323,7 @@ static int process_timeout(sd_netlink *rtnl) {
|
||||
if (c->timeout > n)
|
||||
return 0;
|
||||
|
||||
r = rtnl_message_new_synthetic_error(-ETIMEDOUT, c->serial, &m);
|
||||
r = rtnl_message_new_synthetic_error(rtnl, -ETIMEDOUT, c->serial, &m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -143,13 +143,13 @@ static void test_address_get(sd_netlink *rtnl, int ifindex) {
|
||||
|
||||
}
|
||||
|
||||
static void test_route(void) {
|
||||
static void test_route(sd_netlink *rtnl) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req;
|
||||
struct in_addr addr, addr_data;
|
||||
uint32_t index = 2, u32_data;
|
||||
int r;
|
||||
|
||||
r = sd_rtnl_message_new_route(NULL, &req, RTM_NEWROUTE, AF_INET, RTPROT_STATIC);
|
||||
r = sd_rtnl_message_new_route(rtnl, &req, RTM_NEWROUTE, AF_INET, RTPROT_STATIC);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Could not create RTM_NEWROUTE message: %m");
|
||||
return;
|
||||
@ -291,13 +291,13 @@ static void test_pipe(int ifindex) {
|
||||
assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL);
|
||||
}
|
||||
|
||||
static void test_container(void) {
|
||||
static void test_container(sd_netlink *rtnl) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
|
||||
uint16_t u16_data;
|
||||
uint32_t u32_data;
|
||||
const char *string_data;
|
||||
|
||||
assert_se(sd_rtnl_message_new_link(NULL, &m, RTM_NEWLINK, 0) >= 0);
|
||||
assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0) >= 0);
|
||||
|
||||
assert_se(sd_netlink_message_open_container(m, IFLA_LINKINFO) >= 0);
|
||||
assert_se(sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "vlan") >= 0);
|
||||
@ -369,10 +369,10 @@ static void test_get_addresses(sd_netlink *rtnl) {
|
||||
}
|
||||
}
|
||||
|
||||
static void test_message(void) {
|
||||
static void test_message(sd_netlink *rtnl) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
|
||||
|
||||
assert_se(rtnl_message_new_synthetic_error(-ETIMEDOUT, 1, &m) >= 0);
|
||||
assert_se(rtnl_message_new_synthetic_error(rtnl, -ETIMEDOUT, 1, &m) >= 0);
|
||||
assert_se(sd_netlink_message_get_errno(m) == -ETIMEDOUT);
|
||||
}
|
||||
|
||||
@ -384,19 +384,19 @@ int main(void) {
|
||||
int if_loopback;
|
||||
uint16_t type;
|
||||
|
||||
test_message();
|
||||
|
||||
test_match();
|
||||
|
||||
test_multiple();
|
||||
|
||||
test_route();
|
||||
|
||||
test_container();
|
||||
|
||||
assert_se(sd_netlink_open(&rtnl) >= 0);
|
||||
assert_se(rtnl);
|
||||
|
||||
test_route(rtnl);
|
||||
|
||||
test_message(rtnl);
|
||||
|
||||
test_container(rtnl);
|
||||
|
||||
if_loopback = (int) if_nametoindex("lo");
|
||||
assert_se(if_loopback > 0);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
@ -111,10 +114,10 @@ static void netdev_cancel_callbacks(NetDev *netdev) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
|
||||
netdev_join_callback *callback;
|
||||
|
||||
if (!netdev)
|
||||
if (!netdev || !netdev->manager)
|
||||
return;
|
||||
|
||||
rtnl_message_new_synthetic_error(-ENODEV, 0, &m);
|
||||
rtnl_message_new_synthetic_error(netdev->manager->rtnl, -ENODEV, 0, &m);
|
||||
|
||||
while ((callback = netdev->callbacks)) {
|
||||
if (m) {
|
||||
@ -330,7 +333,7 @@ int netdev_enslave(NetDev *netdev, Link *link, sd_netlink_message_handler_t call
|
||||
} else if (IN_SET(netdev->state, NETDEV_STATE_LINGER, NETDEV_STATE_FAILED)) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
|
||||
|
||||
r = rtnl_message_new_synthetic_error(-ENODEV, 0, &m);
|
||||
r = rtnl_message_new_synthetic_error(netdev->manager->rtnl, -ENODEV, 0, &m);
|
||||
if (r >= 0)
|
||||
callback(netdev->manager->rtnl, m, link);
|
||||
} else {
|
||||
|
@ -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;
|
||||
|
721
src/network/netdev/wireguard.c
Normal file
721
src/network/netdev/wireguard.c
Normal file
@ -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,
|
||||
};
|
98
src/network/netdev/wireguard.h
Normal file
98
src/network/netdev/wireguard.h
Normal file
@ -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);
|
@ -907,6 +907,26 @@ static int systemd_netlink_fd(void) {
|
||||
return rtnl_fd;
|
||||
}
|
||||
|
||||
static int manager_connect_genl(Manager *m) {
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
|
||||
r = sd_genl_socket_open(&m->genl);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_inc_rcvbuf(m->genl, RCVBUF_SIZE);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_attach_event(m->genl, m->event, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int manager_connect_rtnl(Manager *m) {
|
||||
int fd, r;
|
||||
|
||||
@ -1251,6 +1271,10 @@ int manager_new(Manager **ret, sd_event *event) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = manager_connect_genl(m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = manager_connect_udev(m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -1261,6 +1285,14 @@ int manager_new(Manager **ret, sd_event *event) {
|
||||
|
||||
LIST_HEAD_INIT(m->networks);
|
||||
|
||||
r = sd_resolve_default(&m->resolve);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_resolve_attach_event(m->resolve, m->event, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = setup_default_address_pool(m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -1310,6 +1342,8 @@ void manager_free(Manager *m) {
|
||||
sd_netlink_unref(m->rtnl);
|
||||
sd_event_unref(m->event);
|
||||
|
||||
sd_resolve_unref(m->resolve);
|
||||
|
||||
sd_event_source_unref(m->udev_event_source);
|
||||
udev_monitor_unref(m->udev_monitor);
|
||||
udev_unref(m->udev);
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "sd-bus.h"
|
||||
#include "sd-event.h"
|
||||
#include "sd-netlink.h"
|
||||
#include "sd-resolve.h"
|
||||
#include "udev.h"
|
||||
|
||||
#include "dhcp-identifier.h"
|
||||
@ -39,7 +40,10 @@ extern const char* const network_dirs[];
|
||||
|
||||
struct Manager {
|
||||
sd_netlink *rtnl;
|
||||
/* lazy initialized */
|
||||
sd_netlink *genl;
|
||||
sd_event *event;
|
||||
sd_resolve *resolve;
|
||||
sd_event_source *bus_retry_event_source;
|
||||
sd_bus *bus;
|
||||
sd_bus_slot *prepare_for_sleep_slot;
|
||||
|
@ -118,6 +118,7 @@ shared_sources = '''
|
||||
volatile-util.h
|
||||
watchdog.c
|
||||
watchdog.h
|
||||
wireguard-netlink.h
|
||||
'''.split()
|
||||
|
||||
test_tables_h = files('test-tables.h')
|
||||
|
179
src/shared/wireguard-netlink.h
Normal file
179
src/shared/wireguard-netlink.h
Normal file
@ -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)
|
@ -34,7 +34,9 @@
|
||||
_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_WIREGUARD} sd_genl_family;
|
||||
|
||||
/* callback */
|
||||
|
||||
@ -94,6 +96,9 @@ int sd_netlink_message_read_in6_addr(sd_netlink_message *m, unsigned short type,
|
||||
int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned short type);
|
||||
int sd_netlink_message_exit_container(sd_netlink_message *m);
|
||||
|
||||
int sd_netlink_message_open_array(sd_netlink_message *m, uint16_t type);
|
||||
int sd_netlink_message_cancel_array(sd_netlink_message *m);
|
||||
|
||||
int sd_netlink_message_rewind(sd_netlink_message *m);
|
||||
|
||||
sd_netlink_message *sd_netlink_message_next(sd_netlink_message *m);
|
||||
@ -177,6 +182,10 @@ int sd_rtnl_message_routing_policy_rule_get_rtm_type(sd_netlink_message *m, unsi
|
||||
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_netlink, sd_netlink_unref);
|
||||
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_netlink_message, sd_netlink_message_unref);
|
||||
|
||||
/* genl */
|
||||
int sd_genl_socket_open(sd_netlink **nl);
|
||||
int sd_genl_message_new(sd_netlink *nl, sd_genl_family family, uint8_t cmd, sd_netlink_message **m);
|
||||
|
||||
_SD_END_DECLARATIONS;
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user