ip_gre.c 40.4 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
/*
2
 *	Linux NET3:	GRE over IP protocol decoder.
Linus Torvalds's avatar
Linus Torvalds committed
3
4
5
6
7
8
9
10
11
12
 *
 *	Authors: Alexey Kuznetsov (kuznet@ms2.inr.ac.ru)
 *
 *	This program is free software; you can redistribute it and/or
 *	modify it under the terms of the GNU General Public License
 *	as published by the Free Software Foundation; either version
 *	2 of the License, or (at your option) any later version.
 *
 */

13
#include <linux/capability.h>
Linus Torvalds's avatar
Linus Torvalds committed
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <asm/uaccess.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/in.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/if_arp.h>
#include <linux/mroute.h>
#include <linux/init.h>
#include <linux/in6.h>
#include <linux/inetdevice.h>
#include <linux/igmp.h>
#include <linux/netfilter_ipv4.h>
30
#include <linux/etherdevice.h>
31
#include <linux/if_ether.h>
Linus Torvalds's avatar
Linus Torvalds committed
32
33
34
35
36
37
38
39
40
41
42

#include <net/sock.h>
#include <net/ip.h>
#include <net/icmp.h>
#include <net/protocol.h>
#include <net/ipip.h>
#include <net/arp.h>
#include <net/checksum.h>
#include <net/dsfield.h>
#include <net/inet_ecn.h>
#include <net/xfrm.h>
43
44
#include <net/net_namespace.h>
#include <net/netns/generic.h>
Herbert Xu's avatar
Herbert Xu committed
45
#include <net/rtnetlink.h>
Linus Torvalds's avatar
Linus Torvalds committed
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68

#ifdef CONFIG_IPV6
#include <net/ipv6.h>
#include <net/ip6_fib.h>
#include <net/ip6_route.h>
#endif

/*
   Problems & solutions
   --------------------

   1. The most important issue is detecting local dead loops.
   They would cause complete host lockup in transmit, which
   would be "resolved" by stack overflow or, if queueing is enabled,
   with infinite looping in net_bh.

   We cannot track such dead loops during route installation,
   it is infeasible task. The most general solutions would be
   to keep skb->encapsulation counter (sort of local ttl),
   and silently drop packet when it expires. It is the best
   solution, but it supposes maintaing new variable in ALL
   skb, even if no tunneling is used.

69
   Current solution: HARD_TX_LOCK lock breaks dead loops.
Linus Torvalds's avatar
Linus Torvalds committed
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118



   2. Networking dead loops would not kill routers, but would really
   kill network. IP hop limit plays role of "t->recursion" in this case,
   if we copy it from packet being encapsulated to upper header.
   It is very good solution, but it introduces two problems:

   - Routing protocols, using packets with ttl=1 (OSPF, RIP2),
     do not work over tunnels.
   - traceroute does not work. I planned to relay ICMP from tunnel,
     so that this problem would be solved and traceroute output
     would even more informative. This idea appeared to be wrong:
     only Linux complies to rfc1812 now (yes, guys, Linux is the only
     true router now :-)), all routers (at least, in neighbourhood of mine)
     return only 8 bytes of payload. It is the end.

   Hence, if we want that OSPF worked or traceroute said something reasonable,
   we should search for another solution.

   One of them is to parse packet trying to detect inner encapsulation
   made by our node. It is difficult or even impossible, especially,
   taking into account fragmentation. TO be short, tt is not solution at all.

   Current solution: The solution was UNEXPECTEDLY SIMPLE.
   We force DF flag on tunnels with preconfigured hop limit,
   that is ALL. :-) Well, it does not remove the problem completely,
   but exponential growth of network traffic is changed to linear
   (branches, that exceed pmtu are pruned) and tunnel mtu
   fastly degrades to value <68, where looping stops.
   Yes, it is not good if there exists a router in the loop,
   which does not force DF, even when encapsulating packets have DF set.
   But it is not our problem! Nobody could accuse us, we made
   all that we could make. Even if it is your gated who injected
   fatal route to network, even if it were you who configured
   fatal static route: you are innocent. :-)



   3. Really, ipv4/ipip.c, ipv4/ip_gre.c and ipv6/sit.c contain
   practically identical code. It would be good to glue them
   together, but it is not very evident, how to make them modular.
   sit is integral part of IPv6, ipip and gre are naturally modular.
   We could extract common parts (hash table, ioctl etc)
   to a separate module (ip_tunnel.c).

   Alexey Kuznetsov.
 */

Herbert Xu's avatar
Herbert Xu committed
119
static struct rtnl_link_ops ipgre_link_ops __read_mostly;
Linus Torvalds's avatar
Linus Torvalds committed
120
121
static int ipgre_tunnel_init(struct net_device *dev);
static void ipgre_tunnel_setup(struct net_device *dev);
122
static int ipgre_tunnel_bind_dev(struct net_device *dev);
Linus Torvalds's avatar
Linus Torvalds committed
123
124
125

/* Fallback tunnel: no source, no destination, no key, no options */

126
127
#define HASH_SIZE  16

128
129
static int ipgre_net_id;
struct ipgre_net {
130
131
	struct ip_tunnel *tunnels[4][HASH_SIZE];

132
	struct net_device *fb_tunnel_dev;
133
134
};

Linus Torvalds's avatar
Linus Torvalds committed
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/* Tunnel hash table */

/*
   4 hash tables:

   3: (remote,local)
   2: (remote,*)
   1: (*,local)
   0: (*,*)

   We require exact key match i.e. if a key is present in packet
   it will match only tunnel with the same key; if it is not present,
   it will match only keyless tunnel.

   All keysless packets, if not matched configured keyless tunnels
   will match fallback tunnel.
 */

Al Viro's avatar
Al Viro committed
153
#define HASH(addr) (((__force u32)addr^((__force u32)addr>>4))&0xF)
Linus Torvalds's avatar
Linus Torvalds committed
154

155
156
157
158
#define tunnels_r_l	tunnels[3]
#define tunnels_r	tunnels[2]
#define tunnels_l	tunnels[1]
#define tunnels_wc	tunnels[0]
Linus Torvalds's avatar
Linus Torvalds committed
159
160
161
162
163

static DEFINE_RWLOCK(ipgre_lock);

/* Given src, dst and key, find appropriate for input tunnel. */

164
static struct ip_tunnel * ipgre_tunnel_lookup(struct net_device *dev,
165
166
					      __be32 remote, __be32 local,
					      __be32 key, __be16 gre_proto)
Linus Torvalds's avatar
Linus Torvalds committed
167
{
168
169
	struct net *net = dev_net(dev);
	int link = dev->ifindex;
Linus Torvalds's avatar
Linus Torvalds committed
170
171
	unsigned h0 = HASH(remote);
	unsigned h1 = HASH(key);
Timo Teras's avatar
Timo Teras committed
172
	struct ip_tunnel *t, *cand = NULL;
173
	struct ipgre_net *ign = net_generic(net, ipgre_net_id);
174
175
	int dev_type = (gre_proto == htons(ETH_P_TEB)) ?
		       ARPHRD_ETHER : ARPHRD_IPGRE;
Timo Teras's avatar
Timo Teras committed
176
	int score, cand_score = 4;
Linus Torvalds's avatar
Linus Torvalds committed
177

178
	for (t = ign->tunnels_r_l[h0^h1]; t; t = t->next) {
179
180
181
182
183
184
185
186
187
188
		if (local != t->parms.iph.saddr ||
		    remote != t->parms.iph.daddr ||
		    key != t->parms.i_key ||
		    !(t->dev->flags & IFF_UP))
			continue;

		if (t->dev->type != ARPHRD_IPGRE &&
		    t->dev->type != dev_type)
			continue;

Timo Teras's avatar
Timo Teras committed
189
		score = 0;
190
		if (t->parms.link != link)
Timo Teras's avatar
Timo Teras committed
191
			score |= 1;
192
		if (t->dev->type != dev_type)
Timo Teras's avatar
Timo Teras committed
193
194
			score |= 2;
		if (score == 0)
195
			return t;
Timo Teras's avatar
Timo Teras committed
196
197
198
199
200

		if (score < cand_score) {
			cand = t;
			cand_score = score;
		}
Linus Torvalds's avatar
Linus Torvalds committed
201
	}
202

203
	for (t = ign->tunnels_r[h0^h1]; t; t = t->next) {
204
205
206
207
208
209
210
211
212
		if (remote != t->parms.iph.daddr ||
		    key != t->parms.i_key ||
		    !(t->dev->flags & IFF_UP))
			continue;

		if (t->dev->type != ARPHRD_IPGRE &&
		    t->dev->type != dev_type)
			continue;

Timo Teras's avatar
Timo Teras committed
213
		score = 0;
214
		if (t->parms.link != link)
Timo Teras's avatar
Timo Teras committed
215
			score |= 1;
216
		if (t->dev->type != dev_type)
Timo Teras's avatar
Timo Teras committed
217
218
			score |= 2;
		if (score == 0)
219
			return t;
Timo Teras's avatar
Timo Teras committed
220
221
222
223
224

		if (score < cand_score) {
			cand = t;
			cand_score = score;
		}
Linus Torvalds's avatar
Linus Torvalds committed
225
	}
226

227
	for (t = ign->tunnels_l[h1]; t; t = t->next) {
228
229
230
231
232
233
234
235
236
237
238
		if ((local != t->parms.iph.saddr &&
		     (local != t->parms.iph.daddr ||
		      !ipv4_is_multicast(local))) ||
		    key != t->parms.i_key ||
		    !(t->dev->flags & IFF_UP))
			continue;

		if (t->dev->type != ARPHRD_IPGRE &&
		    t->dev->type != dev_type)
			continue;

Timo Teras's avatar
Timo Teras committed
239
		score = 0;
240
		if (t->parms.link != link)
Timo Teras's avatar
Timo Teras committed
241
			score |= 1;
242
		if (t->dev->type != dev_type)
Timo Teras's avatar
Timo Teras committed
243
244
			score |= 2;
		if (score == 0)
245
			return t;
Timo Teras's avatar
Timo Teras committed
246
247
248
249
250

		if (score < cand_score) {
			cand = t;
			cand_score = score;
		}
Linus Torvalds's avatar
Linus Torvalds committed
251
	}
252

253
	for (t = ign->tunnels_wc[h1]; t; t = t->next) {
254
255
256
257
258
259
260
261
		if (t->parms.i_key != key ||
		    !(t->dev->flags & IFF_UP))
			continue;

		if (t->dev->type != ARPHRD_IPGRE &&
		    t->dev->type != dev_type)
			continue;

Timo Teras's avatar
Timo Teras committed
262
		score = 0;
263
		if (t->parms.link != link)
Timo Teras's avatar
Timo Teras committed
264
			score |= 1;
265
		if (t->dev->type != dev_type)
Timo Teras's avatar
Timo Teras committed
266
267
			score |= 2;
		if (score == 0)
268
			return t;
Timo Teras's avatar
Timo Teras committed
269
270
271
272
273

		if (score < cand_score) {
			cand = t;
			cand_score = score;
		}
Linus Torvalds's avatar
Linus Torvalds committed
274
275
	}

Timo Teras's avatar
Timo Teras committed
276
277
	if (cand != NULL)
		return cand;
278

279
	if (ign->fb_tunnel_dev->flags & IFF_UP)
280
		return netdev_priv(ign->fb_tunnel_dev);
281

Linus Torvalds's avatar
Linus Torvalds committed
282
283
284
	return NULL;
}

285
286
static struct ip_tunnel **__ipgre_bucket(struct ipgre_net *ign,
		struct ip_tunnel_parm *parms)
Linus Torvalds's avatar
Linus Torvalds committed
287
{
288
289
290
	__be32 remote = parms->iph.daddr;
	__be32 local = parms->iph.saddr;
	__be32 key = parms->i_key;
Linus Torvalds's avatar
Linus Torvalds committed
291
292
293
294
295
	unsigned h = HASH(key);
	int prio = 0;

	if (local)
		prio |= 1;
296
	if (remote && !ipv4_is_multicast(remote)) {
Linus Torvalds's avatar
Linus Torvalds committed
297
298
299
300
		prio |= 2;
		h ^= HASH(remote);
	}

301
	return &ign->tunnels[prio][h];
Linus Torvalds's avatar
Linus Torvalds committed
302
303
}

304
305
static inline struct ip_tunnel **ipgre_bucket(struct ipgre_net *ign,
		struct ip_tunnel *t)
306
{
307
	return __ipgre_bucket(ign, &t->parms);
308
309
}

310
static void ipgre_tunnel_link(struct ipgre_net *ign, struct ip_tunnel *t)
Linus Torvalds's avatar
Linus Torvalds committed
311
{
312
	struct ip_tunnel **tp = ipgre_bucket(ign, t);
Linus Torvalds's avatar
Linus Torvalds committed
313
314
315
316
317
318
319

	t->next = *tp;
	write_lock_bh(&ipgre_lock);
	*tp = t;
	write_unlock_bh(&ipgre_lock);
}

320
static void ipgre_tunnel_unlink(struct ipgre_net *ign, struct ip_tunnel *t)
Linus Torvalds's avatar
Linus Torvalds committed
321
322
323
{
	struct ip_tunnel **tp;

324
	for (tp = ipgre_bucket(ign, t); *tp; tp = &(*tp)->next) {
Linus Torvalds's avatar
Linus Torvalds committed
325
326
327
328
329
330
331
332
333
		if (t == *tp) {
			write_lock_bh(&ipgre_lock);
			*tp = t->next;
			write_unlock_bh(&ipgre_lock);
			break;
		}
	}
}

334
335
336
static struct ip_tunnel *ipgre_tunnel_find(struct net *net,
					   struct ip_tunnel_parm *parms,
					   int type)
Linus Torvalds's avatar
Linus Torvalds committed
337
{
Al Viro's avatar
Al Viro committed
338
339
340
	__be32 remote = parms->iph.daddr;
	__be32 local = parms->iph.saddr;
	__be32 key = parms->i_key;
341
	int link = parms->link;
342
343
344
345
346
347
348
	struct ip_tunnel *t, **tp;
	struct ipgre_net *ign = net_generic(net, ipgre_net_id);

	for (tp = __ipgre_bucket(ign, parms); (t = *tp) != NULL; tp = &t->next)
		if (local == t->parms.iph.saddr &&
		    remote == t->parms.iph.daddr &&
		    key == t->parms.i_key &&
349
		    link == t->parms.link &&
350
351
352
353
354
355
356
357
358
359
		    type == t->dev->type)
			break;

	return t;
}

static struct ip_tunnel * ipgre_tunnel_locate(struct net *net,
		struct ip_tunnel_parm *parms, int create)
{
	struct ip_tunnel *t, *nt;
Linus Torvalds's avatar
Linus Torvalds committed
360
361
	struct net_device *dev;
	char name[IFNAMSIZ];
362
	struct ipgre_net *ign = net_generic(net, ipgre_net_id);
Linus Torvalds's avatar
Linus Torvalds committed
363

364
365
366
	t = ipgre_tunnel_find(net, parms, ARPHRD_IPGRE);
	if (t || !create)
		return t;
Linus Torvalds's avatar
Linus Torvalds committed
367
368
369

	if (parms->name[0])
		strlcpy(name, parms->name, IFNAMSIZ);
370
371
	else
		sprintf(name, "gre%%d");
Linus Torvalds's avatar
Linus Torvalds committed
372
373
374
375
376

	dev = alloc_netdev(sizeof(*t), name, ipgre_tunnel_setup);
	if (!dev)
	  return NULL;

377
378
	dev_net_set(dev, net);

379
380
381
382
383
	if (strchr(name, '%')) {
		if (dev_alloc_name(dev, name) < 0)
			goto failed_free;
	}

384
	nt = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
385
	nt->parms = *parms;
Herbert Xu's avatar
Herbert Xu committed
386
	dev->rtnl_link_ops = &ipgre_link_ops;
Linus Torvalds's avatar
Linus Torvalds committed
387

388
389
	dev->mtu = ipgre_tunnel_bind_dev(dev);

390
391
	if (register_netdevice(dev) < 0)
		goto failed_free;
Linus Torvalds's avatar
Linus Torvalds committed
392
393

	dev_hold(dev);
394
	ipgre_tunnel_link(ign, nt);
Linus Torvalds's avatar
Linus Torvalds committed
395
396
	return nt;

397
398
failed_free:
	free_netdev(dev);
Linus Torvalds's avatar
Linus Torvalds committed
399
400
401
402
403
	return NULL;
}

static void ipgre_tunnel_uninit(struct net_device *dev)
{
404
405
406
407
	struct net *net = dev_net(dev);
	struct ipgre_net *ign = net_generic(net, ipgre_net_id);

	ipgre_tunnel_unlink(ign, netdev_priv(dev));
Linus Torvalds's avatar
Linus Torvalds committed
408
409
410
411
412
413
414
	dev_put(dev);
}


static void ipgre_err(struct sk_buff *skb, u32 info)
{

415
/* All the routers (except for Linux) return only
Linus Torvalds's avatar
Linus Torvalds committed
416
417
418
419
420
421
422
423
424
425
426
427
   8 bytes of packet payload. It means, that precise relaying of
   ICMP in the real Internet is absolutely infeasible.

   Moreover, Cisco "wise men" put GRE key to the third word
   in GRE header. It makes impossible maintaining even soft state for keyed
   GRE tunnels with enabled checksum. Tell them "thank you".

   Well, I wonder, rfc1812 was written by Cisco employee,
   what the hell these idiots break standrads established
   by themself???
 */

428
	struct iphdr *iph = (struct iphdr *)skb->data;
Al Viro's avatar
Al Viro committed
429
	__be16	     *p = (__be16*)(skb->data+(iph->ihl<<2));
Linus Torvalds's avatar
Linus Torvalds committed
430
	int grehlen = (iph->ihl<<2) + 4;
431
432
	const int type = icmp_hdr(skb)->type;
	const int code = icmp_hdr(skb)->code;
Linus Torvalds's avatar
Linus Torvalds committed
433
	struct ip_tunnel *t;
Al Viro's avatar
Al Viro committed
434
	__be16 flags;
Linus Torvalds's avatar
Linus Torvalds committed
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479

	flags = p[0];
	if (flags&(GRE_CSUM|GRE_KEY|GRE_SEQ|GRE_ROUTING|GRE_VERSION)) {
		if (flags&(GRE_VERSION|GRE_ROUTING))
			return;
		if (flags&GRE_KEY) {
			grehlen += 4;
			if (flags&GRE_CSUM)
				grehlen += 4;
		}
	}

	/* If only 8 bytes returned, keyed message will be dropped here */
	if (skb_headlen(skb) < grehlen)
		return;

	switch (type) {
	default:
	case ICMP_PARAMETERPROB:
		return;

	case ICMP_DEST_UNREACH:
		switch (code) {
		case ICMP_SR_FAILED:
		case ICMP_PORT_UNREACH:
			/* Impossible event. */
			return;
		case ICMP_FRAG_NEEDED:
			/* Soft state for pmtu is maintained by IP core. */
			return;
		default:
			/* All others are translated to HOST_UNREACH.
			   rfc2003 contains "deep thoughts" about NET_UNREACH,
			   I believe they are just ether pollution. --ANK
			 */
			break;
		}
		break;
	case ICMP_TIME_EXCEEDED:
		if (code != ICMP_EXC_TTL)
			return;
		break;
	}

	read_lock(&ipgre_lock);
480
	t = ipgre_tunnel_lookup(skb->dev, iph->daddr, iph->saddr,
481
482
483
				flags & GRE_KEY ?
				*(((__be32 *)p) + (grehlen / 4) - 1) : 0,
				p[1]);
484
485
	if (t == NULL || t->parms.iph.daddr == 0 ||
	    ipv4_is_multicast(t->parms.iph.daddr))
Linus Torvalds's avatar
Linus Torvalds committed
486
487
488
489
490
		goto out;

	if (t->parms.iph.ttl == 0 && type == ICMP_TIME_EXCEEDED)
		goto out;

491
	if (time_before(jiffies, t->err_time + IPTUNNEL_ERR_TIMEO))
Linus Torvalds's avatar
Linus Torvalds committed
492
493
494
495
496
497
498
499
500
501
502
503
504
		t->err_count++;
	else
		t->err_count = 1;
	t->err_time = jiffies;
out:
	read_unlock(&ipgre_lock);
	return;
}

static inline void ipgre_ecn_decapsulate(struct iphdr *iph, struct sk_buff *skb)
{
	if (INET_ECN_is_ce(iph->tos)) {
		if (skb->protocol == htons(ETH_P_IP)) {
505
			IP_ECN_set_ce(ip_hdr(skb));
Linus Torvalds's avatar
Linus Torvalds committed
506
		} else if (skb->protocol == htons(ETH_P_IPV6)) {
507
			IP6_ECN_set_ce(ipv6_hdr(skb));
Linus Torvalds's avatar
Linus Torvalds committed
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
		}
	}
}

static inline u8
ipgre_ecn_encapsulate(u8 tos, struct iphdr *old_iph, struct sk_buff *skb)
{
	u8 inner = 0;
	if (skb->protocol == htons(ETH_P_IP))
		inner = old_iph->tos;
	else if (skb->protocol == htons(ETH_P_IPV6))
		inner = ipv6_get_dsfield((struct ipv6hdr *)old_iph);
	return INET_ECN_encapsulate(tos, inner);
}

static int ipgre_rcv(struct sk_buff *skb)
{
	struct iphdr *iph;
	u8     *h;
Al Viro's avatar
Al Viro committed
527
	__be16    flags;
528
	__sum16   csum = 0;
Al Viro's avatar
Al Viro committed
529
	__be32 key = 0;
Linus Torvalds's avatar
Linus Torvalds committed
530
531
532
	u32    seqno = 0;
	struct ip_tunnel *tunnel;
	int    offset = 4;
533
	__be16 gre_proto;
534
	unsigned int len;
Linus Torvalds's avatar
Linus Torvalds committed
535
536
537
538

	if (!pskb_may_pull(skb, 16))
		goto drop_nolock;

539
	iph = ip_hdr(skb);
Linus Torvalds's avatar
Linus Torvalds committed
540
	h = skb->data;
Al Viro's avatar
Al Viro committed
541
	flags = *(__be16*)h;
Linus Torvalds's avatar
Linus Torvalds committed
542
543
544
545
546
547
548
549
550

	if (flags&(GRE_CSUM|GRE_KEY|GRE_ROUTING|GRE_SEQ|GRE_VERSION)) {
		/* - Version must be 0.
		   - We do not support routing headers.
		 */
		if (flags&(GRE_VERSION|GRE_ROUTING))
			goto drop_nolock;

		if (flags&GRE_CSUM) {
551
			switch (skb->ip_summed) {
552
			case CHECKSUM_COMPLETE:
553
				csum = csum_fold(skb->csum);
554
555
556
557
558
559
				if (!csum)
					break;
				/* fall through */
			case CHECKSUM_NONE:
				skb->csum = 0;
				csum = __skb_checksum_complete(skb);
560
				skb->ip_summed = CHECKSUM_COMPLETE;
Linus Torvalds's avatar
Linus Torvalds committed
561
562
563
564
			}
			offset += 4;
		}
		if (flags&GRE_KEY) {
Al Viro's avatar
Al Viro committed
565
			key = *(__be32*)(h + offset);
Linus Torvalds's avatar
Linus Torvalds committed
566
567
568
			offset += 4;
		}
		if (flags&GRE_SEQ) {
Al Viro's avatar
Al Viro committed
569
			seqno = ntohl(*(__be32*)(h + offset));
Linus Torvalds's avatar
Linus Torvalds committed
570
571
572
573
			offset += 4;
		}
	}

574
575
	gre_proto = *(__be16 *)(h + 2);

Linus Torvalds's avatar
Linus Torvalds committed
576
	read_lock(&ipgre_lock);
577
	if ((tunnel = ipgre_tunnel_lookup(skb->dev,
578
579
					  iph->saddr, iph->daddr, key,
					  gre_proto))) {
580
581
		struct net_device_stats *stats = &tunnel->dev->stats;

Linus Torvalds's avatar
Linus Torvalds committed
582
583
		secpath_reset(skb);

584
		skb->protocol = gre_proto;
Linus Torvalds's avatar
Linus Torvalds committed
585
586
587
588
		/* WCCP version 1 and 2 protocol decoding.
		 * - Change protocol to IP
		 * - When dealing with WCCPv2, Skip extra 4 bytes in GRE header
		 */
589
		if (flags == 0 && gre_proto == htons(ETH_P_WCCP)) {
590
			skb->protocol = htons(ETH_P_IP);
591
			if ((*(h + offset) & 0xF0) != 0x40)
Linus Torvalds's avatar
Linus Torvalds committed
592
593
594
				offset += 4;
		}

595
		skb->mac_header = skb->network_header;
596
		__pskb_pull(skb, offset);
597
		skb_postpull_rcsum(skb, skb_transport_header(skb), offset);
Linus Torvalds's avatar
Linus Torvalds committed
598
599
		skb->pkt_type = PACKET_HOST;
#ifdef CONFIG_NET_IPGRE_BROADCAST
600
		if (ipv4_is_multicast(iph->daddr)) {
Linus Torvalds's avatar
Linus Torvalds committed
601
			/* Looped back packet, drop it! */
Eric Dumazet's avatar
Eric Dumazet committed
602
			if (skb_rtable(skb)->fl.iif == 0)
Linus Torvalds's avatar
Linus Torvalds committed
603
				goto drop;
604
			stats->multicast++;
Linus Torvalds's avatar
Linus Torvalds committed
605
606
607
608
609
610
			skb->pkt_type = PACKET_BROADCAST;
		}
#endif

		if (((flags&GRE_CSUM) && csum) ||
		    (!(flags&GRE_CSUM) && tunnel->parms.i_flags&GRE_CSUM)) {
611
612
			stats->rx_crc_errors++;
			stats->rx_errors++;
Linus Torvalds's avatar
Linus Torvalds committed
613
614
615
616
617
			goto drop;
		}
		if (tunnel->parms.i_flags&GRE_SEQ) {
			if (!(flags&GRE_SEQ) ||
			    (tunnel->i_seqno && (s32)(seqno - tunnel->i_seqno) < 0)) {
618
619
				stats->rx_fifo_errors++;
				stats->rx_errors++;
Linus Torvalds's avatar
Linus Torvalds committed
620
621
622
623
				goto drop;
			}
			tunnel->i_seqno = seqno + 1;
		}
624

625
626
		len = skb->len;

627
628
629
630
631
632
633
634
635
636
637
638
639
		/* Warning: All skb pointers will be invalidated! */
		if (tunnel->dev->type == ARPHRD_ETHER) {
			if (!pskb_may_pull(skb, ETH_HLEN)) {
				stats->rx_length_errors++;
				stats->rx_errors++;
				goto drop;
			}

			iph = ip_hdr(skb);
			skb->protocol = eth_type_trans(skb, tunnel->dev);
			skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN);
		}

640
		stats->rx_packets++;
641
		stats->rx_bytes += len;
Linus Torvalds's avatar
Linus Torvalds committed
642
		skb->dev = tunnel->dev;
Eric Dumazet's avatar
Eric Dumazet committed
643
		skb_dst_drop(skb);
Linus Torvalds's avatar
Linus Torvalds committed
644
		nf_reset(skb);
645
646

		skb_reset_network_header(skb);
Linus Torvalds's avatar
Linus Torvalds committed
647
		ipgre_ecn_decapsulate(iph, skb);
648

Linus Torvalds's avatar
Linus Torvalds committed
649
650
651
652
		netif_rx(skb);
		read_unlock(&ipgre_lock);
		return(0);
	}
653
	icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
Linus Torvalds's avatar
Linus Torvalds committed
654
655
656
657
658
659
660
661

drop:
	read_unlock(&ipgre_lock);
drop_nolock:
	kfree_skb(skb);
	return(0);
}

662
static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
Linus Torvalds's avatar
Linus Torvalds committed
663
{
664
	struct ip_tunnel *tunnel = netdev_priv(dev);
665
	struct net_device_stats *stats = &tunnel->dev->stats;
666
	struct iphdr  *old_iph = ip_hdr(skb);
Linus Torvalds's avatar
Linus Torvalds committed
667
668
	struct iphdr  *tiph;
	u8     tos;
Al Viro's avatar
Al Viro committed
669
	__be16 df;
Linus Torvalds's avatar
Linus Torvalds committed
670
671
672
	struct rtable *rt;     			/* Route to the other host */
	struct net_device *tdev;			/* Device to other host */
	struct iphdr  *iph;			/* Our new IP header */
673
	unsigned int max_headroom;		/* The extra header space needed */
Linus Torvalds's avatar
Linus Torvalds committed
674
	int    gre_hlen;
Al Viro's avatar
Al Viro committed
675
	__be32 dst;
Linus Torvalds's avatar
Linus Torvalds committed
676
677
	int    mtu;

678
679
680
681
	if (dev->type == ARPHRD_ETHER)
		IPCB(skb)->flags = 0;

	if (dev->header_ops && dev->type == ARPHRD_IPGRE) {
Linus Torvalds's avatar
Linus Torvalds committed
682
		gre_hlen = 0;
683
		tiph = (struct iphdr *)skb->data;
Linus Torvalds's avatar
Linus Torvalds committed
684
685
686
687
688
689
690
691
	} else {
		gre_hlen = tunnel->hlen;
		tiph = &tunnel->parms.iph;
	}

	if ((dst = tiph->daddr) == 0) {
		/* NBMA tunnel */

Eric Dumazet's avatar
Eric Dumazet committed
692
		if (skb_dst(skb) == NULL) {
693
			stats->tx_fifo_errors++;
Linus Torvalds's avatar
Linus Torvalds committed
694
695
696
697
			goto tx_error;
		}

		if (skb->protocol == htons(ETH_P_IP)) {
Eric Dumazet's avatar
Eric Dumazet committed
698
			rt = skb_rtable(skb);
Linus Torvalds's avatar
Linus Torvalds committed
699
700
701
702
703
704
705
			if ((dst = rt->rt_gateway) == 0)
				goto tx_error_icmp;
		}
#ifdef CONFIG_IPV6
		else if (skb->protocol == htons(ETH_P_IPV6)) {
			struct in6_addr *addr6;
			int addr_type;
Eric Dumazet's avatar
Eric Dumazet committed
706
			struct neighbour *neigh = skb_dst(skb)->neighbour;
Linus Torvalds's avatar
Linus Torvalds committed
707
708
709
710

			if (neigh == NULL)
				goto tx_error;

711
			addr6 = (struct in6_addr *)&neigh->primary_key;
Linus Torvalds's avatar
Linus Torvalds committed
712
713
714
			addr_type = ipv6_addr_type(addr6);

			if (addr_type == IPV6_ADDR_ANY) {
715
				addr6 = &ipv6_hdr(skb)->daddr;
Linus Torvalds's avatar
Linus Torvalds committed
716
717
718
719
720
721
722
723
724
725
726
727
728
729
				addr_type = ipv6_addr_type(addr6);
			}

			if ((addr_type & IPV6_ADDR_COMPATv4) == 0)
				goto tx_error_icmp;

			dst = addr6->s6_addr32[3];
		}
#endif
		else
			goto tx_error;
	}

	tos = tiph->tos;
730
731
	if (tos == 1) {
		tos = 0;
Linus Torvalds's avatar
Linus Torvalds committed
732
733
734
735
736
737
738
739
740
741
742
		if (skb->protocol == htons(ETH_P_IP))
			tos = old_iph->tos;
	}

	{
		struct flowi fl = { .oif = tunnel->parms.link,
				    .nl_u = { .ip4_u =
					      { .daddr = dst,
						.saddr = tiph->saddr,
						.tos = RT_TOS(tos) } },
				    .proto = IPPROTO_GRE };
743
		if (ip_route_output_key(dev_net(dev), &rt, &fl)) {
744
			stats->tx_carrier_errors++;
Linus Torvalds's avatar
Linus Torvalds committed
745
746
747
748
749
750
751
			goto tx_error;
		}
	}
	tdev = rt->u.dst.dev;

	if (tdev == dev) {
		ip_rt_put(rt);
752
		stats->collisions++;
Linus Torvalds's avatar
Linus Torvalds committed
753
754
755
756
757
		goto tx_error;
	}

	df = tiph->frag_off;
	if (df)
Herbert Xu's avatar
Herbert Xu committed
758
		mtu = dst_mtu(&rt->u.dst) - dev->hard_header_len - tunnel->hlen;
Linus Torvalds's avatar
Linus Torvalds committed
759
	else
Eric Dumazet's avatar
Eric Dumazet committed
760
		mtu = skb_dst(skb) ? dst_mtu(skb_dst(skb)) : dev->mtu;
Linus Torvalds's avatar
Linus Torvalds committed
761

Eric Dumazet's avatar
Eric Dumazet committed
762
763
	if (skb_dst(skb))
		skb_dst(skb)->ops->update_pmtu(skb_dst(skb), mtu);
Linus Torvalds's avatar
Linus Torvalds committed
764
765
766
767
768
769
770
771
772
773
774
775
776

	if (skb->protocol == htons(ETH_P_IP)) {
		df |= (old_iph->frag_off&htons(IP_DF));

		if ((old_iph->frag_off&htons(IP_DF)) &&
		    mtu < ntohs(old_iph->tot_len)) {
			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu));
			ip_rt_put(rt);
			goto tx_error;
		}
	}
#ifdef CONFIG_IPV6
	else if (skb->protocol == htons(ETH_P_IPV6)) {
Eric Dumazet's avatar
Eric Dumazet committed
777
		struct rt6_info *rt6 = (struct rt6_info *)skb_dst(skb);
Linus Torvalds's avatar
Linus Torvalds committed
778

Eric Dumazet's avatar
Eric Dumazet committed
779
		if (rt6 && mtu < dst_mtu(skb_dst(skb)) && mtu >= IPV6_MIN_MTU) {
780
781
			if ((tunnel->parms.iph.daddr &&
			     !ipv4_is_multicast(tunnel->parms.iph.daddr)) ||
Linus Torvalds's avatar
Linus Torvalds committed
782
783
			    rt6->rt6i_dst.plen == 128) {
				rt6->rt6i_flags |= RTF_MODIFIED;
Eric Dumazet's avatar
Eric Dumazet committed
784
				skb_dst(skb)->metrics[RTAX_MTU-1] = mtu;
Linus Torvalds's avatar
Linus Torvalds committed
785
786
787
788
789
790
791
792
793
794
795
796
			}
		}

		if (mtu >= IPV6_MIN_MTU && mtu < skb->len - tunnel->hlen + gre_hlen) {
			icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, dev);
			ip_rt_put(rt);
			goto tx_error;
		}
	}
#endif

	if (tunnel->err_count > 0) {
797
798
		if (time_before(jiffies,
				tunnel->err_time + IPTUNNEL_ERR_TIMEO)) {
Linus Torvalds's avatar
Linus Torvalds committed
799
800
801
802
803
804
805
806
807
			tunnel->err_count--;

			dst_link_failure(skb);
		} else
			tunnel->err_count = 0;
	}

	max_headroom = LL_RESERVED_SPACE(tdev) + gre_hlen;

808
809
	if (skb_headroom(skb) < max_headroom || skb_shared(skb)||
	    (skb_cloned(skb) && !skb_clone_writable(skb, 0))) {
Linus Torvalds's avatar
Linus Torvalds committed
810
811
812
		struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom);
		if (!new_skb) {
			ip_rt_put(rt);
813
			stats->tx_dropped++;
Linus Torvalds's avatar
Linus Torvalds committed
814
			dev_kfree_skb(skb);
815
			return NETDEV_TX_OK;
Linus Torvalds's avatar
Linus Torvalds committed
816
817
818
819
820
		}
		if (skb->sk)
			skb_set_owner_w(new_skb, skb->sk);
		dev_kfree_skb(skb);
		skb = new_skb;
821
		old_iph = ip_hdr(skb);
Linus Torvalds's avatar
Linus Torvalds committed
822
823
	}

824
	skb_reset_transport_header(skb);
825
826
	skb_push(skb, gre_hlen);
	skb_reset_network_header(skb);
Linus Torvalds's avatar
Linus Torvalds committed
827
	memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
828
829
	IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |
			      IPSKB_REROUTED);
Eric Dumazet's avatar
Eric Dumazet committed
830
831
	skb_dst_drop(skb);
	skb_dst_set(skb, &rt->u.dst);
Linus Torvalds's avatar
Linus Torvalds committed
832
833
834
835
836

	/*
	 *	Push down and install the IPIP header.
	 */

837
	iph 			=	ip_hdr(skb);
Linus Torvalds's avatar
Linus Torvalds committed
838
839
840
841
842
843
844
845
846
847
848
849
850
	iph->version		=	4;
	iph->ihl		=	sizeof(struct iphdr) >> 2;
	iph->frag_off		=	df;
	iph->protocol		=	IPPROTO_GRE;
	iph->tos		=	ipgre_ecn_encapsulate(tos, old_iph, skb);
	iph->daddr		=	rt->rt_dst;
	iph->saddr		=	rt->rt_src;

	if ((iph->ttl = tiph->ttl) == 0) {
		if (skb->protocol == htons(ETH_P_IP))
			iph->ttl = old_iph->ttl;
#ifdef CONFIG_IPV6
		else if (skb->protocol == htons(ETH_P_IPV6))
851
			iph->ttl = ((struct ipv6hdr *)old_iph)->hop_limit;
Linus Torvalds's avatar
Linus Torvalds committed
852
853
854
855
856
#endif
		else
			iph->ttl = dst_metric(&rt->u.dst, RTAX_HOPLIMIT);
	}

857
858
859
	((__be16 *)(iph + 1))[0] = tunnel->parms.o_flags;
	((__be16 *)(iph + 1))[1] = (dev->type == ARPHRD_ETHER) ?
				   htons(ETH_P_TEB) : skb->protocol;
Linus Torvalds's avatar
Linus Torvalds committed
860
861

	if (tunnel->parms.o_flags&(GRE_KEY|GRE_CSUM|GRE_SEQ)) {
Al Viro's avatar
Al Viro committed
862
		__be32 *ptr = (__be32*)(((u8*)iph) + tunnel->hlen - 4);
Linus Torvalds's avatar
Linus Torvalds committed
863
864
865
866
867
868
869
870
871
872
873
874

		if (tunnel->parms.o_flags&GRE_SEQ) {
			++tunnel->o_seqno;
			*ptr = htonl(tunnel->o_seqno);
			ptr--;
		}
		if (tunnel->parms.o_flags&GRE_KEY) {
			*ptr = tunnel->parms.o_key;
			ptr--;
		}
		if (tunnel->parms.o_flags&GRE_CSUM) {
			*ptr = 0;
875
			*(__sum16*)ptr = ip_compute_csum((void*)(iph+1), skb->len - sizeof(struct iphdr));
Linus Torvalds's avatar
Linus Torvalds committed
876
877
878
879
880
881
		}
	}

	nf_reset(skb);

	IPTUNNEL_XMIT();
882
	return NETDEV_TX_OK;
Linus Torvalds's avatar
Linus Torvalds committed
883
884
885
886
887
888
889

tx_error_icmp:
	dst_link_failure(skb);

tx_error:
	stats->tx_errors++;
	dev_kfree_skb(skb);
890
	return NETDEV_TX_OK;
Linus Torvalds's avatar
Linus Torvalds committed
891
892
}

893
static int ipgre_tunnel_bind_dev(struct net_device *dev)
894
895
896
897
898
899
900
901
902
903
904
{
	struct net_device *tdev = NULL;
	struct ip_tunnel *tunnel;
	struct iphdr *iph;
	int hlen = LL_MAX_HEADER;
	int mtu = ETH_DATA_LEN;
	int addend = sizeof(struct iphdr) + 4;

	tunnel = netdev_priv(dev);
	iph = &tunnel->parms.iph;

Herbert Xu's avatar
Herbert Xu committed
905
	/* Guess output device to choose reasonable mtu and needed_headroom */
906
907
908
909
910
911
912
913
914

	if (iph->daddr) {
		struct flowi fl = { .oif = tunnel->parms.link,
				    .nl_u = { .ip4_u =
					      { .daddr = iph->daddr,
						.saddr = iph->saddr,
						.tos = RT_TOS(iph->tos) } },
				    .proto = IPPROTO_GRE };
		struct rtable *rt;
915
		if (!ip_route_output_key(dev_net(dev), &rt, &fl)) {
916
917
918
			tdev = rt->u.dst.dev;
			ip_rt_put(rt);
		}
919
920
921

		if (dev->type != ARPHRD_ETHER)
			dev->flags |= IFF_POINTOPOINT;
922
923
924
	}

	if (!tdev && tunnel->parms.link)
925
		tdev = __dev_get_by_index(dev_net(dev), tunnel->parms.link);
926
927

	if (tdev) {
Herbert Xu's avatar
Herbert Xu committed
928
		hlen = tdev->hard_header_len + tdev->needed_headroom;
929
930
931
932
933
934
935
936
937
938
939
940
941
		mtu = tdev->mtu;
	}
	dev->iflink = tunnel->parms.link;

	/* Precalculate GRE options length */
	if (tunnel->parms.o_flags&(GRE_CSUM|GRE_KEY|GRE_SEQ)) {
		if (tunnel->parms.o_flags&GRE_CSUM)
			addend += 4;
		if (tunnel->parms.o_flags&GRE_KEY)
			addend += 4;
		if (tunnel->parms.o_flags&GRE_SEQ)
			addend += 4;
	}
Herbert Xu's avatar
Herbert Xu committed
942
	dev->needed_headroom = addend + hlen;
943
	mtu -= dev->hard_header_len + addend;
944
945
946
947

	if (mtu < 68)
		mtu = 68;

948
949
	tunnel->hlen = addend;

950
	return mtu;
951
952
}

Linus Torvalds's avatar
Linus Torvalds committed
953
954
955
956
957
958
static int
ipgre_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
{
	int err = 0;
	struct ip_tunnel_parm p;
	struct ip_tunnel *t;
959
960
	struct net *net = dev_net(dev);
	struct ipgre_net *ign = net_generic(net, ipgre_net_id);
Linus Torvalds's avatar
Linus Torvalds committed
961
962
963
964

	switch (cmd) {
	case SIOCGETTUNNEL:
		t = NULL;
965
		if (dev == ign->fb_tunnel_dev) {
Linus Torvalds's avatar
Linus Torvalds committed
966
967
968
969
			if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) {
				err = -EFAULT;
				break;
			}
970
			t = ipgre_tunnel_locate(net, &p, 0);
Linus Torvalds's avatar
Linus Torvalds committed
971
972
		}
		if (t == NULL)
973
			t = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
		memcpy(&p, &t->parms, sizeof(p));
		if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p)))
			err = -EFAULT;
		break;

	case SIOCADDTUNNEL:
	case SIOCCHGTUNNEL:
		err = -EPERM;
		if (!capable(CAP_NET_ADMIN))
			goto done;

		err = -EFAULT;
		if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
			goto done;

		err = -EINVAL;
		if (p.iph.version != 4 || p.iph.protocol != IPPROTO_GRE ||
		    p.iph.ihl != 5 || (p.iph.frag_off&htons(~IP_DF)) ||
		    ((p.i_flags|p.o_flags)&(GRE_VERSION|GRE_ROUTING)))
			goto done;
		if (p.iph.ttl)
			p.iph.frag_off |= htons(IP_DF);

		if (!(p.i_flags&GRE_KEY))
			p.i_key = 0;
		if (!(p.o_flags&GRE_KEY))
			p.o_key = 0;

1002
		t = ipgre_tunnel_locate(net, &p, cmd == SIOCADDTUNNEL);
Linus Torvalds's avatar
Linus Torvalds committed
1003

1004
		if (dev != ign->fb_tunnel_dev && cmd == SIOCCHGTUNNEL) {
Linus Torvalds's avatar
Linus Torvalds committed
1005
1006
1007
1008
1009
1010
			if (t != NULL) {
				if (t->dev != dev) {
					err = -EEXIST;
					break;
				}
			} else {
1011
				unsigned nflags = 0;
Linus Torvalds's avatar
Linus Torvalds committed
1012

1013
				t = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
1014

1015
				if (ipv4_is_multicast(p.iph.daddr))
Linus Torvalds's avatar
Linus Torvalds committed
1016
1017
1018
1019
1020
1021
1022
1023
					nflags = IFF_BROADCAST;
				else if (p.iph.daddr)
					nflags = IFF_POINTOPOINT;

				if ((dev->flags^nflags)&(IFF_POINTOPOINT|IFF_BROADCAST)) {
					err = -EINVAL;
					break;
				}
1024
				ipgre_tunnel_unlink(ign, t);
Linus Torvalds's avatar
Linus Torvalds committed
1025
1026
1027
1028
1029
1030
				t->parms.iph.saddr = p.iph.saddr;
				t->parms.iph.daddr = p.iph.daddr;
				t->parms.i_key = p.i_key;
				t->parms.o_key = p.o_key;
				memcpy(dev->dev_addr, &p.iph.saddr, 4);
				memcpy(dev->broadcast, &p.iph.daddr, 4);
1031
				ipgre_tunnel_link(ign, t);
Linus Torvalds's avatar
Linus Torvalds committed
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
				netdev_state_change(dev);
			}
		}

		if (t) {
			err = 0;
			if (cmd == SIOCCHGTUNNEL) {
				t->parms.iph.ttl = p.iph.ttl;
				t->parms.iph.tos = p.iph.tos;
				t->parms.iph.frag_off = p.iph.frag_off;
1042
1043
				if (t->parms.link != p.link) {
					t->parms.link = p.link;
1044
					dev->mtu = ipgre_tunnel_bind_dev(dev);
1045
1046
					netdev_state_change(dev);
				}
Linus Torvalds's avatar
Linus Torvalds committed
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
			}
			if (copy_to_user(ifr->ifr_ifru.ifru_data, &t->parms, sizeof(p)))
				err = -EFAULT;
		} else
			err = (cmd == SIOCADDTUNNEL ? -ENOBUFS : -ENOENT);
		break;

	case SIOCDELTUNNEL:
		err = -EPERM;
		if (!capable(CAP_NET_ADMIN))
			goto done;

1059
		if (dev == ign->fb_tunnel_dev) {
Linus Torvalds's avatar
Linus Torvalds committed
1060
1061
1062
1063
			err = -EFAULT;
			if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
				goto done;
			err = -ENOENT;
1064
			if ((t = ipgre_tunnel_locate(net, &p, 0)) == NULL)
Linus Torvalds's avatar
Linus Torvalds committed
1065
1066
				goto done;
			err = -EPERM;
1067
			if (t == netdev_priv(ign->fb_tunnel_dev))
Linus Torvalds's avatar
Linus Torvalds committed
1068
1069
1070
				goto done;
			dev = t->dev;
		}
1071
1072
		unregister_netdevice(dev);
		err = 0;
Linus Torvalds's avatar
Linus Torvalds committed
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
		break;

	default:
		err = -EINVAL;
	}

done:
	return err;
}

static int ipgre_tunnel_change_mtu(struct net_device *dev, int new_mtu)
{
1085
	struct ip_tunnel *tunnel = netdev_priv(dev);
Herbert Xu's avatar
Herbert Xu committed
1086
1087
	if (new_mtu < 68 ||
	    new_mtu > 0xFFF8 - dev->hard_header_len - tunnel->hlen)
Linus Torvalds's avatar
Linus Torvalds committed
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
		return -EINVAL;
	dev->mtu = new_mtu;
	return 0;
}

/* Nice toy. Unfortunately, useless in real life :-)
   It allows to construct virtual multiprotocol broadcast "LAN"
   over the Internet, provided multicast routing is tuned.


   I have no idea was this bicycle invented before me,
   so that I had to set ARPHRD_IPGRE to a random value.
   I have an impression, that Cisco could make something similar,
   but this feature is apparently missing in IOS<=11.2(8).
1102

Linus Torvalds's avatar
Linus Torvalds committed
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
   I set up 10.66.66/24 and fec0:6666:6666::0/96 as virtual networks
   with broadcast 224.66.66.66. If you have access to mbone, play with me :-)

   ping -t 255 224.66.66.66

   If nobody answers, mbone does not work.

   ip tunnel add Universe mode gre remote 224.66.66.66 local <Your_real_addr> ttl 255
   ip addr add 10.66.66.<somewhat>/24 dev Universe
   ifconfig Universe up
   ifconfig Universe add fe80::<Your_real_addr>/10
   ifconfig Universe add fec0:6666:6666::<Your_real_addr>/96
   ftp 10.66.66.66
   ...
   ftp fec0:6666:6666::193.233.7.65
   ...

 */

1122
1123
1124
static int ipgre_header(struct sk_buff *skb, struct net_device *dev,
			unsigned short type,
			const void *daddr, const void *saddr, unsigned len)
Linus Torvalds's avatar
Linus Torvalds committed
1125
{
1126
	struct ip_tunnel *t = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
1127
	struct iphdr *iph = (struct iphdr *)skb_push(skb, t->hlen);
Al Viro's avatar
Al Viro committed
1128
	__be16 *p = (__be16*)(iph+1);
Linus Torvalds's avatar
Linus Torvalds committed
1129
1130
1131
1132
1133
1134

	memcpy(iph, &t->parms.iph, sizeof(struct iphdr));
	p[0]		= t->parms.o_flags;
	p[1]		= htons(type);

	/*
1135
	 *	Set the source hardware address.
Linus Torvalds's avatar
Linus Torvalds committed
1136
	 */
1137

Linus Torvalds's avatar
Linus Torvalds committed
1138
1139
1140
1141
1142
1143
1144
	if (saddr)
		memcpy(&iph->saddr, saddr, 4);

	if (daddr) {
		memcpy(&iph->daddr, daddr, 4);
		return t->hlen;
	}
1145
	if (iph->daddr && !ipv4_is_multicast(iph->daddr))
Linus Torvalds's avatar
Linus Torvalds committed
1146
		return t->hlen;
1147

Linus Torvalds's avatar
Linus Torvalds committed
1148
1149
1150
	return -t->hlen;
}

1151
1152
static int ipgre_header_parse(const struct sk_buff *skb, unsigned char *haddr)
{
1153
	struct iphdr *iph = (struct iphdr *) skb_mac_header(skb);
1154
1155
1156
1157
	memcpy(haddr, &iph->saddr, 4);
	return 4;
}

1158
1159
static const struct header_ops ipgre_header_ops = {
	.create	= ipgre_header,
1160
	.parse	= ipgre_header_parse,
1161
1162
};

1163
#ifdef CONFIG_NET_IPGRE_BROADCAST
Linus Torvalds's avatar
Linus Torvalds committed
1164
1165
static int ipgre_open(struct net_device *dev)
{
1166
	struct ip_tunnel *t = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
1167

1168
	if (ipv4_is_multicast(t->parms.iph.daddr)) {
Linus Torvalds's avatar
Linus Torvalds committed
1169
1170
1171
1172
1173
1174
1175
		struct flowi fl = { .oif = t->parms.link,
				    .nl_u = { .ip4_u =
					      { .daddr = t->parms.iph.daddr,
						.saddr = t->parms.iph.saddr,
						.tos = RT_TOS(t->parms.iph.tos) } },
				    .proto = IPPROTO_GRE };
		struct rtable *rt;
1176
		if (ip_route_output_key(dev_net(dev), &rt, &fl))
Linus Torvalds's avatar
Linus Torvalds committed
1177
1178
1179
			return -EADDRNOTAVAIL;
		dev = rt->u.dst.dev;
		ip_rt_put(rt);
1180
		if (__in_dev_get_rtnl(dev) == NULL)
Linus Torvalds's avatar
Linus Torvalds committed
1181
1182
			return -EADDRNOTAVAIL;
		t->mlink = dev->ifindex;
1183
		ip_mc_inc_group(__in_dev_get_rtnl(dev), t->parms.iph.daddr);
Linus Torvalds's avatar
Linus Torvalds committed
1184
1185
1186
1187
1188
1189
	}
	return 0;
}

static int ipgre_close(struct net_device *dev)
{
1190
	struct ip_tunnel *t = netdev_priv(dev);
1191

1192
	if (ipv4_is_multicast(t->parms.iph.daddr) && t->mlink) {
1193
		struct in_device *in_dev;
1194
		in_dev = inetdev_by_index(dev_net(dev), t->mlink);
Linus Torvalds's avatar
Linus Torvalds committed
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
		if (in_dev) {
			ip_mc_dec_group(in_dev, t->parms.iph.daddr);
			in_dev_put(in_dev);
		}
	}
	return 0;
}

#endif

1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
static const struct net_device_ops ipgre_netdev_ops = {
	.ndo_init		= ipgre_tunnel_init,
	.ndo_uninit		= ipgre_tunnel_uninit,
#ifdef CONFIG_NET_IPGRE_BROADCAST
	.ndo_open		= ipgre_open,
	.ndo_stop		= ipgre_close,
#endif
	.ndo_start_xmit		= ipgre_tunnel_xmit,
	.ndo_do_ioctl		= ipgre_tunnel_ioctl,
	.ndo_change_mtu		= ipgre_tunnel_change_mtu,
};

Linus Torvalds's avatar
Linus Torvalds committed
1217
1218
static void ipgre_tunnel_setup(struct net_device *dev)
{
1219
	dev->netdev_ops		= &ipgre_netdev_ops;
Linus Torvalds's avatar
Linus Torvalds committed
1220
1221
1222
	dev->destructor 	= free_netdev;

	dev->type		= ARPHRD_IPGRE;
Herbert Xu's avatar
Herbert Xu committed
1223
	dev->needed_headroom 	= LL_MAX_HEADER + sizeof(struct iphdr) + 4;
1224
	dev->mtu		= ETH_DATA_LEN - sizeof(struct iphdr) - 4;
Linus Torvalds's avatar
Linus Torvalds committed
1225
1226
1227
	dev->flags		= IFF_NOARP;
	dev->iflink		= 0;
	dev->addr_len		= 4;
1228
	dev->features		|= NETIF_F_NETNS_LOCAL;
1229
	dev->priv_flags		&= ~IFF_XMIT_DST_RELEASE;
Linus Torvalds's avatar
Linus Torvalds committed
1230
1231
1232
1233
1234
1235
1236
}

static int ipgre_tunnel_init(struct net_device *dev)
{
	struct ip_tunnel *tunnel;
	struct iphdr *iph;

1237
	tunnel = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
	iph = &tunnel->parms.iph;

	tunnel->dev = dev;
	strcpy(tunnel->parms.name, dev->name);

	memcpy(dev->dev_addr, &tunnel->parms.iph.saddr, 4);
	memcpy(dev->broadcast, &tunnel->parms.iph.daddr, 4);

	if (iph->daddr) {
#ifdef CONFIG_NET_IPGRE_BROADCAST
1248
		if (ipv4_is_multicast(iph->daddr)) {
Linus Torvalds's avatar
Linus Torvalds committed
1249
1250
1251
			if (!iph->saddr)
				return -EINVAL;
			dev->flags = IFF_BROADCAST;
1252
			dev->header_ops = &ipgre_header_ops;
Linus Torvalds's avatar
Linus Torvalds committed
1253
1254
		}
#endif
1255
	} else
1256
		dev->header_ops = &ipgre_header_ops;