Blame view

net/ipv4/gre_demux.c 4.31 KB
00959ade3   Dmitry Kozlov   PPTP: PPP over IP...
1
2
3
4
5
6
7
8
9
10
11
  /*
   *	GRE over IPv4 demultiplexer driver
   *
   *	Authors: Dmitry Kozlov (xeb@mail.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.
   *
   */
afd465030   Joe Perches   net: ipv4: Standa...
12
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
00959ade3   Dmitry Kozlov   PPTP: PPP over IP...
13
  #include <linux/module.h>
bda7bb463   Pravin B Shelar   gre: Allow multip...
14
15
  #include <linux/if.h>
  #include <linux/icmp.h>
00959ade3   Dmitry Kozlov   PPTP: PPP over IP...
16
17
18
19
  #include <linux/kernel.h>
  #include <linux/kmod.h>
  #include <linux/skbuff.h>
  #include <linux/in.h>
559fafb94   xeb@mail.ru   gre: fix improper...
20
  #include <linux/ip.h>
00959ade3   Dmitry Kozlov   PPTP: PPP over IP...
21
  #include <linux/netdevice.h>
68c331631   Pravin B Shelar   v4 GRE: Add TCP s...
22
  #include <linux/if_tunnel.h>
00959ade3   Dmitry Kozlov   PPTP: PPP over IP...
23
24
25
  #include <linux/spinlock.h>
  #include <net/protocol.h>
  #include <net/gre.h>
bda7bb463   Pravin B Shelar   gre: Allow multip...
26
27
28
  #include <net/icmp.h>
  #include <net/route.h>
  #include <net/xfrm.h>
00959ade3   Dmitry Kozlov   PPTP: PPP over IP...
29

6f0bcf152   Eric Dumazet   tunnels: add _rcu...
30
  static const struct gre_protocol __rcu *gre_proto[GREPROTO_MAX] __read_mostly;
00959ade3   Dmitry Kozlov   PPTP: PPP over IP...
31
32
33
34
  
  int gre_add_protocol(const struct gre_protocol *proto, u8 version)
  {
  	if (version >= GREPROTO_MAX)
20fd4d1f0   Pravin B Shelar   gre: Simplify gre...
35
  		return -EINVAL;
00959ade3   Dmitry Kozlov   PPTP: PPP over IP...
36

20fd4d1f0   Pravin B Shelar   gre: Simplify gre...
37
38
  	return (cmpxchg((const struct gre_protocol **)&gre_proto[version], NULL, proto) == NULL) ?
  		0 : -EBUSY;
00959ade3   Dmitry Kozlov   PPTP: PPP over IP...
39
40
41
42
43
  }
  EXPORT_SYMBOL_GPL(gre_add_protocol);
  
  int gre_del_protocol(const struct gre_protocol *proto, u8 version)
  {
20fd4d1f0   Pravin B Shelar   gre: Simplify gre...
44
  	int ret;
00959ade3   Dmitry Kozlov   PPTP: PPP over IP...
45
  	if (version >= GREPROTO_MAX)
20fd4d1f0   Pravin B Shelar   gre: Simplify gre...
46
47
48
49
50
51
52
  		return -EINVAL;
  
  	ret = (cmpxchg((const struct gre_protocol **)&gre_proto[version], proto, NULL) == proto) ?
  		0 : -EBUSY;
  
  	if (ret)
  		return ret;
00959ade3   Dmitry Kozlov   PPTP: PPP over IP...
53
54
  	synchronize_rcu();
  	return 0;
00959ade3   Dmitry Kozlov   PPTP: PPP over IP...
55
56
  }
  EXPORT_SYMBOL_GPL(gre_del_protocol);
f132ae7c4   Jiri Benc   gre: change gre_p...
57
  /* Fills in tpi and returns header length to be pulled. */
95f5c64c3   Tom Herbert   gre: Move utility...
58
  int gre_parse_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
e582615ad   Eric Dumazet   gre: fix error ha...
59
  		     bool *csum_err, __be16 proto, int nhs)
95f5c64c3   Tom Herbert   gre: Move utility...
60
61
62
63
  {
  	const struct gre_base_hdr *greh;
  	__be32 *options;
  	int hdr_len;
e582615ad   Eric Dumazet   gre: fix error ha...
64
  	if (unlikely(!pskb_may_pull(skb, nhs + sizeof(struct gre_base_hdr))))
95f5c64c3   Tom Herbert   gre: Move utility...
65
  		return -EINVAL;
e582615ad   Eric Dumazet   gre: fix error ha...
66
  	greh = (struct gre_base_hdr *)(skb->data + nhs);
95f5c64c3   Tom Herbert   gre: Move utility...
67
68
69
70
71
  	if (unlikely(greh->flags & (GRE_VERSION | GRE_ROUTING)))
  		return -EINVAL;
  
  	tpi->flags = gre_flags_to_tnl_flags(greh->flags);
  	hdr_len = gre_calc_hlen(tpi->flags);
e582615ad   Eric Dumazet   gre: fix error ha...
72
  	if (!pskb_may_pull(skb, nhs + hdr_len))
95f5c64c3   Tom Herbert   gre: Move utility...
73
  		return -EINVAL;
e582615ad   Eric Dumazet   gre: fix error ha...
74
  	greh = (struct gre_base_hdr *)(skb->data + nhs);
95f5c64c3   Tom Herbert   gre: Move utility...
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
  	tpi->proto = greh->protocol;
  
  	options = (__be32 *)(greh + 1);
  	if (greh->flags & GRE_CSUM) {
  		if (skb_checksum_simple_validate(skb)) {
  			*csum_err = true;
  			return -EINVAL;
  		}
  
  		skb_checksum_try_convert(skb, IPPROTO_GRE, 0,
  					 null_compute_pseudo);
  		options++;
  	}
  
  	if (greh->flags & GRE_KEY) {
  		tpi->key = *options;
  		options++;
  	} else {
  		tpi->key = 0;
  	}
  	if (unlikely(greh->flags & GRE_SEQ)) {
  		tpi->seq = *options;
  		options++;
  	} else {
  		tpi->seq = 0;
  	}
  	/* WCCP version 1 and 2 protocol decoding.
da73b4e95   Haishuang Yan   gre: Fix wrong tp...
102
  	 * - Change protocol to IPv4/IPv6
95f5c64c3   Tom Herbert   gre: Move utility...
103
104
105
  	 * - When dealing with WCCPv2, Skip extra 4 bytes in GRE header
  	 */
  	if (greh->flags == 0 && tpi->proto == htons(ETH_P_WCCP)) {
da73b4e95   Haishuang Yan   gre: Fix wrong tp...
106
  		tpi->proto = proto;
00b203402   Jiri Benc   gre: remove super...
107
  		if ((*(u8 *)options & 0xF0) != 0x40)
95f5c64c3   Tom Herbert   gre: Move utility...
108
  			hdr_len += 4;
95f5c64c3   Tom Herbert   gre: Move utility...
109
  	}
9b8c6d7bf   Eric Dumazet   gre: better suppo...
110
  	tpi->hdr_len = hdr_len;
f132ae7c4   Jiri Benc   gre: change gre_p...
111
  	return hdr_len;
95f5c64c3   Tom Herbert   gre: Move utility...
112
113
  }
  EXPORT_SYMBOL(gre_parse_header);
00959ade3   Dmitry Kozlov   PPTP: PPP over IP...
114
115
116
117
118
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
  static int gre_rcv(struct sk_buff *skb)
  {
  	const struct gre_protocol *proto;
  	u8 ver;
  	int ret;
  
  	if (!pskb_may_pull(skb, 12))
  		goto drop;
  
  	ver = skb->data[1]&0x7f;
  	if (ver >= GREPROTO_MAX)
  		goto drop;
  
  	rcu_read_lock();
  	proto = rcu_dereference(gre_proto[ver]);
  	if (!proto || !proto->handler)
  		goto drop_unlock;
  	ret = proto->handler(skb);
  	rcu_read_unlock();
  	return ret;
  
  drop_unlock:
  	rcu_read_unlock();
  drop:
  	kfree_skb(skb);
  	return NET_RX_DROP;
  }
  
  static void gre_err(struct sk_buff *skb, u32 info)
  {
  	const struct gre_protocol *proto;
559fafb94   xeb@mail.ru   gre: fix improper...
145
146
  	const struct iphdr *iph = (const struct iphdr *)skb->data;
  	u8 ver = skb->data[(iph->ihl<<2) + 1]&0x7f;
00959ade3   Dmitry Kozlov   PPTP: PPP over IP...
147

00959ade3   Dmitry Kozlov   PPTP: PPP over IP...
148
  	if (ver >= GREPROTO_MAX)
559fafb94   xeb@mail.ru   gre: fix improper...
149
  		return;
00959ade3   Dmitry Kozlov   PPTP: PPP over IP...
150
151
152
  
  	rcu_read_lock();
  	proto = rcu_dereference(gre_proto[ver]);
559fafb94   xeb@mail.ru   gre: fix improper...
153
154
  	if (proto && proto->err_handler)
  		proto->err_handler(skb, info);
00959ade3   Dmitry Kozlov   PPTP: PPP over IP...
155
  	rcu_read_unlock();
00959ade3   Dmitry Kozlov   PPTP: PPP over IP...
156
157
158
159
160
161
162
163
164
165
  }
  
  static const struct net_protocol net_gre_protocol = {
  	.handler     = gre_rcv,
  	.err_handler = gre_err,
  	.netns_ok    = 1,
  };
  
  static int __init gre_init(void)
  {
afd465030   Joe Perches   net: ipv4: Standa...
166
167
  	pr_info("GRE over IPv4 demultiplexor driver
  ");
00959ade3   Dmitry Kozlov   PPTP: PPP over IP...
168
169
  
  	if (inet_add_protocol(&net_gre_protocol, IPPROTO_GRE) < 0) {
afd465030   Joe Perches   net: ipv4: Standa...
170
171
  		pr_err("can't add protocol
  ");
9f57c67c3   Pravin B Shelar   gre: Remove suppo...
172
  		return -EAGAIN;
bda7bb463   Pravin B Shelar   gre: Allow multip...
173
  	}
00959ade3   Dmitry Kozlov   PPTP: PPP over IP...
174
175
176
177
178
179
180
181
182
183
184
185
186
187
  	return 0;
  }
  
  static void __exit gre_exit(void)
  {
  	inet_del_protocol(&net_gre_protocol, IPPROTO_GRE);
  }
  
  module_init(gre_init);
  module_exit(gre_exit);
  
  MODULE_DESCRIPTION("GRE over IPv4 demultiplexer driver");
  MODULE_AUTHOR("D. Kozlov (xeb@mail.ru)");
  MODULE_LICENSE("GPL");