Skip to content

Commit bda7bb4

Browse files
Pravin B Shelardavem330
authored andcommitted
gre: Allow multiple protocol listener for gre protocol.
Currently there is only one user is allowed to register for gre protocol. Following patch adds de-multiplexer. So that multiple modules can listen on gre protocol e.g. kernel gre devices and ovs. Signed-off-by: Pravin B Shelar <pshelar@nicira.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 20fd4d1 commit bda7bb4

File tree

3 files changed

+267
-151
lines changed

3 files changed

+267
-151
lines changed

include/net/gre.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#define GREPROTO_CISCO 0
88
#define GREPROTO_PPTP 1
99
#define GREPROTO_MAX 2
10+
#define GRE_IP_PROTO_MAX 2
1011

1112
struct gre_protocol {
1213
int (*handler)(struct sk_buff *skb);
@@ -22,6 +23,29 @@ struct gre_base_hdr {
2223
int gre_add_protocol(const struct gre_protocol *proto, u8 version);
2324
int gre_del_protocol(const struct gre_protocol *proto, u8 version);
2425

26+
struct gre_cisco_protocol {
27+
int (*handler)(struct sk_buff *skb, const struct tnl_ptk_info *tpi);
28+
int (*err_handler)(struct sk_buff *skb, u32 info,
29+
const struct tnl_ptk_info *tpi);
30+
u8 priority;
31+
};
32+
33+
int gre_cisco_register(struct gre_cisco_protocol *proto);
34+
int gre_cisco_unregister(struct gre_cisco_protocol *proto);
35+
36+
static inline int ip_gre_calc_hlen(__be16 o_flags)
37+
{
38+
int addend = 4;
39+
40+
if (o_flags&TUNNEL_CSUM)
41+
addend += 4;
42+
if (o_flags&TUNNEL_KEY)
43+
addend += 4;
44+
if (o_flags&TUNNEL_SEQ)
45+
addend += 4;
46+
return addend;
47+
}
48+
2549
static inline __be16 gre_flags_to_tnl_flags(__be16 flags)
2650
{
2751
__be16 tflags = 0;

net/ipv4/gre.c

Lines changed: 217 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1414

1515
#include <linux/module.h>
16+
#include <linux/if.h>
17+
#include <linux/icmp.h>
1618
#include <linux/kernel.h>
1719
#include <linux/kmod.h>
1820
#include <linux/skbuff.h>
@@ -24,8 +26,12 @@
2426
#include <net/protocol.h>
2527
#include <net/gre.h>
2628

29+
#include <net/icmp.h>
30+
#include <net/route.h>
31+
#include <net/xfrm.h>
2732

2833
static const struct gre_protocol __rcu *gre_proto[GREPROTO_MAX] __read_mostly;
34+
static struct gre_cisco_protocol __rcu *gre_cisco_proto_list[GRE_IP_PROTO_MAX];
2935

3036
int gre_add_protocol(const struct gre_protocol *proto, u8 version)
3137
{
@@ -55,6 +61,173 @@ int gre_del_protocol(const struct gre_protocol *proto, u8 version)
5561
}
5662
EXPORT_SYMBOL_GPL(gre_del_protocol);
5763

64+
static __sum16 check_checksum(struct sk_buff *skb)
65+
{
66+
__sum16 csum = 0;
67+
68+
switch (skb->ip_summed) {
69+
case CHECKSUM_COMPLETE:
70+
csum = csum_fold(skb->csum);
71+
72+
if (!csum)
73+
break;
74+
/* Fall through. */
75+
76+
case CHECKSUM_NONE:
77+
skb->csum = 0;
78+
csum = __skb_checksum_complete(skb);
79+
skb->ip_summed = CHECKSUM_COMPLETE;
80+
break;
81+
}
82+
83+
return csum;
84+
}
85+
86+
static int parse_gre_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
87+
bool *csum_err)
88+
{
89+
unsigned int ip_hlen = ip_hdrlen(skb);
90+
const struct gre_base_hdr *greh;
91+
__be32 *options;
92+
int hdr_len;
93+
94+
if (unlikely(!pskb_may_pull(skb, sizeof(struct gre_base_hdr))))
95+
return -EINVAL;
96+
97+
greh = (struct gre_base_hdr *)(skb_network_header(skb) + ip_hlen);
98+
if (unlikely(greh->flags & (GRE_VERSION | GRE_ROUTING)))
99+
return -EINVAL;
100+
101+
tpi->flags = gre_flags_to_tnl_flags(greh->flags);
102+
hdr_len = ip_gre_calc_hlen(tpi->flags);
103+
104+
if (!pskb_may_pull(skb, hdr_len))
105+
return -EINVAL;
106+
107+
greh = (struct gre_base_hdr *)(skb_network_header(skb) + ip_hlen);
108+
tpi->proto = greh->protocol;
109+
110+
options = (__be32 *)(greh + 1);
111+
if (greh->flags & GRE_CSUM) {
112+
if (check_checksum(skb)) {
113+
*csum_err = true;
114+
return -EINVAL;
115+
}
116+
options++;
117+
}
118+
119+
if (greh->flags & GRE_KEY) {
120+
tpi->key = *options;
121+
options++;
122+
} else
123+
tpi->key = 0;
124+
125+
if (unlikely(greh->flags & GRE_SEQ)) {
126+
tpi->seq = *options;
127+
options++;
128+
} else
129+
tpi->seq = 0;
130+
131+
/* WCCP version 1 and 2 protocol decoding.
132+
* - Change protocol to IP
133+
* - When dealing with WCCPv2, Skip extra 4 bytes in GRE header
134+
*/
135+
if (greh->flags == 0 && tpi->proto == htons(ETH_P_WCCP)) {
136+
tpi->proto = htons(ETH_P_IP);
137+
if ((*(u8 *)options & 0xF0) != 0x40) {
138+
hdr_len += 4;
139+
if (!pskb_may_pull(skb, hdr_len))
140+
return -EINVAL;
141+
}
142+
}
143+
return 0;
144+
}
145+
146+
static int gre_cisco_rcv(struct sk_buff *skb)
147+
{
148+
struct tnl_ptk_info tpi;
149+
int i;
150+
bool csum_err = false;
151+
152+
if (parse_gre_header(skb, &tpi, &csum_err) < 0)
153+
goto drop;
154+
155+
rcu_read_lock();
156+
for (i = 0; i < GRE_IP_PROTO_MAX; i++) {
157+
struct gre_cisco_protocol *proto;
158+
int ret;
159+
160+
proto = rcu_dereference(gre_cisco_proto_list[i]);
161+
if (!proto)
162+
continue;
163+
ret = proto->handler(skb, &tpi);
164+
if (ret == PACKET_RCVD) {
165+
rcu_read_unlock();
166+
return 0;
167+
}
168+
}
169+
rcu_read_unlock();
170+
171+
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
172+
drop:
173+
kfree_skb(skb);
174+
return 0;
175+
}
176+
177+
static void gre_cisco_err(struct sk_buff *skb, u32 info)
178+
{
179+
/* All the routers (except for Linux) return only
180+
* 8 bytes of packet payload. It means, that precise relaying of
181+
* ICMP in the real Internet is absolutely infeasible.
182+
*
183+
* Moreover, Cisco "wise men" put GRE key to the third word
184+
* in GRE header. It makes impossible maintaining even soft
185+
* state for keyed
186+
* GRE tunnels with enabled checksum. Tell them "thank you".
187+
*
188+
* Well, I wonder, rfc1812 was written by Cisco employee,
189+
* what the hell these idiots break standards established
190+
* by themselves???
191+
*/
192+
193+
const int type = icmp_hdr(skb)->type;
194+
const int code = icmp_hdr(skb)->code;
195+
struct tnl_ptk_info tpi;
196+
bool csum_err = false;
197+
int i;
198+
199+
if (parse_gre_header(skb, &tpi, &csum_err)) {
200+
if (!csum_err) /* ignore csum errors. */
201+
return;
202+
}
203+
204+
if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
205+
ipv4_update_pmtu(skb, dev_net(skb->dev), info,
206+
skb->dev->ifindex, 0, IPPROTO_GRE, 0);
207+
return;
208+
}
209+
if (type == ICMP_REDIRECT) {
210+
ipv4_redirect(skb, dev_net(skb->dev), skb->dev->ifindex, 0,
211+
IPPROTO_GRE, 0);
212+
return;
213+
}
214+
215+
rcu_read_lock();
216+
for (i = 0; i < GRE_IP_PROTO_MAX; i++) {
217+
struct gre_cisco_protocol *proto;
218+
219+
proto = rcu_dereference(gre_cisco_proto_list[i]);
220+
if (!proto)
221+
continue;
222+
223+
if (proto->err_handler(skb, info, &tpi) == PACKET_RCVD)
224+
goto out;
225+
226+
}
227+
out:
228+
rcu_read_unlock();
229+
}
230+
58231
static int gre_rcv(struct sk_buff *skb)
59232
{
60233
const struct gre_protocol *proto;
@@ -206,27 +379,68 @@ static const struct net_offload gre_offload = {
206379
},
207380
};
208381

382+
static const struct gre_protocol ipgre_protocol = {
383+
.handler = gre_cisco_rcv,
384+
.err_handler = gre_cisco_err,
385+
};
386+
387+
int gre_cisco_register(struct gre_cisco_protocol *newp)
388+
{
389+
struct gre_cisco_protocol **proto = (struct gre_cisco_protocol **)
390+
&gre_cisco_proto_list[newp->priority];
391+
392+
return (cmpxchg(proto, NULL, newp) == NULL) ? 0 : -EBUSY;
393+
}
394+
EXPORT_SYMBOL_GPL(gre_cisco_register);
395+
396+
int gre_cisco_unregister(struct gre_cisco_protocol *del_proto)
397+
{
398+
struct gre_cisco_protocol **proto = (struct gre_cisco_protocol **)
399+
&gre_cisco_proto_list[del_proto->priority];
400+
int ret;
401+
402+
ret = (cmpxchg(proto, del_proto, NULL) == del_proto) ? 0 : -EINVAL;
403+
404+
if (ret)
405+
return ret;
406+
407+
synchronize_net();
408+
return 0;
409+
}
410+
EXPORT_SYMBOL_GPL(gre_cisco_unregister);
411+
209412
static int __init gre_init(void)
210413
{
211414
pr_info("GRE over IPv4 demultiplexor driver\n");
212415

213416
if (inet_add_protocol(&net_gre_protocol, IPPROTO_GRE) < 0) {
214417
pr_err("can't add protocol\n");
215-
return -EAGAIN;
418+
goto err;
419+
}
420+
421+
if (gre_add_protocol(&ipgre_protocol, GREPROTO_CISCO) < 0) {
422+
pr_info("%s: can't add ipgre handler\n", __func__);
423+
goto err_gre;
216424
}
217425

218426
if (inet_add_offload(&gre_offload, IPPROTO_GRE)) {
219427
pr_err("can't add protocol offload\n");
220-
inet_del_protocol(&net_gre_protocol, IPPROTO_GRE);
221-
return -EAGAIN;
428+
goto err_gso;
222429
}
223430

224431
return 0;
432+
err_gso:
433+
gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO);
434+
err_gre:
435+
inet_del_protocol(&net_gre_protocol, IPPROTO_GRE);
436+
err:
437+
return -EAGAIN;
225438
}
226439

227440
static void __exit gre_exit(void)
228441
{
229442
inet_del_offload(&gre_offload, IPPROTO_GRE);
443+
gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO);
230444
inet_del_protocol(&net_gre_protocol, IPPROTO_GRE);
231445
}
232446

@@ -236,4 +450,3 @@ module_exit(gre_exit);
236450
MODULE_DESCRIPTION("GRE over IPv4 demultiplexer driver");
237451
MODULE_AUTHOR("D. Kozlov (xeb@mail.ru)");
238452
MODULE_LICENSE("GPL");
239-

0 commit comments

Comments
 (0)