[NETFILTER]: nf_conntrack: split out protocol handling
[powerpc.git] / net / netfilter / nf_conntrack_proto.c
1 /* L3/L4 protocol support for nf_conntrack. */
2
3 /* (C) 1999-2001 Paul `Rusty' Russell
4  * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
5  * (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.
10  */
11
12 #include <linux/types.h>
13 #include <linux/netfilter.h>
14 #include <linux/module.h>
15 #include <linux/skbuff.h>
16 #include <linux/vmalloc.h>
17 #include <linux/stddef.h>
18 #include <linux/err.h>
19 #include <linux/percpu.h>
20 #include <linux/moduleparam.h>
21 #include <linux/notifier.h>
22 #include <linux/kernel.h>
23 #include <linux/netdevice.h>
24
25 #include <net/netfilter/nf_conntrack.h>
26 #include <net/netfilter/nf_conntrack_l3proto.h>
27 #include <net/netfilter/nf_conntrack_protocol.h>
28 #include <net/netfilter/nf_conntrack_core.h>
29
30 struct nf_conntrack_protocol **nf_ct_protos[PF_MAX] __read_mostly;
31 struct nf_conntrack_l3proto *nf_ct_l3protos[PF_MAX] __read_mostly;
32
33 struct nf_conntrack_protocol *
34 __nf_ct_proto_find(u_int16_t l3proto, u_int8_t protocol)
35 {
36         if (unlikely(l3proto >= AF_MAX || nf_ct_protos[l3proto] == NULL))
37                 return &nf_conntrack_generic_protocol;
38
39         return nf_ct_protos[l3proto][protocol];
40 }
41
42 /* this is guaranteed to always return a valid protocol helper, since
43  * it falls back to generic_protocol */
44 struct nf_conntrack_protocol *
45 nf_ct_proto_find_get(u_int16_t l3proto, u_int8_t protocol)
46 {
47         struct nf_conntrack_protocol *p;
48
49         preempt_disable();
50         p = __nf_ct_proto_find(l3proto, protocol);
51         if (!try_module_get(p->me))
52                 p = &nf_conntrack_generic_protocol;
53         preempt_enable();
54
55         return p;
56 }
57
58 void nf_ct_proto_put(struct nf_conntrack_protocol *p)
59 {
60         module_put(p->me);
61 }
62
63 struct nf_conntrack_l3proto *
64 nf_ct_l3proto_find_get(u_int16_t l3proto)
65 {
66         struct nf_conntrack_l3proto *p;
67
68         preempt_disable();
69         p = __nf_ct_l3proto_find(l3proto);
70         if (!try_module_get(p->me))
71                 p = &nf_conntrack_generic_l3proto;
72         preempt_enable();
73
74         return p;
75 }
76
77 void nf_ct_l3proto_put(struct nf_conntrack_l3proto *p)
78 {
79         module_put(p->me);
80 }
81
82 int
83 nf_ct_l3proto_try_module_get(unsigned short l3proto)
84 {
85         int ret;
86         struct nf_conntrack_l3proto *p;
87
88 retry:  p = nf_ct_l3proto_find_get(l3proto);
89         if (p == &nf_conntrack_generic_l3proto) {
90                 ret = request_module("nf_conntrack-%d", l3proto);
91                 if (!ret)
92                         goto retry;
93
94                 return -EPROTOTYPE;
95         }
96
97         return 0;
98 }
99
100 void nf_ct_l3proto_module_put(unsigned short l3proto)
101 {
102         struct nf_conntrack_l3proto *p;
103
104         preempt_disable();
105         p = __nf_ct_l3proto_find(l3proto);
106         preempt_enable();
107
108         module_put(p->me);
109 }
110
111 static int kill_l3proto(struct nf_conn *i, void *data)
112 {
113         return (i->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num ==
114                         ((struct nf_conntrack_l3proto *)data)->l3proto);
115 }
116
117 static int kill_proto(struct nf_conn *i, void *data)
118 {
119         struct nf_conntrack_protocol *proto;
120         proto = (struct nf_conntrack_protocol *)data;
121         return (i->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum ==
122                         proto->proto) &&
123                (i->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num ==
124                         proto->l3proto);
125 }
126
127 int nf_conntrack_l3proto_register(struct nf_conntrack_l3proto *proto)
128 {
129         int ret = 0;
130
131         write_lock_bh(&nf_conntrack_lock);
132         if (nf_ct_l3protos[proto->l3proto] != &nf_conntrack_generic_l3proto) {
133                 ret = -EBUSY;
134                 goto out;
135         }
136         nf_ct_l3protos[proto->l3proto] = proto;
137 out:
138         write_unlock_bh(&nf_conntrack_lock);
139
140         return ret;
141 }
142
143 void nf_conntrack_l3proto_unregister(struct nf_conntrack_l3proto *proto)
144 {
145         write_lock_bh(&nf_conntrack_lock);
146         nf_ct_l3protos[proto->l3proto] = &nf_conntrack_generic_l3proto;
147         write_unlock_bh(&nf_conntrack_lock);
148
149         /* Somebody could be still looking at the proto in bh. */
150         synchronize_net();
151
152         /* Remove all contrack entries for this protocol */
153         nf_ct_iterate_cleanup(kill_l3proto, proto);
154 }
155
156 /* FIXME: Allow NULL functions and sub in pointers to generic for
157    them. --RR */
158 int nf_conntrack_protocol_register(struct nf_conntrack_protocol *proto)
159 {
160         int ret = 0;
161
162 retry:
163         write_lock_bh(&nf_conntrack_lock);
164         if (nf_ct_protos[proto->l3proto]) {
165                 if (nf_ct_protos[proto->l3proto][proto->proto]
166                                 != &nf_conntrack_generic_protocol) {
167                         ret = -EBUSY;
168                         goto out_unlock;
169                 }
170         } else {
171                 /* l3proto may be loaded latter. */
172                 struct nf_conntrack_protocol **proto_array;
173                 int i;
174
175                 write_unlock_bh(&nf_conntrack_lock);
176
177                 proto_array = (struct nf_conntrack_protocol **)
178                                 kmalloc(MAX_NF_CT_PROTO *
179                                          sizeof(struct nf_conntrack_protocol *),
180                                         GFP_KERNEL);
181                 if (proto_array == NULL) {
182                         ret = -ENOMEM;
183                         goto out;
184                 }
185                 for (i = 0; i < MAX_NF_CT_PROTO; i++)
186                         proto_array[i] = &nf_conntrack_generic_protocol;
187
188                 write_lock_bh(&nf_conntrack_lock);
189                 if (nf_ct_protos[proto->l3proto]) {
190                         /* bad timing, but no problem */
191                         write_unlock_bh(&nf_conntrack_lock);
192                         kfree(proto_array);
193                 } else {
194                         nf_ct_protos[proto->l3proto] = proto_array;
195                         write_unlock_bh(&nf_conntrack_lock);
196                 }
197
198                 /*
199                  * Just once because array is never freed until unloading
200                  * nf_conntrack.ko
201                  */
202                 goto retry;
203         }
204
205         nf_ct_protos[proto->l3proto][proto->proto] = proto;
206
207 out_unlock:
208         write_unlock_bh(&nf_conntrack_lock);
209 out:
210         return ret;
211 }
212
213 void nf_conntrack_protocol_unregister(struct nf_conntrack_protocol *proto)
214 {
215         write_lock_bh(&nf_conntrack_lock);
216         nf_ct_protos[proto->l3proto][proto->proto]
217                 = &nf_conntrack_generic_protocol;
218         write_unlock_bh(&nf_conntrack_lock);
219
220         /* Somebody could be still looking at the proto in bh. */
221         synchronize_net();
222
223         /* Remove all contrack entries for this protocol */
224         nf_ct_iterate_cleanup(kill_proto, proto);
225 }