Blame view
net/ipv4/fou.c
23.3 KB
23461551c fou: Support for ... |
1 2 3 4 5 6 7 8 9 |
#include <linux/module.h> #include <linux/errno.h> #include <linux/socket.h> #include <linux/skbuff.h> #include <linux/ip.h> #include <linux/udp.h> #include <linux/types.h> #include <linux/kernel.h> #include <net/genetlink.h> |
37dd02477 gue: Receive side... |
10 |
#include <net/gue.h> |
9dc621afa fou: make local f... |
11 |
#include <net/fou.h> |
23461551c fou: Support for ... |
12 |
#include <net/ip.h> |
afe93325b fou: Add GRO support |
13 |
#include <net/protocol.h> |
23461551c fou: Support for ... |
14 15 16 17 18 |
#include <net/udp.h> #include <net/udp_tunnel.h> #include <net/xfrm.h> #include <uapi/linux/fou.h> #include <uapi/linux/genetlink.h> |
23461551c fou: Support for ... |
19 20 21 |
struct fou { struct socket *sock; u8 protocol; |
fe881ef11 gue: Use checksum... |
22 |
u8 flags; |
4cbcdf2b6 fou: always use b... |
23 |
__be16 port; |
5f914b681 fou: Support IPv6... |
24 |
u8 family; |
7a6c8c34e fou: implement FO... |
25 |
u16 type; |
23461551c fou: Support for ... |
26 |
struct list_head list; |
3036facbb fou: clean up soc... |
27 |
struct rcu_head rcu; |
23461551c fou: Support for ... |
28 |
}; |
fe881ef11 gue: Use checksum... |
29 |
#define FOU_F_REMCSUM_NOPARTIAL BIT(0) |
23461551c fou: Support for ... |
30 |
struct fou_cfg { |
37dd02477 gue: Receive side... |
31 |
u16 type; |
23461551c fou: Support for ... |
32 |
u8 protocol; |
fe881ef11 gue: Use checksum... |
33 |
u8 flags; |
23461551c fou: Support for ... |
34 35 |
struct udp_port_cfg udp_config; }; |
02d793c5b fou: add network ... |
36 37 38 39 40 41 |
static unsigned int fou_net_id; struct fou_net { struct list_head fou_list; struct mutex fou_lock; }; |
23461551c fou: Support for ... |
42 43 44 45 |
static inline struct fou *fou_from_sock(struct sock *sk) { return sk->sk_user_data; } |
5f914b681 fou: Support IPv6... |
46 |
static int fou_recv_pull(struct sk_buff *skb, struct fou *fou, size_t len) |
23461551c fou: Support for ... |
47 |
{ |
23461551c fou: Support for ... |
48 |
/* Remove 'len' bytes from the packet (UDP header and |
5024c33ac gue: Add infrastr... |
49 |
* FOU header if present). |
23461551c fou: Support for ... |
50 |
*/ |
5f914b681 fou: Support IPv6... |
51 52 53 54 55 |
if (fou->family == AF_INET) ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(skb)->tot_len) - len); else ipv6_hdr(skb)->payload_len = htons(ntohs(ipv6_hdr(skb)->payload_len) - len); |
23461551c fou: Support for ... |
56 57 58 |
__skb_pull(skb, len); skb_postpull_rcsum(skb, udp_hdr(skb), len); skb_reset_transport_header(skb); |
a09a4c8dd tunnels: Remove e... |
59 |
return iptunnel_pull_offloads(skb); |
23461551c fou: Support for ... |
60 61 62 63 64 65 66 67 |
} static int fou_udp_recv(struct sock *sk, struct sk_buff *skb) { struct fou *fou = fou_from_sock(sk); if (!fou) return 1; |
5f914b681 fou: Support IPv6... |
68 |
if (fou_recv_pull(skb, fou, sizeof(struct udphdr))) |
a09a4c8dd tunnels: Remove e... |
69 |
goto drop; |
5024c33ac gue: Add infrastr... |
70 71 |
return -fou->protocol; |
a09a4c8dd tunnels: Remove e... |
72 73 74 75 |
drop: kfree_skb(skb); return 0; |
5024c33ac gue: Add infrastr... |
76 |
} |
a8d31c128 gue: Receive side... |
77 |
static struct guehdr *gue_remcsum(struct sk_buff *skb, struct guehdr *guehdr, |
fe881ef11 gue: Use checksum... |
78 79 |
void *data, size_t hdrlen, u8 ipproto, bool nopartial) |
a8d31c128 gue: Receive side... |
80 81 |
{ __be16 *pd = data; |
4fd671ded gue: Call remcsum... |
82 83 |
size_t start = ntohs(pd[0]); size_t offset = ntohs(pd[1]); |
b7fe10e5e gro: Fix remcsum ... |
84 85 86 87 88 |
size_t plen = sizeof(struct udphdr) + hdrlen + max_t(size_t, offset + sizeof(u16), start); if (skb->remcsum_offload) return guehdr; |
a8d31c128 gue: Receive side... |
89 |
|
a8d31c128 gue: Receive side... |
90 91 92 |
if (!pskb_may_pull(skb, plen)) return NULL; guehdr = (struct guehdr *)&udp_hdr(skb)[1]; |
fe881ef11 gue: Use checksum... |
93 94 |
skb_remcsum_process(skb, (void *)guehdr + hdrlen, start, offset, nopartial); |
a8d31c128 gue: Receive side... |
95 96 97 |
return guehdr; } |
5024c33ac gue: Add infrastr... |
98 99 100 101 102 |
static int gue_control_message(struct sk_buff *skb, struct guehdr *guehdr) { /* No support yet */ kfree_skb(skb); return 0; |
23461551c fou: Support for ... |
103 |
} |
37dd02477 gue: Receive side... |
104 105 106 |
static int gue_udp_recv(struct sock *sk, struct sk_buff *skb) { struct fou *fou = fou_from_sock(sk); |
5024c33ac gue: Add infrastr... |
107 |
size_t len, optlen, hdrlen; |
37dd02477 gue: Receive side... |
108 |
struct guehdr *guehdr; |
5024c33ac gue: Add infrastr... |
109 |
void *data; |
a8d31c128 gue: Receive side... |
110 |
u16 doffset = 0; |
37dd02477 gue: Receive side... |
111 112 113 114 115 116 117 |
if (!fou) return 1; len = sizeof(struct udphdr) + sizeof(struct guehdr); if (!pskb_may_pull(skb, len)) goto drop; |
5024c33ac gue: Add infrastr... |
118 |
guehdr = (struct guehdr *)&udp_hdr(skb)[1]; |
c1e48af79 gue: Implement di... |
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
switch (guehdr->version) { case 0: /* Full GUE header present */ break; case 1: { /* Direct encasulation of IPv4 or IPv6 */ int prot; switch (((struct iphdr *)guehdr)->version) { case 4: prot = IPPROTO_IPIP; break; case 6: prot = IPPROTO_IPV6; break; default: goto drop; } if (fou_recv_pull(skb, fou, sizeof(struct udphdr))) goto drop; return -prot; } default: /* Undefined version */ goto drop; } |
5024c33ac gue: Add infrastr... |
148 149 |
optlen = guehdr->hlen << 2; len += optlen; |
37dd02477 gue: Receive side... |
150 |
|
37dd02477 gue: Receive side... |
151 152 |
if (!pskb_may_pull(skb, len)) goto drop; |
5024c33ac gue: Add infrastr... |
153 154 |
/* guehdr may change after pull */ guehdr = (struct guehdr *)&udp_hdr(skb)[1]; |
d8f00d271 ipv4: fix a poten... |
155 |
|
5024c33ac gue: Add infrastr... |
156 |
hdrlen = sizeof(struct guehdr) + optlen; |
37dd02477 gue: Receive side... |
157 |
|
5024c33ac gue: Add infrastr... |
158 |
if (guehdr->version != 0 || validate_gue_flags(guehdr, optlen)) |
37dd02477 gue: Receive side... |
159 |
goto drop; |
5024c33ac gue: Add infrastr... |
160 |
|
a8d31c128 gue: Receive side... |
161 |
hdrlen = sizeof(struct guehdr) + optlen; |
5f914b681 fou: Support IPv6... |
162 163 164 165 166 |
if (fou->family == AF_INET) ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(skb)->tot_len) - len); else ipv6_hdr(skb)->payload_len = htons(ntohs(ipv6_hdr(skb)->payload_len) - len); |
a8d31c128 gue: Receive side... |
167 |
|
a8d31c128 gue: Receive side... |
168 169 170 171 |
/* Pull csum through the guehdr now . This can be used if * there is a remote checksum offload. */ skb_postpull_rcsum(skb, udp_hdr(skb), len); |
5024c33ac gue: Add infrastr... |
172 173 174 175 |
data = &guehdr[1]; if (guehdr->flags & GUE_FLAG_PRIV) { |
a8d31c128 gue: Receive side... |
176 177 178 179 180 181 |
__be32 flags = *(__be32 *)(data + doffset); doffset += GUE_LEN_PRIV; if (flags & GUE_PFLAG_REMCSUM) { guehdr = gue_remcsum(skb, guehdr, data + doffset, |
fe881ef11 gue: Use checksum... |
182 183 184 |
hdrlen, guehdr->proto_ctype, !!(fou->flags & FOU_F_REMCSUM_NOPARTIAL)); |
a8d31c128 gue: Receive side... |
185 186 187 188 |
if (!guehdr) goto drop; data = &guehdr[1]; |
5024c33ac gue: Add infrastr... |
189 |
|
a8d31c128 gue: Receive side... |
190 191 |
doffset += GUE_PLEN_REMCSUM; } |
37dd02477 gue: Receive side... |
192 |
} |
5024c33ac gue: Add infrastr... |
193 194 |
if (unlikely(guehdr->control)) return gue_control_message(skb, guehdr); |
4fd671ded gue: Call remcsum... |
195 |
__skb_pull(skb, sizeof(struct udphdr) + hdrlen); |
a8d31c128 gue: Receive side... |
196 |
skb_reset_transport_header(skb); |
a09a4c8dd tunnels: Remove e... |
197 198 |
if (iptunnel_pull_offloads(skb)) goto drop; |
5024c33ac gue: Add infrastr... |
199 |
return -guehdr->proto_ctype; |
37dd02477 gue: Receive side... |
200 201 202 203 |
drop: kfree_skb(skb); return 0; } |
d92283e33 fou: change to us... |
204 205 206 |
static struct sk_buff **fou_gro_receive(struct sock *sk, struct sk_buff **head, struct sk_buff *skb) |
afe93325b fou: Add GRO support |
207 208 209 |
{ const struct net_offload *ops; struct sk_buff **pp = NULL; |
d92283e33 fou: change to us... |
210 |
u8 proto = fou_from_sock(sk)->protocol; |
efc98d08e fou: eliminate IP... |
211 |
const struct net_offload **offloads; |
afe93325b fou: Add GRO support |
212 |
|
c3483384e gro: Allow tunnel... |
213 214 215 216 217 218 219 |
/* We can clear the encap_mark for FOU as we are essentially doing * one of two possible things. We are either adding an L4 tunnel * header to the outer L3 tunnel header, or we are are simply * treating the GRE tunnel header as though it is a UDP protocol * specific header such as VXLAN or GENEVE. */ NAPI_GRO_CB(skb)->encap_mark = 0; |
a0ca153f9 GRE: Disable segm... |
220 221 |
/* Flag this frame as already having an outer encap header */ NAPI_GRO_CB(skb)->is_fou = 1; |
afe93325b fou: Add GRO support |
222 |
rcu_read_lock(); |
efc98d08e fou: eliminate IP... |
223 |
offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads; |
afe93325b fou: Add GRO support |
224 225 226 |
ops = rcu_dereference(offloads[proto]); if (!ops || !ops->callbacks.gro_receive) goto out_unlock; |
fcd91dd44 net: add recursio... |
227 |
pp = call_gro_receive(ops->callbacks.gro_receive, head, skb); |
afe93325b fou: Add GRO support |
228 229 230 231 232 233 |
out_unlock: rcu_read_unlock(); return pp; } |
d92283e33 fou: change to us... |
234 235 |
static int fou_gro_complete(struct sock *sk, struct sk_buff *skb, int nhoff) |
afe93325b fou: Add GRO support |
236 237 |
{ const struct net_offload *ops; |
d92283e33 fou: change to us... |
238 |
u8 proto = fou_from_sock(sk)->protocol; |
afe93325b fou: Add GRO support |
239 |
int err = -ENOSYS; |
efc98d08e fou: eliminate IP... |
240 |
const struct net_offload **offloads; |
afe93325b fou: Add GRO support |
241 242 |
rcu_read_lock(); |
efc98d08e fou: eliminate IP... |
243 |
offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads; |
afe93325b fou: Add GRO support |
244 245 246 247 248 |
ops = rcu_dereference(offloads[proto]); if (WARN_ON(!ops || !ops->callbacks.gro_complete)) goto out_unlock; err = ops->callbacks.gro_complete(skb, nhoff); |
229740c63 udp_offload: Set ... |
249 |
skb_set_inner_mac_header(skb, nhoff); |
afe93325b fou: Add GRO support |
250 251 252 253 254 |
out_unlock: rcu_read_unlock(); return err; } |
a8d31c128 gue: Receive side... |
255 256 |
static struct guehdr *gue_gro_remcsum(struct sk_buff *skb, unsigned int off, struct guehdr *guehdr, void *data, |
b7fe10e5e gro: Fix remcsum ... |
257 258 |
size_t hdrlen, struct gro_remcsum *grc, bool nopartial) |
a8d31c128 gue: Receive side... |
259 260 |
{ __be16 *pd = data; |
4fd671ded gue: Call remcsum... |
261 262 |
size_t start = ntohs(pd[0]); size_t offset = ntohs(pd[1]); |
a8d31c128 gue: Receive side... |
263 264 |
if (skb->remcsum_offload) |
b7fe10e5e gro: Fix remcsum ... |
265 |
return guehdr; |
a8d31c128 gue: Receive side... |
266 |
|
4fd671ded gue: Call remcsum... |
267 |
if (!NAPI_GRO_CB(skb)->csum_valid) |
a8d31c128 gue: Receive side... |
268 |
return NULL; |
b7fe10e5e gro: Fix remcsum ... |
269 270 |
guehdr = skb_gro_remcsum_process(skb, (void *)guehdr, off, hdrlen, start, offset, grc, nopartial); |
a8d31c128 gue: Receive side... |
271 272 273 274 275 |
skb->remcsum_offload = 1; return guehdr; } |
d92283e33 fou: change to us... |
276 277 278 |
static struct sk_buff **gue_gro_receive(struct sock *sk, struct sk_buff **head, struct sk_buff *skb) |
37dd02477 gue: Receive side... |
279 280 281 282 283 |
{ const struct net_offload **offloads; const struct net_offload *ops; struct sk_buff **pp = NULL; struct sk_buff *p; |
37dd02477 gue: Receive side... |
284 |
struct guehdr *guehdr; |
5024c33ac gue: Add infrastr... |
285 286 |
size_t len, optlen, hdrlen, off; void *data; |
a8d31c128 gue: Receive side... |
287 |
u16 doffset = 0; |
37dd02477 gue: Receive side... |
288 |
int flush = 1; |
d92283e33 fou: change to us... |
289 |
struct fou *fou = fou_from_sock(sk); |
26c4f7da3 net: Fix remcsum ... |
290 |
struct gro_remcsum grc; |
c1e48af79 gue: Implement di... |
291 |
u8 proto; |
26c4f7da3 net: Fix remcsum ... |
292 293 |
skb_gro_remcsum_init(&grc); |
37dd02477 gue: Receive side... |
294 295 |
off = skb_gro_offset(skb); |
5024c33ac gue: Add infrastr... |
296 |
len = off + sizeof(*guehdr); |
37dd02477 gue: Receive side... |
297 |
guehdr = skb_gro_header_fast(skb, off); |
5024c33ac gue: Add infrastr... |
298 299 |
if (skb_gro_header_hard(skb, len)) { guehdr = skb_gro_header_slow(skb, len, off); |
37dd02477 gue: Receive side... |
300 301 302 |
if (unlikely(!guehdr)) goto out; } |
c1e48af79 gue: Implement di... |
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 |
switch (guehdr->version) { case 0: break; case 1: switch (((struct iphdr *)guehdr)->version) { case 4: proto = IPPROTO_IPIP; break; case 6: proto = IPPROTO_IPV6; break; default: goto out; } goto next_proto; default: goto out; } |
5024c33ac gue: Add infrastr... |
321 322 |
optlen = guehdr->hlen << 2; len += optlen; |
37dd02477 gue: Receive side... |
323 |
|
5024c33ac gue: Add infrastr... |
324 325 326 327 328 |
if (skb_gro_header_hard(skb, len)) { guehdr = skb_gro_header_slow(skb, len, off); if (unlikely(!guehdr)) goto out; } |
37dd02477 gue: Receive side... |
329 |
|
5024c33ac gue: Add infrastr... |
330 331 332 |
if (unlikely(guehdr->control) || guehdr->version != 0 || validate_gue_flags(guehdr, optlen)) goto out; |
37dd02477 gue: Receive side... |
333 |
|
5024c33ac gue: Add infrastr... |
334 |
hdrlen = sizeof(*guehdr) + optlen; |
a8d31c128 gue: Receive side... |
335 336 337 |
/* Adjust NAPI_GRO_CB(skb)->csum to account for guehdr, * this is needed if there is a remote checkcsum offload. */ |
5024c33ac gue: Add infrastr... |
338 339 340 341 342 |
skb_gro_postpull_rcsum(skb, guehdr, hdrlen); data = &guehdr[1]; if (guehdr->flags & GUE_FLAG_PRIV) { |
a8d31c128 gue: Receive side... |
343 |
__be32 flags = *(__be32 *)(data + doffset); |
5024c33ac gue: Add infrastr... |
344 |
|
a8d31c128 gue: Receive side... |
345 346 347 348 |
doffset += GUE_LEN_PRIV; if (flags & GUE_PFLAG_REMCSUM) { guehdr = gue_gro_remcsum(skb, off, guehdr, |
b7fe10e5e gro: Fix remcsum ... |
349 |
data + doffset, hdrlen, &grc, |
fe881ef11 gue: Use checksum... |
350 351 |
!!(fou->flags & FOU_F_REMCSUM_NOPARTIAL)); |
b7fe10e5e gro: Fix remcsum ... |
352 |
|
a8d31c128 gue: Receive side... |
353 354 355 356 357 358 359 |
if (!guehdr) goto out; data = &guehdr[1]; doffset += GUE_PLEN_REMCSUM; } |
37dd02477 gue: Receive side... |
360 |
} |
a8d31c128 gue: Receive side... |
361 |
skb_gro_pull(skb, hdrlen); |
37dd02477 gue: Receive side... |
362 363 364 365 366 367 368 369 370 |
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 |
5024c33ac gue: Add infrastr... |
371 |
* hlen, version, proto_ctype, and flags. |
37dd02477 gue: Receive side... |
372 373 374 375 376 377 378 379 380 381 382 383 384 |
*/ 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; } } |
c1e48af79 gue: Implement di... |
385 386 387 |
proto = guehdr->proto_ctype; next_proto: |
c3483384e gro: Allow tunnel... |
388 389 390 391 392 393 394 |
/* We can clear the encap_mark for GUE as we are essentially doing * one of two possible things. We are either adding an L4 tunnel * header to the outer L3 tunnel header, or we are are simply * treating the GRE tunnel header as though it is a UDP protocol * specific header such as VXLAN or GENEVE. */ NAPI_GRO_CB(skb)->encap_mark = 0; |
a0ca153f9 GRE: Disable segm... |
395 396 |
/* Flag this frame as already having an outer encap header */ NAPI_GRO_CB(skb)->is_fou = 1; |
5024c33ac gue: Add infrastr... |
397 398 |
rcu_read_lock(); offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads; |
c1e48af79 gue: Implement di... |
399 |
ops = rcu_dereference(offloads[proto]); |
270136613 fou: Do WARN_ON_O... |
400 |
if (WARN_ON_ONCE(!ops || !ops->callbacks.gro_receive)) |
5024c33ac gue: Add infrastr... |
401 |
goto out_unlock; |
37dd02477 gue: Receive side... |
402 |
|
fcd91dd44 net: add recursio... |
403 |
pp = call_gro_receive(ops->callbacks.gro_receive, head, skb); |
c194cf93c gro: Defer cleari... |
404 |
flush = 0; |
37dd02477 gue: Receive side... |
405 406 407 408 |
out_unlock: rcu_read_unlock(); out: |
b364a914c net: fix use-afte... |
409 |
skb_gro_flush_final_remcsum(skb, pp, flush, &grc); |
37dd02477 gue: Receive side... |
410 411 412 |
return pp; } |
d92283e33 fou: change to us... |
413 |
static int gue_gro_complete(struct sock *sk, struct sk_buff *skb, int nhoff) |
37dd02477 gue: Receive side... |
414 415 416 417 |
{ const struct net_offload **offloads; struct guehdr *guehdr = (struct guehdr *)(skb->data + nhoff); const struct net_offload *ops; |
c1e48af79 gue: Implement di... |
418 |
unsigned int guehlen = 0; |
37dd02477 gue: Receive side... |
419 420 |
u8 proto; int err = -ENOENT; |
c1e48af79 gue: Implement di... |
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 |
switch (guehdr->version) { case 0: proto = guehdr->proto_ctype; guehlen = sizeof(*guehdr) + (guehdr->hlen << 2); break; case 1: switch (((struct iphdr *)guehdr)->version) { case 4: proto = IPPROTO_IPIP; break; case 6: proto = IPPROTO_IPV6; break; default: return err; } break; default: return err; } |
37dd02477 gue: Receive side... |
441 442 443 444 445 446 447 448 |
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); |
229740c63 udp_offload: Set ... |
449 |
skb_set_inner_mac_header(skb, nhoff + guehlen); |
37dd02477 gue: Receive side... |
450 451 452 453 |
out_unlock: rcu_read_unlock(); return err; } |
02d793c5b fou: add network ... |
454 |
static int fou_add_to_port_list(struct net *net, struct fou *fou) |
23461551c fou: Support for ... |
455 |
{ |
02d793c5b fou: add network ... |
456 |
struct fou_net *fn = net_generic(net, fou_net_id); |
23461551c fou: Support for ... |
457 |
struct fou *fout; |
02d793c5b fou: add network ... |
458 459 |
mutex_lock(&fn->fou_lock); list_for_each_entry(fout, &fn->fou_list, list) { |
5f914b681 fou: Support IPv6... |
460 461 |
if (fou->port == fout->port && fou->family == fout->family) { |
02d793c5b fou: add network ... |
462 |
mutex_unlock(&fn->fou_lock); |
23461551c fou: Support for ... |
463 464 465 |
return -EALREADY; } } |
02d793c5b fou: add network ... |
466 467 |
list_add(&fou->list, &fn->fou_list); mutex_unlock(&fn->fou_lock); |
23461551c fou: Support for ... |
468 469 470 471 472 473 474 |
return 0; } static void fou_release(struct fou *fou) { struct socket *sock = fou->sock; |
23461551c fou: Support for ... |
475 |
|
23461551c fou: Support for ... |
476 |
list_del(&fou->list); |
02d793c5b fou: add network ... |
477 |
udp_tunnel_sock_release(sock); |
23461551c fou: Support for ... |
478 |
|
3036facbb fou: clean up soc... |
479 |
kfree_rcu(fou, rcu); |
23461551c fou: Support for ... |
480 481 482 483 484 |
} static int fou_create(struct net *net, struct fou_cfg *cfg, struct socket **sockp) { |
23461551c fou: Support for ... |
485 |
struct socket *sock = NULL; |
02d793c5b fou: add network ... |
486 |
struct fou *fou = NULL; |
23461551c fou: Support for ... |
487 |
struct sock *sk; |
440924bbc fou: Call setup_u... |
488 |
struct udp_tunnel_sock_cfg tunnel_cfg; |
02d793c5b fou: add network ... |
489 |
int err; |
23461551c fou: Support for ... |
490 491 492 493 494 495 496 497 498 499 500 501 502 503 |
/* Open UDP socket */ err = udp_sock_create(net, &cfg->udp_config, &sock); if (err < 0) goto error; /* Allocate FOU port structure */ fou = kzalloc(sizeof(*fou), GFP_KERNEL); if (!fou) { err = -ENOMEM; goto error; } sk = sock->sk; |
37dd02477 gue: Receive side... |
504 |
fou->port = cfg->udp_config.local_udp_port; |
5f914b681 fou: Support IPv6... |
505 506 |
fou->family = cfg->udp_config.family; fou->flags = cfg->flags; |
440924bbc fou: Call setup_u... |
507 508 509 510 511 512 513 |
fou->type = cfg->type; fou->sock = sock; memset(&tunnel_cfg, 0, sizeof(tunnel_cfg)); tunnel_cfg.encap_type = 1; tunnel_cfg.sk_user_data = fou; tunnel_cfg.encap_destroy = NULL; |
37dd02477 gue: Receive side... |
514 515 516 517 |
/* Initial for fou type */ switch (cfg->type) { case FOU_ENCAP_DIRECT: |
440924bbc fou: Call setup_u... |
518 519 520 521 |
tunnel_cfg.encap_rcv = fou_udp_recv; tunnel_cfg.gro_receive = fou_gro_receive; tunnel_cfg.gro_complete = fou_gro_complete; fou->protocol = cfg->protocol; |
37dd02477 gue: Receive side... |
522 523 |
break; case FOU_ENCAP_GUE: |
440924bbc fou: Call setup_u... |
524 525 526 |
tunnel_cfg.encap_rcv = gue_udp_recv; tunnel_cfg.gro_receive = gue_gro_receive; tunnel_cfg.gro_complete = gue_gro_complete; |
37dd02477 gue: Receive side... |
527 528 529 530 531 |
break; default: err = -EINVAL; goto error; } |
23461551c fou: Support for ... |
532 |
|
440924bbc fou: Call setup_u... |
533 |
setup_udp_tunnel_sock(net, sock, &tunnel_cfg); |
23461551c fou: Support for ... |
534 535 |
sk->sk_allocation = GFP_ATOMIC; |
02d793c5b fou: add network ... |
536 |
err = fou_add_to_port_list(net, fou); |
23461551c fou: Support for ... |
537 538 539 540 541 542 543 544 545 546 547 |
if (err) goto error; if (sockp) *sockp = sock; return 0; error: kfree(fou); if (sock) |
02d793c5b fou: add network ... |
548 |
udp_tunnel_sock_release(sock); |
23461551c fou: Support for ... |
549 550 551 552 553 554 |
return err; } static int fou_destroy(struct net *net, struct fou_cfg *cfg) { |
02d793c5b fou: add network ... |
555 |
struct fou_net *fn = net_generic(net, fou_net_id); |
4cbcdf2b6 fou: always use b... |
556 |
__be16 port = cfg->udp_config.local_udp_port; |
5f914b681 fou: Support IPv6... |
557 |
u8 family = cfg->udp_config.family; |
23461551c fou: Support for ... |
558 |
int err = -EINVAL; |
02d793c5b fou: add network ... |
559 |
struct fou *fou; |
23461551c fou: Support for ... |
560 |
|
02d793c5b fou: add network ... |
561 562 |
mutex_lock(&fn->fou_lock); list_for_each_entry(fou, &fn->fou_list, list) { |
5f914b681 fou: Support IPv6... |
563 |
if (fou->port == port && fou->family == family) { |
23461551c fou: Support for ... |
564 565 566 567 568 |
fou_release(fou); err = 0; break; } } |
02d793c5b fou: add network ... |
569 |
mutex_unlock(&fn->fou_lock); |
23461551c fou: Support for ... |
570 571 572 |
return err; } |
489111e5c genetlink: static... |
573 |
static struct genl_family fou_nl_family; |
23461551c fou: Support for ... |
574 |
|
3f18ff2b4 fou: make nla_pol... |
575 |
static const struct nla_policy fou_nl_policy[FOU_ATTR_MAX + 1] = { |
23461551c fou: Support for ... |
576 577 578 |
[FOU_ATTR_PORT] = { .type = NLA_U16, }, [FOU_ATTR_AF] = { .type = NLA_U8, }, [FOU_ATTR_IPPROTO] = { .type = NLA_U8, }, |
37dd02477 gue: Receive side... |
579 |
[FOU_ATTR_TYPE] = { .type = NLA_U8, }, |
fe881ef11 gue: Use checksum... |
580 |
[FOU_ATTR_REMCSUM_NOPARTIAL] = { .type = NLA_FLAG, }, |
23461551c fou: Support for ... |
581 582 583 584 585 586 587 588 589 590 591 |
}; static int parse_nl_config(struct genl_info *info, struct fou_cfg *cfg) { memset(cfg, 0, sizeof(*cfg)); cfg->udp_config.family = AF_INET; if (info->attrs[FOU_ATTR_AF]) { u8 family = nla_get_u8(info->attrs[FOU_ATTR_AF]); |
5f914b681 fou: Support IPv6... |
592 593 594 595 596 597 598 599 600 |
switch (family) { case AF_INET: break; case AF_INET6: cfg->udp_config.ipv6_v6only = 1; break; default: return -EAFNOSUPPORT; } |
23461551c fou: Support for ... |
601 602 603 604 605 |
cfg->udp_config.family = family; } if (info->attrs[FOU_ATTR_PORT]) { |
4cbcdf2b6 fou: always use b... |
606 |
__be16 port = nla_get_be16(info->attrs[FOU_ATTR_PORT]); |
23461551c fou: Support for ... |
607 608 609 610 611 612 |
cfg->udp_config.local_udp_port = port; } if (info->attrs[FOU_ATTR_IPPROTO]) cfg->protocol = nla_get_u8(info->attrs[FOU_ATTR_IPPROTO]); |
37dd02477 gue: Receive side... |
613 614 |
if (info->attrs[FOU_ATTR_TYPE]) cfg->type = nla_get_u8(info->attrs[FOU_ATTR_TYPE]); |
fe881ef11 gue: Use checksum... |
615 616 |
if (info->attrs[FOU_ATTR_REMCSUM_NOPARTIAL]) cfg->flags |= FOU_F_REMCSUM_NOPARTIAL; |
23461551c fou: Support for ... |
617 618 619 620 621 |
return 0; } static int fou_nl_cmd_add_port(struct sk_buff *skb, struct genl_info *info) { |
02d793c5b fou: add network ... |
622 |
struct net *net = genl_info_net(info); |
23461551c fou: Support for ... |
623 624 625 626 627 628 |
struct fou_cfg cfg; int err; err = parse_nl_config(info, &cfg); if (err) return err; |
02d793c5b fou: add network ... |
629 |
return fou_create(net, &cfg, NULL); |
23461551c fou: Support for ... |
630 631 632 633 |
} static int fou_nl_cmd_rm_port(struct sk_buff *skb, struct genl_info *info) { |
02d793c5b fou: add network ... |
634 |
struct net *net = genl_info_net(info); |
23461551c fou: Support for ... |
635 |
struct fou_cfg cfg; |
67270636a fou: exit early w... |
636 |
int err; |
23461551c fou: Support for ... |
637 |
|
67270636a fou: exit early w... |
638 639 640 |
err = parse_nl_config(info, &cfg); if (err) return err; |
23461551c fou: Support for ... |
641 |
|
02d793c5b fou: add network ... |
642 |
return fou_destroy(net, &cfg); |
23461551c fou: Support for ... |
643 |
} |
7a6c8c34e fou: implement FO... |
644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 |
static int fou_fill_info(struct fou *fou, struct sk_buff *msg) { if (nla_put_u8(msg, FOU_ATTR_AF, fou->sock->sk->sk_family) || nla_put_be16(msg, FOU_ATTR_PORT, fou->port) || nla_put_u8(msg, FOU_ATTR_IPPROTO, fou->protocol) || nla_put_u8(msg, FOU_ATTR_TYPE, fou->type)) return -1; if (fou->flags & FOU_F_REMCSUM_NOPARTIAL) if (nla_put_flag(msg, FOU_ATTR_REMCSUM_NOPARTIAL)) return -1; return 0; } static int fou_dump_info(struct fou *fou, u32 portid, u32 seq, u32 flags, struct sk_buff *skb, u8 cmd) { void *hdr; hdr = genlmsg_put(skb, portid, seq, &fou_nl_family, flags, cmd); if (!hdr) return -ENOMEM; if (fou_fill_info(fou, skb) < 0) goto nla_put_failure; genlmsg_end(skb, hdr); return 0; nla_put_failure: genlmsg_cancel(skb, hdr); return -EMSGSIZE; } static int fou_nl_cmd_get_port(struct sk_buff *skb, struct genl_info *info) { struct net *net = genl_info_net(info); struct fou_net *fn = net_generic(net, fou_net_id); struct sk_buff *msg; struct fou_cfg cfg; struct fou *fout; __be16 port; |
5f914b681 fou: Support IPv6... |
686 |
u8 family; |
7a6c8c34e fou: implement FO... |
687 688 689 690 691 692 693 694 |
int ret; ret = parse_nl_config(info, &cfg); if (ret) return ret; port = cfg.udp_config.local_udp_port; if (port == 0) return -EINVAL; |
5f914b681 fou: Support IPv6... |
695 696 697 |
family = cfg.udp_config.family; if (family != AF_INET && family != AF_INET6) return -EINVAL; |
7a6c8c34e fou: implement FO... |
698 699 700 701 702 703 704 |
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return -ENOMEM; ret = -ESRCH; mutex_lock(&fn->fou_lock); list_for_each_entry(fout, &fn->fou_list, list) { |
5f914b681 fou: Support IPv6... |
705 |
if (port == fout->port && family == fout->family) { |
7a6c8c34e fou: implement FO... |
706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 |
ret = fou_dump_info(fout, info->snd_portid, info->snd_seq, 0, msg, info->genlhdr->cmd); break; } } mutex_unlock(&fn->fou_lock); if (ret < 0) goto out_free; return genlmsg_reply(msg, info); out_free: nlmsg_free(msg); return ret; } static int fou_nl_dump(struct sk_buff *skb, struct netlink_callback *cb) { struct net *net = sock_net(skb->sk); struct fou_net *fn = net_generic(net, fou_net_id); struct fou *fout; int idx = 0, ret; mutex_lock(&fn->fou_lock); list_for_each_entry(fout, &fn->fou_list, list) { if (idx++ < cb->args[0]) continue; ret = fou_dump_info(fout, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, skb, FOU_CMD_GET); if (ret) |
540207ae6 fou: avoid missin... |
738 |
break; |
7a6c8c34e fou: implement FO... |
739 740 |
} mutex_unlock(&fn->fou_lock); |
7a6c8c34e fou: implement FO... |
741 742 743 |
cb->args[0] = idx; return skb->len; } |
23461551c fou: Support for ... |
744 745 746 747 748 749 750 751 752 753 754 755 756 |
static const struct genl_ops fou_nl_ops[] = { { .cmd = FOU_CMD_ADD, .doit = fou_nl_cmd_add_port, .policy = fou_nl_policy, .flags = GENL_ADMIN_PERM, }, { .cmd = FOU_CMD_DEL, .doit = fou_nl_cmd_rm_port, .policy = fou_nl_policy, .flags = GENL_ADMIN_PERM, }, |
7a6c8c34e fou: implement FO... |
757 758 759 760 761 762 |
{ .cmd = FOU_CMD_GET, .doit = fou_nl_cmd_get_port, .dumpit = fou_nl_dump, .policy = fou_nl_policy, }, |
23461551c fou: Support for ... |
763 |
}; |
56989f6d8 genetlink: mark f... |
764 |
static struct genl_family fou_nl_family __ro_after_init = { |
489111e5c genetlink: static... |
765 766 767 768 769 770 771 772 773 |
.hdrsize = 0, .name = FOU_GENL_NAME, .version = FOU_GENL_VERSION, .maxattr = FOU_ATTR_MAX, .netnsok = true, .module = THIS_MODULE, .ops = fou_nl_ops, .n_ops = ARRAY_SIZE(fou_nl_ops), }; |
a8c5f90fb ip_tunnel: Ops re... |
774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 |
size_t fou_encap_hlen(struct ip_tunnel_encap *e) { return sizeof(struct udphdr); } EXPORT_SYMBOL(fou_encap_hlen); size_t gue_encap_hlen(struct ip_tunnel_encap *e) { size_t len; bool need_priv = false; len = sizeof(struct udphdr) + sizeof(struct guehdr); if (e->flags & TUNNEL_ENCAP_FLAG_REMCSUM) { len += GUE_PLEN_REMCSUM; need_priv = true; } len += need_priv ? GUE_LEN_PRIV : 0; return len; } EXPORT_SYMBOL(gue_encap_hlen); |
dc969b81e fou: Split out {f... |
797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 |
int __fou_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e, u8 *protocol, __be16 *sport, int type) { int err; err = iptunnel_handle_offloads(skb, type); if (err) return err; *sport = e->sport ? : udp_flow_src_port(dev_net(skb->dev), skb, 0, 0, false); return 0; } EXPORT_SYMBOL(__fou_build_header); |
dc969b81e fou: Split out {f... |
812 813 |
int __gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e, u8 *protocol, __be16 *sport, int type) |
63487babf net: Move fou_bui... |
814 |
{ |
63487babf net: Move fou_bui... |
815 |
struct guehdr *guehdr; |
b17f709a2 gue: TX support f... |
816 |
size_t hdrlen, optlen = 0; |
5024c33ac gue: Add infrastr... |
817 818 |
void *data; bool need_priv = false; |
aed069df0 ip_tunnel_core: i... |
819 |
int err; |
5024c33ac gue: Add infrastr... |
820 |
|
b17f709a2 gue: TX support f... |
821 822 |
if ((e->flags & TUNNEL_ENCAP_FLAG_REMCSUM) && skb->ip_summed == CHECKSUM_PARTIAL) { |
b17f709a2 gue: TX support f... |
823 824 825 826 |
optlen += GUE_PLEN_REMCSUM; type |= SKB_GSO_TUNNEL_REMCSUM; need_priv = true; } |
5024c33ac gue: Add infrastr... |
827 |
optlen += need_priv ? GUE_LEN_PRIV : 0; |
63487babf net: Move fou_bui... |
828 |
|
aed069df0 ip_tunnel_core: i... |
829 830 831 |
err = iptunnel_handle_offloads(skb, type); if (err) return err; |
63487babf net: Move fou_bui... |
832 833 |
/* Get source port (based on flow hash) before skb_push */ |
dc969b81e fou: Split out {f... |
834 835 |
*sport = e->sport ? : udp_flow_src_port(dev_net(skb->dev), skb, 0, 0, false); |
63487babf net: Move fou_bui... |
836 |
|
b17f709a2 gue: TX support f... |
837 838 839 |
hdrlen = sizeof(struct guehdr) + optlen; skb_push(skb, hdrlen); |
63487babf net: Move fou_bui... |
840 841 |
guehdr = (struct guehdr *)skb->data; |
5024c33ac gue: Add infrastr... |
842 |
guehdr->control = 0; |
63487babf net: Move fou_bui... |
843 |
guehdr->version = 0; |
5024c33ac gue: Add infrastr... |
844 |
guehdr->hlen = optlen >> 2; |
63487babf net: Move fou_bui... |
845 |
guehdr->flags = 0; |
5024c33ac gue: Add infrastr... |
846 847 848 849 850 851 852 853 854 855 |
guehdr->proto_ctype = *protocol; data = &guehdr[1]; if (need_priv) { __be32 *flags = data; guehdr->flags |= GUE_FLAG_PRIV; *flags = 0; data += GUE_LEN_PRIV; |
b17f709a2 gue: TX support f... |
856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 |
if (type & SKB_GSO_TUNNEL_REMCSUM) { u16 csum_start = skb_checksum_start_offset(skb); __be16 *pd = data; if (csum_start < hdrlen) return -EINVAL; csum_start -= hdrlen; pd[0] = htons(csum_start); pd[1] = htons(csum_start + skb->csum_offset); if (!skb_is_gso(skb)) { skb->ip_summed = CHECKSUM_NONE; skb->encapsulation = 0; } *flags |= GUE_PFLAG_REMCSUM; data += GUE_PLEN_REMCSUM; } |
5024c33ac gue: Add infrastr... |
875 |
} |
63487babf net: Move fou_bui... |
876 |
|
dc969b81e fou: Split out {f... |
877 878 879 |
return 0; } EXPORT_SYMBOL(__gue_build_header); |
9dc621afa fou: make local f... |
880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 |
#ifdef CONFIG_NET_FOU_IP_TUNNELS static void fou_build_udp(struct sk_buff *skb, struct ip_tunnel_encap *e, struct flowi4 *fl4, u8 *protocol, __be16 sport) { struct udphdr *uh; skb_push(skb, sizeof(struct udphdr)); skb_reset_transport_header(skb); uh = udp_hdr(skb); uh->dest = e->dport; uh->source = sport; uh->len = htons(skb->len); udp_set_csum(!(e->flags & TUNNEL_ENCAP_FLAG_CSUM), skb, fl4->saddr, fl4->daddr, skb->len); *protocol = IPPROTO_UDP; } static int fou_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e, u8 *protocol, struct flowi4 *fl4) { int type = e->flags & TUNNEL_ENCAP_FLAG_CSUM ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL; __be16 sport; int err; err = __fou_build_header(skb, e, protocol, &sport, type); if (err) return err; fou_build_udp(skb, e, fl4, protocol, sport); return 0; } static int gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e, u8 *protocol, struct flowi4 *fl4) |
dc969b81e fou: Split out {f... |
920 921 922 923 924 925 926 927 928 |
{ int type = e->flags & TUNNEL_ENCAP_FLAG_CSUM ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL; __be16 sport; int err; err = __gue_build_header(skb, e, protocol, &sport, type); if (err) return err; |
63487babf net: Move fou_bui... |
929 930 931 932 |
fou_build_udp(skb, e, fl4, protocol, sport); return 0; } |
63487babf net: Move fou_bui... |
933 |
|
a8c5f90fb ip_tunnel: Ops re... |
934 |
|
5eeb29221 fou: Don't use co... |
935 |
static const struct ip_tunnel_encap_ops fou_iptun_ops = { |
a8c5f90fb ip_tunnel: Ops re... |
936 937 938 |
.encap_hlen = fou_encap_hlen, .build_header = fou_build_header, }; |
5eeb29221 fou: Don't use co... |
939 |
static const struct ip_tunnel_encap_ops gue_iptun_ops = { |
a8c5f90fb ip_tunnel: Ops re... |
940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 |
.encap_hlen = gue_encap_hlen, .build_header = gue_build_header, }; static int ip_tunnel_encap_add_fou_ops(void) { int ret; ret = ip_tunnel_encap_add_ops(&fou_iptun_ops, TUNNEL_ENCAP_FOU); if (ret < 0) { pr_err("can't add fou ops "); return ret; } ret = ip_tunnel_encap_add_ops(&gue_iptun_ops, TUNNEL_ENCAP_GUE); if (ret < 0) { pr_err("can't add gue ops "); ip_tunnel_encap_del_ops(&fou_iptun_ops, TUNNEL_ENCAP_FOU); return ret; } return 0; } static void ip_tunnel_encap_del_fou_ops(void) { ip_tunnel_encap_del_ops(&fou_iptun_ops, TUNNEL_ENCAP_FOU); ip_tunnel_encap_del_ops(&gue_iptun_ops, TUNNEL_ENCAP_GUE); } #else static int ip_tunnel_encap_add_fou_ops(void) { return 0; } |
882288c05 FOU: Fix no retur... |
978 |
static void ip_tunnel_encap_del_fou_ops(void) |
a8c5f90fb ip_tunnel: Ops re... |
979 980 981 982 |
{ } #endif |
02d793c5b fou: add network ... |
983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 |
static __net_init int fou_init_net(struct net *net) { struct fou_net *fn = net_generic(net, fou_net_id); INIT_LIST_HEAD(&fn->fou_list); mutex_init(&fn->fou_lock); return 0; } static __net_exit void fou_exit_net(struct net *net) { struct fou_net *fn = net_generic(net, fou_net_id); struct fou *fou, *next; /* Close all the FOU sockets */ mutex_lock(&fn->fou_lock); list_for_each_entry_safe(fou, next, &fn->fou_list, list) fou_release(fou); mutex_unlock(&fn->fou_lock); } static struct pernet_operations fou_net_ops = { .init = fou_init_net, .exit = fou_exit_net, .id = &fou_net_id, .size = sizeof(struct fou_net), }; |
23461551c fou: Support for ... |
1010 1011 1012 |
static int __init fou_init(void) { int ret; |
02d793c5b fou: add network ... |
1013 1014 1015 |
ret = register_pernet_device(&fou_net_ops); if (ret) goto exit; |
489111e5c genetlink: static... |
1016 |
ret = genl_register_family(&fou_nl_family); |
a8c5f90fb ip_tunnel: Ops re... |
1017 |
if (ret < 0) |
02d793c5b fou: add network ... |
1018 |
goto unregister; |
a8c5f90fb ip_tunnel: Ops re... |
1019 1020 |
ret = ip_tunnel_encap_add_fou_ops(); |
02d793c5b fou: add network ... |
1021 1022 |
if (ret == 0) return 0; |
a8c5f90fb ip_tunnel: Ops re... |
1023 |
|
02d793c5b fou: add network ... |
1024 1025 1026 |
genl_unregister_family(&fou_nl_family); unregister: unregister_pernet_device(&fou_net_ops); |
a8c5f90fb ip_tunnel: Ops re... |
1027 |
exit: |
23461551c fou: Support for ... |
1028 1029 1030 1031 1032 |
return ret; } static void __exit fou_fini(void) { |
a8c5f90fb ip_tunnel: Ops re... |
1033 |
ip_tunnel_encap_del_fou_ops(); |
23461551c fou: Support for ... |
1034 |
genl_unregister_family(&fou_nl_family); |
02d793c5b fou: add network ... |
1035 |
unregister_pernet_device(&fou_net_ops); |
23461551c fou: Support for ... |
1036 1037 1038 1039 1040 1041 |
} module_init(fou_init); module_exit(fou_fini); MODULE_AUTHOR("Tom Herbert <therbert@google.com>"); MODULE_LICENSE("GPL"); |