patch-o-matic-ng-20050810 pptp-conntrack-nat
[linux-2.6.git] / net / ipv4 / netfilter / ip_conntrack_proto_gre.c
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/config.h>
27 #include <linux/module.h>
28 #include <linux/types.h>
29 #include <linux/timer.h>
30 #include <linux/netfilter.h>
31 #include <linux/ip.h>
32 #include <linux/in.h>
33 #include <linux/list.h>
34
35 #include <linux/netfilter_ipv4/lockhelp.h>
36
37 DECLARE_RWLOCK(ip_ct_gre_lock);
38 #define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_ct_gre_lock)
39 #define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_ct_gre_lock)
40
41 #include <linux/netfilter_ipv4/listhelp.h>
42 #include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
43 #include <linux/netfilter_ipv4/ip_conntrack_helper.h>
44 #include <linux/netfilter_ipv4/ip_conntrack_core.h>
45
46 #include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h>
47 #include <linux/netfilter_ipv4/ip_conntrack_pptp.h>
48
49 MODULE_LICENSE("GPL");
50 MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
51 MODULE_DESCRIPTION("netfilter connection tracking protocol helper for GRE");
52
53 /* shamelessly stolen from ip_conntrack_proto_udp.c */
54 #define GRE_TIMEOUT             (30*HZ)
55 #define GRE_STREAM_TIMEOUT      (180*HZ)
56
57 #if 0
58 #define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s: " format, __FILE__, __FUNCTION__, ## args)
59 #define DUMP_TUPLE_GRE(x) printk("%u.%u.%u.%u:0x%x -> %u.%u.%u.%u:0x%x\n", \
60                         NIPQUAD((x)->src.ip), ntohs((x)->src.u.gre.key), \
61                         NIPQUAD((x)->dst.ip), ntohs((x)->dst.u.gre.key))
62 #else
63 #define DEBUGP(x, args...)
64 #define DUMP_TUPLE_GRE(x)
65 #endif
66                                 
67 /* GRE KEYMAP HANDLING FUNCTIONS */
68 static LIST_HEAD(gre_keymap_list);
69
70 static inline int gre_key_cmpfn(const struct ip_ct_gre_keymap *km,
71                                 const struct ip_conntrack_tuple *t)
72 {
73         return ((km->tuple.src.ip == t->src.ip) &&
74                 (km->tuple.dst.ip == t->dst.ip) &&
75                 (km->tuple.dst.protonum == t->dst.protonum) &&
76                 (km->tuple.dst.u.all == t->dst.u.all));
77 }
78
79 /* look up the source key for a given tuple */
80 static u_int32_t gre_keymap_lookup(struct ip_conntrack_tuple *t)
81 {
82         struct ip_ct_gre_keymap *km;
83         u_int32_t key = 0;
84
85         READ_LOCK(&ip_ct_gre_lock);
86         km = LIST_FIND(&gre_keymap_list, gre_key_cmpfn,
87                         struct ip_ct_gre_keymap *, t);
88         if (km)
89                 key = km->tuple.src.u.gre.key;
90         READ_UNLOCK(&ip_ct_gre_lock);
91         
92         DEBUGP("lookup src key 0x%x up key for ", key);
93         DUMP_TUPLE_GRE(t);
94
95         return key;
96 }
97
98 /* add a single keymap entry, associate with specified master ct */
99 int
100 ip_ct_gre_keymap_add(struct ip_conntrack *ct,
101                      struct ip_conntrack_tuple *t, int reply)
102 {
103         struct ip_ct_gre_keymap *km, *old;
104
105         if (!ct->helper || strcmp(ct->helper->name, "pptp")) {
106                 DEBUGP("refusing to add GRE keymap to non-pptp session\n");
107                 return -1;
108         }
109
110         km = kmalloc(sizeof(*km), GFP_ATOMIC);
111         if (!km)
112                 return -1;
113
114         /* initializing list head should be sufficient */
115         memset(km, 0, sizeof(*km));
116
117         memcpy(&km->tuple, t, sizeof(*t));
118
119         if (!reply) {
120                 if (ct->help.ct_pptp_info.keymap_orig) {
121                         kfree(km);
122
123                         /* check whether it's a retransmission */
124                         old = LIST_FIND(&gre_keymap_list, gre_key_cmpfn,
125                                         struct ip_ct_gre_keymap *, t);
126                         if (old == ct->help.ct_pptp_info.keymap_orig) {
127                                 DEBUGP("retransmission\n");
128                                 return 0;
129                         }
130
131                         DEBUGP("trying to override keymap_orig for ct %p\n",
132                                 ct);
133                         return -2;
134                 }
135                 ct->help.ct_pptp_info.keymap_orig = km;
136         } else {
137                 if (ct->help.ct_pptp_info.keymap_reply) {
138                         kfree(km);
139
140                         /* check whether it's a retransmission */
141                         old = LIST_FIND(&gre_keymap_list, gre_key_cmpfn,
142                                         struct ip_ct_gre_keymap *, t);
143                         if (old == ct->help.ct_pptp_info.keymap_reply) {
144                                 DEBUGP("retransmission\n");
145                                 return 0;
146                         }
147
148                         DEBUGP("trying to override keymap_reply for ct %p\n",
149                                 ct);
150                         return -2;
151                 }
152                 ct->help.ct_pptp_info.keymap_reply = km;
153         }
154
155         DEBUGP("adding new entry %p: ", km);
156         DUMP_TUPLE_GRE(&km->tuple);
157
158         WRITE_LOCK(&ip_ct_gre_lock);
159         list_append(&gre_keymap_list, km);
160         WRITE_UNLOCK(&ip_ct_gre_lock);
161
162         return 0;
163 }
164
165 /* destroy the keymap entries associated with specified master ct */
166 void ip_ct_gre_keymap_destroy(struct ip_conntrack *ct)
167 {
168         DEBUGP("entering for ct %p\n", ct);
169
170         if (!ct->helper || strcmp(ct->helper->name, "pptp")) {
171                 DEBUGP("refusing to destroy GRE keymap to non-pptp session\n");
172                 return;
173         }
174
175         WRITE_LOCK(&ip_ct_gre_lock);
176         if (ct->help.ct_pptp_info.keymap_orig) {
177                 DEBUGP("removing %p from list\n", 
178                         ct->help.ct_pptp_info.keymap_orig);
179                 list_del(&ct->help.ct_pptp_info.keymap_orig->list);
180                 kfree(ct->help.ct_pptp_info.keymap_orig);
181                 ct->help.ct_pptp_info.keymap_orig = NULL;
182         }
183         if (ct->help.ct_pptp_info.keymap_reply) {
184                 DEBUGP("removing %p from list\n",
185                         ct->help.ct_pptp_info.keymap_reply);
186                 list_del(&ct->help.ct_pptp_info.keymap_reply->list);
187                 kfree(ct->help.ct_pptp_info.keymap_reply);
188                 ct->help.ct_pptp_info.keymap_reply = NULL;
189         }
190         WRITE_UNLOCK(&ip_ct_gre_lock);
191 }
192
193
194 /* PUBLIC CONNTRACK PROTO HELPER FUNCTIONS */
195
196 /* invert gre part of tuple */
197 static int gre_invert_tuple(struct ip_conntrack_tuple *tuple,
198                             const struct ip_conntrack_tuple *orig)
199 {
200         tuple->dst.u.gre.key = orig->src.u.gre.key;
201         tuple->src.u.gre.key = orig->dst.u.gre.key;
202
203         return 1;
204 }
205
206 /* gre hdr info to tuple */
207 static int gre_pkt_to_tuple(const struct sk_buff *skb,
208                            unsigned int dataoff,
209                            struct ip_conntrack_tuple *tuple)
210 {
211         struct gre_hdr _grehdr, *grehdr;
212         struct gre_hdr_pptp _pgrehdr, *pgrehdr;
213         u_int32_t srckey;
214
215         grehdr = skb_header_pointer(skb, dataoff, sizeof(_grehdr), &_grehdr);
216         /* PPTP header is variable length, only need up to the call_id field */
217         pgrehdr = skb_header_pointer(skb, dataoff, 8, &_pgrehdr);
218
219         if (!grehdr || !pgrehdr)
220                 return 0;
221
222         switch (grehdr->version) {
223                 case GRE_VERSION_1701:
224                         if (!grehdr->key) {
225                                 DEBUGP("Can't track GRE without key\n");
226                                 return 0;
227                         }
228                         tuple->dst.u.gre.key = *(gre_key(grehdr));
229                         break;
230
231                 case GRE_VERSION_PPTP:
232                         if (ntohs(grehdr->protocol) != GRE_PROTOCOL_PPTP) {
233                                 DEBUGP("GRE_VERSION_PPTP but unknown proto\n");
234                                 return 0;
235                         }
236                         tuple->dst.u.gre.key = pgrehdr->call_id;
237                         break;
238
239                 default:
240                         printk(KERN_WARNING "unknown GRE version %hu\n",
241                                 grehdr->version);
242                         return 0;
243         }
244
245         srckey = gre_keymap_lookup(tuple);
246
247         tuple->src.u.gre.key = srckey;
248 #if 0
249         DEBUGP("found src key %x for tuple ", ntohs(srckey));
250         DUMP_TUPLE_GRE(tuple);
251 #endif
252
253         return 1;
254 }
255
256 /* print gre part of tuple */
257 static int gre_print_tuple(struct seq_file *s,
258                            const struct ip_conntrack_tuple *tuple)
259 {
260         return seq_printf(s, "srckey=0x%x dstkey=0x%x ", 
261                           ntohs(tuple->src.u.gre.key),
262                           ntohs(tuple->dst.u.gre.key));
263 }
264
265 /* print private data for conntrack */
266 static int gre_print_conntrack(struct seq_file *s,
267                                const struct ip_conntrack *ct)
268 {
269         return seq_printf(s, "timeout=%u, stream_timeout=%u ",
270                           (ct->proto.gre.timeout / HZ),
271                           (ct->proto.gre.stream_timeout / HZ));
272 }
273
274 /* Returns verdict for packet, and may modify conntrack */
275 static int gre_packet(struct ip_conntrack *ct,
276                       const struct sk_buff *skb,
277                       enum ip_conntrack_info conntrackinfo)
278 {
279         /* If we've seen traffic both ways, this is a GRE connection.
280          * Extend timeout. */
281         if (ct->status & IPS_SEEN_REPLY) {
282                 ip_ct_refresh_acct(ct, conntrackinfo, skb,
283                                    ct->proto.gre.stream_timeout);
284                 /* Also, more likely to be important, and not a probe. */
285                 set_bit(IPS_ASSURED_BIT, &ct->status);
286         } else
287                 ip_ct_refresh_acct(ct, conntrackinfo, skb,
288                                    ct->proto.gre.timeout);
289         
290         return NF_ACCEPT;
291 }
292
293 /* Called when a new connection for this protocol found. */
294 static int gre_new(struct ip_conntrack *ct,
295                    const struct sk_buff *skb)
296
297         DEBUGP(": ");
298         DUMP_TUPLE_GRE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
299
300         /* initialize to sane value.  Ideally a conntrack helper
301          * (e.g. in case of pptp) is increasing them */
302         ct->proto.gre.stream_timeout = GRE_STREAM_TIMEOUT;
303         ct->proto.gre.timeout = GRE_TIMEOUT;
304
305         return 1;
306 }
307
308 /* Called when a conntrack entry has already been removed from the hashes
309  * and is about to be deleted from memory */
310 static void gre_destroy(struct ip_conntrack *ct)
311 {
312         struct ip_conntrack *master = ct->master;
313         DEBUGP(" entering\n");
314
315         if (!master)
316                 DEBUGP("no master !?!\n");
317         else
318                 ip_ct_gre_keymap_destroy(master);
319 }
320
321 /* protocol helper struct */
322 static struct ip_conntrack_protocol gre = { 
323         .proto           = IPPROTO_GRE,
324         .name            = "gre", 
325         .pkt_to_tuple    = gre_pkt_to_tuple,
326         .invert_tuple    = gre_invert_tuple,
327         .print_tuple     = gre_print_tuple,
328         .print_conntrack = gre_print_conntrack,
329         .packet          = gre_packet,
330         .new             = gre_new,
331         .destroy         = gre_destroy,
332         .me              = THIS_MODULE
333 };
334
335 /* ip_conntrack_proto_gre initialization */
336 static int __init init(void)
337 {
338         int retcode;
339
340         if ((retcode = ip_conntrack_protocol_register(&gre))) {
341                 printk(KERN_ERR "Unable to register conntrack protocol "
342                        "helper for gre: %d\n", retcode);
343                 return -EIO;
344         }
345
346         return 0;
347 }
348
349 static void __exit fini(void)
350 {
351         struct list_head *pos, *n;
352
353         /* delete all keymap entries */
354         WRITE_LOCK(&ip_ct_gre_lock);
355         list_for_each_safe(pos, n, &gre_keymap_list) {
356                 DEBUGP("deleting keymap %p at module unload time\n", pos);
357                 list_del(pos);
358                 kfree(pos);
359         }
360         WRITE_UNLOCK(&ip_ct_gre_lock);
361
362         ip_conntrack_protocol_unregister(&gre); 
363 }
364
365 EXPORT_SYMBOL(ip_ct_gre_keymap_add);
366 EXPORT_SYMBOL(ip_ct_gre_keymap_destroy);
367
368 module_init(init);
369 module_exit(fini);