This work add support to generic netlink to sd-netlink.
See https://lwn.net/Articles/208755/
networkd: add support FooOverUDP support to IPIP tunnel netdev
https://lwn.net/Articles/614348/
Example conf:
/lib/systemd/network/1-fou-tunnel.netdev
```
[NetDev]
Name=fou-tun
Kind=fou
[FooOverUDP]
Port=5555
Protocol=4
```
/lib/systemd/network/ipip-tunnel.netdev
```
[NetDev]
Name=ipip-tun
Kind=ipip
[Tunnel]
Independent=true
Local=10.65.208.212
Remote=10.65.208.211
FooOverUDP=true
FOUDestinationPort=5555
```
$ ip -d link show ipip-tun
```
5: ipip-tun@NONE: <POINTOPOINT,NOARP> mtu 1472 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ipip 10.65.208.212 peer 10.65.208.211 promiscuity 0
ipip remote 10.65.208.211 local 10.65.208.212 ttl inherit pmtudisc encap fou encap-sport auto encap-dport 5555 noencap-csum noencap-csum6 noencap-remcsum numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
```
<row><entry><varname>netdevsim</varname></entry>
<entry> A simulator. This simulated networking device is used for testing various networking APIs and at this time is particularly focused on testing hardware offloading related interfaces.</entry></row>
+
+ <row><entry><varname>fou</varname></entry>
+ <entry>Foo-over-UDP tunneling.</entry></row>
+
</tbody>
</tgroup>
</table>
</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>FooOverUDP=</varname></term>
+ <listitem>
+ <para>A boolean. Specifies whether <varname>FooOverUDP=</varname> tunnel is to be configured.
+ Defaults to false. For more detail information see
+ <ulink url="https://lwn.net/Articles/614348">Foo over UDP</ulink></para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>FOUDestinationPort=</varname></term>
+ <listitem>
+ <para>The <varname>FOUDestinationPort=</varname> specifies the UDP destination port for encapsulation.
+ This field is mandatory and is not set by default.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>FOUSourcePort=</varname></term>
+ <listitem>
+ <para>The <constant>FOUSourcePort=</constant> specifies the UDP source port for encapsulation. Defaults to <varname>0</varname>,
+ that is, the source port for packets is left to the network stack to decide.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>Encapsulation=</varname></term>
+ <listitem>
+ <para>Accepts the same key as <literal>[FooOverUDP]</literal></para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
+
+ <refsect1>
+ <title>[FooOverUDP] Section Options</title>
+
+ <para>The <literal>[FooOverUDP]</literal> section only applies for
+ netdevs of kind <literal>fou</literal> and accepts the
+ following keys:</para>
+
+ <variablelist class='network-directives'>
+ <varlistentry>
+ <term><varname>Protocol=</varname></term>
+ <listitem>
+ <para>The <varname>Protocol=</varname> specifies the protocol number of the
+ packets arriving at the UDP port. This field is mandatory and is not set by default. Valid range is 1-255.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>Encapsulation=</varname></term>
+ <listitem>
+ <para>Specifies the encapsulation mechanism used to store networking packets of various protocols inside the UDP packets. Supports the following values:
+
+ <literal>FooOverUDP</literal> provides the simplest no frills model of UDP encapsulation, it simply encapsulates
+ packets directly in the UDP payload.
+ <literal>GenericUDPEncapsulation</literal> is a generic and extensible encapsulation, it allows encapsulation of packets for any IP
+ protocol and optional data as part of the encapsulation.
+ For more detailed information see <ulink url="https://lwn.net/Articles/615044">Generic UDP Encapsulation</ulink>.
+ Defaults to <literal>FooOverUDP</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>Port=</varname></term>
+ <listitem>
+ <para>Specifies the port number, where the IP encapsulation packets will arrive. Please take note that the packets
+ will arrive with the encapsulation will be removed. Then they will be manually fed back into the network stack, and sent ahead
+ for delivery to the real destination. This option is mandatory.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
<refsect1>
<title>[Peer] Section Options</title>
Remote=192.169.224.239
TTL=64</programlisting>
</example>
+ <example>
+ <title>/etc/systemd/network/1-fou-tunnel.netdev</title>
+ <programlisting>[NetDev]
+Name=fou-tun
+Kind=fou
+
+[FooOverUDP]
+Port=5555
+Protocol=4
+ </programlisting>
+ </example>
+ <example>
+ <title>/etc/systemd/network/25-fou-ipip.netdev</title>
+ <programlisting>[NetDev]
+[NetDev]
+Name=ipip-tun
+Kind=ipip
+
+[Tunnel]
+Independent=true
+Local=10.65.208.212
+Remote=10.65.208.211
+FooOverUDP=true
+FOUDestinationPort=5555
+ </programlisting>
+ </example>
<example>
<title>/etc/systemd/network/25-tap.netdev</title>
<programlisting>[NetDev]
['FRA_UID_RANGE', 'linux/fib_rules.h'],
['LO_FLAGS_PARTSCAN', 'linux/loop.h'],
['VXCAN_INFO_PEER', 'linux/can/vxcan.h'],
+ ['FOU_ATTR_REMCSUM_NOPARTIAL', 'linux/fou.h'],
+ ['FOU_CMD_GET', 'linux/fou.h'],
+ ['FOU_ENCAP_GUE', 'linux/fou.h'],
]
prefix = decl.length() > 2 ? decl[2] : ''
have = cc.has_header_symbol(decl[1], decl[0], prefix : prefix)
#define TASK_COMM_LEN 16
#endif
+#ifndef FOU_GENL_NAME
+#define FOU_GENL_NAME "fou"
+#endif
+
+#ifndef FOU_GENL_VERSION
+#define FOU_GENL_VERSION 0x1
+#endif
+
+#if !HAVE_FOU_ATTR_REMCSUM_NOPARTIAL
+#define FOU_ATTR_UNSPEC 0
+#define FOU_ATTR_PORT 1
+#define FOU_ATTR_AF 2
+#define FOU_ATTR_IPPROTO 3
+#define FOU_ATTR_TYPE 4
+#define FOU_ATTR_REMCSUM_NOPARTIAL 5
+#define __FOU_ATTR_MAX 6
+
+#define FOU_ATTR_MAX (__FOU_ATTR_MAX - 1)
+#endif
+
+#if !HAVE_FOU_CMD_GET
+#define FOU_CMD_UNSPEC 0
+#define FOU_CMD_ADD 1
+#define FOU_CMD_DEL 2
+#define FOU_CMD_GET 3
+#define __FOU_CMD_MAX 4
+
+#define FOU_CMD_MAX (__FOU_CMD_MAX - 1)
+#endif
+
+#if !HAVE_FOU_ENCAP_GUE
+#define FOU_ENCAP_UNSPEC 0
+#define FOU_ENCAP_DIRECT 1
+#define FOU_ENCAP_GUE 2
+#define __FOU_ENCAP_MAX 3
+
+#define FOU_ENCAP_MAX (__FOU_ENCAP_MAX - 1)
+#endif
+
#include "missing_syscall.h"
static const genl_family genl_families[] = {
[SD_GENL_ID_CTRL] = { .name = "", .version = 1 },
[SD_GENL_WIREGUARD] = { .name = "wireguard", .version = 1 },
+ [SD_GENL_FOU] = { .name = "fou", .version = 1 },
};
int sd_genl_socket_open(sd_netlink **ret) {
#include <linux/if_link.h>
#include <linux/if_tunnel.h>
#include <linux/veth.h>
+
+#if HAVE_FOU_CMD_GET
+#include <linux/fou.h>
+#endif
+
#if HAVE_VXCAN_INFO_PEER
#include <linux/can/vxcan.h>
#endif
.types = genl_ctrl_id_ctrl_cmds,
};
+static const NLType genl_fou_types[] = {
+ [FOU_ATTR_PORT] = { .type = NETLINK_TYPE_U16 },
+ [FOU_ATTR_AF] = { .type = NETLINK_TYPE_U8 },
+ [FOU_ATTR_IPPROTO] = { .type = NETLINK_TYPE_U8 },
+ [FOU_ATTR_TYPE] = { .type = NETLINK_TYPE_U8 },
+ [FOU_ATTR_REMCSUM_NOPARTIAL] = { .type = NETLINK_TYPE_FLAG },
+};
+
+static const NLTypeSystem genl_fou_type_system = {
+ .count = ELEMENTSOF(genl_fou_types),
+ .types = genl_fou_types,
+};
+
+static const NLType genl_fou_cmds[] = {
+ [FOU_CMD_ADD] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_fou_type_system },
+ [FOU_CMD_DEL] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_fou_type_system },
+ [FOU_CMD_GET] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_fou_type_system },
+};
+
+static const NLTypeSystem genl_fou_cmds_type_system = {
+ .count = ELEMENTSOF(genl_fou_cmds),
+ .types = genl_fou_cmds,
+};
+
static const NLType genl_families[] = {
- [SD_GENL_ID_CTRL] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_ctrl_id_ctrl_type_system },
+ [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 },
+ [SD_GENL_FOU] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_fou_cmds_type_system},
};
const NLTypeSystem genl_family_type_system_root = {
netdev/wireguard.h
netdev/netdevsim.c
netdev/netdevsim.h
+ netdev/fou-tunnel.c
+ netdev/fou-tunnel.h
networkd-address-label.c
networkd-address-label.h
networkd-address-pool.c
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <linux/ip.h>
+
+#include "conf-parser.h"
+#include "missing.h"
+#include "netdev/fou-tunnel.h"
+#include "networkd-link.h"
+#include "networkd-manager.h"
+#include "parse-util.h"
+#include "sd-netlink.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "util.h"
+
+static const char* const fou_encap_type_table[_NETDEV_FOO_OVER_UDP_ENCAP_MAX] = {
+ [NETDEV_FOO_OVER_UDP_ENCAP_DIRECT] = "FooOverUDP",
+ [NETDEV_FOO_OVER_UDP_ENCAP_GUE] = "GenericUDPEncapsulation",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(fou_encap_type, FooOverUDPEncapType);
+DEFINE_CONFIG_PARSE_ENUM(config_parse_fou_encap_type, fou_encap_type, FooOverUDPEncapType, "Failed to parse Foo Over UDP Encap type");
+
+static int netdev_fill_fou_tunnel_message(NetDev *netdev, sd_netlink_message **ret) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
+ FouTunnel *t;
+ int r;
+
+ assert(netdev);
+
+ t = FOU(netdev);
+
+ assert(t);
+
+ r = sd_genl_message_new(netdev->manager->genl, SD_GENL_FOU, FOU_CMD_ADD, &m);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Failed to allocate generic netlink message: %m");
+
+ r = sd_netlink_message_append_u16(m, FOU_ATTR_PORT, htobe16(t->port));
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_PORT attribute: %m");
+
+ r = sd_netlink_message_append_u8(m, FOU_ATTR_TYPE, FOU_ENCAP_GUE);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_TYPE attribute: %m");
+
+ r = sd_netlink_message_append_u8(m, FOU_ATTR_AF, AF_INET);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_AF attribute: %m");
+
+ r = sd_netlink_message_append_u8(m, FOU_ATTR_IPPROTO, t->fou_protocol);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_IPPROTO attribute: %m");
+
+ *ret = m;
+ m = NULL;
+
+ return 0;
+}
+
+static int netdev_fou_tunnel_create(NetDev *netdev) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL, *reply = NULL;
+ uint32_t serial;
+ FouTunnel *t;
+ int r;
+
+ assert(netdev);
+
+ t = FOU(netdev);
+
+ assert(t);
+
+ r = netdev_fill_fou_tunnel_message(netdev, &m);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_send(netdev->manager->genl, m, &serial);
+ if (r < 0 && r != -EADDRINUSE)
+ return log_netdev_error_errno(netdev, r, "Failed to add FooOverUDP tunnel: %m");
+
+ return 0;
+}
+
+static int netdev_fou_tunnel_verify(NetDev *netdev, const char *filename) {
+ FouTunnel *t;
+
+ assert(netdev);
+ assert(filename);
+
+ t = FOU(netdev);
+
+ assert(t);
+
+ if (t->fou_encap_type == NETDEV_FOO_OVER_UDP_ENCAP_DIRECT && t->fou_protocol <= 0) {
+ log_netdev_error(netdev, "FooOverUDP missing protocol configured in %s. Ignoring", filename);
+ return -EINVAL;
+ }
+
+ if (t->fou_encap_type == NETDEV_FOO_OVER_UDP_ENCAP_GUE && t->fou_protocol > 0) {
+ log_netdev_error(netdev, "FooOverUDP GUE can't be set with protocol configured in %s. Ignoring", filename);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void fou_tunnel_init(NetDev *netdev) {
+ FouTunnel *t;
+
+ assert(netdev);
+
+ t = FOU(netdev);
+
+ assert(t);
+
+ t->fou_encap_type = NETDEV_FOO_OVER_UDP_ENCAP_DIRECT;
+}
+
+const NetDevVTable foutnl_vtable = {
+ .object_size = sizeof(FouTunnel),
+ .init = fou_tunnel_init,
+ .sections = "Match\0NetDev\0FooOverUDP\0",
+ .create = netdev_fou_tunnel_create,
+ .create_type = NETDEV_CREATE_INDEPENDENT,
+ .config_verify = netdev_fou_tunnel_verify,
+};
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#if HAVE_FOU_CMD_GET
+#include <linux/fou.h>
+#endif
+
+#include "in-addr-util.h"
+#include "netdev/netdev.h"
+
+typedef enum FooOverUDPEncapType {
+ NETDEV_FOO_OVER_UDP_ENCAP_UNSPEC = FOU_ENCAP_UNSPEC,
+ NETDEV_FOO_OVER_UDP_ENCAP_DIRECT = FOU_ENCAP_DIRECT,
+ NETDEV_FOO_OVER_UDP_ENCAP_GUE = FOU_ENCAP_GUE,
+ _NETDEV_FOO_OVER_UDP_ENCAP_MAX,
+ _NETDEV_FOO_OVER_UDP_ENCAP_INVALID = -1,
+} FooOverUDPEncapType;
+
+typedef struct FouTunnel {
+ NetDev meta;
+
+ uint8_t fou_protocol;
+
+ uint16_t port;
+
+ FooOverUDPEncapType fou_encap_type;
+} FouTunnel;
+
+DEFINE_NETDEV_CAST(FOU, FouTunnel);
+extern const NetDevVTable foutnl_vtable;
+
+const char *fou_encap_type_to_string(FooOverUDPEncapType d) _const_;
+FooOverUDPEncapType fou_encap_type_from_string(const char *d) _pure_;
+
+int config_parse_fou_encap_type(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);
#include "netdev/netdev.h"
#include "netdev/vxcan.h"
#include "netdev/wireguard.h"
+#include "netdev/fou-tunnel.h"
#include "vlan-util.h"
%}
struct ConfigPerfItem;
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)
+Tunnel.FooOverUDP, config_parse_bool, 0, offsetof(Tunnel, fou_tunnel)
+Tunnel.FOUDestinationPort, config_parse_ip_port, 0, offsetof(Tunnel, fou_destination_port)
+Tunnel.FOUSourcePort, config_parse_ip_port, 0, offsetof(Tunnel, encap_src_port)
+Tunnel.Encapsulation, config_parse_fou_encap_type, 0, offsetof(Tunnel, fou_encap_type)
+FooOverUDP.Protocol, config_parse_uint8, 0, offsetof(FouTunnel, fou_protocol)
+FooOverUDP.Encapsulation, config_parse_fou_encap_type, 0, offsetof(FouTunnel, fou_encap_type)
+FooOverUDP.Port, config_parse_ip_port, 0, offsetof(FouTunnel, port)
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)
#include "netdev/vxcan.h"
#include "netdev/wireguard.h"
#include "netdev/netdevsim.h"
+#include "netdev/fou-tunnel.h"
const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = {
[NETDEV_KIND_BRIDGE] = &bridge_vtable,
[NETDEV_KIND_VXCAN] = &vxcan_vtable,
[NETDEV_KIND_WIREGUARD] = &wireguard_vtable,
[NETDEV_KIND_NETDEVSIM] = &netdevsim_vtable,
+ [NETDEV_KIND_FOU] = &foutnl_vtable,
};
static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
[NETDEV_KIND_VXCAN] = "vxcan",
[NETDEV_KIND_WIREGUARD] = "wireguard",
[NETDEV_KIND_NETDEVSIM] = "netdevsim",
+ [NETDEV_KIND_FOU] = "fou",
};
DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind);
NETDEV_KIND_VXCAN,
NETDEV_KIND_WIREGUARD,
NETDEV_KIND_NETDEVSIM,
+ NETDEV_KIND_FOU,
_NETDEV_KIND_MAX,
_NETDEV_KIND_INVALID = -1
} NetDevKind;
#include "sd-netlink.h"
+#if HAVE_FOU_CMD_GET
+#include <linux/fou.h>
+#endif
+
#include "conf-parser.h"
#include "missing.h"
#include "networkd-link.h"
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_PMTUDISC attribute: %m");
+ if (t->fou_tunnel) {
+
+ r = sd_netlink_message_append_u16(m, IFLA_IPTUN_ENCAP_TYPE, t->fou_encap_type);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_ENCAP_TYPE attribute: %m");
+
+ r = sd_netlink_message_append_u16(m, IFLA_IPTUN_ENCAP_SPORT, htobe16(t->encap_src_port));
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_ENCAP_SPORT attribute: %m");
+
+ r = sd_netlink_message_append_u16(m, IFLA_IPTUN_ENCAP_DPORT, htobe16(t->fou_destination_port));
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_ENCAP_DPORT attribute: %m");
+ }
+
return r;
}
return -EINVAL;
}
+ if (t->fou_tunnel && t->fou_destination_port <= 0) {
+ log_netdev_error(netdev, "FooOverUDP missing port configured in %s. Ignoring", filename);
+ return -EINVAL;
+ }
+
return 0;
}
assert(t);
t->pmtudisc = true;
+ t->fou_encap_type = FOU_ENCAP_DIRECT;
}
static void sit_init(NetDev *n) {
#include "in-addr-util.h"
#include "netdev/netdev.h"
+#include "netdev/fou-tunnel.h"
typedef enum Ip6TnlMode {
NETDEV_IP6_TNL_MODE_IP6IP6,
union in_addr_union remote;
Ip6TnlMode ip6tnl_mode;
+ FooOverUDPEncapType fou_encap_type;
bool pmtudisc;
bool copy_dscp;
bool independent;
+ bool fou_tunnel;
+
+ uint16_t encap_src_port;
+ uint16_t fou_destination_port;
} Tunnel;
DEFINE_NETDEV_CAST(IPIP, Tunnel);
unsigned section_line, const char *lvalue,
int ltype, const char *rvalue, void *data,
void *userdata);
+
int config_parse_tunnel_key(const char *unit, const char *filename,
unsigned line, const char *section,
unsigned section_line, const char *lvalue,
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;
+typedef enum {SD_GENL_ID_CTRL, SD_GENL_WIREGUARD, SD_GENL_FOU} sd_genl_family;
/* callback */