Skip to content

Commit

Permalink
gue: Receive side for Generic UDP Encapsulation
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Tom Herbert authored and David S. Miller committed Oct 3, 2014
1 parent efc98d0 commit 37dd024
Show file tree
Hide file tree
Showing 3 changed files with 217 additions and 9 deletions.
23 changes: 23 additions & 0 deletions include/net/gue.h
Original file line number Diff line number Diff line change
@@ -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
7 changes: 7 additions & 0 deletions include/uapi/linux/fou.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ enum {
FOU_ATTR_PORT, /* u16 */
FOU_ATTR_AF, /* u8 */
FOU_ATTR_IPPROTO, /* u8 */
FOU_ATTR_TYPE, /* u8 */

__FOU_ATTR_MAX,
};
Expand All @@ -27,6 +28,12 @@ enum {
__FOU_CMD_MAX,
};

enum {
FOU_ENCAP_UNSPEC,
FOU_ENCAP_DIRECT,
FOU_ENCAP_GUE,
};

#define FOU_CMD_MAX (__FOU_CMD_MAX - 1)

#endif /* _UAPI_LINUX_FOU_H */
196 changes: 187 additions & 9 deletions net/ipv4/fou.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <linux/types.h>
#include <linux/kernel.h>
#include <net/genetlink.h>
#include <net/gue.h>
#include <net/ip.h>
#include <net/protocol.h>
#include <net/udp.h>
Expand All @@ -27,6 +28,7 @@ struct fou {
};

struct fou_cfg {
u16 type;
u8 protocol;
struct udp_port_cfg udp_config;
};
Expand Down Expand Up @@ -64,6 +66,41 @@ static int fou_udp_recv(struct sock *sk, struct sk_buff *skb)
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,
struct sk_buff *skb)
{
Expand Down Expand Up @@ -107,6 +144,112 @@ static int fou_gro_complete(struct sk_buff *skb, int nhoff)
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)
{
struct fou *fout;
Expand Down Expand Up @@ -142,6 +285,28 @@ static void fou_release(struct fou *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,
struct socket **sockp)
{
Expand All @@ -164,10 +329,24 @@ static int fou_create(struct net *net, struct fou_cfg *cfg,

sk = sock->sk;

/* Mark socket as an encapsulation socket. See net/ipv4/udp.c */
fou->protocol = cfg->protocol;
fou->port = cfg->udp_config.local_udp_port;
udp_sk(sk)->encap_rcv = fou_udp_recv;
fou->port = cfg->udp_config.local_udp_port;

/* Initial for fou type */
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_encap_enable();
Expand All @@ -179,11 +358,6 @@ static int fou_create(struct net *net, struct fou_cfg *cfg,

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) {
err = udp_add_offload(&fou->udp_offloads);
if (err)
Expand Down Expand Up @@ -240,6 +414,7 @@ static struct nla_policy fou_nl_policy[FOU_ATTR_MAX + 1] = {
[FOU_ATTR_PORT] = { .type = NLA_U16, },
[FOU_ATTR_AF] = { .type = NLA_U8, },
[FOU_ATTR_IPPROTO] = { .type = NLA_U8, },
[FOU_ATTR_TYPE] = { .type = NLA_U8, },
};

static int parse_nl_config(struct genl_info *info,
Expand Down Expand Up @@ -267,6 +442,9 @@ static int parse_nl_config(struct genl_info *info,
if (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;
}

Expand Down

0 comments on commit 37dd024

Please sign in to comment.