]>
Commit | Line | Data |
---|---|---|
080774a2 HW |
1 | /* Connection tracking via netlink socket. Allows for user space |
2 | * protocol helpers and general trouble making from userspace. | |
3 | * | |
4 | * (C) 2001 by Jay Schulist <jschlst@samba.org> | |
5 | * (C) 2002-2005 by Harald Welte <laforge@gnumonks.org> | |
6 | * (C) 2003 by Patrick Mchardy <kaber@trash.net> | |
1cde6436 | 7 | * (C) 2005-2006 by Pablo Neira Ayuso <pablo@eurodev.net> |
080774a2 HW |
8 | * |
9 | * I've reworked this stuff to use attributes instead of conntrack | |
10 | * structures. 5.44 am. I need more tea. --pablo 05/07/11. | |
11 | * | |
12 | * Initial connection tracking via netlink development funded and | |
13 | * generally made possible by Network Robots, Inc. (www.networkrobots.com) | |
14 | * | |
15 | * Further development of this code funded by Astaro AG (http://www.astaro.com) | |
16 | * | |
17 | * This software may be used and distributed according to the terms | |
18 | * of the GNU General Public License, incorporated herein by reference. | |
19 | */ | |
20 | ||
21 | #include <linux/init.h> | |
22 | #include <linux/module.h> | |
23 | #include <linux/kernel.h> | |
24 | #include <linux/types.h> | |
25 | #include <linux/timer.h> | |
26 | #include <linux/skbuff.h> | |
27 | #include <linux/errno.h> | |
28 | #include <linux/netlink.h> | |
29 | #include <linux/spinlock.h> | |
de919820 | 30 | #include <linux/interrupt.h> |
080774a2 | 31 | #include <linux/notifier.h> |
080774a2 HW |
32 | |
33 | #include <linux/netfilter.h> | |
080774a2 HW |
34 | #include <linux/netfilter_ipv4/ip_conntrack.h> |
35 | #include <linux/netfilter_ipv4/ip_conntrack_core.h> | |
36 | #include <linux/netfilter_ipv4/ip_conntrack_helper.h> | |
37 | #include <linux/netfilter_ipv4/ip_conntrack_protocol.h> | |
38 | #include <linux/netfilter_ipv4/ip_nat_protocol.h> | |
39 | ||
40 | #include <linux/netfilter/nfnetlink.h> | |
41 | #include <linux/netfilter/nfnetlink_conntrack.h> | |
42 | ||
43 | MODULE_LICENSE("GPL"); | |
44 | ||
45 | static char __initdata version[] = "0.90"; | |
46 | ||
080774a2 HW |
47 | static inline int |
48 | ctnetlink_dump_tuples_proto(struct sk_buff *skb, | |
1cde6436 PNA |
49 | const struct ip_conntrack_tuple *tuple, |
50 | struct ip_conntrack_protocol *proto) | |
080774a2 | 51 | { |
eaae4fa4 | 52 | int ret = 0; |
1cde6436 | 53 | struct nfattr *nest_parms = NFA_NEST(skb, CTA_TUPLE_PROTO); |
080774a2 HW |
54 | |
55 | NFA_PUT(skb, CTA_PROTO_NUM, sizeof(u_int8_t), &tuple->dst.protonum); | |
56 | ||
00cb277a | 57 | if (likely(proto->tuple_to_nfattr)) |
eaae4fa4 | 58 | ret = proto->tuple_to_nfattr(skb, tuple); |
00cb277a | 59 | |
1cde6436 | 60 | NFA_NEST_END(skb, nest_parms); |
080774a2 | 61 | |
eaae4fa4 | 62 | return ret; |
080774a2 HW |
63 | |
64 | nfattr_failure: | |
65 | return -1; | |
66 | } | |
67 | ||
68 | static inline int | |
1cde6436 PNA |
69 | ctnetlink_dump_tuples_ip(struct sk_buff *skb, |
70 | const struct ip_conntrack_tuple *tuple) | |
080774a2 | 71 | { |
1cde6436 | 72 | struct nfattr *nest_parms = NFA_NEST(skb, CTA_TUPLE_IP); |
080774a2 | 73 | |
cdcb71bf AV |
74 | NFA_PUT(skb, CTA_IP_V4_SRC, sizeof(__be32), &tuple->src.ip); |
75 | NFA_PUT(skb, CTA_IP_V4_DST, sizeof(__be32), &tuple->dst.ip); | |
080774a2 | 76 | |
080774a2 HW |
77 | NFA_NEST_END(skb, nest_parms); |
78 | ||
1cde6436 | 79 | return 0; |
080774a2 HW |
80 | |
81 | nfattr_failure: | |
82 | return -1; | |
83 | } | |
84 | ||
1cde6436 PNA |
85 | static inline int |
86 | ctnetlink_dump_tuples(struct sk_buff *skb, | |
87 | const struct ip_conntrack_tuple *tuple) | |
88 | { | |
89 | int ret; | |
90 | struct ip_conntrack_protocol *proto; | |
91 | ||
92 | ret = ctnetlink_dump_tuples_ip(skb, tuple); | |
93 | if (unlikely(ret < 0)) | |
94 | return ret; | |
95 | ||
96 | proto = ip_conntrack_proto_find_get(tuple->dst.protonum); | |
97 | ret = ctnetlink_dump_tuples_proto(skb, tuple, proto); | |
98 | ip_conntrack_proto_put(proto); | |
99 | ||
100 | return ret; | |
101 | } | |
102 | ||
080774a2 HW |
103 | static inline int |
104 | ctnetlink_dump_status(struct sk_buff *skb, const struct ip_conntrack *ct) | |
105 | { | |
cdcb71bf | 106 | __be32 status = htonl((u_int32_t) ct->status); |
080774a2 HW |
107 | NFA_PUT(skb, CTA_STATUS, sizeof(status), &status); |
108 | return 0; | |
109 | ||
110 | nfattr_failure: | |
111 | return -1; | |
112 | } | |
113 | ||
114 | static inline int | |
115 | ctnetlink_dump_timeout(struct sk_buff *skb, const struct ip_conntrack *ct) | |
116 | { | |
117 | long timeout_l = ct->timeout.expires - jiffies; | |
cdcb71bf | 118 | __be32 timeout; |
080774a2 HW |
119 | |
120 | if (timeout_l < 0) | |
121 | timeout = 0; | |
122 | else | |
123 | timeout = htonl(timeout_l / HZ); | |
124 | ||
125 | NFA_PUT(skb, CTA_TIMEOUT, sizeof(timeout), &timeout); | |
126 | return 0; | |
127 | ||
128 | nfattr_failure: | |
129 | return -1; | |
130 | } | |
131 | ||
132 | static inline int | |
133 | ctnetlink_dump_protoinfo(struct sk_buff *skb, const struct ip_conntrack *ct) | |
134 | { | |
135 | struct ip_conntrack_protocol *proto = ip_conntrack_proto_find_get(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum); | |
136 | ||
137 | struct nfattr *nest_proto; | |
138 | int ret; | |
00cb277a PNA |
139 | |
140 | if (!proto->to_nfattr) { | |
141 | ip_conntrack_proto_put(proto); | |
080774a2 | 142 | return 0; |
00cb277a | 143 | } |
080774a2 HW |
144 | |
145 | nest_proto = NFA_NEST(skb, CTA_PROTOINFO); | |
146 | ||
147 | ret = proto->to_nfattr(skb, nest_proto, ct); | |
148 | ||
149 | ip_conntrack_proto_put(proto); | |
150 | ||
151 | NFA_NEST_END(skb, nest_proto); | |
152 | ||
153 | return ret; | |
154 | ||
155 | nfattr_failure: | |
c537b75a | 156 | ip_conntrack_proto_put(proto); |
080774a2 HW |
157 | return -1; |
158 | } | |
159 | ||
160 | static inline int | |
161 | ctnetlink_dump_helpinfo(struct sk_buff *skb, const struct ip_conntrack *ct) | |
162 | { | |
163 | struct nfattr *nest_helper; | |
164 | ||
165 | if (!ct->helper) | |
166 | return 0; | |
167 | ||
168 | nest_helper = NFA_NEST(skb, CTA_HELP); | |
a9b305c4 | 169 | NFA_PUT(skb, CTA_HELP_NAME, strlen(ct->helper->name), ct->helper->name); |
080774a2 HW |
170 | |
171 | if (ct->helper->to_nfattr) | |
172 | ct->helper->to_nfattr(skb, ct); | |
173 | ||
174 | NFA_NEST_END(skb, nest_helper); | |
175 | ||
176 | return 0; | |
177 | ||
178 | nfattr_failure: | |
179 | return -1; | |
180 | } | |
181 | ||
182 | #ifdef CONFIG_IP_NF_CT_ACCT | |
183 | static inline int | |
184 | ctnetlink_dump_counters(struct sk_buff *skb, const struct ip_conntrack *ct, | |
185 | enum ip_conntrack_dir dir) | |
186 | { | |
187 | enum ctattr_type type = dir ? CTA_COUNTERS_REPLY: CTA_COUNTERS_ORIG; | |
188 | struct nfattr *nest_count = NFA_NEST(skb, type); | |
cdcb71bf | 189 | __be32 tmp; |
080774a2 | 190 | |
a051a8f7 | 191 | tmp = htonl(ct->counters[dir].packets); |
cdcb71bf | 192 | NFA_PUT(skb, CTA_COUNTERS32_PACKETS, sizeof(__be32), &tmp); |
080774a2 | 193 | |
a051a8f7 | 194 | tmp = htonl(ct->counters[dir].bytes); |
cdcb71bf | 195 | NFA_PUT(skb, CTA_COUNTERS32_BYTES, sizeof(__be32), &tmp); |
080774a2 HW |
196 | |
197 | NFA_NEST_END(skb, nest_count); | |
198 | ||
199 | return 0; | |
200 | ||
201 | nfattr_failure: | |
202 | return -1; | |
203 | } | |
204 | #else | |
205 | #define ctnetlink_dump_counters(a, b, c) (0) | |
206 | #endif | |
207 | ||
208 | #ifdef CONFIG_IP_NF_CONNTRACK_MARK | |
209 | static inline int | |
210 | ctnetlink_dump_mark(struct sk_buff *skb, const struct ip_conntrack *ct) | |
211 | { | |
cdcb71bf | 212 | __be32 mark = htonl(ct->mark); |
080774a2 | 213 | |
cdcb71bf | 214 | NFA_PUT(skb, CTA_MARK, sizeof(__be32), &mark); |
080774a2 HW |
215 | return 0; |
216 | ||
217 | nfattr_failure: | |
218 | return -1; | |
219 | } | |
220 | #else | |
221 | #define ctnetlink_dump_mark(a, b) (0) | |
222 | #endif | |
223 | ||
224 | static inline int | |
225 | ctnetlink_dump_id(struct sk_buff *skb, const struct ip_conntrack *ct) | |
226 | { | |
cdcb71bf AV |
227 | __be32 id = htonl(ct->id); |
228 | NFA_PUT(skb, CTA_ID, sizeof(__be32), &id); | |
080774a2 HW |
229 | return 0; |
230 | ||
231 | nfattr_failure: | |
232 | return -1; | |
233 | } | |
234 | ||
235 | static inline int | |
236 | ctnetlink_dump_use(struct sk_buff *skb, const struct ip_conntrack *ct) | |
237 | { | |
cdcb71bf | 238 | __be32 use = htonl(atomic_read(&ct->ct_general.use)); |
080774a2 | 239 | |
cdcb71bf | 240 | NFA_PUT(skb, CTA_USE, sizeof(__be32), &use); |
080774a2 HW |
241 | return 0; |
242 | ||
243 | nfattr_failure: | |
244 | return -1; | |
245 | } | |
246 | ||
247 | #define tuple(ct, dir) (&(ct)->tuplehash[dir].tuple) | |
248 | ||
249 | static int | |
250 | ctnetlink_fill_info(struct sk_buff *skb, u32 pid, u32 seq, | |
251 | int event, int nowait, | |
252 | const struct ip_conntrack *ct) | |
253 | { | |
254 | struct nlmsghdr *nlh; | |
255 | struct nfgenmsg *nfmsg; | |
256 | struct nfattr *nest_parms; | |
257 | unsigned char *b; | |
258 | ||
259 | b = skb->tail; | |
260 | ||
261 | event |= NFNL_SUBSYS_CTNETLINK << 8; | |
262 | nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(struct nfgenmsg)); | |
263 | nfmsg = NLMSG_DATA(nlh); | |
264 | ||
265 | nlh->nlmsg_flags = (nowait && pid) ? NLM_F_MULTI : 0; | |
266 | nfmsg->nfgen_family = AF_INET; | |
267 | nfmsg->version = NFNETLINK_V0; | |
268 | nfmsg->res_id = 0; | |
269 | ||
270 | nest_parms = NFA_NEST(skb, CTA_TUPLE_ORIG); | |
271 | if (ctnetlink_dump_tuples(skb, tuple(ct, IP_CT_DIR_ORIGINAL)) < 0) | |
272 | goto nfattr_failure; | |
273 | NFA_NEST_END(skb, nest_parms); | |
274 | ||
275 | nest_parms = NFA_NEST(skb, CTA_TUPLE_REPLY); | |
276 | if (ctnetlink_dump_tuples(skb, tuple(ct, IP_CT_DIR_REPLY)) < 0) | |
277 | goto nfattr_failure; | |
278 | NFA_NEST_END(skb, nest_parms); | |
279 | ||
280 | if (ctnetlink_dump_status(skb, ct) < 0 || | |
281 | ctnetlink_dump_timeout(skb, ct) < 0 || | |
282 | ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 || | |
283 | ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0 || | |
284 | ctnetlink_dump_protoinfo(skb, ct) < 0 || | |
285 | ctnetlink_dump_helpinfo(skb, ct) < 0 || | |
286 | ctnetlink_dump_mark(skb, ct) < 0 || | |
287 | ctnetlink_dump_id(skb, ct) < 0 || | |
288 | ctnetlink_dump_use(skb, ct) < 0) | |
289 | goto nfattr_failure; | |
290 | ||
291 | nlh->nlmsg_len = skb->tail - b; | |
292 | return skb->len; | |
293 | ||
294 | nlmsg_failure: | |
295 | nfattr_failure: | |
296 | skb_trim(skb, b - skb->data); | |
297 | return -1; | |
298 | } | |
299 | ||
300 | #ifdef CONFIG_IP_NF_CONNTRACK_EVENTS | |
301 | static int ctnetlink_conntrack_event(struct notifier_block *this, | |
302 | unsigned long events, void *ptr) | |
303 | { | |
304 | struct nlmsghdr *nlh; | |
305 | struct nfgenmsg *nfmsg; | |
306 | struct nfattr *nest_parms; | |
307 | struct ip_conntrack *ct = (struct ip_conntrack *)ptr; | |
308 | struct sk_buff *skb; | |
309 | unsigned int type; | |
310 | unsigned char *b; | |
ac6d439d | 311 | unsigned int flags = 0, group; |
080774a2 HW |
312 | |
313 | /* ignore our fake conntrack entry */ | |
314 | if (ct == &ip_conntrack_untracked) | |
315 | return NOTIFY_DONE; | |
316 | ||
317 | if (events & IPCT_DESTROY) { | |
318 | type = IPCTNL_MSG_CT_DELETE; | |
ac6d439d | 319 | group = NFNLGRP_CONNTRACK_DESTROY; |
0368309c | 320 | } else if (events & (IPCT_NEW | IPCT_RELATED)) { |
080774a2 HW |
321 | type = IPCTNL_MSG_CT_NEW; |
322 | flags = NLM_F_CREATE|NLM_F_EXCL; | |
323 | /* dump everything */ | |
324 | events = ~0UL; | |
ac6d439d | 325 | group = NFNLGRP_CONNTRACK_NEW; |
1a31526b | 326 | } else if (events & (IPCT_STATUS | IPCT_PROTOINFO)) { |
080774a2 | 327 | type = IPCTNL_MSG_CT_NEW; |
ac6d439d | 328 | group = NFNLGRP_CONNTRACK_UPDATE; |
0368309c PNA |
329 | } else |
330 | return NOTIFY_DONE; | |
a2427692 PM |
331 | |
332 | if (!nfnetlink_has_listeners(group)) | |
333 | return NOTIFY_DONE; | |
334 | ||
080774a2 HW |
335 | skb = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC); |
336 | if (!skb) | |
337 | return NOTIFY_DONE; | |
338 | ||
339 | b = skb->tail; | |
340 | ||
341 | type |= NFNL_SUBSYS_CTNETLINK << 8; | |
342 | nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(struct nfgenmsg)); | |
343 | nfmsg = NLMSG_DATA(nlh); | |
344 | ||
345 | nlh->nlmsg_flags = flags; | |
346 | nfmsg->nfgen_family = AF_INET; | |
347 | nfmsg->version = NFNETLINK_V0; | |
348 | nfmsg->res_id = 0; | |
349 | ||
350 | nest_parms = NFA_NEST(skb, CTA_TUPLE_ORIG); | |
351 | if (ctnetlink_dump_tuples(skb, tuple(ct, IP_CT_DIR_ORIGINAL)) < 0) | |
352 | goto nfattr_failure; | |
353 | NFA_NEST_END(skb, nest_parms); | |
354 | ||
355 | nest_parms = NFA_NEST(skb, CTA_TUPLE_REPLY); | |
356 | if (ctnetlink_dump_tuples(skb, tuple(ct, IP_CT_DIR_REPLY)) < 0) | |
357 | goto nfattr_failure; | |
358 | NFA_NEST_END(skb, nest_parms); | |
359 | ||
360 | /* NAT stuff is now a status flag */ | |
361 | if ((events & IPCT_STATUS || events & IPCT_NATINFO) | |
362 | && ctnetlink_dump_status(skb, ct) < 0) | |
363 | goto nfattr_failure; | |
364 | if (events & IPCT_REFRESH | |
365 | && ctnetlink_dump_timeout(skb, ct) < 0) | |
366 | goto nfattr_failure; | |
367 | if (events & IPCT_PROTOINFO | |
368 | && ctnetlink_dump_protoinfo(skb, ct) < 0) | |
369 | goto nfattr_failure; | |
370 | if (events & IPCT_HELPINFO | |
371 | && ctnetlink_dump_helpinfo(skb, ct) < 0) | |
372 | goto nfattr_failure; | |
373 | ||
374 | if (ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 || | |
375 | ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0) | |
376 | goto nfattr_failure; | |
377 | ||
b9a37e0c PNA |
378 | if (events & IPCT_MARK |
379 | && ctnetlink_dump_mark(skb, ct) < 0) | |
380 | goto nfattr_failure; | |
381 | ||
080774a2 | 382 | nlh->nlmsg_len = skb->tail - b; |
ac6d439d | 383 | nfnetlink_send(skb, 0, group, 0); |
080774a2 HW |
384 | return NOTIFY_DONE; |
385 | ||
386 | nlmsg_failure: | |
387 | nfattr_failure: | |
388 | kfree_skb(skb); | |
389 | return NOTIFY_DONE; | |
390 | } | |
391 | #endif /* CONFIG_IP_NF_CONNTRACK_EVENTS */ | |
392 | ||
393 | static int ctnetlink_done(struct netlink_callback *cb) | |
394 | { | |
89f2e218 PM |
395 | if (cb->args[1]) |
396 | ip_conntrack_put((struct ip_conntrack *)cb->args[1]); | |
080774a2 HW |
397 | return 0; |
398 | } | |
399 | ||
400 | static int | |
401 | ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb) | |
402 | { | |
89f2e218 | 403 | struct ip_conntrack *ct, *last; |
080774a2 HW |
404 | struct ip_conntrack_tuple_hash *h; |
405 | struct list_head *i; | |
080774a2 | 406 | |
080774a2 | 407 | read_lock_bh(&ip_conntrack_lock); |
d205dc40 | 408 | last = (struct ip_conntrack *)cb->args[1]; |
89f2e218 PM |
409 | for (; cb->args[0] < ip_conntrack_htable_size; cb->args[0]++) { |
410 | restart: | |
ff21d577 | 411 | list_for_each_prev(i, &ip_conntrack_hash[cb->args[0]]) { |
080774a2 HW |
412 | h = (struct ip_conntrack_tuple_hash *) i; |
413 | if (DIRECTION(h) != IP_CT_DIR_ORIGINAL) | |
414 | continue; | |
415 | ct = tuplehash_to_ctrack(h); | |
d205dc40 PM |
416 | if (cb->args[1]) { |
417 | if (ct != last) | |
89f2e218 | 418 | continue; |
d205dc40 | 419 | cb->args[1] = 0; |
89f2e218 | 420 | } |
080774a2 HW |
421 | if (ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).pid, |
422 | cb->nlh->nlmsg_seq, | |
423 | IPCTNL_MSG_CT_NEW, | |
89f2e218 PM |
424 | 1, ct) < 0) { |
425 | nf_conntrack_get(&ct->ct_general); | |
426 | cb->args[1] = (unsigned long)ct; | |
080774a2 | 427 | goto out; |
89f2e218 | 428 | } |
01f34848 PNA |
429 | #ifdef CONFIG_NF_CT_ACCT |
430 | if (NFNL_MSG_TYPE(cb->nlh->nlmsg_type) == | |
431 | IPCTNL_MSG_CT_GET_CTRZERO) | |
432 | memset(&ct->counters, 0, sizeof(ct->counters)); | |
433 | #endif | |
89f2e218 | 434 | } |
d205dc40 | 435 | if (cb->args[1]) { |
89f2e218 PM |
436 | cb->args[1] = 0; |
437 | goto restart; | |
080774a2 HW |
438 | } |
439 | } | |
89f2e218 | 440 | out: |
080774a2 | 441 | read_unlock_bh(&ip_conntrack_lock); |
d205dc40 PM |
442 | if (last) |
443 | ip_conntrack_put(last); | |
080774a2 | 444 | |
080774a2 HW |
445 | return skb->len; |
446 | } | |
447 | ||
dbd36ea4 | 448 | static const size_t cta_min_ip[CTA_IP_MAX] = { |
cdcb71bf AV |
449 | [CTA_IP_V4_SRC-1] = sizeof(__be32), |
450 | [CTA_IP_V4_DST-1] = sizeof(__be32), | |
080774a2 HW |
451 | }; |
452 | ||
453 | static inline int | |
454 | ctnetlink_parse_tuple_ip(struct nfattr *attr, struct ip_conntrack_tuple *tuple) | |
455 | { | |
456 | struct nfattr *tb[CTA_IP_MAX]; | |
457 | ||
a2506c04 | 458 | nfattr_parse_nested(tb, CTA_IP_MAX, attr); |
080774a2 HW |
459 | |
460 | if (nfattr_bad_size(tb, CTA_IP_MAX, cta_min_ip)) | |
461 | return -EINVAL; | |
462 | ||
463 | if (!tb[CTA_IP_V4_SRC-1]) | |
464 | return -EINVAL; | |
cdcb71bf | 465 | tuple->src.ip = *(__be32 *)NFA_DATA(tb[CTA_IP_V4_SRC-1]); |
080774a2 HW |
466 | |
467 | if (!tb[CTA_IP_V4_DST-1]) | |
468 | return -EINVAL; | |
cdcb71bf | 469 | tuple->dst.ip = *(__be32 *)NFA_DATA(tb[CTA_IP_V4_DST-1]); |
080774a2 | 470 | |
080774a2 | 471 | return 0; |
080774a2 HW |
472 | } |
473 | ||
dbd36ea4 | 474 | static const size_t cta_min_proto[CTA_PROTO_MAX] = { |
0be7fa92 | 475 | [CTA_PROTO_NUM-1] = sizeof(u_int8_t), |
080774a2 HW |
476 | [CTA_PROTO_SRC_PORT-1] = sizeof(u_int16_t), |
477 | [CTA_PROTO_DST_PORT-1] = sizeof(u_int16_t), | |
478 | [CTA_PROTO_ICMP_TYPE-1] = sizeof(u_int8_t), | |
479 | [CTA_PROTO_ICMP_CODE-1] = sizeof(u_int8_t), | |
480 | [CTA_PROTO_ICMP_ID-1] = sizeof(u_int16_t), | |
481 | }; | |
482 | ||
483 | static inline int | |
484 | ctnetlink_parse_tuple_proto(struct nfattr *attr, | |
485 | struct ip_conntrack_tuple *tuple) | |
486 | { | |
487 | struct nfattr *tb[CTA_PROTO_MAX]; | |
488 | struct ip_conntrack_protocol *proto; | |
489 | int ret = 0; | |
490 | ||
a2506c04 | 491 | nfattr_parse_nested(tb, CTA_PROTO_MAX, attr); |
080774a2 HW |
492 | |
493 | if (nfattr_bad_size(tb, CTA_PROTO_MAX, cta_min_proto)) | |
494 | return -EINVAL; | |
495 | ||
496 | if (!tb[CTA_PROTO_NUM-1]) | |
497 | return -EINVAL; | |
0be7fa92 | 498 | tuple->dst.protonum = *(u_int8_t *)NFA_DATA(tb[CTA_PROTO_NUM-1]); |
080774a2 HW |
499 | |
500 | proto = ip_conntrack_proto_find_get(tuple->dst.protonum); | |
501 | ||
00cb277a | 502 | if (likely(proto->nfattr_to_tuple)) |
080774a2 | 503 | ret = proto->nfattr_to_tuple(tb, tuple); |
00cb277a PNA |
504 | |
505 | ip_conntrack_proto_put(proto); | |
080774a2 HW |
506 | |
507 | return ret; | |
080774a2 HW |
508 | } |
509 | ||
510 | static inline int | |
511 | ctnetlink_parse_tuple(struct nfattr *cda[], struct ip_conntrack_tuple *tuple, | |
512 | enum ctattr_tuple type) | |
513 | { | |
514 | struct nfattr *tb[CTA_TUPLE_MAX]; | |
515 | int err; | |
516 | ||
080774a2 HW |
517 | memset(tuple, 0, sizeof(*tuple)); |
518 | ||
a2506c04 | 519 | nfattr_parse_nested(tb, CTA_TUPLE_MAX, cda[type-1]); |
080774a2 HW |
520 | |
521 | if (!tb[CTA_TUPLE_IP-1]) | |
522 | return -EINVAL; | |
523 | ||
524 | err = ctnetlink_parse_tuple_ip(tb[CTA_TUPLE_IP-1], tuple); | |
525 | if (err < 0) | |
526 | return err; | |
527 | ||
528 | if (!tb[CTA_TUPLE_PROTO-1]) | |
529 | return -EINVAL; | |
530 | ||
531 | err = ctnetlink_parse_tuple_proto(tb[CTA_TUPLE_PROTO-1], tuple); | |
532 | if (err < 0) | |
533 | return err; | |
534 | ||
535 | /* orig and expect tuples get DIR_ORIGINAL */ | |
536 | if (type == CTA_TUPLE_REPLY) | |
537 | tuple->dst.dir = IP_CT_DIR_REPLY; | |
538 | else | |
539 | tuple->dst.dir = IP_CT_DIR_ORIGINAL; | |
540 | ||
080774a2 | 541 | return 0; |
080774a2 HW |
542 | } |
543 | ||
544 | #ifdef CONFIG_IP_NF_NAT_NEEDED | |
dbd36ea4 | 545 | static const size_t cta_min_protonat[CTA_PROTONAT_MAX] = { |
080774a2 HW |
546 | [CTA_PROTONAT_PORT_MIN-1] = sizeof(u_int16_t), |
547 | [CTA_PROTONAT_PORT_MAX-1] = sizeof(u_int16_t), | |
548 | }; | |
549 | ||
550 | static int ctnetlink_parse_nat_proto(struct nfattr *attr, | |
551 | const struct ip_conntrack *ct, | |
552 | struct ip_nat_range *range) | |
553 | { | |
554 | struct nfattr *tb[CTA_PROTONAT_MAX]; | |
555 | struct ip_nat_protocol *npt; | |
556 | ||
a2506c04 | 557 | nfattr_parse_nested(tb, CTA_PROTONAT_MAX, attr); |
080774a2 HW |
558 | |
559 | if (nfattr_bad_size(tb, CTA_PROTONAT_MAX, cta_min_protonat)) | |
fe902a91 | 560 | return -EINVAL; |
080774a2 HW |
561 | |
562 | npt = ip_nat_proto_find_get(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum); | |
080774a2 HW |
563 | |
564 | if (!npt->nfattr_to_range) { | |
565 | ip_nat_proto_put(npt); | |
566 | return 0; | |
567 | } | |
568 | ||
569 | /* nfattr_to_range returns 1 if it parsed, 0 if not, neg. on error */ | |
570 | if (npt->nfattr_to_range(tb, range) > 0) | |
571 | range->flags |= IP_NAT_RANGE_PROTO_SPECIFIED; | |
572 | ||
573 | ip_nat_proto_put(npt); | |
574 | ||
080774a2 | 575 | return 0; |
080774a2 HW |
576 | } |
577 | ||
56558208 | 578 | static const size_t cta_min_nat[CTA_NAT_MAX] = { |
cdcb71bf AV |
579 | [CTA_NAT_MINIP-1] = sizeof(__be32), |
580 | [CTA_NAT_MAXIP-1] = sizeof(__be32), | |
56558208 PNA |
581 | }; |
582 | ||
080774a2 | 583 | static inline int |
3726add7 | 584 | ctnetlink_parse_nat(struct nfattr *nat, |
080774a2 HW |
585 | const struct ip_conntrack *ct, struct ip_nat_range *range) |
586 | { | |
587 | struct nfattr *tb[CTA_NAT_MAX]; | |
588 | int err; | |
589 | ||
080774a2 HW |
590 | memset(range, 0, sizeof(*range)); |
591 | ||
3726add7 | 592 | nfattr_parse_nested(tb, CTA_NAT_MAX, nat); |
080774a2 | 593 | |
56558208 PNA |
594 | if (nfattr_bad_size(tb, CTA_NAT_MAX, cta_min_nat)) |
595 | return -EINVAL; | |
596 | ||
080774a2 | 597 | if (tb[CTA_NAT_MINIP-1]) |
cdcb71bf | 598 | range->min_ip = *(__be32 *)NFA_DATA(tb[CTA_NAT_MINIP-1]); |
080774a2 HW |
599 | |
600 | if (!tb[CTA_NAT_MAXIP-1]) | |
601 | range->max_ip = range->min_ip; | |
602 | else | |
cdcb71bf | 603 | range->max_ip = *(__be32 *)NFA_DATA(tb[CTA_NAT_MAXIP-1]); |
080774a2 HW |
604 | |
605 | if (range->min_ip) | |
606 | range->flags |= IP_NAT_RANGE_MAP_IPS; | |
607 | ||
608 | if (!tb[CTA_NAT_PROTO-1]) | |
609 | return 0; | |
610 | ||
611 | err = ctnetlink_parse_nat_proto(tb[CTA_NAT_PROTO-1], ct, range); | |
612 | if (err < 0) | |
613 | return err; | |
614 | ||
080774a2 | 615 | return 0; |
080774a2 HW |
616 | } |
617 | #endif | |
618 | ||
619 | static inline int | |
620 | ctnetlink_parse_help(struct nfattr *attr, char **helper_name) | |
621 | { | |
622 | struct nfattr *tb[CTA_HELP_MAX]; | |
623 | ||
a2506c04 | 624 | nfattr_parse_nested(tb, CTA_HELP_MAX, attr); |
080774a2 HW |
625 | |
626 | if (!tb[CTA_HELP_NAME-1]) | |
627 | return -EINVAL; | |
628 | ||
629 | *helper_name = NFA_DATA(tb[CTA_HELP_NAME-1]); | |
630 | ||
631 | return 0; | |
080774a2 HW |
632 | } |
633 | ||
56558208 | 634 | static const size_t cta_min[CTA_MAX] = { |
cdcb71bf AV |
635 | [CTA_STATUS-1] = sizeof(__be32), |
636 | [CTA_TIMEOUT-1] = sizeof(__be32), | |
637 | [CTA_MARK-1] = sizeof(__be32), | |
638 | [CTA_USE-1] = sizeof(__be32), | |
639 | [CTA_ID-1] = sizeof(__be32) | |
56558208 PNA |
640 | }; |
641 | ||
080774a2 HW |
642 | static int |
643 | ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb, | |
644 | struct nlmsghdr *nlh, struct nfattr *cda[], int *errp) | |
645 | { | |
646 | struct ip_conntrack_tuple_hash *h; | |
647 | struct ip_conntrack_tuple tuple; | |
648 | struct ip_conntrack *ct; | |
649 | int err = 0; | |
650 | ||
56558208 PNA |
651 | if (nfattr_bad_size(cda, CTA_MAX, cta_min)) |
652 | return -EINVAL; | |
653 | ||
080774a2 HW |
654 | if (cda[CTA_TUPLE_ORIG-1]) |
655 | err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_ORIG); | |
656 | else if (cda[CTA_TUPLE_REPLY-1]) | |
657 | err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_REPLY); | |
658 | else { | |
659 | /* Flush the whole table */ | |
660 | ip_conntrack_flush(); | |
661 | return 0; | |
662 | } | |
663 | ||
664 | if (err < 0) | |
665 | return err; | |
666 | ||
667 | h = ip_conntrack_find_get(&tuple, NULL); | |
9ea8cfd6 | 668 | if (!h) |
080774a2 | 669 | return -ENOENT; |
080774a2 HW |
670 | |
671 | ct = tuplehash_to_ctrack(h); | |
672 | ||
673 | if (cda[CTA_ID-1]) { | |
cdcb71bf | 674 | u_int32_t id = ntohl(*(__be32 *)NFA_DATA(cda[CTA_ID-1])); |
080774a2 HW |
675 | if (ct->id != id) { |
676 | ip_conntrack_put(ct); | |
677 | return -ENOENT; | |
678 | } | |
679 | } | |
2fdf1faa | 680 | if (del_timer(&ct->timeout)) |
080774a2 | 681 | ct->timeout.function((unsigned long)ct); |
2fdf1faa | 682 | |
080774a2 | 683 | ip_conntrack_put(ct); |
080774a2 HW |
684 | |
685 | return 0; | |
686 | } | |
687 | ||
688 | static int | |
689 | ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb, | |
690 | struct nlmsghdr *nlh, struct nfattr *cda[], int *errp) | |
691 | { | |
692 | struct ip_conntrack_tuple_hash *h; | |
693 | struct ip_conntrack_tuple tuple; | |
694 | struct ip_conntrack *ct; | |
695 | struct sk_buff *skb2 = NULL; | |
696 | int err = 0; | |
697 | ||
080774a2 HW |
698 | if (nlh->nlmsg_flags & NLM_F_DUMP) { |
699 | struct nfgenmsg *msg = NLMSG_DATA(nlh); | |
700 | u32 rlen; | |
701 | ||
702 | if (msg->nfgen_family != AF_INET) | |
703 | return -EAFNOSUPPORT; | |
704 | ||
01f34848 PNA |
705 | #ifndef CONFIG_IP_NF_CT_ACCT |
706 | if (NFNL_MSG_TYPE(nlh->nlmsg_type) == IPCTNL_MSG_CT_GET_CTRZERO) | |
080774a2 HW |
707 | return -ENOTSUPP; |
708 | #endif | |
01f34848 PNA |
709 | if ((*errp = netlink_dump_start(ctnl, skb, nlh, |
710 | ctnetlink_dump_table, | |
711 | ctnetlink_done)) != 0) | |
080774a2 | 712 | return -EINVAL; |
080774a2 HW |
713 | |
714 | rlen = NLMSG_ALIGN(nlh->nlmsg_len); | |
715 | if (rlen > skb->len) | |
716 | rlen = skb->len; | |
717 | skb_pull(skb, rlen); | |
718 | return 0; | |
719 | } | |
720 | ||
56558208 PNA |
721 | if (nfattr_bad_size(cda, CTA_MAX, cta_min)) |
722 | return -EINVAL; | |
723 | ||
080774a2 HW |
724 | if (cda[CTA_TUPLE_ORIG-1]) |
725 | err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_ORIG); | |
726 | else if (cda[CTA_TUPLE_REPLY-1]) | |
727 | err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_REPLY); | |
728 | else | |
729 | return -EINVAL; | |
730 | ||
731 | if (err < 0) | |
732 | return err; | |
733 | ||
734 | h = ip_conntrack_find_get(&tuple, NULL); | |
9ea8cfd6 | 735 | if (!h) |
080774a2 | 736 | return -ENOENT; |
9ea8cfd6 | 737 | |
080774a2 HW |
738 | ct = tuplehash_to_ctrack(h); |
739 | ||
740 | err = -ENOMEM; | |
81e5c27d | 741 | skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); |
080774a2 HW |
742 | if (!skb2) { |
743 | ip_conntrack_put(ct); | |
744 | return -ENOMEM; | |
745 | } | |
080774a2 HW |
746 | |
747 | err = ctnetlink_fill_info(skb2, NETLINK_CB(skb).pid, nlh->nlmsg_seq, | |
748 | IPCTNL_MSG_CT_NEW, 1, ct); | |
749 | ip_conntrack_put(ct); | |
750 | if (err <= 0) | |
0f81eb4d | 751 | goto free; |
080774a2 HW |
752 | |
753 | err = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT); | |
754 | if (err < 0) | |
755 | goto out; | |
756 | ||
080774a2 HW |
757 | return 0; |
758 | ||
0f81eb4d HW |
759 | free: |
760 | kfree_skb(skb2); | |
080774a2 | 761 | out: |
fcda4612 | 762 | return err; |
080774a2 HW |
763 | } |
764 | ||
765 | static inline int | |
766 | ctnetlink_change_status(struct ip_conntrack *ct, struct nfattr *cda[]) | |
767 | { | |
d000eaf7 | 768 | unsigned long d; |
cdcb71bf | 769 | unsigned status = ntohl(*(__be32 *)NFA_DATA(cda[CTA_STATUS-1])); |
080774a2 HW |
770 | d = ct->status ^ status; |
771 | ||
772 | if (d & (IPS_EXPECTED|IPS_CONFIRMED|IPS_DYING)) | |
773 | /* unchangeable */ | |
774 | return -EINVAL; | |
775 | ||
776 | if (d & IPS_SEEN_REPLY && !(status & IPS_SEEN_REPLY)) | |
777 | /* SEEN_REPLY bit can only be set */ | |
778 | return -EINVAL; | |
779 | ||
780 | ||
781 | if (d & IPS_ASSURED && !(status & IPS_ASSURED)) | |
782 | /* ASSURED bit can only be set */ | |
783 | return -EINVAL; | |
784 | ||
3726add7 | 785 | if (cda[CTA_NAT_SRC-1] || cda[CTA_NAT_DST-1]) { |
080774a2 HW |
786 | #ifndef CONFIG_IP_NF_NAT_NEEDED |
787 | return -EINVAL; | |
788 | #else | |
080774a2 HW |
789 | struct ip_nat_range range; |
790 | ||
3726add7 PM |
791 | if (cda[CTA_NAT_DST-1]) { |
792 | if (ctnetlink_parse_nat(cda[CTA_NAT_DST-1], ct, | |
793 | &range) < 0) | |
794 | return -EINVAL; | |
795 | if (ip_nat_initialized(ct, | |
796 | HOOK2MANIP(NF_IP_PRE_ROUTING))) | |
797 | return -EEXIST; | |
798 | ip_nat_setup_info(ct, &range, NF_IP_PRE_ROUTING); | |
799 | } | |
800 | if (cda[CTA_NAT_SRC-1]) { | |
801 | if (ctnetlink_parse_nat(cda[CTA_NAT_SRC-1], ct, | |
802 | &range) < 0) | |
803 | return -EINVAL; | |
804 | if (ip_nat_initialized(ct, | |
805 | HOOK2MANIP(NF_IP_POST_ROUTING))) | |
806 | return -EEXIST; | |
807 | ip_nat_setup_info(ct, &range, NF_IP_POST_ROUTING); | |
808 | } | |
080774a2 HW |
809 | #endif |
810 | } | |
811 | ||
812 | /* Be careful here, modifying NAT bits can screw up things, | |
813 | * so don't let users modify them directly if they don't pass | |
814 | * ip_nat_range. */ | |
815 | ct->status |= status & ~(IPS_NAT_DONE_MASK | IPS_NAT_MASK); | |
816 | return 0; | |
817 | } | |
818 | ||
819 | ||
820 | static inline int | |
821 | ctnetlink_change_helper(struct ip_conntrack *ct, struct nfattr *cda[]) | |
822 | { | |
823 | struct ip_conntrack_helper *helper; | |
824 | char *helpname; | |
825 | int err; | |
826 | ||
080774a2 HW |
827 | /* don't change helper of sibling connections */ |
828 | if (ct->master) | |
829 | return -EINVAL; | |
830 | ||
831 | err = ctnetlink_parse_help(cda[CTA_HELP-1], &helpname); | |
832 | if (err < 0) | |
833 | return err; | |
834 | ||
835 | helper = __ip_conntrack_helper_find_byname(helpname); | |
836 | if (!helper) { | |
837 | if (!strcmp(helpname, "")) | |
838 | helper = NULL; | |
839 | else | |
840 | return -EINVAL; | |
841 | } | |
842 | ||
843 | if (ct->helper) { | |
844 | if (!helper) { | |
845 | /* we had a helper before ... */ | |
846 | ip_ct_remove_expectations(ct); | |
847 | ct->helper = NULL; | |
848 | } else { | |
849 | /* need to zero data of old helper */ | |
850 | memset(&ct->help, 0, sizeof(ct->help)); | |
851 | } | |
852 | } | |
853 | ||
854 | ct->helper = helper; | |
855 | ||
856 | return 0; | |
857 | } | |
858 | ||
859 | static inline int | |
860 | ctnetlink_change_timeout(struct ip_conntrack *ct, struct nfattr *cda[]) | |
861 | { | |
cdcb71bf | 862 | u_int32_t timeout = ntohl(*(__be32 *)NFA_DATA(cda[CTA_TIMEOUT-1])); |
080774a2 HW |
863 | |
864 | if (!del_timer(&ct->timeout)) | |
865 | return -ETIME; | |
866 | ||
867 | ct->timeout.expires = jiffies + timeout * HZ; | |
868 | add_timer(&ct->timeout); | |
869 | ||
870 | return 0; | |
871 | } | |
872 | ||
061cb4a0 PNA |
873 | static inline int |
874 | ctnetlink_change_protoinfo(struct ip_conntrack *ct, struct nfattr *cda[]) | |
875 | { | |
876 | struct nfattr *tb[CTA_PROTOINFO_MAX], *attr = cda[CTA_PROTOINFO-1]; | |
877 | struct ip_conntrack_protocol *proto; | |
878 | u_int16_t npt = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum; | |
879 | int err = 0; | |
880 | ||
a2506c04 | 881 | nfattr_parse_nested(tb, CTA_PROTOINFO_MAX, attr); |
061cb4a0 PNA |
882 | |
883 | proto = ip_conntrack_proto_find_get(npt); | |
061cb4a0 PNA |
884 | |
885 | if (proto->from_nfattr) | |
886 | err = proto->from_nfattr(tb, ct); | |
887 | ip_conntrack_proto_put(proto); | |
888 | ||
889 | return err; | |
061cb4a0 PNA |
890 | } |
891 | ||
080774a2 HW |
892 | static int |
893 | ctnetlink_change_conntrack(struct ip_conntrack *ct, struct nfattr *cda[]) | |
894 | { | |
895 | int err; | |
896 | ||
080774a2 HW |
897 | if (cda[CTA_HELP-1]) { |
898 | err = ctnetlink_change_helper(ct, cda); | |
899 | if (err < 0) | |
900 | return err; | |
901 | } | |
902 | ||
903 | if (cda[CTA_TIMEOUT-1]) { | |
904 | err = ctnetlink_change_timeout(ct, cda); | |
905 | if (err < 0) | |
906 | return err; | |
907 | } | |
908 | ||
909 | if (cda[CTA_STATUS-1]) { | |
910 | err = ctnetlink_change_status(ct, cda); | |
911 | if (err < 0) | |
912 | return err; | |
913 | } | |
914 | ||
061cb4a0 PNA |
915 | if (cda[CTA_PROTOINFO-1]) { |
916 | err = ctnetlink_change_protoinfo(ct, cda); | |
917 | if (err < 0) | |
918 | return err; | |
919 | } | |
920 | ||
02a78cdf PNA |
921 | #if defined(CONFIG_IP_NF_CONNTRACK_MARK) |
922 | if (cda[CTA_MARK-1]) | |
cdcb71bf | 923 | ct->mark = ntohl(*(__be32 *)NFA_DATA(cda[CTA_MARK-1])); |
02a78cdf PNA |
924 | #endif |
925 | ||
080774a2 HW |
926 | return 0; |
927 | } | |
928 | ||
929 | static int | |
930 | ctnetlink_create_conntrack(struct nfattr *cda[], | |
931 | struct ip_conntrack_tuple *otuple, | |
932 | struct ip_conntrack_tuple *rtuple) | |
933 | { | |
934 | struct ip_conntrack *ct; | |
935 | int err = -EINVAL; | |
936 | ||
080774a2 HW |
937 | ct = ip_conntrack_alloc(otuple, rtuple); |
938 | if (ct == NULL || IS_ERR(ct)) | |
939 | return -ENOMEM; | |
940 | ||
941 | if (!cda[CTA_TIMEOUT-1]) | |
942 | goto err; | |
cdcb71bf | 943 | ct->timeout.expires = ntohl(*(__be32 *)NFA_DATA(cda[CTA_TIMEOUT-1])); |
080774a2 HW |
944 | |
945 | ct->timeout.expires = jiffies + ct->timeout.expires * HZ; | |
946 | ct->status |= IPS_CONFIRMED; | |
947 | ||
948 | err = ctnetlink_change_status(ct, cda); | |
949 | if (err < 0) | |
950 | goto err; | |
951 | ||
061cb4a0 PNA |
952 | if (cda[CTA_PROTOINFO-1]) { |
953 | err = ctnetlink_change_protoinfo(ct, cda); | |
954 | if (err < 0) | |
955 | return err; | |
956 | } | |
957 | ||
d4d6bb41 PNA |
958 | #if defined(CONFIG_IP_NF_CONNTRACK_MARK) |
959 | if (cda[CTA_MARK-1]) | |
cdcb71bf | 960 | ct->mark = ntohl(*(__be32 *)NFA_DATA(cda[CTA_MARK-1])); |
d4d6bb41 PNA |
961 | #endif |
962 | ||
080774a2 HW |
963 | ct->helper = ip_conntrack_helper_find_get(rtuple); |
964 | ||
965 | add_timer(&ct->timeout); | |
966 | ip_conntrack_hash_insert(ct); | |
967 | ||
968 | if (ct->helper) | |
969 | ip_conntrack_helper_put(ct->helper); | |
970 | ||
080774a2 HW |
971 | return 0; |
972 | ||
973 | err: | |
974 | ip_conntrack_free(ct); | |
975 | return err; | |
976 | } | |
977 | ||
978 | static int | |
979 | ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb, | |
980 | struct nlmsghdr *nlh, struct nfattr *cda[], int *errp) | |
981 | { | |
982 | struct ip_conntrack_tuple otuple, rtuple; | |
983 | struct ip_conntrack_tuple_hash *h = NULL; | |
984 | int err = 0; | |
985 | ||
56558208 PNA |
986 | if (nfattr_bad_size(cda, CTA_MAX, cta_min)) |
987 | return -EINVAL; | |
988 | ||
080774a2 HW |
989 | if (cda[CTA_TUPLE_ORIG-1]) { |
990 | err = ctnetlink_parse_tuple(cda, &otuple, CTA_TUPLE_ORIG); | |
991 | if (err < 0) | |
992 | return err; | |
993 | } | |
994 | ||
995 | if (cda[CTA_TUPLE_REPLY-1]) { | |
996 | err = ctnetlink_parse_tuple(cda, &rtuple, CTA_TUPLE_REPLY); | |
997 | if (err < 0) | |
998 | return err; | |
999 | } | |
1000 | ||
1001 | write_lock_bh(&ip_conntrack_lock); | |
1002 | if (cda[CTA_TUPLE_ORIG-1]) | |
1003 | h = __ip_conntrack_find(&otuple, NULL); | |
1004 | else if (cda[CTA_TUPLE_REPLY-1]) | |
1005 | h = __ip_conntrack_find(&rtuple, NULL); | |
1006 | ||
1007 | if (h == NULL) { | |
1008 | write_unlock_bh(&ip_conntrack_lock); | |
080774a2 HW |
1009 | err = -ENOENT; |
1010 | if (nlh->nlmsg_flags & NLM_F_CREATE) | |
1011 | err = ctnetlink_create_conntrack(cda, &otuple, &rtuple); | |
88aa0429 PN |
1012 | return err; |
1013 | } | |
1014 | /* implicit 'else' */ | |
1015 | ||
1016 | /* we only allow nat config for new conntracks */ | |
3726add7 | 1017 | if (cda[CTA_NAT_SRC-1] || cda[CTA_NAT_DST-1]) { |
88aa0429 | 1018 | err = -EINVAL; |
080774a2 | 1019 | goto out_unlock; |
080774a2 HW |
1020 | } |
1021 | ||
1022 | /* We manipulate the conntrack inside the global conntrack table lock, | |
1023 | * so there's no need to increase the refcount */ | |
080774a2 HW |
1024 | err = -EEXIST; |
1025 | if (!(nlh->nlmsg_flags & NLM_F_EXCL)) | |
1026 | err = ctnetlink_change_conntrack(tuplehash_to_ctrack(h), cda); | |
1027 | ||
1028 | out_unlock: | |
1029 | write_unlock_bh(&ip_conntrack_lock); | |
1030 | return err; | |
1031 | } | |
1032 | ||
1033 | /*********************************************************************** | |
1034 | * EXPECT | |
1035 | ***********************************************************************/ | |
1036 | ||
1037 | static inline int | |
1038 | ctnetlink_exp_dump_tuple(struct sk_buff *skb, | |
1039 | const struct ip_conntrack_tuple *tuple, | |
1040 | enum ctattr_expect type) | |
1041 | { | |
1042 | struct nfattr *nest_parms = NFA_NEST(skb, type); | |
1043 | ||
1044 | if (ctnetlink_dump_tuples(skb, tuple) < 0) | |
1045 | goto nfattr_failure; | |
1046 | ||
1047 | NFA_NEST_END(skb, nest_parms); | |
1048 | ||
1049 | return 0; | |
1050 | ||
1051 | nfattr_failure: | |
1052 | return -1; | |
1053 | } | |
1054 | ||
1cde6436 PNA |
1055 | static inline int |
1056 | ctnetlink_exp_dump_mask(struct sk_buff *skb, | |
1057 | const struct ip_conntrack_tuple *tuple, | |
1058 | const struct ip_conntrack_tuple *mask) | |
1059 | { | |
1060 | int ret; | |
1061 | struct ip_conntrack_protocol *proto; | |
1062 | struct nfattr *nest_parms = NFA_NEST(skb, CTA_EXPECT_MASK); | |
1063 | ||
1064 | ret = ctnetlink_dump_tuples_ip(skb, mask); | |
1065 | if (unlikely(ret < 0)) | |
1066 | goto nfattr_failure; | |
1067 | ||
1068 | proto = ip_conntrack_proto_find_get(tuple->dst.protonum); | |
1069 | ret = ctnetlink_dump_tuples_proto(skb, mask, proto); | |
1070 | ip_conntrack_proto_put(proto); | |
1071 | if (unlikely(ret < 0)) | |
1072 | goto nfattr_failure; | |
1073 | ||
1074 | NFA_NEST_END(skb, nest_parms); | |
1075 | ||
1076 | return 0; | |
1077 | ||
1078 | nfattr_failure: | |
1079 | return -1; | |
1080 | } | |
1081 | ||
080774a2 HW |
1082 | static inline int |
1083 | ctnetlink_exp_dump_expect(struct sk_buff *skb, | |
1084 | const struct ip_conntrack_expect *exp) | |
1085 | { | |
1444fc55 | 1086 | struct ip_conntrack *master = exp->master; |
cdcb71bf AV |
1087 | __be32 timeout = htonl((exp->timeout.expires - jiffies) / HZ); |
1088 | __be32 id = htonl(exp->id); | |
080774a2 HW |
1089 | |
1090 | if (ctnetlink_exp_dump_tuple(skb, &exp->tuple, CTA_EXPECT_TUPLE) < 0) | |
1091 | goto nfattr_failure; | |
1cde6436 | 1092 | if (ctnetlink_exp_dump_mask(skb, &exp->tuple, &exp->mask) < 0) |
080774a2 | 1093 | goto nfattr_failure; |
1444fc55 HW |
1094 | if (ctnetlink_exp_dump_tuple(skb, |
1095 | &master->tuplehash[IP_CT_DIR_ORIGINAL].tuple, | |
1096 | CTA_EXPECT_MASTER) < 0) | |
1097 | goto nfattr_failure; | |
080774a2 | 1098 | |
cdcb71bf AV |
1099 | NFA_PUT(skb, CTA_EXPECT_TIMEOUT, sizeof(__be32), &timeout); |
1100 | NFA_PUT(skb, CTA_EXPECT_ID, sizeof(__be32), &id); | |
080774a2 HW |
1101 | |
1102 | return 0; | |
1103 | ||
1104 | nfattr_failure: | |
1105 | return -1; | |
1106 | } | |
1107 | ||
1108 | static int | |
1109 | ctnetlink_exp_fill_info(struct sk_buff *skb, u32 pid, u32 seq, | |
1110 | int event, | |
1111 | int nowait, | |
1112 | const struct ip_conntrack_expect *exp) | |
1113 | { | |
1114 | struct nlmsghdr *nlh; | |
1115 | struct nfgenmsg *nfmsg; | |
1116 | unsigned char *b; | |
1117 | ||
1118 | b = skb->tail; | |
1119 | ||
1120 | event |= NFNL_SUBSYS_CTNETLINK_EXP << 8; | |
1121 | nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(struct nfgenmsg)); | |
1122 | nfmsg = NLMSG_DATA(nlh); | |
1123 | ||
1124 | nlh->nlmsg_flags = (nowait && pid) ? NLM_F_MULTI : 0; | |
1125 | nfmsg->nfgen_family = AF_INET; | |
1126 | nfmsg->version = NFNETLINK_V0; | |
1127 | nfmsg->res_id = 0; | |
1128 | ||
1129 | if (ctnetlink_exp_dump_expect(skb, exp) < 0) | |
1130 | goto nfattr_failure; | |
1131 | ||
1132 | nlh->nlmsg_len = skb->tail - b; | |
1133 | return skb->len; | |
1134 | ||
1135 | nlmsg_failure: | |
1136 | nfattr_failure: | |
1137 | skb_trim(skb, b - skb->data); | |
1138 | return -1; | |
1139 | } | |
1140 | ||
1141 | #ifdef CONFIG_IP_NF_CONNTRACK_EVENTS | |
1142 | static int ctnetlink_expect_event(struct notifier_block *this, | |
1143 | unsigned long events, void *ptr) | |
1144 | { | |
1145 | struct nlmsghdr *nlh; | |
1146 | struct nfgenmsg *nfmsg; | |
1147 | struct ip_conntrack_expect *exp = (struct ip_conntrack_expect *)ptr; | |
1148 | struct sk_buff *skb; | |
1149 | unsigned int type; | |
1150 | unsigned char *b; | |
1151 | int flags = 0; | |
080774a2 HW |
1152 | |
1153 | if (events & IPEXP_NEW) { | |
1154 | type = IPCTNL_MSG_EXP_NEW; | |
1155 | flags = NLM_F_CREATE|NLM_F_EXCL; | |
1156 | } else | |
1157 | return NOTIFY_DONE; | |
1158 | ||
b3a27bfb PNA |
1159 | if (!nfnetlink_has_listeners(NFNLGRP_CONNTRACK_EXP_NEW)) |
1160 | return NOTIFY_DONE; | |
1161 | ||
080774a2 HW |
1162 | skb = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC); |
1163 | if (!skb) | |
1164 | return NOTIFY_DONE; | |
1165 | ||
1166 | b = skb->tail; | |
1167 | ||
b633ad5f | 1168 | type |= NFNL_SUBSYS_CTNETLINK_EXP << 8; |
080774a2 HW |
1169 | nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(struct nfgenmsg)); |
1170 | nfmsg = NLMSG_DATA(nlh); | |
1171 | ||
1172 | nlh->nlmsg_flags = flags; | |
1173 | nfmsg->nfgen_family = AF_INET; | |
1174 | nfmsg->version = NFNETLINK_V0; | |
1175 | nfmsg->res_id = 0; | |
1176 | ||
1177 | if (ctnetlink_exp_dump_expect(skb, exp) < 0) | |
1178 | goto nfattr_failure; | |
1179 | ||
1180 | nlh->nlmsg_len = skb->tail - b; | |
ac6d439d | 1181 | nfnetlink_send(skb, 0, NFNLGRP_CONNTRACK_EXP_NEW, 0); |
080774a2 HW |
1182 | return NOTIFY_DONE; |
1183 | ||
1184 | nlmsg_failure: | |
1185 | nfattr_failure: | |
1186 | kfree_skb(skb); | |
1187 | return NOTIFY_DONE; | |
1188 | } | |
1189 | #endif | |
1190 | ||
1191 | static int | |
1192 | ctnetlink_exp_dump_table(struct sk_buff *skb, struct netlink_callback *cb) | |
1193 | { | |
1194 | struct ip_conntrack_expect *exp = NULL; | |
1195 | struct list_head *i; | |
1196 | u_int32_t *id = (u_int32_t *) &cb->args[0]; | |
1197 | ||
080774a2 | 1198 | read_lock_bh(&ip_conntrack_lock); |
ff21d577 | 1199 | list_for_each_prev(i, &ip_conntrack_expect_list) { |
080774a2 HW |
1200 | exp = (struct ip_conntrack_expect *) i; |
1201 | if (exp->id <= *id) | |
1202 | continue; | |
1203 | if (ctnetlink_exp_fill_info(skb, NETLINK_CB(cb->skb).pid, | |
1204 | cb->nlh->nlmsg_seq, | |
1205 | IPCTNL_MSG_EXP_NEW, | |
1206 | 1, exp) < 0) | |
1207 | goto out; | |
1208 | *id = exp->id; | |
1209 | } | |
1210 | out: | |
1211 | read_unlock_bh(&ip_conntrack_lock); | |
1212 | ||
080774a2 HW |
1213 | return skb->len; |
1214 | } | |
1215 | ||
56558208 | 1216 | static const size_t cta_min_exp[CTA_EXPECT_MAX] = { |
cdcb71bf AV |
1217 | [CTA_EXPECT_TIMEOUT-1] = sizeof(__be32), |
1218 | [CTA_EXPECT_ID-1] = sizeof(__be32) | |
56558208 PNA |
1219 | }; |
1220 | ||
080774a2 HW |
1221 | static int |
1222 | ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb, | |
1223 | struct nlmsghdr *nlh, struct nfattr *cda[], int *errp) | |
1224 | { | |
1225 | struct ip_conntrack_tuple tuple; | |
1226 | struct ip_conntrack_expect *exp; | |
1227 | struct sk_buff *skb2; | |
1228 | int err = 0; | |
1229 | ||
56558208 PNA |
1230 | if (nfattr_bad_size(cda, CTA_EXPECT_MAX, cta_min_exp)) |
1231 | return -EINVAL; | |
1232 | ||
080774a2 HW |
1233 | if (nlh->nlmsg_flags & NLM_F_DUMP) { |
1234 | struct nfgenmsg *msg = NLMSG_DATA(nlh); | |
1235 | u32 rlen; | |
1236 | ||
1237 | if (msg->nfgen_family != AF_INET) | |
1238 | return -EAFNOSUPPORT; | |
1239 | ||
1240 | if ((*errp = netlink_dump_start(ctnl, skb, nlh, | |
1241 | ctnetlink_exp_dump_table, | |
1242 | ctnetlink_done)) != 0) | |
1243 | return -EINVAL; | |
1244 | rlen = NLMSG_ALIGN(nlh->nlmsg_len); | |
1245 | if (rlen > skb->len) | |
1246 | rlen = skb->len; | |
1247 | skb_pull(skb, rlen); | |
1248 | return 0; | |
1249 | } | |
1250 | ||
1444fc55 HW |
1251 | if (cda[CTA_EXPECT_MASTER-1]) |
1252 | err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_MASTER); | |
080774a2 HW |
1253 | else |
1254 | return -EINVAL; | |
1255 | ||
1256 | if (err < 0) | |
1257 | return err; | |
1258 | ||
a41bc002 | 1259 | exp = ip_conntrack_expect_find(&tuple); |
080774a2 HW |
1260 | if (!exp) |
1261 | return -ENOENT; | |
1262 | ||
a856a19a | 1263 | if (cda[CTA_EXPECT_ID-1]) { |
cdcb71bf | 1264 | __be32 id = *(__be32 *)NFA_DATA(cda[CTA_EXPECT_ID-1]); |
a856a19a PNA |
1265 | if (exp->id != ntohl(id)) { |
1266 | ip_conntrack_expect_put(exp); | |
1267 | return -ENOENT; | |
1268 | } | |
1269 | } | |
1270 | ||
080774a2 HW |
1271 | err = -ENOMEM; |
1272 | skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); | |
1273 | if (!skb2) | |
1274 | goto out; | |
4e9b8269 | 1275 | |
080774a2 HW |
1276 | err = ctnetlink_exp_fill_info(skb2, NETLINK_CB(skb).pid, |
1277 | nlh->nlmsg_seq, IPCTNL_MSG_EXP_NEW, | |
1278 | 1, exp); | |
1279 | if (err <= 0) | |
0f81eb4d | 1280 | goto free; |
080774a2 HW |
1281 | |
1282 | ip_conntrack_expect_put(exp); | |
1283 | ||
0f81eb4d | 1284 | return netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT); |
080774a2 | 1285 | |
0f81eb4d HW |
1286 | free: |
1287 | kfree_skb(skb2); | |
080774a2 HW |
1288 | out: |
1289 | ip_conntrack_expect_put(exp); | |
080774a2 HW |
1290 | return err; |
1291 | } | |
1292 | ||
1293 | static int | |
1294 | ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb, | |
1295 | struct nlmsghdr *nlh, struct nfattr *cda[], int *errp) | |
1296 | { | |
1297 | struct ip_conntrack_expect *exp, *tmp; | |
1298 | struct ip_conntrack_tuple tuple; | |
1299 | struct ip_conntrack_helper *h; | |
1300 | int err; | |
1301 | ||
56558208 PNA |
1302 | if (nfattr_bad_size(cda, CTA_EXPECT_MAX, cta_min_exp)) |
1303 | return -EINVAL; | |
1304 | ||
1444fc55 HW |
1305 | if (cda[CTA_EXPECT_TUPLE-1]) { |
1306 | /* delete a single expect by tuple */ | |
1307 | err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_TUPLE); | |
1308 | if (err < 0) | |
1309 | return err; | |
1310 | ||
1311 | /* bump usage count to 2 */ | |
a41bc002 | 1312 | exp = ip_conntrack_expect_find(&tuple); |
1444fc55 HW |
1313 | if (!exp) |
1314 | return -ENOENT; | |
1315 | ||
1316 | if (cda[CTA_EXPECT_ID-1]) { | |
cdcb71bf AV |
1317 | __be32 id = |
1318 | *(__be32 *)NFA_DATA(cda[CTA_EXPECT_ID-1]); | |
1444fc55 HW |
1319 | if (exp->id != ntohl(id)) { |
1320 | ip_conntrack_expect_put(exp); | |
1321 | return -ENOENT; | |
1322 | } | |
1323 | } | |
1324 | ||
1325 | /* after list removal, usage count == 1 */ | |
1326 | ip_conntrack_unexpect_related(exp); | |
1327 | /* have to put what we 'get' above. | |
1328 | * after this line usage count == 0 */ | |
1329 | ip_conntrack_expect_put(exp); | |
1330 | } else if (cda[CTA_EXPECT_HELP_NAME-1]) { | |
1331 | char *name = NFA_DATA(cda[CTA_EXPECT_HELP_NAME-1]); | |
080774a2 HW |
1332 | |
1333 | /* delete all expectations for this helper */ | |
1334 | write_lock_bh(&ip_conntrack_lock); | |
1335 | h = __ip_conntrack_helper_find_byname(name); | |
1336 | if (!h) { | |
1337 | write_unlock_bh(&ip_conntrack_lock); | |
1338 | return -EINVAL; | |
1339 | } | |
1340 | list_for_each_entry_safe(exp, tmp, &ip_conntrack_expect_list, | |
1341 | list) { | |
1342 | if (exp->master->helper == h | |
49719eb3 PNA |
1343 | && del_timer(&exp->timeout)) { |
1344 | ip_ct_unlink_expect(exp); | |
1345 | ip_conntrack_expect_put(exp); | |
1346 | } | |
080774a2 | 1347 | } |
9fb9cbb1 | 1348 | write_unlock_bh(&ip_conntrack_lock); |
080774a2 HW |
1349 | } else { |
1350 | /* This basically means we have to flush everything*/ | |
1351 | write_lock_bh(&ip_conntrack_lock); | |
1352 | list_for_each_entry_safe(exp, tmp, &ip_conntrack_expect_list, | |
1353 | list) { | |
49719eb3 PNA |
1354 | if (del_timer(&exp->timeout)) { |
1355 | ip_ct_unlink_expect(exp); | |
1356 | ip_conntrack_expect_put(exp); | |
1357 | } | |
080774a2 HW |
1358 | } |
1359 | write_unlock_bh(&ip_conntrack_lock); | |
080774a2 HW |
1360 | } |
1361 | ||
080774a2 HW |
1362 | return 0; |
1363 | } | |
1364 | static int | |
1365 | ctnetlink_change_expect(struct ip_conntrack_expect *x, struct nfattr *cda[]) | |
1366 | { | |
1367 | return -EOPNOTSUPP; | |
1368 | } | |
1369 | ||
1370 | static int | |
1371 | ctnetlink_create_expect(struct nfattr *cda[]) | |
1372 | { | |
1373 | struct ip_conntrack_tuple tuple, mask, master_tuple; | |
1374 | struct ip_conntrack_tuple_hash *h = NULL; | |
1375 | struct ip_conntrack_expect *exp; | |
1376 | struct ip_conntrack *ct; | |
1377 | int err = 0; | |
1378 | ||
1444fc55 | 1379 | /* caller guarantees that those three CTA_EXPECT_* exist */ |
080774a2 HW |
1380 | err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_TUPLE); |
1381 | if (err < 0) | |
1382 | return err; | |
bd9a26b7 | 1383 | err = ctnetlink_parse_tuple(cda, &mask, CTA_EXPECT_MASK); |
080774a2 HW |
1384 | if (err < 0) |
1385 | return err; | |
1444fc55 | 1386 | err = ctnetlink_parse_tuple(cda, &master_tuple, CTA_EXPECT_MASTER); |
080774a2 HW |
1387 | if (err < 0) |
1388 | return err; | |
1389 | ||
1390 | /* Look for master conntrack of this expectation */ | |
1391 | h = ip_conntrack_find_get(&master_tuple, NULL); | |
1392 | if (!h) | |
1393 | return -ENOENT; | |
1394 | ct = tuplehash_to_ctrack(h); | |
1395 | ||
1396 | if (!ct->helper) { | |
1397 | /* such conntrack hasn't got any helper, abort */ | |
1398 | err = -EINVAL; | |
1399 | goto out; | |
1400 | } | |
1401 | ||
1402 | exp = ip_conntrack_expect_alloc(ct); | |
1403 | if (!exp) { | |
1404 | err = -ENOMEM; | |
1405 | goto out; | |
1406 | } | |
1407 | ||
1408 | exp->expectfn = NULL; | |
2248bcfc | 1409 | exp->flags = 0; |
080774a2 HW |
1410 | exp->master = ct; |
1411 | memcpy(&exp->tuple, &tuple, sizeof(struct ip_conntrack_tuple)); | |
1412 | memcpy(&exp->mask, &mask, sizeof(struct ip_conntrack_tuple)); | |
1413 | ||
1414 | err = ip_conntrack_expect_related(exp); | |
1415 | ip_conntrack_expect_put(exp); | |
1416 | ||
1417 | out: | |
1418 | ip_conntrack_put(tuplehash_to_ctrack(h)); | |
1419 | return err; | |
1420 | } | |
1421 | ||
1422 | static int | |
1423 | ctnetlink_new_expect(struct sock *ctnl, struct sk_buff *skb, | |
1424 | struct nlmsghdr *nlh, struct nfattr *cda[], int *errp) | |
1425 | { | |
1426 | struct ip_conntrack_tuple tuple; | |
1427 | struct ip_conntrack_expect *exp; | |
1428 | int err = 0; | |
1429 | ||
56558208 PNA |
1430 | if (nfattr_bad_size(cda, CTA_EXPECT_MAX, cta_min_exp)) |
1431 | return -EINVAL; | |
1432 | ||
1444fc55 HW |
1433 | if (!cda[CTA_EXPECT_TUPLE-1] |
1434 | || !cda[CTA_EXPECT_MASK-1] | |
1435 | || !cda[CTA_EXPECT_MASTER-1]) | |
080774a2 HW |
1436 | return -EINVAL; |
1437 | ||
1438 | err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_TUPLE); | |
1439 | if (err < 0) | |
1440 | return err; | |
1441 | ||
1442 | write_lock_bh(&ip_conntrack_lock); | |
1443 | exp = __ip_conntrack_expect_find(&tuple); | |
1444 | ||
1445 | if (!exp) { | |
1446 | write_unlock_bh(&ip_conntrack_lock); | |
1447 | err = -ENOENT; | |
1448 | if (nlh->nlmsg_flags & NLM_F_CREATE) | |
1449 | err = ctnetlink_create_expect(cda); | |
1450 | return err; | |
1451 | } | |
1452 | ||
1453 | err = -EEXIST; | |
1454 | if (!(nlh->nlmsg_flags & NLM_F_EXCL)) | |
1455 | err = ctnetlink_change_expect(exp, cda); | |
1456 | write_unlock_bh(&ip_conntrack_lock); | |
1457 | ||
080774a2 HW |
1458 | return err; |
1459 | } | |
1460 | ||
1461 | #ifdef CONFIG_IP_NF_CONNTRACK_EVENTS | |
1462 | static struct notifier_block ctnl_notifier = { | |
1463 | .notifier_call = ctnetlink_conntrack_event, | |
1464 | }; | |
1465 | ||
1466 | static struct notifier_block ctnl_notifier_exp = { | |
1467 | .notifier_call = ctnetlink_expect_event, | |
1468 | }; | |
1469 | #endif | |
1470 | ||
1471 | static struct nfnl_callback ctnl_cb[IPCTNL_MSG_MAX] = { | |
1472 | [IPCTNL_MSG_CT_NEW] = { .call = ctnetlink_new_conntrack, | |
37d2e7a2 | 1473 | .attr_count = CTA_MAX, }, |
080774a2 | 1474 | [IPCTNL_MSG_CT_GET] = { .call = ctnetlink_get_conntrack, |
37d2e7a2 | 1475 | .attr_count = CTA_MAX, }, |
080774a2 | 1476 | [IPCTNL_MSG_CT_DELETE] = { .call = ctnetlink_del_conntrack, |
37d2e7a2 | 1477 | .attr_count = CTA_MAX, }, |
080774a2 | 1478 | [IPCTNL_MSG_CT_GET_CTRZERO] = { .call = ctnetlink_get_conntrack, |
37d2e7a2 | 1479 | .attr_count = CTA_MAX, }, |
080774a2 HW |
1480 | }; |
1481 | ||
28b19d99 | 1482 | static struct nfnl_callback ctnl_exp_cb[IPCTNL_MSG_EXP_MAX] = { |
080774a2 | 1483 | [IPCTNL_MSG_EXP_GET] = { .call = ctnetlink_get_expect, |
37d2e7a2 | 1484 | .attr_count = CTA_EXPECT_MAX, }, |
080774a2 | 1485 | [IPCTNL_MSG_EXP_NEW] = { .call = ctnetlink_new_expect, |
37d2e7a2 | 1486 | .attr_count = CTA_EXPECT_MAX, }, |
080774a2 | 1487 | [IPCTNL_MSG_EXP_DELETE] = { .call = ctnetlink_del_expect, |
37d2e7a2 | 1488 | .attr_count = CTA_EXPECT_MAX, }, |
080774a2 HW |
1489 | }; |
1490 | ||
1491 | static struct nfnetlink_subsystem ctnl_subsys = { | |
1492 | .name = "conntrack", | |
1493 | .subsys_id = NFNL_SUBSYS_CTNETLINK, | |
1494 | .cb_count = IPCTNL_MSG_MAX, | |
080774a2 HW |
1495 | .cb = ctnl_cb, |
1496 | }; | |
1497 | ||
1498 | static struct nfnetlink_subsystem ctnl_exp_subsys = { | |
1499 | .name = "conntrack_expect", | |
1500 | .subsys_id = NFNL_SUBSYS_CTNETLINK_EXP, | |
1501 | .cb_count = IPCTNL_MSG_EXP_MAX, | |
080774a2 HW |
1502 | .cb = ctnl_exp_cb, |
1503 | }; | |
1504 | ||
119a3184 | 1505 | MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTNETLINK); |
34f9a2e4 | 1506 | MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTNETLINK_EXP); |
119a3184 | 1507 | |
080774a2 HW |
1508 | static int __init ctnetlink_init(void) |
1509 | { | |
1510 | int ret; | |
1511 | ||
1512 | printk("ctnetlink v%s: registering with nfnetlink.\n", version); | |
1513 | ret = nfnetlink_subsys_register(&ctnl_subsys); | |
1514 | if (ret < 0) { | |
1515 | printk("ctnetlink_init: cannot register with nfnetlink.\n"); | |
1516 | goto err_out; | |
1517 | } | |
1518 | ||
1519 | ret = nfnetlink_subsys_register(&ctnl_exp_subsys); | |
1520 | if (ret < 0) { | |
1521 | printk("ctnetlink_init: cannot register exp with nfnetlink.\n"); | |
1522 | goto err_unreg_subsys; | |
1523 | } | |
1524 | ||
1525 | #ifdef CONFIG_IP_NF_CONNTRACK_EVENTS | |
1526 | ret = ip_conntrack_register_notifier(&ctnl_notifier); | |
1527 | if (ret < 0) { | |
1528 | printk("ctnetlink_init: cannot register notifier.\n"); | |
1529 | goto err_unreg_exp_subsys; | |
1530 | } | |
1531 | ||
1532 | ret = ip_conntrack_expect_register_notifier(&ctnl_notifier_exp); | |
1533 | if (ret < 0) { | |
1534 | printk("ctnetlink_init: cannot expect register notifier.\n"); | |
1535 | goto err_unreg_notifier; | |
1536 | } | |
1537 | #endif | |
1538 | ||
1539 | return 0; | |
1540 | ||
1541 | #ifdef CONFIG_IP_NF_CONNTRACK_EVENTS | |
1542 | err_unreg_notifier: | |
1543 | ip_conntrack_unregister_notifier(&ctnl_notifier); | |
1544 | err_unreg_exp_subsys: | |
1545 | nfnetlink_subsys_unregister(&ctnl_exp_subsys); | |
1546 | #endif | |
1547 | err_unreg_subsys: | |
1548 | nfnetlink_subsys_unregister(&ctnl_subsys); | |
1549 | err_out: | |
1550 | return ret; | |
1551 | } | |
1552 | ||
1553 | static void __exit ctnetlink_exit(void) | |
1554 | { | |
1555 | printk("ctnetlink: unregistering from nfnetlink.\n"); | |
1556 | ||
1557 | #ifdef CONFIG_IP_NF_CONNTRACK_EVENTS | |
e64a70be | 1558 | ip_conntrack_expect_unregister_notifier(&ctnl_notifier_exp); |
080774a2 HW |
1559 | ip_conntrack_unregister_notifier(&ctnl_notifier); |
1560 | #endif | |
1561 | ||
1562 | nfnetlink_subsys_unregister(&ctnl_exp_subsys); | |
1563 | nfnetlink_subsys_unregister(&ctnl_subsys); | |
1564 | return; | |
1565 | } | |
1566 | ||
1567 | module_init(ctnetlink_init); | |
1568 | module_exit(ctnetlink_exit); |