1 /*
2  * ip_conntrack_proto_gre.c - Version 3.0
3  *
4  * Connection tracking protocol helper module for GRE.
5  *
6  * GRE is a generic encapsulation protocol, which is generally not very
7  * suited for NAT, as it has no protocol-specific part as port numbers.
8  *
9  * It has an optional key field, which may help us distinguishing two
10  * connections between the same two hosts.
11  *
12  * GRE is defined in RFC 1701 and RFC 1702, as well as RFC 2784
13  *
14  * PPTP is built on top of a modified version of GRE, and has a mandatory
15  * field called "CallID", which serves us for the same purpose as the key
16  * field in plain GRE.
17  *
18  * Documentation about PPTP can be found in RFC 2637
19  *
20  * (C) 2000-2005 by Harald Welte <laforge@gnumonks.org>
21  *
22  * Development of this code funded by Astaro AG (http://www.astaro.com/)
23  *
24  */
25 
26 #include <linux/module.h>
27 #include <linux/types.h>
28 #include <linux/timer.h>
29 #include <linux/list.h>
30 #include <linux/seq_file.h>
31 #include <linux/in.h>
32 #include <linux/skbuff.h>
33 
34 #include <net/netfilter/nf_conntrack_l4proto.h>
35 #include <net/netfilter/nf_conntrack_helper.h>
36 #include <net/netfilter/nf_conntrack_core.h>
37 #include <linux/netfilter/nf_conntrack_proto_gre.h>
38 #include <linux/netfilter/nf_conntrack_pptp.h>
39 
40 #define GRE_TIMEOUT		(30 * HZ)
41 #define GRE_STREAM_TIMEOUT	(180 * HZ)
42 
43 #if 0
44 #define DEBUGP(format, args...)	printk(KERN_DEBUG "%s:%s: " format, __FILE__, __FUNCTION__, ## args)
45 #else
46 #define DEBUGP(x, args...)
47 #endif
48 
49 static DEFINE_RWLOCK(nf_ct_gre_lock);
50 static LIST_HEAD(gre_keymap_list);
51 
52 void nf_ct_gre_keymap_flush(void)
53 {
54 	struct list_head *pos, *n;
55 
56 	write_lock_bh(&nf_ct_gre_lock);
57 	list_for_each_safe(pos, n, &gre_keymap_list) {
58 		list_del(pos);
59 		kfree(pos);
60 	}
61 	write_unlock_bh(&nf_ct_gre_lock);
62 }
63 EXPORT_SYMBOL(nf_ct_gre_keymap_flush);
64 
65 static inline int gre_key_cmpfn(const struct nf_ct_gre_keymap *km,
66 				const struct nf_conntrack_tuple *t)
67 {
68 	return km->tuple.src.l3num == t->src.l3num &&
69 	       !memcmp(&km->tuple.src.u3, &t->src.u3, sizeof(t->src.u3)) &&
70 	       !memcmp(&km->tuple.dst.u3, &t->dst.u3, sizeof(t->dst.u3)) &&
71 	       km->tuple.dst.protonum == t->dst.protonum &&
72 	       km->tuple.dst.u.all == t->dst.u.all;
73 }
74 
75 /* look up the source key for a given tuple */
76 static __be16 gre_keymap_lookup(struct nf_conntrack_tuple *t)
77 {
78 	struct nf_ct_gre_keymap *km;
79 	__be16 key = 0;
80 
81 	read_lock_bh(&nf_ct_gre_lock);
82 	list_for_each_entry(km, &gre_keymap_list, list) {
83 		if (gre_key_cmpfn(km, t)) {
84 			key = km->tuple.src.u.gre.key;
85 			break;
86 		}
87 	}
88 	read_unlock_bh(&nf_ct_gre_lock);
89 
90 	DEBUGP("lookup src key 0x%x for ", key);
91 	NF_CT_DUMP_TUPLE(t);
92 
93 	return key;
94 }
95 
96 /* add a single keymap entry, associate with specified master ct */
97 int nf_ct_gre_keymap_add(struct nf_conn *ct, enum ip_conntrack_dir dir,
98 			 struct nf_conntrack_tuple *t)
99 {
100 	struct nf_conn_help *help = nfct_help(ct);
101 	struct nf_ct_gre_keymap **kmp, *km;
102 
103 	BUG_ON(strcmp(help->helper->name, "pptp"));
104 	kmp = &help->help.ct_pptp_info.keymap[dir];
105 	if (*kmp) {
106 		/* check whether it's a retransmission */
107 		list_for_each_entry(km, &gre_keymap_list, list) {
108 			if (gre_key_cmpfn(km, t) && km == *kmp)
109 				return 0;
110 		}
111 		DEBUGP("trying to override keymap_%s for ct %p\n",
112 			dir == IP_CT_DIR_REPLY ? "reply" : "orig", ct);
113 		return -EEXIST;
114 	}
115 
116 	km = kmalloc(sizeof(*km), GFP_ATOMIC);
117 	if (!km)
118 		return -ENOMEM;
119 	memcpy(&km->tuple, t, sizeof(*t));
120 	*kmp = km;
121 
122 	DEBUGP("adding new entry %p: ", km);
123 	NF_CT_DUMP_TUPLE(&km->tuple);
124 
125 	write_lock_bh(&nf_ct_gre_lock);
126 	list_add_tail(&km->list, &gre_keymap_list);
127 	write_unlock_bh(&nf_ct_gre_lock);
128 
129 	return 0;
130 }
131 EXPORT_SYMBOL_GPL(nf_ct_gre_keymap_add);
132 
133 /* destroy the keymap entries associated with specified master ct */
134 void nf_ct_gre_keymap_destroy(struct nf_conn *ct)
135 {
136 	struct nf_conn_help *help = nfct_help(ct);
137 	enum ip_conntrack_dir dir;
138 
139 	DEBUGP("entering for ct %p\n", ct);
140 	BUG_ON(strcmp(help->helper->name, "pptp"));
141 
142 	write_lock_bh(&nf_ct_gre_lock);
143 	for (dir = IP_CT_DIR_ORIGINAL; dir < IP_CT_DIR_MAX; dir++) {
144 		if (help->help.ct_pptp_info.keymap[dir]) {
145 			DEBUGP("removing %p from list\n",
146 				help->help.ct_pptp_info.keymap[dir]);
147 			list_del(&help->help.ct_pptp_info.keymap[dir]->list);
148 			kfree(help->help.ct_pptp_info.keymap[dir]);
149 			help->help.ct_pptp_info.keymap[dir] = NULL;
150 		}
151 	}
152 	write_unlock_bh(&nf_ct_gre_lock);
153 }
154 EXPORT_SYMBOL_GPL(nf_ct_gre_keymap_destroy);
155 
156 /* PUBLIC CONNTRACK PROTO HELPER FUNCTIONS */
157 
158 /* invert gre part of tuple */
159 static int gre_invert_tuple(struct nf_conntrack_tuple *tuple,
160 			    const struct nf_conntrack_tuple *orig)
161 {
162 	tuple->dst.u.gre.key = orig->src.u.gre.key;
163 	tuple->src.u.gre.key = orig->dst.u.gre.key;
164 	return 1;
165 }
166 
167 /* gre hdr info to tuple */
168 static int gre_pkt_to_tuple(const struct sk_buff *skb,
169 			   unsigned int dataoff,
170 			   struct nf_conntrack_tuple *tuple)
171 {
172 	struct gre_hdr_pptp _pgrehdr, *pgrehdr;
173 	__be16 srckey;
174 	struct gre_hdr _grehdr, *grehdr;
175 
176 	/* first only delinearize old RFC1701 GRE header */
177 	grehdr = skb_header_pointer(skb, dataoff, sizeof(_grehdr), &_grehdr);
178 	if (!grehdr || grehdr->version != GRE_VERSION_PPTP) {
179 		/* try to behave like "nf_conntrack_proto_generic" */
180 		tuple->src.u.all = 0;
181 		tuple->dst.u.all = 0;
182 		return 1;
183 	}
184 
185 	/* PPTP header is variable length, only need up to the call_id field */
186 	pgrehdr = skb_header_pointer(skb, dataoff, 8, &_pgrehdr);
187 	if (!pgrehdr)
188 		return 1;
189 
190 	if (ntohs(grehdr->protocol) != GRE_PROTOCOL_PPTP) {
191 		DEBUGP("GRE_VERSION_PPTP but unknown proto\n");
192 		return 0;
193 	}
194 
195 	tuple->dst.u.gre.key = pgrehdr->call_id;
196 	srckey = gre_keymap_lookup(tuple);
197 	tuple->src.u.gre.key = srckey;
198 
199 	return 1;
200 }
201 
202 /* print gre part of tuple */
203 static int gre_print_tuple(struct seq_file *s,
204 			   const struct nf_conntrack_tuple *tuple)
205 {
206 	return seq_printf(s, "srckey=0x%x dstkey=0x%x ",
207 			  ntohs(tuple->src.u.gre.key),
208 			  ntohs(tuple->dst.u.gre.key));
209 }
210 
211 /* print private data for conntrack */
212 static int gre_print_conntrack(struct seq_file *s,
213 			       const struct nf_conn *ct)
214 {
215 	return seq_printf(s, "timeout=%u, stream_timeout=%u ",
216 			  (ct->proto.gre.timeout / HZ),
217 			  (ct->proto.gre.stream_timeout / HZ));
218 }
219 
220 /* Returns verdict for packet, and may modify conntrack */
221 static int gre_packet(struct nf_conn *ct,
222 		      const struct sk_buff *skb,
223 		      unsigned int dataoff,
224 		      enum ip_conntrack_info ctinfo,
225 		      int pf,
226 		      unsigned int hooknum)
227 {
228 	/* If we've seen traffic both ways, this is a GRE connection.
229 	 * Extend timeout. */
230 	if (ct->status & IPS_SEEN_REPLY) {
231 		nf_ct_refresh_acct(ct, ctinfo, skb,
232 				   ct->proto.gre.stream_timeout);
233 		/* Also, more likely to be important, and not a probe. */
234 		set_bit(IPS_ASSURED_BIT, &ct->status);
235 		nf_conntrack_event_cache(IPCT_STATUS, skb);
236 	} else
237 		nf_ct_refresh_acct(ct, ctinfo, skb,
238 				   ct->proto.gre.timeout);
239 
240 	return NF_ACCEPT;
241 }
242 
243 /* Called when a new connection for this protocol found. */
244 static int gre_new(struct nf_conn *ct, const struct sk_buff *skb,
245 		   unsigned int dataoff)
246 {
247 	DEBUGP(": ");
248 	NF_CT_DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
249 
250 	/* initialize to sane value.  Ideally a conntrack helper
251 	 * (e.g. in case of pptp) is increasing them */
252 	ct->proto.gre.stream_timeout = GRE_STREAM_TIMEOUT;
253 	ct->proto.gre.timeout = GRE_TIMEOUT;
254 
255 	return 1;
256 }
257 
258 /* Called when a conntrack entry has already been removed from the hashes
259  * and is about to be deleted from memory */
260 static void gre_destroy(struct nf_conn *ct)
261 {
262 	struct nf_conn *master = ct->master;
263 	DEBUGP(" entering\n");
264 
265 	if (!master)
266 		DEBUGP("no master !?!\n");
267 	else
268 		nf_ct_gre_keymap_destroy(master);
269 }
270 
271 /* protocol helper struct */
272 static struct nf_conntrack_l4proto nf_conntrack_l4proto_gre4 = {
273 	.l3proto	 = AF_INET,
274 	.l4proto	 = IPPROTO_GRE,
275 	.name		 = "gre",
276 	.pkt_to_tuple	 = gre_pkt_to_tuple,
277 	.invert_tuple	 = gre_invert_tuple,
278 	.print_tuple	 = gre_print_tuple,
279 	.print_conntrack = gre_print_conntrack,
280 	.packet		 = gre_packet,
281 	.new		 = gre_new,
282 	.destroy	 = gre_destroy,
283 	.me 		 = THIS_MODULE,
284 #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
285 	.tuple_to_nfattr = nf_ct_port_tuple_to_nfattr,
286 	.nfattr_to_tuple = nf_ct_port_nfattr_to_tuple,
287 #endif
288 };
289 
290 static int __init nf_ct_proto_gre_init(void)
291 {
292 	return nf_conntrack_l4proto_register(&nf_conntrack_l4proto_gre4);
293 }
294 
295 static void nf_ct_proto_gre_fini(void)
296 {
297 	nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_gre4);
298 	nf_ct_gre_keymap_flush();
299 }
300 
301 module_init(nf_ct_proto_gre_init);
302 module_exit(nf_ct_proto_gre_fini);
303 
304 MODULE_LICENSE("GPL");
305