fedora core 6 1.2949 + vserver 2.2.0
[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/module.h>
27 #include <linux/types.h>
28 #include <linux/timer.h>
29 #include <linux/netfilter.h>
30 #include <linux/ip.h>
31 #include <linux/in.h>
32 #include <linux/list.h>
33 #include <linux/seq_file.h>
34 #include <linux/interrupt.h>
35
36 static DEFINE_RWLOCK(ip_ct_gre_lock);
37
38 #include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
39 #include <linux/netfilter_ipv4/ip_conntrack_helper.h>
40 #include <linux/netfilter_ipv4/ip_conntrack_core.h>
41
42 #include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h>
43 #include <linux/netfilter_ipv4/ip_conntrack_pptp.h>
44
45 MODULE_LICENSE("GPL");
46 MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
47 MODULE_DESCRIPTION("netfilter connection tracking protocol helper for GRE");
48
49 /* shamelessly stolen from ip_conntrack_proto_udp.c */
50 #define GRE_TIMEOUT             (30*HZ)
51 #define GRE_STREAM_TIMEOUT      (180*HZ)
52
53 #if 0
54 #define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s: " format, __FILE__, __FUNCTION__, ## args)
55 #define DUMP_TUPLE_GRE(x) printk("%u.%u.%u.%u:0x%x -> %u.%u.%u.%u:0x%x\n", \
56                         NIPQUAD((x)->src.ip), ntohs((x)->src.u.gre.key), \
57                         NIPQUAD((x)->dst.ip), ntohs((x)->dst.u.gre.key))
58 #else
59 #define DEBUGP(x, args...)
60 #define DUMP_TUPLE_GRE(x)
61 #endif
62
63 /* GRE KEYMAP HANDLING FUNCTIONS */
64 static LIST_HEAD(gre_keymap_list);
65
66 static inline int gre_key_cmpfn(const struct ip_ct_gre_keymap *km,
67                                 const struct ip_conntrack_tuple *t)
68 {
69         return ((km->tuple.src.ip == t->src.ip) &&
70                 (km->tuple.dst.ip == t->dst.ip) &&
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 ip_conntrack_tuple *t)
77 {
78         struct ip_ct_gre_keymap *km;
79         __be16 key = 0;
80
81         read_lock_bh(&ip_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(&ip_ct_gre_lock);
89
90         DEBUGP("lookup src key 0x%x up key for ", key);
91         DUMP_TUPLE_GRE(t);
92
93         return key;
94 }
95
96 /* add a single keymap entry, associate with specified master ct */
97 int
98 ip_ct_gre_keymap_add(struct ip_conntrack *ct,
99                      struct ip_conntrack_tuple *t, int reply)
100 {
101         struct ip_ct_gre_keymap **exist_km, *km;
102
103         if (!ct->helper || strcmp(ct->helper->name, "pptp")) {
104                 DEBUGP("refusing to add GRE keymap to non-pptp session\n");
105                 return -1;
106         }
107
108         if (!reply)
109                 exist_km = &ct->help.ct_pptp_info.keymap_orig;
110         else
111                 exist_km = &ct->help.ct_pptp_info.keymap_reply;
112
113         if (*exist_km) {
114                 /* check whether it's a retransmission */
115                 list_for_each_entry(km, &gre_keymap_list, list) {
116                         if (gre_key_cmpfn(km, t) && km == *exist_km)
117                                 return 0;
118                 }
119                 DEBUGP("trying to override keymap_%s for ct %p\n",
120                         reply? "reply":"orig", ct);
121                 return -EEXIST;
122         }
123
124         km = kmalloc(sizeof(*km), GFP_ATOMIC);
125         if (!km)
126                 return -ENOMEM;
127
128         memcpy(&km->tuple, t, sizeof(*t));
129         *exist_km = km;
130
131         DEBUGP("adding new entry %p: ", km);
132         DUMP_TUPLE_GRE(&km->tuple);
133
134         write_lock_bh(&ip_ct_gre_lock);
135         list_add_tail(&km->list, &gre_keymap_list);
136         write_unlock_bh(&ip_ct_gre_lock);
137
138         return 0;
139 }
140
141 /* destroy the keymap entries associated with specified master ct */
142 void ip_ct_gre_keymap_destroy(struct ip_conntrack *ct)
143 {
144         DEBUGP("entering for ct %p\n", ct);
145
146         if (!ct->helper || strcmp(ct->helper->name, "pptp")) {
147                 DEBUGP("refusing to destroy GRE keymap to non-pptp session\n");
148                 return;
149         }
150
151         write_lock_bh(&ip_ct_gre_lock);
152         if (ct->help.ct_pptp_info.keymap_orig) {
153                 DEBUGP("removing %p from list\n",
154                         ct->help.ct_pptp_info.keymap_orig);
155                 list_del(&ct->help.ct_pptp_info.keymap_orig->list);
156                 kfree(ct->help.ct_pptp_info.keymap_orig);
157                 ct->help.ct_pptp_info.keymap_orig = NULL;
158         }
159         if (ct->help.ct_pptp_info.keymap_reply) {
160                 DEBUGP("removing %p from list\n",
161                         ct->help.ct_pptp_info.keymap_reply);
162                 list_del(&ct->help.ct_pptp_info.keymap_reply->list);
163                 kfree(ct->help.ct_pptp_info.keymap_reply);
164                 ct->help.ct_pptp_info.keymap_reply = NULL;
165         }
166         write_unlock_bh(&ip_ct_gre_lock);
167 }
168
169
170 /* PUBLIC CONNTRACK PROTO HELPER FUNCTIONS */
171
172 /* invert gre part of tuple */
173 static int gre_invert_tuple(struct ip_conntrack_tuple *tuple,
174                             const struct ip_conntrack_tuple *orig)
175 {
176         tuple->dst.u.gre.key = orig->src.u.gre.key;
177         tuple->src.u.gre.key = orig->dst.u.gre.key;
178
179         return 1;
180 }
181
182 /* gre hdr info to tuple */
183 static int gre_pkt_to_tuple(const struct sk_buff *skb,
184                            unsigned int dataoff,
185                            struct ip_conntrack_tuple *tuple)
186 {
187         struct gre_hdr_pptp _pgrehdr, *pgrehdr;
188         __be16 srckey;
189         struct gre_hdr _grehdr, *grehdr;
190
191         /* first only delinearize old RFC1701 GRE header */
192         grehdr = skb_header_pointer(skb, dataoff, sizeof(_grehdr), &_grehdr);
193         if (!grehdr || grehdr->version != GRE_VERSION_PPTP) {
194                 /* try to behave like "ip_conntrack_proto_generic" */
195                 tuple->src.u.all = 0;
196                 tuple->dst.u.all = 0;
197                 return 1;
198         }
199
200         /* PPTP header is variable length, only need up to the call_id field */
201         pgrehdr = skb_header_pointer(skb, dataoff, 8, &_pgrehdr);
202         if (!pgrehdr)
203                 return 1;
204
205         if (ntohs(grehdr->protocol) != GRE_PROTOCOL_PPTP) {
206                 DEBUGP("GRE_VERSION_PPTP but unknown proto\n");
207                 return 0;
208         }
209
210         tuple->dst.u.gre.key = pgrehdr->call_id;
211         srckey = gre_keymap_lookup(tuple);
212         tuple->src.u.gre.key = srckey;
213
214         return 1;
215 }
216
217 /* print gre part of tuple */
218 static int gre_print_tuple(struct seq_file *s,
219                            const struct ip_conntrack_tuple *tuple)
220 {
221         return seq_printf(s, "srckey=0x%x dstkey=0x%x ",
222                           ntohs(tuple->src.u.gre.key),
223                           ntohs(tuple->dst.u.gre.key));
224 }
225
226 /* print private data for conntrack */
227 static int gre_print_conntrack(struct seq_file *s,
228                                const struct ip_conntrack *ct)
229 {
230         return seq_printf(s, "timeout=%u, stream_timeout=%u ",
231                           (ct->proto.gre.timeout / HZ),
232                           (ct->proto.gre.stream_timeout / HZ));
233 }
234
235 /* Returns verdict for packet, and may modify conntrack */
236 static int gre_packet(struct ip_conntrack *ct,
237                       const struct sk_buff *skb,
238                       enum ip_conntrack_info conntrackinfo)
239 {
240         /* If we've seen traffic both ways, this is a GRE connection.
241          * Extend timeout. */
242         if (ct->status & IPS_SEEN_REPLY) {
243                 ip_ct_refresh_acct(ct, conntrackinfo, skb,
244                                    ct->proto.gre.stream_timeout);
245                 /* Also, more likely to be important, and not a probe. */
246                 set_bit(IPS_ASSURED_BIT, &ct->status);
247                 ip_conntrack_event_cache(IPCT_STATUS, skb);
248         } else
249                 ip_ct_refresh_acct(ct, conntrackinfo, skb,
250                                    ct->proto.gre.timeout);
251
252         return NF_ACCEPT;
253 }
254
255 /* Called when a new connection for this protocol found. */
256 static int gre_new(struct ip_conntrack *ct,
257                    const struct sk_buff *skb)
258 {
259         DEBUGP(": ");
260         DUMP_TUPLE_GRE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
261
262         /* initialize to sane value.  Ideally a conntrack helper
263          * (e.g. in case of pptp) is increasing them */
264         ct->proto.gre.stream_timeout = GRE_STREAM_TIMEOUT;
265         ct->proto.gre.timeout = GRE_TIMEOUT;
266
267         return 1;
268 }
269
270 /* Called when a conntrack entry has already been removed from the hashes
271  * and is about to be deleted from memory */
272 static void gre_destroy(struct ip_conntrack *ct)
273 {
274         struct ip_conntrack *master = ct->master;
275         DEBUGP(" entering\n");
276
277         if (!master)
278                 DEBUGP("no master !?!\n");
279         else
280                 ip_ct_gre_keymap_destroy(master);
281 }
282
283 /* protocol helper struct */
284 static struct ip_conntrack_protocol gre = {
285         .proto           = IPPROTO_GRE,
286         .name            = "gre",
287         .pkt_to_tuple    = gre_pkt_to_tuple,
288         .invert_tuple    = gre_invert_tuple,
289         .print_tuple     = gre_print_tuple,
290         .print_conntrack = gre_print_conntrack,
291         .packet          = gre_packet,
292         .new             = gre_new,
293         .destroy         = gre_destroy,
294         .me              = THIS_MODULE,
295 #if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \
296     defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE)
297         .tuple_to_nfattr = ip_ct_port_tuple_to_nfattr,
298         .nfattr_to_tuple = ip_ct_port_nfattr_to_tuple,
299 #endif
300 };
301
302 /* ip_conntrack_proto_gre initialization */
303 int __init ip_ct_proto_gre_init(void)
304 {
305         return ip_conntrack_protocol_register(&gre);
306 }
307
308 /* This cannot be __exit, as it is invoked from ip_conntrack_helper_pptp.c's
309  * init() code on errors.
310  */
311 void ip_ct_proto_gre_fini(void)
312 {
313         struct list_head *pos, *n;
314
315         /* delete all keymap entries */
316         write_lock_bh(&ip_ct_gre_lock);
317         list_for_each_safe(pos, n, &gre_keymap_list) {
318                 DEBUGP("deleting keymap %p at module unload time\n", pos);
319                 list_del(pos);
320                 kfree(pos);
321         }
322         write_unlock_bh(&ip_ct_gre_lock);
323
324         ip_conntrack_protocol_unregister(&gre);
325 }
326
327 EXPORT_SYMBOL(ip_ct_gre_keymap_add);
328 EXPORT_SYMBOL(ip_ct_gre_keymap_destroy);