This patch performs flow dissection for GUE and FOU. This is an optional feature on the receiver and is set by FOU_ATTR_DEEP_HASH netlink configuration. When enable the UDP socket flow_dissect function is set to fou_flow_dissect or gue_flow_dissect as appropriate. These functions return FLOW_DIS_RET_IPPROTO and set ip protocol argument. In the case of GUE the header is parsed to find the protocol number.
Signed-off-by: Tom Herbert <t...@herbertland.com> --- include/uapi/linux/fou.h | 1 + net/ipv4/fou.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/fou.h b/include/uapi/linux/fou.h index d2947c5..2c837eb 100644 --- a/include/uapi/linux/fou.h +++ b/include/uapi/linux/fou.h @@ -15,6 +15,7 @@ enum { FOU_ATTR_IPPROTO, /* u8 */ FOU_ATTR_TYPE, /* u8 */ FOU_ATTR_REMCSUM_NOPARTIAL, /* flag */ + FOU_ATTR_DEEP_HASH, /* flag */ __FOU_ATTR_MAX, }; diff --git a/net/ipv4/fou.c b/net/ipv4/fou.c index cf50f7e..95ac5a8 100644 --- a/net/ipv4/fou.c +++ b/net/ipv4/fou.c @@ -27,7 +27,8 @@ struct fou { struct rcu_head rcu; }; -#define FOU_F_REMCSUM_NOPARTIAL BIT(0) +#define FOU_F_REMCSUM_NOPARTIAL BIT(0) +#define FOU_F_DEEP_HASH BIT(1) struct fou_cfg { u16 type; @@ -281,6 +282,16 @@ static int fou_gro_complete(struct sock *sk, struct sk_buff *skb, return err; } +static int fou_flow_dissect(struct sock *sk, const struct sk_buff *skb, + void *data, int hlen, int *nhoff, u8 *ip_proto, + __be16 *proto) +{ + *ip_proto = fou_from_sock(sk)->protocol; + *nhoff += sizeof(struct udphdr); + + return FLOW_DIS_RET_IPPROTO; +} + static struct guehdr *gue_gro_remcsum(struct sk_buff *skb, unsigned int off, struct guehdr *guehdr, void *data, size_t hdrlen, struct gro_remcsum *grc, @@ -498,6 +509,48 @@ static int gue_gro_complete(struct sock *sk, struct sk_buff *skb, int nhoff) return err; } +static int gue_flow_dissect(struct sock *sk, const struct sk_buff *skb, + void *data, int hlen, int *nhoff, u8 *ip_proto, + __be16 *proto) +{ + struct guehdr _hdr, *hdr; + + hdr = __skb_header_pointer(skb, *nhoff + sizeof(struct udphdr), + sizeof(_hdr), data, hlen, &_hdr); + if (!hdr) + return FLOW_DIS_RET_BAD; + + switch (hdr->version) { + case 0: /* Full GUE header present */ + if (hdr->control) + return FLOW_DIS_RET_PASS; + + *nhoff += sizeof(struct udphdr) + sizeof(_hdr) + + (hdr->hlen << 2); + *ip_proto = hdr->proto_ctype; + + return FLOW_DIS_RET_IPPROTO; + case 1: + /* Direct encasulation of IPv4 or IPv6 */ + + switch (((struct iphdr *)hdr)->version) { + case 4: + *nhoff += sizeof(struct udphdr); + *ip_proto = IPPROTO_IPIP; + return FLOW_DIS_RET_IPPROTO; + case 6: + *nhoff += sizeof(struct udphdr); + *ip_proto = IPPROTO_IPV6; + return FLOW_DIS_RET_IPPROTO; + default: + return FLOW_DIS_RET_PASS; + } + + default: + return FLOW_DIS_RET_PASS; + } +} + static int fou_add_to_port_list(struct net *net, struct fou *fou) { struct fou_net *fn = net_generic(net, fou_net_id); @@ -568,12 +621,16 @@ static int fou_create(struct net *net, struct fou_cfg *cfg, tunnel_cfg.encap_rcv = fou_udp_recv; tunnel_cfg.gro_receive = fou_gro_receive; tunnel_cfg.gro_complete = fou_gro_complete; + if (cfg->flags & FOU_F_DEEP_HASH) + tunnel_cfg.flow_dissect = fou_flow_dissect; fou->protocol = cfg->protocol; break; case FOU_ENCAP_GUE: tunnel_cfg.encap_rcv = gue_udp_recv; tunnel_cfg.gro_receive = gue_gro_receive; tunnel_cfg.gro_complete = gue_gro_complete; + if (cfg->flags & FOU_F_DEEP_HASH) + tunnel_cfg.flow_dissect = gue_flow_dissect; break; default: err = -EINVAL; @@ -637,6 +694,7 @@ static const struct nla_policy fou_nl_policy[FOU_ATTR_MAX + 1] = { [FOU_ATTR_IPPROTO] = { .type = NLA_U8, }, [FOU_ATTR_TYPE] = { .type = NLA_U8, }, [FOU_ATTR_REMCSUM_NOPARTIAL] = { .type = NLA_FLAG, }, + [FOU_ATTR_DEEP_HASH] = { .type = NLA_FLAG }, }; static int parse_nl_config(struct genl_info *info, @@ -677,6 +735,9 @@ static int parse_nl_config(struct genl_info *info, if (info->attrs[FOU_ATTR_REMCSUM_NOPARTIAL]) cfg->flags |= FOU_F_REMCSUM_NOPARTIAL; + if (info->attrs[FOU_ATTR_DEEP_HASH]) + cfg->flags |= FOU_F_DEEP_HASH; + return 0; } @@ -717,6 +778,11 @@ static int fou_fill_info(struct fou *fou, struct sk_buff *msg) if (fou->flags & FOU_F_REMCSUM_NOPARTIAL) if (nla_put_flag(msg, FOU_ATTR_REMCSUM_NOPARTIAL)) return -1; + + if (fou->flags & FOU_F_DEEP_HASH) + if (nla_put_flag(msg, FOU_ATTR_DEEP_HASH)) + return -1; + return 0; } -- 2.9.3