100959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov/*
200959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov *	GRE over IPv4 demultiplexer driver
300959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov *
400959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov *	Authors: Dmitry Kozlov (xeb@mail.ru)
500959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov *
600959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov *	This program is free software; you can redistribute it and/or
700959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov *	modify it under the terms of the GNU General Public License
800959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov *	as published by the Free Software Foundation; either version
900959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov *	2 of the License, or (at your option) any later version.
1000959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov *
1100959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov */
1200959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov
13afd465030acb4098abcb6b965a5aebc7ea2209e0Joe Perches#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
14afd465030acb4098abcb6b965a5aebc7ea2209e0Joe Perches
1500959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov#include <linux/module.h>
16bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar#include <linux/if.h>
17bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar#include <linux/icmp.h>
1800959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov#include <linux/kernel.h>
1900959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov#include <linux/kmod.h>
2000959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov#include <linux/skbuff.h>
2100959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov#include <linux/in.h>
22559fafb94ad9e4cd8774f39241917c57396f9fc5xeb@mail.ru#include <linux/ip.h>
2300959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov#include <linux/netdevice.h>
2468c331631143f5f039baac99a650e0b9e1ea02b6Pravin B Shelar#include <linux/if_tunnel.h>
2500959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov#include <linux/spinlock.h>
2600959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov#include <net/protocol.h>
2700959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov#include <net/gre.h>
2800959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov
29bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar#include <net/icmp.h>
30bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar#include <net/route.h>
31bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar#include <net/xfrm.h>
3200959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov
336f0bcf152582e7403155627a38e07bf3ef7f3cf5Eric Dumazetstatic const struct gre_protocol __rcu *gre_proto[GREPROTO_MAX] __read_mostly;
34bda7bb46343647f68591366731295a0f3eea59edPravin B Shelarstatic struct gre_cisco_protocol __rcu *gre_cisco_proto_list[GRE_IP_PROTO_MAX];
3500959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov
3600959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlovint gre_add_protocol(const struct gre_protocol *proto, u8 version)
3700959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov{
3800959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov	if (version >= GREPROTO_MAX)
3920fd4d1f04da07d09192ad8ad366a70d5125bfafPravin B Shelar		return -EINVAL;
4000959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov
4120fd4d1f04da07d09192ad8ad366a70d5125bfafPravin B Shelar	return (cmpxchg((const struct gre_protocol **)&gre_proto[version], NULL, proto) == NULL) ?
4220fd4d1f04da07d09192ad8ad366a70d5125bfafPravin B Shelar		0 : -EBUSY;
4300959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov}
4400959ade36acadc00e757f87060bf6e4501d545fDmitry KozlovEXPORT_SYMBOL_GPL(gre_add_protocol);
4500959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov
4600959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlovint gre_del_protocol(const struct gre_protocol *proto, u8 version)
4700959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov{
4820fd4d1f04da07d09192ad8ad366a70d5125bfafPravin B Shelar	int ret;
4920fd4d1f04da07d09192ad8ad366a70d5125bfafPravin B Shelar
5000959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov	if (version >= GREPROTO_MAX)
5120fd4d1f04da07d09192ad8ad366a70d5125bfafPravin B Shelar		return -EINVAL;
5220fd4d1f04da07d09192ad8ad366a70d5125bfafPravin B Shelar
5320fd4d1f04da07d09192ad8ad366a70d5125bfafPravin B Shelar	ret = (cmpxchg((const struct gre_protocol **)&gre_proto[version], proto, NULL) == proto) ?
5420fd4d1f04da07d09192ad8ad366a70d5125bfafPravin B Shelar		0 : -EBUSY;
5520fd4d1f04da07d09192ad8ad366a70d5125bfafPravin B Shelar
5620fd4d1f04da07d09192ad8ad366a70d5125bfafPravin B Shelar	if (ret)
5720fd4d1f04da07d09192ad8ad366a70d5125bfafPravin B Shelar		return ret;
5820fd4d1f04da07d09192ad8ad366a70d5125bfafPravin B Shelar
5900959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov	synchronize_rcu();
6000959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov	return 0;
6100959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov}
6200959ade36acadc00e757f87060bf6e4501d545fDmitry KozlovEXPORT_SYMBOL_GPL(gre_del_protocol);
6300959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov
64752f36da68e9136df8918461d651723a43627e04Pravin B Shelarvoid gre_build_header(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
65752f36da68e9136df8918461d651723a43627e04Pravin B Shelar		      int hdr_len)
66752f36da68e9136df8918461d651723a43627e04Pravin B Shelar{
67752f36da68e9136df8918461d651723a43627e04Pravin B Shelar	struct gre_base_hdr *greh;
68752f36da68e9136df8918461d651723a43627e04Pravin B Shelar
69752f36da68e9136df8918461d651723a43627e04Pravin B Shelar	skb_push(skb, hdr_len);
70752f36da68e9136df8918461d651723a43627e04Pravin B Shelar
71d0a7ebbc119738439ff00f7fadbd343ae20ea5e8Amritha Nambiar	skb_reset_transport_header(skb);
72752f36da68e9136df8918461d651723a43627e04Pravin B Shelar	greh = (struct gre_base_hdr *)skb->data;
73752f36da68e9136df8918461d651723a43627e04Pravin B Shelar	greh->flags = tnl_flags_to_gre_flags(tpi->flags);
74752f36da68e9136df8918461d651723a43627e04Pravin B Shelar	greh->protocol = tpi->proto;
75752f36da68e9136df8918461d651723a43627e04Pravin B Shelar
76752f36da68e9136df8918461d651723a43627e04Pravin B Shelar	if (tpi->flags&(TUNNEL_KEY|TUNNEL_CSUM|TUNNEL_SEQ)) {
77752f36da68e9136df8918461d651723a43627e04Pravin B Shelar		__be32 *ptr = (__be32 *)(((u8 *)greh) + hdr_len - 4);
78752f36da68e9136df8918461d651723a43627e04Pravin B Shelar
79752f36da68e9136df8918461d651723a43627e04Pravin B Shelar		if (tpi->flags&TUNNEL_SEQ) {
80752f36da68e9136df8918461d651723a43627e04Pravin B Shelar			*ptr = tpi->seq;
81752f36da68e9136df8918461d651723a43627e04Pravin B Shelar			ptr--;
82752f36da68e9136df8918461d651723a43627e04Pravin B Shelar		}
83752f36da68e9136df8918461d651723a43627e04Pravin B Shelar		if (tpi->flags&TUNNEL_KEY) {
84752f36da68e9136df8918461d651723a43627e04Pravin B Shelar			*ptr = tpi->key;
85752f36da68e9136df8918461d651723a43627e04Pravin B Shelar			ptr--;
86752f36da68e9136df8918461d651723a43627e04Pravin B Shelar		}
87752f36da68e9136df8918461d651723a43627e04Pravin B Shelar		if (tpi->flags&TUNNEL_CSUM &&
884749c09c37030ccdc44aecebe0f71b02a377fc14Tom Herbert		    !(skb_shinfo(skb)->gso_type &
894749c09c37030ccdc44aecebe0f71b02a377fc14Tom Herbert		      (SKB_GSO_GRE|SKB_GSO_GRE_CSUM))) {
90752f36da68e9136df8918461d651723a43627e04Pravin B Shelar			*ptr = 0;
91752f36da68e9136df8918461d651723a43627e04Pravin B Shelar			*(__sum16 *)ptr = csum_fold(skb_checksum(skb, 0,
92752f36da68e9136df8918461d651723a43627e04Pravin B Shelar								 skb->len, 0));
93752f36da68e9136df8918461d651723a43627e04Pravin B Shelar		}
94752f36da68e9136df8918461d651723a43627e04Pravin B Shelar	}
95752f36da68e9136df8918461d651723a43627e04Pravin B Shelar}
96752f36da68e9136df8918461d651723a43627e04Pravin B ShelarEXPORT_SYMBOL_GPL(gre_build_header);
97752f36da68e9136df8918461d651723a43627e04Pravin B Shelar
98bda7bb46343647f68591366731295a0f3eea59edPravin B Shelarstatic int parse_gre_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
99bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar			    bool *csum_err)
100bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar{
101bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	const struct gre_base_hdr *greh;
102bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	__be32 *options;
103bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	int hdr_len;
104bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar
105bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	if (unlikely(!pskb_may_pull(skb, sizeof(struct gre_base_hdr))))
106bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar		return -EINVAL;
107bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar
1081e701f16982a9d15488a5aa8c7f5c41444b1de67Tom Herbert	greh = (struct gre_base_hdr *)skb_transport_header(skb);
109bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	if (unlikely(greh->flags & (GRE_VERSION | GRE_ROUTING)))
110bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar		return -EINVAL;
111bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar
112bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	tpi->flags = gre_flags_to_tnl_flags(greh->flags);
113bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	hdr_len = ip_gre_calc_hlen(tpi->flags);
114bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar
115bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	if (!pskb_may_pull(skb, hdr_len))
116bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar		return -EINVAL;
117bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar
1181e701f16982a9d15488a5aa8c7f5c41444b1de67Tom Herbert	greh = (struct gre_base_hdr *)skb_transport_header(skb);
119bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	tpi->proto = greh->protocol;
120bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar
121bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	options = (__be32 *)(greh + 1);
122bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	if (greh->flags & GRE_CSUM) {
123b1036c6a470ccf5f18490a7ce4c99422d3bf77c4Tom Herbert		if (skb_checksum_simple_validate(skb)) {
124bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar			*csum_err = true;
125bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar			return -EINVAL;
126bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar		}
127884d338c041c2aa4536ade8620efa585e7c57f3cTom Herbert
128884d338c041c2aa4536ade8620efa585e7c57f3cTom Herbert		skb_checksum_try_convert(skb, IPPROTO_GRE, 0,
129884d338c041c2aa4536ade8620efa585e7c57f3cTom Herbert					 null_compute_pseudo);
130884d338c041c2aa4536ade8620efa585e7c57f3cTom Herbert
131bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar		options++;
132bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	}
133bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar
134bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	if (greh->flags & GRE_KEY) {
135bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar		tpi->key = *options;
136bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar		options++;
137bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	} else
138bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar		tpi->key = 0;
139bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar
140bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	if (unlikely(greh->flags & GRE_SEQ)) {
141bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar		tpi->seq = *options;
142bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar		options++;
143bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	} else
144bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar		tpi->seq = 0;
145bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar
146bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	/* WCCP version 1 and 2 protocol decoding.
147bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	 * - Change protocol to IP
148bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	 * - When dealing with WCCPv2, Skip extra 4 bytes in GRE header
149bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	 */
150bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	if (greh->flags == 0 && tpi->proto == htons(ETH_P_WCCP)) {
151bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar		tpi->proto = htons(ETH_P_IP);
152bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar		if ((*(u8 *)options & 0xF0) != 0x40) {
153bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar			hdr_len += 4;
154bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar			if (!pskb_may_pull(skb, hdr_len))
155bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar				return -EINVAL;
156bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar		}
157bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	}
1583d7b46cd20e300bd6989fb1f43d46f1b9645816ePravin B Shelar
1593d7b46cd20e300bd6989fb1f43d46f1b9645816ePravin B Shelar	return iptunnel_pull_header(skb, hdr_len, tpi->proto);
160bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar}
161bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar
162bda7bb46343647f68591366731295a0f3eea59edPravin B Shelarstatic int gre_cisco_rcv(struct sk_buff *skb)
163bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar{
164bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	struct tnl_ptk_info tpi;
165bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	int i;
166bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	bool csum_err = false;
167bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar
168fbd02dd405d0724a0f25897ed4a6813297c9b96fPravin B Shelar#ifdef CONFIG_NET_IPGRE_BROADCAST
169fbd02dd405d0724a0f25897ed4a6813297c9b96fPravin B Shelar	if (ipv4_is_multicast(ip_hdr(skb)->daddr)) {
170fbd02dd405d0724a0f25897ed4a6813297c9b96fPravin B Shelar		/* Looped back packet, drop it! */
171fbd02dd405d0724a0f25897ed4a6813297c9b96fPravin B Shelar		if (rt_is_output_route(skb_rtable(skb)))
172fbd02dd405d0724a0f25897ed4a6813297c9b96fPravin B Shelar			goto drop;
173fbd02dd405d0724a0f25897ed4a6813297c9b96fPravin B Shelar	}
174fbd02dd405d0724a0f25897ed4a6813297c9b96fPravin B Shelar#endif
175fbd02dd405d0724a0f25897ed4a6813297c9b96fPravin B Shelar
176bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	if (parse_gre_header(skb, &tpi, &csum_err) < 0)
177bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar		goto drop;
178bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar
179bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	rcu_read_lock();
180bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	for (i = 0; i < GRE_IP_PROTO_MAX; i++) {
181bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar		struct gre_cisco_protocol *proto;
182bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar		int ret;
183bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar
184bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar		proto = rcu_dereference(gre_cisco_proto_list[i]);
185bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar		if (!proto)
186bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar			continue;
187bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar		ret = proto->handler(skb, &tpi);
188bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar		if (ret == PACKET_RCVD) {
189bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar			rcu_read_unlock();
190bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar			return 0;
191bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar		}
192bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	}
193bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	rcu_read_unlock();
194bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar
195bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
196bda7bb46343647f68591366731295a0f3eea59edPravin B Shelardrop:
197bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	kfree_skb(skb);
198bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	return 0;
199bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar}
200bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar
201bda7bb46343647f68591366731295a0f3eea59edPravin B Shelarstatic void gre_cisco_err(struct sk_buff *skb, u32 info)
202bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar{
203bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	/* All the routers (except for Linux) return only
204bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	 * 8 bytes of packet payload. It means, that precise relaying of
205bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	 * ICMP in the real Internet is absolutely infeasible.
206bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	 *
207bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	 * Moreover, Cisco "wise men" put GRE key to the third word
208bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	 * in GRE header. It makes impossible maintaining even soft
209bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	 * state for keyed
210bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	 * GRE tunnels with enabled checksum. Tell them "thank you".
211bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	 *
212bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	 * Well, I wonder, rfc1812 was written by Cisco employee,
213bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	 * what the hell these idiots break standards established
214bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	 * by themselves???
215bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	 */
216bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar
217bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	const int type = icmp_hdr(skb)->type;
218bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	const int code = icmp_hdr(skb)->code;
219bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	struct tnl_ptk_info tpi;
220bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	bool csum_err = false;
221bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	int i;
222bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar
223bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	if (parse_gre_header(skb, &tpi, &csum_err)) {
224bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar		if (!csum_err)		/* ignore csum errors. */
225bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar			return;
226bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	}
227bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar
228bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
229bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar		ipv4_update_pmtu(skb, dev_net(skb->dev), info,
230bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar				skb->dev->ifindex, 0, IPPROTO_GRE, 0);
231bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar		return;
232bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	}
233bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	if (type == ICMP_REDIRECT) {
234bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar		ipv4_redirect(skb, dev_net(skb->dev), skb->dev->ifindex, 0,
235bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar				IPPROTO_GRE, 0);
236bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar		return;
237bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	}
238bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar
239bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	rcu_read_lock();
240bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	for (i = 0; i < GRE_IP_PROTO_MAX; i++) {
241bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar		struct gre_cisco_protocol *proto;
242bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar
243bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar		proto = rcu_dereference(gre_cisco_proto_list[i]);
244bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar		if (!proto)
245bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar			continue;
246bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar
247bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar		if (proto->err_handler(skb, info, &tpi) == PACKET_RCVD)
248bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar			goto out;
249bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar
250bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	}
251bda7bb46343647f68591366731295a0f3eea59edPravin B Shelarout:
252bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	rcu_read_unlock();
253bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar}
254bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar
25500959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlovstatic int gre_rcv(struct sk_buff *skb)
25600959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov{
25700959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov	const struct gre_protocol *proto;
25800959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov	u8 ver;
25900959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov	int ret;
26000959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov
26100959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov	if (!pskb_may_pull(skb, 12))
26200959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov		goto drop;
26300959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov
26400959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov	ver = skb->data[1]&0x7f;
26500959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov	if (ver >= GREPROTO_MAX)
26600959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov		goto drop;
26700959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov
26800959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov	rcu_read_lock();
26900959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov	proto = rcu_dereference(gre_proto[ver]);
27000959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov	if (!proto || !proto->handler)
27100959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov		goto drop_unlock;
27200959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov	ret = proto->handler(skb);
27300959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov	rcu_read_unlock();
27400959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov	return ret;
27500959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov
27600959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlovdrop_unlock:
27700959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov	rcu_read_unlock();
27800959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlovdrop:
27900959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov	kfree_skb(skb);
28000959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov	return NET_RX_DROP;
28100959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov}
28200959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov
28300959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlovstatic void gre_err(struct sk_buff *skb, u32 info)
28400959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov{
28500959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov	const struct gre_protocol *proto;
286559fafb94ad9e4cd8774f39241917c57396f9fc5xeb@mail.ru	const struct iphdr *iph = (const struct iphdr *)skb->data;
287559fafb94ad9e4cd8774f39241917c57396f9fc5xeb@mail.ru	u8 ver = skb->data[(iph->ihl<<2) + 1]&0x7f;
28800959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov
28900959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov	if (ver >= GREPROTO_MAX)
290559fafb94ad9e4cd8774f39241917c57396f9fc5xeb@mail.ru		return;
29100959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov
29200959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov	rcu_read_lock();
29300959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov	proto = rcu_dereference(gre_proto[ver]);
294559fafb94ad9e4cd8774f39241917c57396f9fc5xeb@mail.ru	if (proto && proto->err_handler)
295559fafb94ad9e4cd8774f39241917c57396f9fc5xeb@mail.ru		proto->err_handler(skb, info);
29600959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov	rcu_read_unlock();
29700959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov}
29800959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov
29900959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlovstatic const struct net_protocol net_gre_protocol = {
30000959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov	.handler     = gre_rcv,
30100959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov	.err_handler = gre_err,
30200959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov	.netns_ok    = 1,
30300959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov};
30400959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov
305bda7bb46343647f68591366731295a0f3eea59edPravin B Shelarstatic const struct gre_protocol ipgre_protocol = {
306bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	.handler     = gre_cisco_rcv,
307bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	.err_handler = gre_cisco_err,
308bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar};
309bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar
310bda7bb46343647f68591366731295a0f3eea59edPravin B Shelarint gre_cisco_register(struct gre_cisco_protocol *newp)
311bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar{
312bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	struct gre_cisco_protocol **proto = (struct gre_cisco_protocol **)
313bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar					    &gre_cisco_proto_list[newp->priority];
314bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar
315bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	return (cmpxchg(proto, NULL, newp) == NULL) ? 0 : -EBUSY;
316bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar}
317bda7bb46343647f68591366731295a0f3eea59edPravin B ShelarEXPORT_SYMBOL_GPL(gre_cisco_register);
318bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar
319bda7bb46343647f68591366731295a0f3eea59edPravin B Shelarint gre_cisco_unregister(struct gre_cisco_protocol *del_proto)
320bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar{
321bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	struct gre_cisco_protocol **proto = (struct gre_cisco_protocol **)
322bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar					    &gre_cisco_proto_list[del_proto->priority];
323bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	int ret;
324bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar
325bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	ret = (cmpxchg(proto, del_proto, NULL) == del_proto) ? 0 : -EINVAL;
326bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar
327bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	if (ret)
328bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar		return ret;
329bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar
330bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	synchronize_net();
331bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	return 0;
332bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar}
333bda7bb46343647f68591366731295a0f3eea59edPravin B ShelarEXPORT_SYMBOL_GPL(gre_cisco_unregister);
334bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar
33500959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlovstatic int __init gre_init(void)
33600959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov{
337afd465030acb4098abcb6b965a5aebc7ea2209e0Joe Perches	pr_info("GRE over IPv4 demultiplexor driver\n");
33800959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov
33900959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov	if (inet_add_protocol(&net_gre_protocol, IPPROTO_GRE) < 0) {
340afd465030acb4098abcb6b965a5aebc7ea2209e0Joe Perches		pr_err("can't add protocol\n");
341bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar		goto err;
342bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	}
343bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar
344bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	if (gre_add_protocol(&ipgre_protocol, GREPROTO_CISCO) < 0) {
345bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar		pr_info("%s: can't add ipgre handler\n", __func__);
346bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar		goto err_gre;
34700959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov	}
34800959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov
34900959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov	return 0;
350bda7bb46343647f68591366731295a0f3eea59edPravin B Shelarerr_gre:
351bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	inet_del_protocol(&net_gre_protocol, IPPROTO_GRE);
352bda7bb46343647f68591366731295a0f3eea59edPravin B Shelarerr:
353bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	return -EAGAIN;
35400959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov}
35500959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov
35600959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlovstatic void __exit gre_exit(void)
35700959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov{
358bda7bb46343647f68591366731295a0f3eea59edPravin B Shelar	gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO);
35900959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov	inet_del_protocol(&net_gre_protocol, IPPROTO_GRE);
36000959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov}
36100959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov
36200959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlovmodule_init(gre_init);
36300959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlovmodule_exit(gre_exit);
36400959ade36acadc00e757f87060bf6e4501d545fDmitry Kozlov
36500959ade36acadc00e757f87060bf6e4501d545fDmitry KozlovMODULE_DESCRIPTION("GRE over IPv4 demultiplexer driver");
36600959ade36acadc00e757f87060bf6e4501d545fDmitry KozlovMODULE_AUTHOR("D. Kozlov (xeb@mail.ru)");
36700959ade36acadc00e757f87060bf6e4501d545fDmitry KozlovMODULE_LICENSE("GPL");
368