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