networkd and sd-netlink: add support for Generic netlink And FooOverUDP to IPIP tunnel

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
```
This commit is contained in:
Susant Sahani 2018-06-03 12:37:41 +05:30 committed by Zbigniew Jędrzejewski-Szmek
parent c07fe6d0df
commit 53cb501a13
14 changed files with 386 additions and 2 deletions

View File

@ -163,6 +163,10 @@
<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>
@ -879,8 +883,76 @@
</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>
@ -1379,6 +1451,32 @@ Local=192.168.223.238
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]

View File

@ -464,6 +464,9 @@ foreach decl : [['IFLA_INET6_ADDR_GEN_MODE', 'linux/if_link.h'],
['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)

View File

@ -1409,4 +1409,43 @@ struct statx {
#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"

View File

@ -12,6 +12,7 @@ typedef struct {
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) {

View File

@ -16,6 +16,11 @@
#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
@ -733,9 +738,34 @@ static const NLTypeSystem genl_ctrl_id_ctrl_type_system = {
.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 = {

View File

@ -35,6 +35,8 @@ sources = files('''
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

View File

@ -0,0 +1,128 @@
/* 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,
};

View File

@ -0,0 +1,39 @@
/* 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);

View File

@ -19,6 +19,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
#include "netdev/netdev.h"
#include "netdev/vxcan.h"
#include "netdev/wireguard.h"
#include "netdev/fou-tunnel.h"
#include "vlan-util.h"
%}
struct ConfigPerfItem;
@ -65,6 +66,13 @@ Tunnel.CopyDSCP, config_parse_bool, 0,
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)

View File

@ -34,6 +34,7 @@
#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,
@ -62,6 +63,7 @@ const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = {
[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] = {
@ -91,6 +93,7 @@ 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);

View File

@ -43,6 +43,7 @@ typedef enum NetDevKind {
NETDEV_KIND_VXCAN,
NETDEV_KIND_WIREGUARD,
NETDEV_KIND_NETDEVSIM,
NETDEV_KIND_FOU,
_NETDEV_KIND_MAX,
_NETDEV_KIND_INVALID = -1
} NetDevKind;

View File

@ -8,6 +8,10 @@
#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"
@ -61,6 +65,21 @@ static int netdev_ipip_fill_message_create(NetDev *netdev, Link *link, sd_netlin
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;
}
@ -417,6 +436,11 @@ static int netdev_tunnel_verify(NetDev *netdev, const char *filename) {
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;
}
@ -600,6 +624,7 @@ static void ipip_init(NetDev *n) {
assert(t);
t->pmtudisc = true;
t->fou_encap_type = FOU_ENCAP_DIRECT;
}
static void sit_init(NetDev *n) {

View File

@ -4,6 +4,7 @@
#include "in-addr-util.h"
#include "netdev/netdev.h"
#include "netdev/fou-tunnel.h"
typedef enum Ip6TnlMode {
NETDEV_IP6_TNL_MODE_IP6IP6,
@ -40,10 +41,15 @@ typedef struct Tunnel {
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);
@ -96,6 +102,7 @@ int config_parse_encap_limit(const char *unit, const char *filename,
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,

View File

@ -32,7 +32,7 @@ _SD_BEGIN_DECLARATIONS;
typedef struct sd_netlink sd_netlink;
typedef struct sd_genl_socket sd_genl_socket;
typedef struct sd_netlink_message sd_netlink_message;
typedef enum {SD_GENL_ID_CTRL, SD_GENL_WIREGUARD} sd_genl_family;
typedef enum {SD_GENL_ID_CTRL, SD_GENL_WIREGUARD, SD_GENL_FOU} sd_genl_family;
/* callback */