Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Tom Herbert and committed by
David S. Miller
37dd0247 efc98d08

+217 -9
+23
include/net/gue.h
··· 1 + #ifndef __NET_GUE_H 2 + #define __NET_GUE_H 3 + 4 + struct guehdr { 5 + union { 6 + struct { 7 + #if defined(__LITTLE_ENDIAN_BITFIELD) 8 + __u8 hlen:4, 9 + version:4; 10 + #elif defined (__BIG_ENDIAN_BITFIELD) 11 + __u8 version:4, 12 + hlen:4; 13 + #else 14 + #error "Please fix <asm/byteorder.h>" 15 + #endif 16 + __u8 next_hdr; 17 + __u16 flags; 18 + }; 19 + __u32 word; 20 + }; 21 + }; 22 + 23 + #endif
+7
include/uapi/linux/fou.h
··· 13 13 FOU_ATTR_PORT, /* u16 */ 14 14 FOU_ATTR_AF, /* u8 */ 15 15 FOU_ATTR_IPPROTO, /* u8 */ 16 + FOU_ATTR_TYPE, /* u8 */ 16 17 17 18 __FOU_ATTR_MAX, 18 19 }; ··· 26 25 FOU_CMD_DEL, 27 26 28 27 __FOU_CMD_MAX, 28 + }; 29 + 30 + enum { 31 + FOU_ENCAP_UNSPEC, 32 + FOU_ENCAP_DIRECT, 33 + FOU_ENCAP_GUE, 29 34 }; 30 35 31 36 #define FOU_CMD_MAX (__FOU_CMD_MAX - 1)
+187 -9
net/ipv4/fou.c
··· 7 7 #include <linux/types.h> 8 8 #include <linux/kernel.h> 9 9 #include <net/genetlink.h> 10 + #include <net/gue.h> 10 11 #include <net/ip.h> 11 12 #include <net/protocol.h> 12 13 #include <net/udp.h> ··· 28 27 }; 29 28 30 29 struct fou_cfg { 30 + u16 type; 31 31 u8 protocol; 32 32 struct udp_port_cfg udp_config; 33 33 }; ··· 64 62 65 63 return fou_udp_encap_recv_deliver(skb, fou->protocol, 66 64 sizeof(struct udphdr)); 65 + } 66 + 67 + static int gue_udp_recv(struct sock *sk, struct sk_buff *skb) 68 + { 69 + struct fou *fou = fou_from_sock(sk); 70 + size_t len; 71 + struct guehdr *guehdr; 72 + struct udphdr *uh; 73 + 74 + if (!fou) 75 + return 1; 76 + 77 + len = sizeof(struct udphdr) + sizeof(struct guehdr); 78 + if (!pskb_may_pull(skb, len)) 79 + goto drop; 80 + 81 + uh = udp_hdr(skb); 82 + guehdr = (struct guehdr *)&uh[1]; 83 + 84 + len += guehdr->hlen << 2; 85 + if (!pskb_may_pull(skb, len)) 86 + goto drop; 87 + 88 + if (guehdr->version != 0) 89 + goto drop; 90 + 91 + if (guehdr->flags) { 92 + /* No support yet */ 93 + goto drop; 94 + } 95 + 96 + return fou_udp_encap_recv_deliver(skb, guehdr->next_hdr, len); 97 + drop: 98 + kfree_skb(skb); 99 + return 0; 67 100 } 68 101 69 102 static struct sk_buff **fou_gro_receive(struct sk_buff **head, ··· 144 107 return err; 145 108 } 146 109 110 + static struct sk_buff **gue_gro_receive(struct sk_buff **head, 111 + struct sk_buff *skb) 112 + { 113 + const struct net_offload **offloads; 114 + const struct net_offload *ops; 115 + struct sk_buff **pp = NULL; 116 + struct sk_buff *p; 117 + u8 proto; 118 + struct guehdr *guehdr; 119 + unsigned int hlen, guehlen; 120 + unsigned int off; 121 + int flush = 1; 122 + 123 + off = skb_gro_offset(skb); 124 + hlen = off + sizeof(*guehdr); 125 + guehdr = skb_gro_header_fast(skb, off); 126 + if (skb_gro_header_hard(skb, hlen)) { 127 + guehdr = skb_gro_header_slow(skb, hlen, off); 128 + if (unlikely(!guehdr)) 129 + goto out; 130 + } 131 + 132 + proto = guehdr->next_hdr; 133 + 134 + rcu_read_lock(); 135 + offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads; 136 + ops = rcu_dereference(offloads[proto]); 137 + if (WARN_ON(!ops || !ops->callbacks.gro_receive)) 138 + goto out_unlock; 139 + 140 + guehlen = sizeof(*guehdr) + (guehdr->hlen << 2); 141 + 142 + hlen = off + guehlen; 143 + if (skb_gro_header_hard(skb, hlen)) { 144 + guehdr = skb_gro_header_slow(skb, hlen, off); 145 + if (unlikely(!guehdr)) 146 + goto out_unlock; 147 + } 148 + 149 + flush = 0; 150 + 151 + for (p = *head; p; p = p->next) { 152 + const struct guehdr *guehdr2; 153 + 154 + if (!NAPI_GRO_CB(p)->same_flow) 155 + continue; 156 + 157 + guehdr2 = (struct guehdr *)(p->data + off); 158 + 159 + /* Compare base GUE header to be equal (covers 160 + * hlen, version, next_hdr, and flags. 161 + */ 162 + if (guehdr->word != guehdr2->word) { 163 + NAPI_GRO_CB(p)->same_flow = 0; 164 + continue; 165 + } 166 + 167 + /* Compare optional fields are the same. */ 168 + if (guehdr->hlen && memcmp(&guehdr[1], &guehdr2[1], 169 + guehdr->hlen << 2)) { 170 + NAPI_GRO_CB(p)->same_flow = 0; 171 + continue; 172 + } 173 + } 174 + 175 + skb_gro_pull(skb, guehlen); 176 + 177 + /* Adjusted NAPI_GRO_CB(skb)->csum after skb_gro_pull()*/ 178 + skb_gro_postpull_rcsum(skb, guehdr, guehlen); 179 + 180 + pp = ops->callbacks.gro_receive(head, skb); 181 + 182 + out_unlock: 183 + rcu_read_unlock(); 184 + out: 185 + NAPI_GRO_CB(skb)->flush |= flush; 186 + 187 + return pp; 188 + } 189 + 190 + static int gue_gro_complete(struct sk_buff *skb, int nhoff) 191 + { 192 + const struct net_offload **offloads; 193 + struct guehdr *guehdr = (struct guehdr *)(skb->data + nhoff); 194 + const struct net_offload *ops; 195 + unsigned int guehlen; 196 + u8 proto; 197 + int err = -ENOENT; 198 + 199 + proto = guehdr->next_hdr; 200 + 201 + guehlen = sizeof(*guehdr) + (guehdr->hlen << 2); 202 + 203 + rcu_read_lock(); 204 + offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads; 205 + ops = rcu_dereference(offloads[proto]); 206 + if (WARN_ON(!ops || !ops->callbacks.gro_complete)) 207 + goto out_unlock; 208 + 209 + err = ops->callbacks.gro_complete(skb, nhoff + guehlen); 210 + 211 + out_unlock: 212 + rcu_read_unlock(); 213 + return err; 214 + } 215 + 147 216 static int fou_add_to_port_list(struct fou *fou) 148 217 { 149 218 struct fou *fout; ··· 285 142 kfree(fou); 286 143 } 287 144 145 + static int fou_encap_init(struct sock *sk, struct fou *fou, struct fou_cfg *cfg) 146 + { 147 + udp_sk(sk)->encap_rcv = fou_udp_recv; 148 + fou->protocol = cfg->protocol; 149 + fou->udp_offloads.callbacks.gro_receive = fou_gro_receive; 150 + fou->udp_offloads.callbacks.gro_complete = fou_gro_complete; 151 + fou->udp_offloads.port = cfg->udp_config.local_udp_port; 152 + fou->udp_offloads.ipproto = cfg->protocol; 153 + 154 + return 0; 155 + } 156 + 157 + static int gue_encap_init(struct sock *sk, struct fou *fou, struct fou_cfg *cfg) 158 + { 159 + udp_sk(sk)->encap_rcv = gue_udp_recv; 160 + fou->udp_offloads.callbacks.gro_receive = gue_gro_receive; 161 + fou->udp_offloads.callbacks.gro_complete = gue_gro_complete; 162 + fou->udp_offloads.port = cfg->udp_config.local_udp_port; 163 + 164 + return 0; 165 + } 166 + 288 167 static int fou_create(struct net *net, struct fou_cfg *cfg, 289 168 struct socket **sockp) 290 169 { ··· 329 164 330 165 sk = sock->sk; 331 166 332 - /* Mark socket as an encapsulation socket. See net/ipv4/udp.c */ 333 - fou->protocol = cfg->protocol; 334 - fou->port = cfg->udp_config.local_udp_port; 335 - udp_sk(sk)->encap_rcv = fou_udp_recv; 167 + fou->port = cfg->udp_config.local_udp_port; 168 + 169 + /* Initial for fou type */ 170 + switch (cfg->type) { 171 + case FOU_ENCAP_DIRECT: 172 + err = fou_encap_init(sk, fou, cfg); 173 + if (err) 174 + goto error; 175 + break; 176 + case FOU_ENCAP_GUE: 177 + err = gue_encap_init(sk, fou, cfg); 178 + if (err) 179 + goto error; 180 + break; 181 + default: 182 + err = -EINVAL; 183 + goto error; 184 + } 336 185 337 186 udp_sk(sk)->encap_type = 1; 338 187 udp_encap_enable(); ··· 357 178 udp_set_convert_csum(sk, true); 358 179 359 180 sk->sk_allocation = GFP_ATOMIC; 360 - 361 - fou->udp_offloads.callbacks.gro_receive = fou_gro_receive; 362 - fou->udp_offloads.callbacks.gro_complete = fou_gro_complete; 363 - fou->udp_offloads.port = cfg->udp_config.local_udp_port; 364 - fou->udp_offloads.ipproto = cfg->protocol; 365 181 366 182 if (cfg->udp_config.family == AF_INET) { 367 183 err = udp_add_offload(&fou->udp_offloads); ··· 414 240 [FOU_ATTR_PORT] = { .type = NLA_U16, }, 415 241 [FOU_ATTR_AF] = { .type = NLA_U8, }, 416 242 [FOU_ATTR_IPPROTO] = { .type = NLA_U8, }, 243 + [FOU_ATTR_TYPE] = { .type = NLA_U8, }, 417 244 }; 418 245 419 246 static int parse_nl_config(struct genl_info *info, ··· 441 266 442 267 if (info->attrs[FOU_ATTR_IPPROTO]) 443 268 cfg->protocol = nla_get_u8(info->attrs[FOU_ATTR_IPPROTO]); 269 + 270 + if (info->attrs[FOU_ATTR_TYPE]) 271 + cfg->type = nla_get_u8(info->attrs[FOU_ATTR_TYPE]); 444 272 445 273 return 0; 446 274 }