gue: Receive side for Generic UDP Encapsulation
This patch adds support receiving for GUE packets in the fou module. The fou module now supports direct foo-over-udp (no encapsulation header) and GUE. To support this a type parameter is added to the fou netlink parameters. For a GUE socket we define gue_udp_recv, gue_gro_receive, and gue_gro_complete to handle the specifics of the GUE protocol. Most of the code to manage and configure sockets is common with the fou. Signed-off-by: Tom Herbert <therbert@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
efc98d08e1
commit
37dd024779
23
include/net/gue.h
Normal file
23
include/net/gue.h
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#ifndef __NET_GUE_H
|
||||||
|
#define __NET_GUE_H
|
||||||
|
|
||||||
|
struct guehdr {
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
#if defined(__LITTLE_ENDIAN_BITFIELD)
|
||||||
|
__u8 hlen:4,
|
||||||
|
version:4;
|
||||||
|
#elif defined (__BIG_ENDIAN_BITFIELD)
|
||||||
|
__u8 version:4,
|
||||||
|
hlen:4;
|
||||||
|
#else
|
||||||
|
#error "Please fix <asm/byteorder.h>"
|
||||||
|
#endif
|
||||||
|
__u8 next_hdr;
|
||||||
|
__u16 flags;
|
||||||
|
};
|
||||||
|
__u32 word;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -13,6 +13,7 @@ enum {
|
|||||||
FOU_ATTR_PORT, /* u16 */
|
FOU_ATTR_PORT, /* u16 */
|
||||||
FOU_ATTR_AF, /* u8 */
|
FOU_ATTR_AF, /* u8 */
|
||||||
FOU_ATTR_IPPROTO, /* u8 */
|
FOU_ATTR_IPPROTO, /* u8 */
|
||||||
|
FOU_ATTR_TYPE, /* u8 */
|
||||||
|
|
||||||
__FOU_ATTR_MAX,
|
__FOU_ATTR_MAX,
|
||||||
};
|
};
|
||||||
@ -27,6 +28,12 @@ enum {
|
|||||||
__FOU_CMD_MAX,
|
__FOU_CMD_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
FOU_ENCAP_UNSPEC,
|
||||||
|
FOU_ENCAP_DIRECT,
|
||||||
|
FOU_ENCAP_GUE,
|
||||||
|
};
|
||||||
|
|
||||||
#define FOU_CMD_MAX (__FOU_CMD_MAX - 1)
|
#define FOU_CMD_MAX (__FOU_CMD_MAX - 1)
|
||||||
|
|
||||||
#endif /* _UAPI_LINUX_FOU_H */
|
#endif /* _UAPI_LINUX_FOU_H */
|
||||||
|
196
net/ipv4/fou.c
196
net/ipv4/fou.c
@ -7,6 +7,7 @@
|
|||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <net/genetlink.h>
|
#include <net/genetlink.h>
|
||||||
|
#include <net/gue.h>
|
||||||
#include <net/ip.h>
|
#include <net/ip.h>
|
||||||
#include <net/protocol.h>
|
#include <net/protocol.h>
|
||||||
#include <net/udp.h>
|
#include <net/udp.h>
|
||||||
@ -27,6 +28,7 @@ struct fou {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct fou_cfg {
|
struct fou_cfg {
|
||||||
|
u16 type;
|
||||||
u8 protocol;
|
u8 protocol;
|
||||||
struct udp_port_cfg udp_config;
|
struct udp_port_cfg udp_config;
|
||||||
};
|
};
|
||||||
@ -64,6 +66,41 @@ static int fou_udp_recv(struct sock *sk, struct sk_buff *skb)
|
|||||||
sizeof(struct udphdr));
|
sizeof(struct udphdr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int gue_udp_recv(struct sock *sk, struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct fou *fou = fou_from_sock(sk);
|
||||||
|
size_t len;
|
||||||
|
struct guehdr *guehdr;
|
||||||
|
struct udphdr *uh;
|
||||||
|
|
||||||
|
if (!fou)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
len = sizeof(struct udphdr) + sizeof(struct guehdr);
|
||||||
|
if (!pskb_may_pull(skb, len))
|
||||||
|
goto drop;
|
||||||
|
|
||||||
|
uh = udp_hdr(skb);
|
||||||
|
guehdr = (struct guehdr *)&uh[1];
|
||||||
|
|
||||||
|
len += guehdr->hlen << 2;
|
||||||
|
if (!pskb_may_pull(skb, len))
|
||||||
|
goto drop;
|
||||||
|
|
||||||
|
if (guehdr->version != 0)
|
||||||
|
goto drop;
|
||||||
|
|
||||||
|
if (guehdr->flags) {
|
||||||
|
/* No support yet */
|
||||||
|
goto drop;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fou_udp_encap_recv_deliver(skb, guehdr->next_hdr, len);
|
||||||
|
drop:
|
||||||
|
kfree_skb(skb);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static struct sk_buff **fou_gro_receive(struct sk_buff **head,
|
static struct sk_buff **fou_gro_receive(struct sk_buff **head,
|
||||||
struct sk_buff *skb)
|
struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
@ -107,6 +144,112 @@ out_unlock:
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct sk_buff **gue_gro_receive(struct sk_buff **head,
|
||||||
|
struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
const struct net_offload **offloads;
|
||||||
|
const struct net_offload *ops;
|
||||||
|
struct sk_buff **pp = NULL;
|
||||||
|
struct sk_buff *p;
|
||||||
|
u8 proto;
|
||||||
|
struct guehdr *guehdr;
|
||||||
|
unsigned int hlen, guehlen;
|
||||||
|
unsigned int off;
|
||||||
|
int flush = 1;
|
||||||
|
|
||||||
|
off = skb_gro_offset(skb);
|
||||||
|
hlen = off + sizeof(*guehdr);
|
||||||
|
guehdr = skb_gro_header_fast(skb, off);
|
||||||
|
if (skb_gro_header_hard(skb, hlen)) {
|
||||||
|
guehdr = skb_gro_header_slow(skb, hlen, off);
|
||||||
|
if (unlikely(!guehdr))
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
proto = guehdr->next_hdr;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
|
||||||
|
ops = rcu_dereference(offloads[proto]);
|
||||||
|
if (WARN_ON(!ops || !ops->callbacks.gro_receive))
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
|
guehlen = sizeof(*guehdr) + (guehdr->hlen << 2);
|
||||||
|
|
||||||
|
hlen = off + guehlen;
|
||||||
|
if (skb_gro_header_hard(skb, hlen)) {
|
||||||
|
guehdr = skb_gro_header_slow(skb, hlen, off);
|
||||||
|
if (unlikely(!guehdr))
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
flush = 0;
|
||||||
|
|
||||||
|
for (p = *head; p; p = p->next) {
|
||||||
|
const struct guehdr *guehdr2;
|
||||||
|
|
||||||
|
if (!NAPI_GRO_CB(p)->same_flow)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
guehdr2 = (struct guehdr *)(p->data + off);
|
||||||
|
|
||||||
|
/* Compare base GUE header to be equal (covers
|
||||||
|
* hlen, version, next_hdr, and flags.
|
||||||
|
*/
|
||||||
|
if (guehdr->word != guehdr2->word) {
|
||||||
|
NAPI_GRO_CB(p)->same_flow = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compare optional fields are the same. */
|
||||||
|
if (guehdr->hlen && memcmp(&guehdr[1], &guehdr2[1],
|
||||||
|
guehdr->hlen << 2)) {
|
||||||
|
NAPI_GRO_CB(p)->same_flow = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
skb_gro_pull(skb, guehlen);
|
||||||
|
|
||||||
|
/* Adjusted NAPI_GRO_CB(skb)->csum after skb_gro_pull()*/
|
||||||
|
skb_gro_postpull_rcsum(skb, guehdr, guehlen);
|
||||||
|
|
||||||
|
pp = ops->callbacks.gro_receive(head, skb);
|
||||||
|
|
||||||
|
out_unlock:
|
||||||
|
rcu_read_unlock();
|
||||||
|
out:
|
||||||
|
NAPI_GRO_CB(skb)->flush |= flush;
|
||||||
|
|
||||||
|
return pp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gue_gro_complete(struct sk_buff *skb, int nhoff)
|
||||||
|
{
|
||||||
|
const struct net_offload **offloads;
|
||||||
|
struct guehdr *guehdr = (struct guehdr *)(skb->data + nhoff);
|
||||||
|
const struct net_offload *ops;
|
||||||
|
unsigned int guehlen;
|
||||||
|
u8 proto;
|
||||||
|
int err = -ENOENT;
|
||||||
|
|
||||||
|
proto = guehdr->next_hdr;
|
||||||
|
|
||||||
|
guehlen = sizeof(*guehdr) + (guehdr->hlen << 2);
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
|
||||||
|
ops = rcu_dereference(offloads[proto]);
|
||||||
|
if (WARN_ON(!ops || !ops->callbacks.gro_complete))
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
|
err = ops->callbacks.gro_complete(skb, nhoff + guehlen);
|
||||||
|
|
||||||
|
out_unlock:
|
||||||
|
rcu_read_unlock();
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static int fou_add_to_port_list(struct fou *fou)
|
static int fou_add_to_port_list(struct fou *fou)
|
||||||
{
|
{
|
||||||
struct fou *fout;
|
struct fou *fout;
|
||||||
@ -142,6 +285,28 @@ static void fou_release(struct fou *fou)
|
|||||||
kfree(fou);
|
kfree(fou);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int fou_encap_init(struct sock *sk, struct fou *fou, struct fou_cfg *cfg)
|
||||||
|
{
|
||||||
|
udp_sk(sk)->encap_rcv = fou_udp_recv;
|
||||||
|
fou->protocol = cfg->protocol;
|
||||||
|
fou->udp_offloads.callbacks.gro_receive = fou_gro_receive;
|
||||||
|
fou->udp_offloads.callbacks.gro_complete = fou_gro_complete;
|
||||||
|
fou->udp_offloads.port = cfg->udp_config.local_udp_port;
|
||||||
|
fou->udp_offloads.ipproto = cfg->protocol;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gue_encap_init(struct sock *sk, struct fou *fou, struct fou_cfg *cfg)
|
||||||
|
{
|
||||||
|
udp_sk(sk)->encap_rcv = gue_udp_recv;
|
||||||
|
fou->udp_offloads.callbacks.gro_receive = gue_gro_receive;
|
||||||
|
fou->udp_offloads.callbacks.gro_complete = gue_gro_complete;
|
||||||
|
fou->udp_offloads.port = cfg->udp_config.local_udp_port;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int fou_create(struct net *net, struct fou_cfg *cfg,
|
static int fou_create(struct net *net, struct fou_cfg *cfg,
|
||||||
struct socket **sockp)
|
struct socket **sockp)
|
||||||
{
|
{
|
||||||
@ -164,10 +329,24 @@ static int fou_create(struct net *net, struct fou_cfg *cfg,
|
|||||||
|
|
||||||
sk = sock->sk;
|
sk = sock->sk;
|
||||||
|
|
||||||
/* Mark socket as an encapsulation socket. See net/ipv4/udp.c */
|
fou->port = cfg->udp_config.local_udp_port;
|
||||||
fou->protocol = cfg->protocol;
|
|
||||||
fou->port = cfg->udp_config.local_udp_port;
|
/* Initial for fou type */
|
||||||
udp_sk(sk)->encap_rcv = fou_udp_recv;
|
switch (cfg->type) {
|
||||||
|
case FOU_ENCAP_DIRECT:
|
||||||
|
err = fou_encap_init(sk, fou, cfg);
|
||||||
|
if (err)
|
||||||
|
goto error;
|
||||||
|
break;
|
||||||
|
case FOU_ENCAP_GUE:
|
||||||
|
err = gue_encap_init(sk, fou, cfg);
|
||||||
|
if (err)
|
||||||
|
goto error;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
err = -EINVAL;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
udp_sk(sk)->encap_type = 1;
|
udp_sk(sk)->encap_type = 1;
|
||||||
udp_encap_enable();
|
udp_encap_enable();
|
||||||
@ -179,11 +358,6 @@ static int fou_create(struct net *net, struct fou_cfg *cfg,
|
|||||||
|
|
||||||
sk->sk_allocation = GFP_ATOMIC;
|
sk->sk_allocation = GFP_ATOMIC;
|
||||||
|
|
||||||
fou->udp_offloads.callbacks.gro_receive = fou_gro_receive;
|
|
||||||
fou->udp_offloads.callbacks.gro_complete = fou_gro_complete;
|
|
||||||
fou->udp_offloads.port = cfg->udp_config.local_udp_port;
|
|
||||||
fou->udp_offloads.ipproto = cfg->protocol;
|
|
||||||
|
|
||||||
if (cfg->udp_config.family == AF_INET) {
|
if (cfg->udp_config.family == AF_INET) {
|
||||||
err = udp_add_offload(&fou->udp_offloads);
|
err = udp_add_offload(&fou->udp_offloads);
|
||||||
if (err)
|
if (err)
|
||||||
@ -240,6 +414,7 @@ static struct nla_policy fou_nl_policy[FOU_ATTR_MAX + 1] = {
|
|||||||
[FOU_ATTR_PORT] = { .type = NLA_U16, },
|
[FOU_ATTR_PORT] = { .type = NLA_U16, },
|
||||||
[FOU_ATTR_AF] = { .type = NLA_U8, },
|
[FOU_ATTR_AF] = { .type = NLA_U8, },
|
||||||
[FOU_ATTR_IPPROTO] = { .type = NLA_U8, },
|
[FOU_ATTR_IPPROTO] = { .type = NLA_U8, },
|
||||||
|
[FOU_ATTR_TYPE] = { .type = NLA_U8, },
|
||||||
};
|
};
|
||||||
|
|
||||||
static int parse_nl_config(struct genl_info *info,
|
static int parse_nl_config(struct genl_info *info,
|
||||||
@ -267,6 +442,9 @@ static int parse_nl_config(struct genl_info *info,
|
|||||||
if (info->attrs[FOU_ATTR_IPPROTO])
|
if (info->attrs[FOU_ATTR_IPPROTO])
|
||||||
cfg->protocol = nla_get_u8(info->attrs[FOU_ATTR_IPPROTO]);
|
cfg->protocol = nla_get_u8(info->attrs[FOU_ATTR_IPPROTO]);
|
||||||
|
|
||||||
|
if (info->attrs[FOU_ATTR_TYPE])
|
||||||
|
cfg->type = nla_get_u8(info->attrs[FOU_ATTR_TYPE]);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user