Blame view

net/ipv4/fou.c 23.3 KB
23461551c   Tom Herbert   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   Tom Herbert   gue: Receive side...
10
  #include <net/gue.h>
9dc621afa   stephen hemminger   fou: make local f...
11
  #include <net/fou.h>
23461551c   Tom Herbert   fou: Support for ...
12
  #include <net/ip.h>
afe93325b   Tom Herbert   fou: Add GRO support
13
  #include <net/protocol.h>
23461551c   Tom Herbert   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   Tom Herbert   fou: Support for ...
19
20
21
  struct fou {
  	struct socket *sock;
  	u8 protocol;
fe881ef11   Tom Herbert   gue: Use checksum...
22
  	u8 flags;
4cbcdf2b6   WANG Cong   fou: always use b...
23
  	__be16 port;
5f914b681   Tom Herbert   fou: Support IPv6...
24
  	u8 family;
7a6c8c34e   WANG Cong   fou: implement FO...
25
  	u16 type;
23461551c   Tom Herbert   fou: Support for ...
26
  	struct list_head list;
3036facbb   Hannes Frederic Sowa   fou: clean up soc...
27
  	struct rcu_head rcu;
23461551c   Tom Herbert   fou: Support for ...
28
  };
fe881ef11   Tom Herbert   gue: Use checksum...
29
  #define FOU_F_REMCSUM_NOPARTIAL BIT(0)
23461551c   Tom Herbert   fou: Support for ...
30
  struct fou_cfg {
37dd02477   Tom Herbert   gue: Receive side...
31
  	u16 type;
23461551c   Tom Herbert   fou: Support for ...
32
  	u8 protocol;
fe881ef11   Tom Herbert   gue: Use checksum...
33
  	u8 flags;
23461551c   Tom Herbert   fou: Support for ...
34
35
  	struct udp_port_cfg udp_config;
  };
02d793c5b   WANG Cong   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   Tom Herbert   fou: Support for ...
42
43
44
45
  static inline struct fou *fou_from_sock(struct sock *sk)
  {
  	return sk->sk_user_data;
  }
5f914b681   Tom Herbert   fou: Support IPv6...
46
  static int fou_recv_pull(struct sk_buff *skb, struct fou *fou, size_t len)
23461551c   Tom Herbert   fou: Support for ...
47
  {
23461551c   Tom Herbert   fou: Support for ...
48
  	/* Remove 'len' bytes from the packet (UDP header and
5024c33ac   Tom Herbert   gue: Add infrastr...
49
  	 * FOU header if present).
23461551c   Tom Herbert   fou: Support for ...
50
  	 */
5f914b681   Tom Herbert   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   Tom Herbert   fou: Support for ...
56
57
58
  	__skb_pull(skb, len);
  	skb_postpull_rcsum(skb, udp_hdr(skb), len);
  	skb_reset_transport_header(skb);
a09a4c8dd   Jesse Gross   tunnels: Remove e...
59
  	return iptunnel_pull_offloads(skb);
23461551c   Tom Herbert   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   Tom Herbert   fou: Support IPv6...
68
  	if (fou_recv_pull(skb, fou, sizeof(struct udphdr)))
a09a4c8dd   Jesse Gross   tunnels: Remove e...
69
  		goto drop;
5024c33ac   Tom Herbert   gue: Add infrastr...
70
71
  
  	return -fou->protocol;
a09a4c8dd   Jesse Gross   tunnels: Remove e...
72
73
74
75
  
  drop:
  	kfree_skb(skb);
  	return 0;
5024c33ac   Tom Herbert   gue: Add infrastr...
76
  }
a8d31c128   Tom Herbert   gue: Receive side...
77
  static struct guehdr *gue_remcsum(struct sk_buff *skb, struct guehdr *guehdr,
fe881ef11   Tom Herbert   gue: Use checksum...
78
79
  				  void *data, size_t hdrlen, u8 ipproto,
  				  bool nopartial)
a8d31c128   Tom Herbert   gue: Receive side...
80
81
  {
  	__be16 *pd = data;
4fd671ded   Tom Herbert   gue: Call remcsum...
82
83
  	size_t start = ntohs(pd[0]);
  	size_t offset = ntohs(pd[1]);
b7fe10e5e   Tom Herbert   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   Tom Herbert   gue: Receive side...
89

a8d31c128   Tom Herbert   gue: Receive side...
90
91
92
  	if (!pskb_may_pull(skb, plen))
  		return NULL;
  	guehdr = (struct guehdr *)&udp_hdr(skb)[1];
fe881ef11   Tom Herbert   gue: Use checksum...
93
94
  	skb_remcsum_process(skb, (void *)guehdr + hdrlen,
  			    start, offset, nopartial);
a8d31c128   Tom Herbert   gue: Receive side...
95
96
97
  
  	return guehdr;
  }
5024c33ac   Tom Herbert   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   Tom Herbert   fou: Support for ...
103
  }
37dd02477   Tom Herbert   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   Tom Herbert   gue: Add infrastr...
107
  	size_t len, optlen, hdrlen;
37dd02477   Tom Herbert   gue: Receive side...
108
  	struct guehdr *guehdr;
5024c33ac   Tom Herbert   gue: Add infrastr...
109
  	void *data;
a8d31c128   Tom Herbert   gue: Receive side...
110
  	u16 doffset = 0;
37dd02477   Tom Herbert   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   Tom Herbert   gue: Add infrastr...
118
  	guehdr = (struct guehdr *)&udp_hdr(skb)[1];
c1e48af79   Tom Herbert   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   Tom Herbert   gue: Add infrastr...
148
149
  	optlen = guehdr->hlen << 2;
  	len += optlen;
37dd02477   Tom Herbert   gue: Receive side...
150

37dd02477   Tom Herbert   gue: Receive side...
151
152
  	if (!pskb_may_pull(skb, len))
  		goto drop;
5024c33ac   Tom Herbert   gue: Add infrastr...
153
154
  	/* guehdr may change after pull */
  	guehdr = (struct guehdr *)&udp_hdr(skb)[1];
d8f00d271   Li RongQing   ipv4: fix a poten...
155

5024c33ac   Tom Herbert   gue: Add infrastr...
156
  	hdrlen = sizeof(struct guehdr) + optlen;
37dd02477   Tom Herbert   gue: Receive side...
157

5024c33ac   Tom Herbert   gue: Add infrastr...
158
  	if (guehdr->version != 0 || validate_gue_flags(guehdr, optlen))
37dd02477   Tom Herbert   gue: Receive side...
159
  		goto drop;
5024c33ac   Tom Herbert   gue: Add infrastr...
160

a8d31c128   Tom Herbert   gue: Receive side...
161
  	hdrlen = sizeof(struct guehdr) + optlen;
5f914b681   Tom Herbert   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   Tom Herbert   gue: Receive side...
167

a8d31c128   Tom Herbert   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   Tom Herbert   gue: Add infrastr...
172
173
174
175
  
  	data = &guehdr[1];
  
  	if (guehdr->flags & GUE_FLAG_PRIV) {
a8d31c128   Tom Herbert   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   Tom Herbert   gue: Use checksum...
182
183
184
  					     hdrlen, guehdr->proto_ctype,
  					     !!(fou->flags &
  						FOU_F_REMCSUM_NOPARTIAL));
a8d31c128   Tom Herbert   gue: Receive side...
185
186
187
188
  			if (!guehdr)
  				goto drop;
  
  			data = &guehdr[1];
5024c33ac   Tom Herbert   gue: Add infrastr...
189

a8d31c128   Tom Herbert   gue: Receive side...
190
191
  			doffset += GUE_PLEN_REMCSUM;
  		}
37dd02477   Tom Herbert   gue: Receive side...
192
  	}
5024c33ac   Tom Herbert   gue: Add infrastr...
193
194
  	if (unlikely(guehdr->control))
  		return gue_control_message(skb, guehdr);
4fd671ded   Tom Herbert   gue: Call remcsum...
195
  	__skb_pull(skb, sizeof(struct udphdr) + hdrlen);
a8d31c128   Tom Herbert   gue: Receive side...
196
  	skb_reset_transport_header(skb);
a09a4c8dd   Jesse Gross   tunnels: Remove e...
197
198
  	if (iptunnel_pull_offloads(skb))
  		goto drop;
5024c33ac   Tom Herbert   gue: Add infrastr...
199
  	return -guehdr->proto_ctype;
37dd02477   Tom Herbert   gue: Receive side...
200
201
202
203
  drop:
  	kfree_skb(skb);
  	return 0;
  }
d92283e33   Tom Herbert   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   Tom Herbert   fou: Add GRO support
207
208
209
  {
  	const struct net_offload *ops;
  	struct sk_buff **pp = NULL;
d92283e33   Tom Herbert   fou: change to us...
210
  	u8 proto = fou_from_sock(sk)->protocol;
efc98d08e   Tom Herbert   fou: eliminate IP...
211
  	const struct net_offload **offloads;
afe93325b   Tom Herbert   fou: Add GRO support
212

c3483384e   Alexander Duyck   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   Alexander Duyck   GRE: Disable segm...
220
221
  	/* Flag this frame as already having an outer encap header */
  	NAPI_GRO_CB(skb)->is_fou = 1;
afe93325b   Tom Herbert   fou: Add GRO support
222
  	rcu_read_lock();
efc98d08e   Tom Herbert   fou: eliminate IP...
223
  	offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
afe93325b   Tom Herbert   fou: Add GRO support
224
225
226
  	ops = rcu_dereference(offloads[proto]);
  	if (!ops || !ops->callbacks.gro_receive)
  		goto out_unlock;
fcd91dd44   Sabrina Dubroca   net: add recursio...
227
  	pp = call_gro_receive(ops->callbacks.gro_receive, head, skb);
afe93325b   Tom Herbert   fou: Add GRO support
228
229
230
231
232
233
  
  out_unlock:
  	rcu_read_unlock();
  
  	return pp;
  }
d92283e33   Tom Herbert   fou: change to us...
234
235
  static int fou_gro_complete(struct sock *sk, struct sk_buff *skb,
  			    int nhoff)
afe93325b   Tom Herbert   fou: Add GRO support
236
237
  {
  	const struct net_offload *ops;
d92283e33   Tom Herbert   fou: change to us...
238
  	u8 proto = fou_from_sock(sk)->protocol;
afe93325b   Tom Herbert   fou: Add GRO support
239
  	int err = -ENOSYS;
efc98d08e   Tom Herbert   fou: eliminate IP...
240
  	const struct net_offload **offloads;
afe93325b   Tom Herbert   fou: Add GRO support
241
242
  
  	rcu_read_lock();
efc98d08e   Tom Herbert   fou: eliminate IP...
243
  	offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
afe93325b   Tom Herbert   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   Jarno Rajahalme   udp_offload: Set ...
249
  	skb_set_inner_mac_header(skb, nhoff);
afe93325b   Tom Herbert   fou: Add GRO support
250
251
252
253
254
  out_unlock:
  	rcu_read_unlock();
  
  	return err;
  }
a8d31c128   Tom Herbert   gue: Receive side...
255
256
  static struct guehdr *gue_gro_remcsum(struct sk_buff *skb, unsigned int off,
  				      struct guehdr *guehdr, void *data,
b7fe10e5e   Tom Herbert   gro: Fix remcsum ...
257
258
  				      size_t hdrlen, struct gro_remcsum *grc,
  				      bool nopartial)
a8d31c128   Tom Herbert   gue: Receive side...
259
260
  {
  	__be16 *pd = data;
4fd671ded   Tom Herbert   gue: Call remcsum...
261
262
  	size_t start = ntohs(pd[0]);
  	size_t offset = ntohs(pd[1]);
a8d31c128   Tom Herbert   gue: Receive side...
263
264
  
  	if (skb->remcsum_offload)
b7fe10e5e   Tom Herbert   gro: Fix remcsum ...
265
  		return guehdr;
a8d31c128   Tom Herbert   gue: Receive side...
266

4fd671ded   Tom Herbert   gue: Call remcsum...
267
  	if (!NAPI_GRO_CB(skb)->csum_valid)
a8d31c128   Tom Herbert   gue: Receive side...
268
  		return NULL;
b7fe10e5e   Tom Herbert   gro: Fix remcsum ...
269
270
  	guehdr = skb_gro_remcsum_process(skb, (void *)guehdr, off, hdrlen,
  					 start, offset, grc, nopartial);
a8d31c128   Tom Herbert   gue: Receive side...
271
272
273
274
275
  
  	skb->remcsum_offload = 1;
  
  	return guehdr;
  }
d92283e33   Tom Herbert   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   Tom Herbert   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   Tom Herbert   gue: Receive side...
284
  	struct guehdr *guehdr;
5024c33ac   Tom Herbert   gue: Add infrastr...
285
286
  	size_t len, optlen, hdrlen, off;
  	void *data;
a8d31c128   Tom Herbert   gue: Receive side...
287
  	u16 doffset = 0;
37dd02477   Tom Herbert   gue: Receive side...
288
  	int flush = 1;
d92283e33   Tom Herbert   fou: change to us...
289
  	struct fou *fou = fou_from_sock(sk);
26c4f7da3   Tom Herbert   net: Fix remcsum ...
290
  	struct gro_remcsum grc;
c1e48af79   Tom Herbert   gue: Implement di...
291
  	u8 proto;
26c4f7da3   Tom Herbert   net: Fix remcsum ...
292
293
  
  	skb_gro_remcsum_init(&grc);
37dd02477   Tom Herbert   gue: Receive side...
294
295
  
  	off = skb_gro_offset(skb);
5024c33ac   Tom Herbert   gue: Add infrastr...
296
  	len = off + sizeof(*guehdr);
37dd02477   Tom Herbert   gue: Receive side...
297
  	guehdr = skb_gro_header_fast(skb, off);
5024c33ac   Tom Herbert   gue: Add infrastr...
298
299
  	if (skb_gro_header_hard(skb, len)) {
  		guehdr = skb_gro_header_slow(skb, len, off);
37dd02477   Tom Herbert   gue: Receive side...
300
301
302
  		if (unlikely(!guehdr))
  			goto out;
  	}
c1e48af79   Tom Herbert   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   Tom Herbert   gue: Add infrastr...
321
322
  	optlen = guehdr->hlen << 2;
  	len += optlen;
37dd02477   Tom Herbert   gue: Receive side...
323

5024c33ac   Tom Herbert   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   Tom Herbert   gue: Receive side...
329

5024c33ac   Tom Herbert   gue: Add infrastr...
330
331
332
  	if (unlikely(guehdr->control) || guehdr->version != 0 ||
  	    validate_gue_flags(guehdr, optlen))
  		goto out;
37dd02477   Tom Herbert   gue: Receive side...
333

5024c33ac   Tom Herbert   gue: Add infrastr...
334
  	hdrlen = sizeof(*guehdr) + optlen;
a8d31c128   Tom Herbert   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   Tom Herbert   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   Tom Herbert   gue: Receive side...
343
  		__be32 flags = *(__be32 *)(data + doffset);
5024c33ac   Tom Herbert   gue: Add infrastr...
344

a8d31c128   Tom Herbert   gue: Receive side...
345
346
347
348
  		doffset += GUE_LEN_PRIV;
  
  		if (flags & GUE_PFLAG_REMCSUM) {
  			guehdr = gue_gro_remcsum(skb, off, guehdr,
b7fe10e5e   Tom Herbert   gro: Fix remcsum ...
349
  						 data + doffset, hdrlen, &grc,
fe881ef11   Tom Herbert   gue: Use checksum...
350
351
  						 !!(fou->flags &
  						    FOU_F_REMCSUM_NOPARTIAL));
b7fe10e5e   Tom Herbert   gro: Fix remcsum ...
352

a8d31c128   Tom Herbert   gue: Receive side...
353
354
355
356
357
358
359
  			if (!guehdr)
  				goto out;
  
  			data = &guehdr[1];
  
  			doffset += GUE_PLEN_REMCSUM;
  		}
37dd02477   Tom Herbert   gue: Receive side...
360
  	}
a8d31c128   Tom Herbert   gue: Receive side...
361
  	skb_gro_pull(skb, hdrlen);
37dd02477   Tom Herbert   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   Tom Herbert   gue: Add infrastr...
371
  		 * hlen, version, proto_ctype, and flags.
37dd02477   Tom Herbert   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   Tom Herbert   gue: Implement di...
385
386
387
  	proto = guehdr->proto_ctype;
  
  next_proto:
c3483384e   Alexander Duyck   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   Alexander Duyck   GRE: Disable segm...
395
396
  	/* Flag this frame as already having an outer encap header */
  	NAPI_GRO_CB(skb)->is_fou = 1;
5024c33ac   Tom Herbert   gue: Add infrastr...
397
398
  	rcu_read_lock();
  	offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
c1e48af79   Tom Herbert   gue: Implement di...
399
  	ops = rcu_dereference(offloads[proto]);
270136613   Tom Herbert   fou: Do WARN_ON_O...
400
  	if (WARN_ON_ONCE(!ops || !ops->callbacks.gro_receive))
5024c33ac   Tom Herbert   gue: Add infrastr...
401
  		goto out_unlock;
37dd02477   Tom Herbert   gue: Receive side...
402

fcd91dd44   Sabrina Dubroca   net: add recursio...
403
  	pp = call_gro_receive(ops->callbacks.gro_receive, head, skb);
c194cf93c   Alexander Duyck   gro: Defer cleari...
404
  	flush = 0;
37dd02477   Tom Herbert   gue: Receive side...
405
406
407
408
  
  out_unlock:
  	rcu_read_unlock();
  out:
b364a914c   Sabrina Dubroca   net: fix use-afte...
409
  	skb_gro_flush_final_remcsum(skb, pp, flush, &grc);
37dd02477   Tom Herbert   gue: Receive side...
410
411
412
  
  	return pp;
  }
d92283e33   Tom Herbert   fou: change to us...
413
  static int gue_gro_complete(struct sock *sk, struct sk_buff *skb, int nhoff)
37dd02477   Tom Herbert   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   Tom Herbert   gue: Implement di...
418
  	unsigned int guehlen = 0;
37dd02477   Tom Herbert   gue: Receive side...
419
420
  	u8 proto;
  	int err = -ENOENT;
c1e48af79   Tom Herbert   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   Tom Herbert   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   Jarno Rajahalme   udp_offload: Set ...
449
  	skb_set_inner_mac_header(skb, nhoff + guehlen);
37dd02477   Tom Herbert   gue: Receive side...
450
451
452
453
  out_unlock:
  	rcu_read_unlock();
  	return err;
  }
02d793c5b   WANG Cong   fou: add network ...
454
  static int fou_add_to_port_list(struct net *net, struct fou *fou)
23461551c   Tom Herbert   fou: Support for ...
455
  {
02d793c5b   WANG Cong   fou: add network ...
456
  	struct fou_net *fn = net_generic(net, fou_net_id);
23461551c   Tom Herbert   fou: Support for ...
457
  	struct fou *fout;
02d793c5b   WANG Cong   fou: add network ...
458
459
  	mutex_lock(&fn->fou_lock);
  	list_for_each_entry(fout, &fn->fou_list, list) {
5f914b681   Tom Herbert   fou: Support IPv6...
460
461
  		if (fou->port == fout->port &&
  		    fou->family == fout->family) {
02d793c5b   WANG Cong   fou: add network ...
462
  			mutex_unlock(&fn->fou_lock);
23461551c   Tom Herbert   fou: Support for ...
463
464
465
  			return -EALREADY;
  		}
  	}
02d793c5b   WANG Cong   fou: add network ...
466
467
  	list_add(&fou->list, &fn->fou_list);
  	mutex_unlock(&fn->fou_lock);
23461551c   Tom Herbert   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   Tom Herbert   fou: Support for ...
475

23461551c   Tom Herbert   fou: Support for ...
476
  	list_del(&fou->list);
02d793c5b   WANG Cong   fou: add network ...
477
  	udp_tunnel_sock_release(sock);
23461551c   Tom Herbert   fou: Support for ...
478

3036facbb   Hannes Frederic Sowa   fou: clean up soc...
479
  	kfree_rcu(fou, rcu);
23461551c   Tom Herbert   fou: Support for ...
480
481
482
483
484
  }
  
  static int fou_create(struct net *net, struct fou_cfg *cfg,
  		      struct socket **sockp)
  {
23461551c   Tom Herbert   fou: Support for ...
485
  	struct socket *sock = NULL;
02d793c5b   WANG Cong   fou: add network ...
486
  	struct fou *fou = NULL;
23461551c   Tom Herbert   fou: Support for ...
487
  	struct sock *sk;
440924bbc   Tom Herbert   fou: Call setup_u...
488
  	struct udp_tunnel_sock_cfg tunnel_cfg;
02d793c5b   WANG Cong   fou: add network ...
489
  	int err;
23461551c   Tom Herbert   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   Tom Herbert   gue: Receive side...
504
  	fou->port = cfg->udp_config.local_udp_port;
5f914b681   Tom Herbert   fou: Support IPv6...
505
506
  	fou->family = cfg->udp_config.family;
  	fou->flags = cfg->flags;
440924bbc   Tom Herbert   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   Tom Herbert   gue: Receive side...
514
515
516
517
  
  	/* Initial for fou type */
  	switch (cfg->type) {
  	case FOU_ENCAP_DIRECT:
440924bbc   Tom Herbert   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   Tom Herbert   gue: Receive side...
522
523
  		break;
  	case FOU_ENCAP_GUE:
440924bbc   Tom Herbert   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   Tom Herbert   gue: Receive side...
527
528
529
530
531
  		break;
  	default:
  		err = -EINVAL;
  		goto error;
  	}
23461551c   Tom Herbert   fou: Support for ...
532

440924bbc   Tom Herbert   fou: Call setup_u...
533
  	setup_udp_tunnel_sock(net, sock, &tunnel_cfg);
23461551c   Tom Herbert   fou: Support for ...
534
535
  
  	sk->sk_allocation = GFP_ATOMIC;
02d793c5b   WANG Cong   fou: add network ...
536
  	err = fou_add_to_port_list(net, fou);
23461551c   Tom Herbert   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   WANG Cong   fou: add network ...
548
  		udp_tunnel_sock_release(sock);
23461551c   Tom Herbert   fou: Support for ...
549
550
551
552
553
554
  
  	return err;
  }
  
  static int fou_destroy(struct net *net, struct fou_cfg *cfg)
  {
02d793c5b   WANG Cong   fou: add network ...
555
  	struct fou_net *fn = net_generic(net, fou_net_id);
4cbcdf2b6   WANG Cong   fou: always use b...
556
  	__be16 port = cfg->udp_config.local_udp_port;
5f914b681   Tom Herbert   fou: Support IPv6...
557
  	u8 family = cfg->udp_config.family;
23461551c   Tom Herbert   fou: Support for ...
558
  	int err = -EINVAL;
02d793c5b   WANG Cong   fou: add network ...
559
  	struct fou *fou;
23461551c   Tom Herbert   fou: Support for ...
560

02d793c5b   WANG Cong   fou: add network ...
561
562
  	mutex_lock(&fn->fou_lock);
  	list_for_each_entry(fou, &fn->fou_list, list) {
5f914b681   Tom Herbert   fou: Support IPv6...
563
  		if (fou->port == port && fou->family == family) {
23461551c   Tom Herbert   fou: Support for ...
564
565
566
567
568
  			fou_release(fou);
  			err = 0;
  			break;
  		}
  	}
02d793c5b   WANG Cong   fou: add network ...
569
  	mutex_unlock(&fn->fou_lock);
23461551c   Tom Herbert   fou: Support for ...
570
571
572
  
  	return err;
  }
489111e5c   Johannes Berg   genetlink: static...
573
  static struct genl_family fou_nl_family;
23461551c   Tom Herbert   fou: Support for ...
574

3f18ff2b4   stephen hemminger   fou: make nla_pol...
575
  static const struct nla_policy fou_nl_policy[FOU_ATTR_MAX + 1] = {
23461551c   Tom Herbert   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   Tom Herbert   gue: Receive side...
579
  	[FOU_ATTR_TYPE] = { .type = NLA_U8, },
fe881ef11   Tom Herbert   gue: Use checksum...
580
  	[FOU_ATTR_REMCSUM_NOPARTIAL] = { .type = NLA_FLAG, },
23461551c   Tom Herbert   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   Tom Herbert   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   Tom Herbert   fou: Support for ...
601
602
603
604
605
  
  		cfg->udp_config.family = family;
  	}
  
  	if (info->attrs[FOU_ATTR_PORT]) {
4cbcdf2b6   WANG Cong   fou: always use b...
606
  		__be16 port = nla_get_be16(info->attrs[FOU_ATTR_PORT]);
23461551c   Tom Herbert   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   Tom Herbert   gue: Receive side...
613
614
  	if (info->attrs[FOU_ATTR_TYPE])
  		cfg->type = nla_get_u8(info->attrs[FOU_ATTR_TYPE]);
fe881ef11   Tom Herbert   gue: Use checksum...
615
616
  	if (info->attrs[FOU_ATTR_REMCSUM_NOPARTIAL])
  		cfg->flags |= FOU_F_REMCSUM_NOPARTIAL;
23461551c   Tom Herbert   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   WANG Cong   fou: add network ...
622
  	struct net *net = genl_info_net(info);
23461551c   Tom Herbert   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   WANG Cong   fou: add network ...
629
  	return fou_create(net, &cfg, NULL);
23461551c   Tom Herbert   fou: Support for ...
630
631
632
633
  }
  
  static int fou_nl_cmd_rm_port(struct sk_buff *skb, struct genl_info *info)
  {
02d793c5b   WANG Cong   fou: add network ...
634
  	struct net *net = genl_info_net(info);
23461551c   Tom Herbert   fou: Support for ...
635
  	struct fou_cfg cfg;
67270636a   WANG Cong   fou: exit early w...
636
  	int err;
23461551c   Tom Herbert   fou: Support for ...
637

67270636a   WANG Cong   fou: exit early w...
638
639
640
  	err = parse_nl_config(info, &cfg);
  	if (err)
  		return err;
23461551c   Tom Herbert   fou: Support for ...
641

02d793c5b   WANG Cong   fou: add network ...
642
  	return fou_destroy(net, &cfg);
23461551c   Tom Herbert   fou: Support for ...
643
  }
7a6c8c34e   WANG Cong   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   Tom Herbert   fou: Support IPv6...
686
  	u8 family;
7a6c8c34e   WANG Cong   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   Tom Herbert   fou: Support IPv6...
695
696
697
  	family = cfg.udp_config.family;
  	if (family != AF_INET && family != AF_INET6)
  		return -EINVAL;
7a6c8c34e   WANG Cong   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   Tom Herbert   fou: Support IPv6...
705
  		if (port == fout->port && family == fout->family) {
7a6c8c34e   WANG Cong   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   WANG Cong   fou: avoid missin...
738
  			break;
7a6c8c34e   WANG Cong   fou: implement FO...
739
740
  	}
  	mutex_unlock(&fn->fou_lock);
7a6c8c34e   WANG Cong   fou: implement FO...
741
742
743
  	cb->args[0] = idx;
  	return skb->len;
  }
23461551c   Tom Herbert   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   WANG Cong   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   Tom Herbert   fou: Support for ...
763
  };
56989f6d8   Johannes Berg   genetlink: mark f...
764
  static struct genl_family fou_nl_family __ro_after_init = {
489111e5c   Johannes Berg   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   Tom Herbert   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   Tom Herbert   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   Tom Herbert   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   Tom Herbert   net: Move fou_bui...
814
  {
63487babf   Tom Herbert   net: Move fou_bui...
815
  	struct guehdr *guehdr;
b17f709a2   Tom Herbert   gue: TX support f...
816
  	size_t hdrlen, optlen = 0;
5024c33ac   Tom Herbert   gue: Add infrastr...
817
818
  	void *data;
  	bool need_priv = false;
aed069df0   Alexander Duyck   ip_tunnel_core: i...
819
  	int err;
5024c33ac   Tom Herbert   gue: Add infrastr...
820

b17f709a2   Tom Herbert   gue: TX support f...
821
822
  	if ((e->flags & TUNNEL_ENCAP_FLAG_REMCSUM) &&
  	    skb->ip_summed == CHECKSUM_PARTIAL) {
b17f709a2   Tom Herbert   gue: TX support f...
823
824
825
826
  		optlen += GUE_PLEN_REMCSUM;
  		type |= SKB_GSO_TUNNEL_REMCSUM;
  		need_priv = true;
  	}
5024c33ac   Tom Herbert   gue: Add infrastr...
827
  	optlen += need_priv ? GUE_LEN_PRIV : 0;
63487babf   Tom Herbert   net: Move fou_bui...
828

aed069df0   Alexander Duyck   ip_tunnel_core: i...
829
830
831
  	err = iptunnel_handle_offloads(skb, type);
  	if (err)
  		return err;
63487babf   Tom Herbert   net: Move fou_bui...
832
833
  
  	/* Get source port (based on flow hash) before skb_push */
dc969b81e   Tom Herbert   fou: Split out {f...
834
835
  	*sport = e->sport ? : udp_flow_src_port(dev_net(skb->dev),
  						skb, 0, 0, false);
63487babf   Tom Herbert   net: Move fou_bui...
836

b17f709a2   Tom Herbert   gue: TX support f...
837
838
839
  	hdrlen = sizeof(struct guehdr) + optlen;
  
  	skb_push(skb, hdrlen);
63487babf   Tom Herbert   net: Move fou_bui...
840
841
  
  	guehdr = (struct guehdr *)skb->data;
5024c33ac   Tom Herbert   gue: Add infrastr...
842
  	guehdr->control = 0;
63487babf   Tom Herbert   net: Move fou_bui...
843
  	guehdr->version = 0;
5024c33ac   Tom Herbert   gue: Add infrastr...
844
  	guehdr->hlen = optlen >> 2;
63487babf   Tom Herbert   net: Move fou_bui...
845
  	guehdr->flags = 0;
5024c33ac   Tom Herbert   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   Tom Herbert   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   Tom Herbert   gue: Add infrastr...
875
  	}
63487babf   Tom Herbert   net: Move fou_bui...
876

dc969b81e   Tom Herbert   fou: Split out {f...
877
878
879
  	return 0;
  }
  EXPORT_SYMBOL(__gue_build_header);
9dc621afa   stephen hemminger   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   Tom Herbert   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   Tom Herbert   net: Move fou_bui...
929
930
931
932
  	fou_build_udp(skb, e, fl4, protocol, sport);
  
  	return 0;
  }
63487babf   Tom Herbert   net: Move fou_bui...
933

a8c5f90fb   Tom Herbert   ip_tunnel: Ops re...
934

5eeb29221   Andi Kleen   fou: Don't use co...
935
  static const struct ip_tunnel_encap_ops fou_iptun_ops = {
a8c5f90fb   Tom Herbert   ip_tunnel: Ops re...
936
937
938
  	.encap_hlen = fou_encap_hlen,
  	.build_header = fou_build_header,
  };
5eeb29221   Andi Kleen   fou: Don't use co...
939
  static const struct ip_tunnel_encap_ops gue_iptun_ops = {
a8c5f90fb   Tom Herbert   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   Thomas Graf   FOU: Fix no retur...
978
  static void ip_tunnel_encap_del_fou_ops(void)
a8c5f90fb   Tom Herbert   ip_tunnel: Ops re...
979
980
981
982
  {
  }
  
  #endif
02d793c5b   WANG Cong   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   Tom Herbert   fou: Support for ...
1010
1011
1012
  static int __init fou_init(void)
  {
  	int ret;
02d793c5b   WANG Cong   fou: add network ...
1013
1014
1015
  	ret = register_pernet_device(&fou_net_ops);
  	if (ret)
  		goto exit;
489111e5c   Johannes Berg   genetlink: static...
1016
  	ret = genl_register_family(&fou_nl_family);
a8c5f90fb   Tom Herbert   ip_tunnel: Ops re...
1017
  	if (ret < 0)
02d793c5b   WANG Cong   fou: add network ...
1018
  		goto unregister;
a8c5f90fb   Tom Herbert   ip_tunnel: Ops re...
1019
1020
  
  	ret = ip_tunnel_encap_add_fou_ops();
02d793c5b   WANG Cong   fou: add network ...
1021
1022
  	if (ret == 0)
  		return 0;
a8c5f90fb   Tom Herbert   ip_tunnel: Ops re...
1023

02d793c5b   WANG Cong   fou: add network ...
1024
1025
1026
  	genl_unregister_family(&fou_nl_family);
  unregister:
  	unregister_pernet_device(&fou_net_ops);
a8c5f90fb   Tom Herbert   ip_tunnel: Ops re...
1027
  exit:
23461551c   Tom Herbert   fou: Support for ...
1028
1029
1030
1031
1032
  	return ret;
  }
  
  static void __exit fou_fini(void)
  {
a8c5f90fb   Tom Herbert   ip_tunnel: Ops re...
1033
  	ip_tunnel_encap_del_fou_ops();
23461551c   Tom Herbert   fou: Support for ...
1034
  	genl_unregister_family(&fou_nl_family);
02d793c5b   WANG Cong   fou: add network ...
1035
  	unregister_pernet_device(&fou_net_ops);
23461551c   Tom Herbert   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");