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; | |
ac6d439d | 323 | group = NFNLGRP_CONNTRACK_NEW; |
1a31526b | 324 | } else if (events & (IPCT_STATUS | IPCT_PROTOINFO)) { |
080774a2 | 325 | type = IPCTNL_MSG_CT_NEW; |
ac6d439d | 326 | group = NFNLGRP_CONNTRACK_UPDATE; |
0368309c PNA |
327 | } else |
328 | return NOTIFY_DONE; | |
a2427692 PM |
329 | |
330 | if (!nfnetlink_has_listeners(group)) | |
331 | return NOTIFY_DONE; | |
332 | ||
080774a2 HW |
333 | skb = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC); |
334 | if (!skb) | |
335 | return NOTIFY_DONE; | |
336 | ||
337 | b = skb->tail; | |
338 | ||
339 | type |= NFNL_SUBSYS_CTNETLINK << 8; | |
340 | nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(struct nfgenmsg)); | |
341 | nfmsg = NLMSG_DATA(nlh); | |
342 | ||
343 | nlh->nlmsg_flags = flags; | |
344 | nfmsg->nfgen_family = AF_INET; | |
345 | nfmsg->version = NFNETLINK_V0; | |
346 | nfmsg->res_id = 0; | |
347 | ||
348 | nest_parms = NFA_NEST(skb, CTA_TUPLE_ORIG); | |
349 | if (ctnetlink_dump_tuples(skb, tuple(ct, IP_CT_DIR_ORIGINAL)) < 0) | |
350 | goto nfattr_failure; | |
351 | NFA_NEST_END(skb, nest_parms); | |
352 | ||
353 | nest_parms = NFA_NEST(skb, CTA_TUPLE_REPLY); | |
354 | if (ctnetlink_dump_tuples(skb, tuple(ct, IP_CT_DIR_REPLY)) < 0) | |
355 | goto nfattr_failure; | |
356 | NFA_NEST_END(skb, nest_parms); | |
080774a2 | 357 | |
7b621c1e PNA |
358 | if (events & IPCT_DESTROY) { |
359 | if (ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 || | |
360 | ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0) | |
361 | goto nfattr_failure; | |
362 | } else { | |
363 | if (ctnetlink_dump_status(skb, ct) < 0) | |
364 | goto nfattr_failure; | |
365 | ||
366 | if (ctnetlink_dump_timeout(skb, ct) < 0) | |
367 | goto nfattr_failure; | |
368 | ||
369 | if (events & IPCT_PROTOINFO | |
370 | && ctnetlink_dump_protoinfo(skb, ct) < 0) | |
371 | goto nfattr_failure; | |
372 | ||
373 | if ((events & IPCT_HELPER || ct->helper) | |
374 | && ctnetlink_dump_helpinfo(skb, ct) < 0) | |
375 | goto nfattr_failure; | |
376 | ||
377 | if ((events & IPCT_MARK || ct->mark) | |
378 | && ctnetlink_dump_mark(skb, ct) < 0) | |
379 | goto nfattr_failure; | |
380 | ||
381 | if (events & IPCT_COUNTER_FILLING && | |
382 | (ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 || | |
383 | ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0)) | |
384 | goto nfattr_failure; | |
385 | } | |
b9a37e0c | 386 | |
080774a2 | 387 | nlh->nlmsg_len = skb->tail - b; |
ac6d439d | 388 | nfnetlink_send(skb, 0, group, 0); |
080774a2 HW |
389 | return NOTIFY_DONE; |
390 | ||
391 | nlmsg_failure: | |
392 | nfattr_failure: | |
393 | kfree_skb(skb); | |
394 | return NOTIFY_DONE; | |
395 | } | |
396 | #endif /* CONFIG_IP_NF_CONNTRACK_EVENTS */ | |
397 | ||
398 | static int ctnetlink_done(struct netlink_callback *cb) | |
399 | { | |
89f2e218 PM |
400 | if (cb->args[1]) |
401 | ip_conntrack_put((struct ip_conntrack *)cb->args[1]); | |
080774a2 HW |
402 | return 0; |
403 | } | |
404 | ||
405 | static int | |
406 | ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb) | |
407 | { | |
89f2e218 | 408 | struct ip_conntrack *ct, *last; |
080774a2 HW |
409 | struct ip_conntrack_tuple_hash *h; |
410 | struct list_head *i; | |
080774a2 | 411 | |
080774a2 | 412 | read_lock_bh(&ip_conntrack_lock); |
d205dc40 | 413 | last = (struct ip_conntrack *)cb->args[1]; |
89f2e218 PM |
414 | for (; cb->args[0] < ip_conntrack_htable_size; cb->args[0]++) { |
415 | restart: | |
ff21d577 | 416 | list_for_each_prev(i, &ip_conntrack_hash[cb->args[0]]) { |
080774a2 HW |
417 | h = (struct ip_conntrack_tuple_hash *) i; |
418 | if (DIRECTION(h) != IP_CT_DIR_ORIGINAL) | |
419 | continue; | |
420 | ct = tuplehash_to_ctrack(h); | |
d205dc40 PM |
421 | if (cb->args[1]) { |
422 | if (ct != last) | |
89f2e218 | 423 | continue; |
d205dc40 | 424 | cb->args[1] = 0; |
89f2e218 | 425 | } |
080774a2 HW |
426 | if (ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).pid, |
427 | cb->nlh->nlmsg_seq, | |
428 | IPCTNL_MSG_CT_NEW, | |
89f2e218 PM |
429 | 1, ct) < 0) { |
430 | nf_conntrack_get(&ct->ct_general); | |
431 | cb->args[1] = (unsigned long)ct; | |
080774a2 | 432 | goto out; |
89f2e218 | 433 | } |
01f34848 PNA |
434 | #ifdef CONFIG_NF_CT_ACCT |
435 | if (NFNL_MSG_TYPE(cb->nlh->nlmsg_type) == | |
436 | IPCTNL_MSG_CT_GET_CTRZERO) | |
437 | memset(&ct->counters, 0, sizeof(ct->counters)); | |
438 | #endif | |
89f2e218 | 439 | } |
d205dc40 | 440 | if (cb->args[1]) { |
89f2e218 PM |
441 | cb->args[1] = 0; |
442 | goto restart; | |
080774a2 HW |
443 | } |
444 | } | |
89f2e218 | 445 | out: |
080774a2 | 446 | read_unlock_bh(&ip_conntrack_lock); |
d205dc40 PM |
447 | if (last) |
448 | ip_conntrack_put(last); | |
080774a2 | 449 | |
080774a2 HW |
450 | return skb->len; |
451 | } | |
452 | ||
dbd36ea4 | 453 | static const size_t cta_min_ip[CTA_IP_MAX] = { |
cdcb71bf AV |
454 | [CTA_IP_V4_SRC-1] = sizeof(__be32), |
455 | [CTA_IP_V4_DST-1] = sizeof(__be32), | |
080774a2 HW |
456 | }; |
457 | ||
458 | static inline int | |
459 | ctnetlink_parse_tuple_ip(struct nfattr *attr, struct ip_conntrack_tuple *tuple) | |
460 | { | |
461 | struct nfattr *tb[CTA_IP_MAX]; | |
462 | ||
a2506c04 | 463 | nfattr_parse_nested(tb, CTA_IP_MAX, attr); |
080774a2 HW |
464 | |
465 | if (nfattr_bad_size(tb, CTA_IP_MAX, cta_min_ip)) | |
466 | return -EINVAL; | |
467 | ||
468 | if (!tb[CTA_IP_V4_SRC-1]) | |
469 | return -EINVAL; | |
cdcb71bf | 470 | tuple->src.ip = *(__be32 *)NFA_DATA(tb[CTA_IP_V4_SRC-1]); |
080774a2 HW |
471 | |
472 | if (!tb[CTA_IP_V4_DST-1]) | |
473 | return -EINVAL; | |
cdcb71bf | 474 | tuple->dst.ip = *(__be32 *)NFA_DATA(tb[CTA_IP_V4_DST-1]); |
080774a2 | 475 | |
080774a2 | 476 | return 0; |
080774a2 HW |
477 | } |
478 | ||
dbd36ea4 | 479 | static const size_t cta_min_proto[CTA_PROTO_MAX] = { |
0be7fa92 | 480 | [CTA_PROTO_NUM-1] = sizeof(u_int8_t), |
080774a2 HW |
481 | [CTA_PROTO_SRC_PORT-1] = sizeof(u_int16_t), |
482 | [CTA_PROTO_DST_PORT-1] = sizeof(u_int16_t), | |
483 | [CTA_PROTO_ICMP_TYPE-1] = sizeof(u_int8_t), | |
484 | [CTA_PROTO_ICMP_CODE-1] = sizeof(u_int8_t), | |
485 | [CTA_PROTO_ICMP_ID-1] = sizeof(u_int16_t), | |
486 | }; | |
487 | ||
488 | static inline int | |
489 | ctnetlink_parse_tuple_proto(struct nfattr *attr, | |
490 | struct ip_conntrack_tuple *tuple) | |
491 | { | |
492 | struct nfattr *tb[CTA_PROTO_MAX]; | |
493 | struct ip_conntrack_protocol *proto; | |
494 | int ret = 0; | |
495 | ||
a2506c04 | 496 | nfattr_parse_nested(tb, CTA_PROTO_MAX, attr); |
080774a2 HW |
497 | |
498 | if (nfattr_bad_size(tb, CTA_PROTO_MAX, cta_min_proto)) | |
499 | return -EINVAL; | |
500 | ||
501 | if (!tb[CTA_PROTO_NUM-1]) | |
502 | return -EINVAL; | |
0be7fa92 | 503 | tuple->dst.protonum = *(u_int8_t *)NFA_DATA(tb[CTA_PROTO_NUM-1]); |
080774a2 HW |
504 | |
505 | proto = ip_conntrack_proto_find_get(tuple->dst.protonum); | |
506 | ||
00cb277a | 507 | if (likely(proto->nfattr_to_tuple)) |
080774a2 | 508 | ret = proto->nfattr_to_tuple(tb, tuple); |
00cb277a PNA |
509 | |
510 | ip_conntrack_proto_put(proto); | |
080774a2 HW |
511 | |
512 | return ret; | |
080774a2 HW |
513 | } |
514 | ||
515 | static inline int | |
516 | ctnetlink_parse_tuple(struct nfattr *cda[], struct ip_conntrack_tuple *tuple, | |
517 | enum ctattr_tuple type) | |
518 | { | |
519 | struct nfattr *tb[CTA_TUPLE_MAX]; | |
520 | int err; | |
521 | ||
080774a2 HW |
522 | memset(tuple, 0, sizeof(*tuple)); |
523 | ||
a2506c04 | 524 | nfattr_parse_nested(tb, CTA_TUPLE_MAX, cda[type-1]); |
080774a2 HW |
525 | |
526 | if (!tb[CTA_TUPLE_IP-1]) | |
527 | return -EINVAL; | |
528 | ||
529 | err = ctnetlink_parse_tuple_ip(tb[CTA_TUPLE_IP-1], tuple); | |
530 | if (err < 0) | |
531 | return err; | |
532 | ||
533 | if (!tb[CTA_TUPLE_PROTO-1]) | |
534 | return -EINVAL; | |
535 | ||
536 | err = ctnetlink_parse_tuple_proto(tb[CTA_TUPLE_PROTO-1], tuple); | |
537 | if (err < 0) | |
538 | return err; | |
539 | ||
540 | /* orig and expect tuples get DIR_ORIGINAL */ | |
541 | if (type == CTA_TUPLE_REPLY) | |
542 | tuple->dst.dir = IP_CT_DIR_REPLY; | |
543 | else | |
544 | tuple->dst.dir = IP_CT_DIR_ORIGINAL; | |
545 | ||
080774a2 | 546 | return 0; |
080774a2 HW |
547 | } |
548 | ||
549 | #ifdef CONFIG_IP_NF_NAT_NEEDED | |
dbd36ea4 | 550 | static const size_t cta_min_protonat[CTA_PROTONAT_MAX] = { |
080774a2 HW |
551 | [CTA_PROTONAT_PORT_MIN-1] = sizeof(u_int16_t), |
552 | [CTA_PROTONAT_PORT_MAX-1] = sizeof(u_int16_t), | |
553 | }; | |
554 | ||
555 | static int ctnetlink_parse_nat_proto(struct nfattr *attr, | |
556 | const struct ip_conntrack *ct, | |
557 | struct ip_nat_range *range) | |
558 | { | |
559 | struct nfattr *tb[CTA_PROTONAT_MAX]; | |
560 | struct ip_nat_protocol *npt; | |
561 | ||
a2506c04 | 562 | nfattr_parse_nested(tb, CTA_PROTONAT_MAX, attr); |
080774a2 HW |
563 | |
564 | if (nfattr_bad_size(tb, CTA_PROTONAT_MAX, cta_min_protonat)) | |
fe902a91 | 565 | return -EINVAL; |
080774a2 HW |
566 | |
567 | npt = ip_nat_proto_find_get(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum); | |
080774a2 HW |
568 | |
569 | if (!npt->nfattr_to_range) { | |
570 | ip_nat_proto_put(npt); | |
571 | return 0; | |
572 | } | |
573 | ||
574 | /* nfattr_to_range returns 1 if it parsed, 0 if not, neg. on error */ | |
575 | if (npt->nfattr_to_range(tb, range) > 0) | |
576 | range->flags |= IP_NAT_RANGE_PROTO_SPECIFIED; | |
577 | ||
578 | ip_nat_proto_put(npt); | |
579 | ||
080774a2 | 580 | return 0; |
080774a2 HW |
581 | } |
582 | ||
56558208 | 583 | static const size_t cta_min_nat[CTA_NAT_MAX] = { |
cdcb71bf AV |
584 | [CTA_NAT_MINIP-1] = sizeof(__be32), |
585 | [CTA_NAT_MAXIP-1] = sizeof(__be32), | |
56558208 PNA |
586 | }; |
587 | ||
080774a2 | 588 | static inline int |
3726add7 | 589 | ctnetlink_parse_nat(struct nfattr *nat, |
080774a2 HW |
590 | const struct ip_conntrack *ct, struct ip_nat_range *range) |
591 | { | |
592 | struct nfattr *tb[CTA_NAT_MAX]; | |
593 | int err; | |
594 | ||
080774a2 HW |
595 | memset(range, 0, sizeof(*range)); |
596 | ||
3726add7 | 597 | nfattr_parse_nested(tb, CTA_NAT_MAX, nat); |
080774a2 | 598 | |
56558208 PNA |
599 | if (nfattr_bad_size(tb, CTA_NAT_MAX, cta_min_nat)) |
600 | return -EINVAL; | |
601 | ||
080774a2 | 602 | if (tb[CTA_NAT_MINIP-1]) |
cdcb71bf | 603 | range->min_ip = *(__be32 *)NFA_DATA(tb[CTA_NAT_MINIP-1]); |
080774a2 HW |
604 | |
605 | if (!tb[CTA_NAT_MAXIP-1]) | |
606 | range->max_ip = range->min_ip; | |
607 | else | |
cdcb71bf | 608 | range->max_ip = *(__be32 *)NFA_DATA(tb[CTA_NAT_MAXIP-1]); |
080774a2 HW |
609 | |
610 | if (range->min_ip) | |
611 | range->flags |= IP_NAT_RANGE_MAP_IPS; | |
612 | ||
613 | if (!tb[CTA_NAT_PROTO-1]) | |
614 | return 0; | |
615 | ||
616 | err = ctnetlink_parse_nat_proto(tb[CTA_NAT_PROTO-1], ct, range); | |
617 | if (err < 0) | |
618 | return err; | |
619 | ||
080774a2 | 620 | return 0; |
080774a2 HW |
621 | } |
622 | #endif | |
623 | ||
624 | static inline int | |
625 | ctnetlink_parse_help(struct nfattr *attr, char **helper_name) | |
626 | { | |
627 | struct nfattr *tb[CTA_HELP_MAX]; | |
628 | ||
a2506c04 | 629 | nfattr_parse_nested(tb, CTA_HELP_MAX, attr); |
080774a2 HW |
630 | |
631 | if (!tb[CTA_HELP_NAME-1]) | |
632 | return -EINVAL; | |
633 | ||
634 | *helper_name = NFA_DATA(tb[CTA_HELP_NAME-1]); | |
635 | ||
636 | return 0; | |
080774a2 HW |
637 | } |
638 | ||
56558208 | 639 | static const size_t cta_min[CTA_MAX] = { |
cdcb71bf AV |
640 | [CTA_STATUS-1] = sizeof(__be32), |
641 | [CTA_TIMEOUT-1] = sizeof(__be32), | |
642 | [CTA_MARK-1] = sizeof(__be32), | |
643 | [CTA_USE-1] = sizeof(__be32), | |
644 | [CTA_ID-1] = sizeof(__be32) | |
56558208 PNA |
645 | }; |
646 | ||
080774a2 HW |
647 | static int |
648 | ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb, | |
649 | struct nlmsghdr *nlh, struct nfattr *cda[], int *errp) | |
650 | { | |
651 | struct ip_conntrack_tuple_hash *h; | |
652 | struct ip_conntrack_tuple tuple; | |
653 | struct ip_conntrack *ct; | |
654 | int err = 0; | |
655 | ||
56558208 PNA |
656 | if (nfattr_bad_size(cda, CTA_MAX, cta_min)) |
657 | return -EINVAL; | |
658 | ||
080774a2 HW |
659 | if (cda[CTA_TUPLE_ORIG-1]) |
660 | err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_ORIG); | |
661 | else if (cda[CTA_TUPLE_REPLY-1]) | |
662 | err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_REPLY); | |
663 | else { | |
664 | /* Flush the whole table */ | |
665 | ip_conntrack_flush(); | |
666 | return 0; | |
667 | } | |
668 | ||
669 | if (err < 0) | |
670 | return err; | |
671 | ||
672 | h = ip_conntrack_find_get(&tuple, NULL); | |
9ea8cfd6 | 673 | if (!h) |
080774a2 | 674 | return -ENOENT; |
080774a2 HW |
675 | |
676 | ct = tuplehash_to_ctrack(h); | |
677 | ||
678 | if (cda[CTA_ID-1]) { | |
cdcb71bf | 679 | u_int32_t id = ntohl(*(__be32 *)NFA_DATA(cda[CTA_ID-1])); |
080774a2 HW |
680 | if (ct->id != id) { |
681 | ip_conntrack_put(ct); | |
682 | return -ENOENT; | |
683 | } | |
684 | } | |
2fdf1faa | 685 | if (del_timer(&ct->timeout)) |
080774a2 | 686 | ct->timeout.function((unsigned long)ct); |
2fdf1faa | 687 | |
080774a2 | 688 | ip_conntrack_put(ct); |
080774a2 HW |
689 | |
690 | return 0; | |
691 | } | |
692 | ||
693 | static int | |
694 | ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb, | |
695 | struct nlmsghdr *nlh, struct nfattr *cda[], int *errp) | |
696 | { | |
697 | struct ip_conntrack_tuple_hash *h; | |
698 | struct ip_conntrack_tuple tuple; | |
699 | struct ip_conntrack *ct; | |
700 | struct sk_buff *skb2 = NULL; | |
701 | int err = 0; | |
702 | ||
080774a2 HW |
703 | if (nlh->nlmsg_flags & NLM_F_DUMP) { |
704 | struct nfgenmsg *msg = NLMSG_DATA(nlh); | |
705 | u32 rlen; | |
706 | ||
707 | if (msg->nfgen_family != AF_INET) | |
708 | return -EAFNOSUPPORT; | |
709 | ||
01f34848 PNA |
710 | #ifndef CONFIG_IP_NF_CT_ACCT |
711 | if (NFNL_MSG_TYPE(nlh->nlmsg_type) == IPCTNL_MSG_CT_GET_CTRZERO) | |
080774a2 HW |
712 | return -ENOTSUPP; |
713 | #endif | |
01f34848 PNA |
714 | if ((*errp = netlink_dump_start(ctnl, skb, nlh, |
715 | ctnetlink_dump_table, | |
716 | ctnetlink_done)) != 0) | |
080774a2 | 717 | return -EINVAL; |
080774a2 HW |
718 | |
719 | rlen = NLMSG_ALIGN(nlh->nlmsg_len); | |
720 | if (rlen > skb->len) | |
721 | rlen = skb->len; | |
722 | skb_pull(skb, rlen); | |
723 | return 0; | |
724 | } | |
725 | ||
56558208 PNA |
726 | if (nfattr_bad_size(cda, CTA_MAX, cta_min)) |
727 | return -EINVAL; | |
728 | ||
080774a2 HW |
729 | if (cda[CTA_TUPLE_ORIG-1]) |
730 | err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_ORIG); | |
731 | else if (cda[CTA_TUPLE_REPLY-1]) | |
732 | err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_REPLY); | |
733 | else | |
734 | return -EINVAL; | |
735 | ||
736 | if (err < 0) | |
737 | return err; | |
738 | ||
739 | h = ip_conntrack_find_get(&tuple, NULL); | |
9ea8cfd6 | 740 | if (!h) |
080774a2 | 741 | return -ENOENT; |
9ea8cfd6 | 742 | |
080774a2 HW |
743 | ct = tuplehash_to_ctrack(h); |
744 | ||
745 | err = -ENOMEM; | |
81e5c27d | 746 | skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); |
080774a2 HW |
747 | if (!skb2) { |
748 | ip_conntrack_put(ct); | |
749 | return -ENOMEM; | |
750 | } | |
080774a2 HW |
751 | |
752 | err = ctnetlink_fill_info(skb2, NETLINK_CB(skb).pid, nlh->nlmsg_seq, | |
753 | IPCTNL_MSG_CT_NEW, 1, ct); | |
754 | ip_conntrack_put(ct); | |
755 | if (err <= 0) | |
0f81eb4d | 756 | goto free; |
080774a2 HW |
757 | |
758 | err = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT); | |
759 | if (err < 0) | |
760 | goto out; | |
761 | ||
080774a2 HW |
762 | return 0; |
763 | ||
0f81eb4d HW |
764 | free: |
765 | kfree_skb(skb2); | |
080774a2 | 766 | out: |
fcda4612 | 767 | return err; |
080774a2 HW |
768 | } |
769 | ||
770 | static inline int | |
771 | ctnetlink_change_status(struct ip_conntrack *ct, struct nfattr *cda[]) | |
772 | { | |
d000eaf7 | 773 | unsigned long d; |
cdcb71bf | 774 | unsigned status = ntohl(*(__be32 *)NFA_DATA(cda[CTA_STATUS-1])); |
080774a2 HW |
775 | d = ct->status ^ status; |
776 | ||
777 | if (d & (IPS_EXPECTED|IPS_CONFIRMED|IPS_DYING)) | |
778 | /* unchangeable */ | |
779 | return -EINVAL; | |
780 | ||
781 | if (d & IPS_SEEN_REPLY && !(status & IPS_SEEN_REPLY)) | |
782 | /* SEEN_REPLY bit can only be set */ | |
783 | return -EINVAL; | |
784 | ||
785 | ||
786 | if (d & IPS_ASSURED && !(status & IPS_ASSURED)) | |
787 | /* ASSURED bit can only be set */ | |
788 | return -EINVAL; | |
789 | ||
3726add7 | 790 | if (cda[CTA_NAT_SRC-1] || cda[CTA_NAT_DST-1]) { |
080774a2 HW |
791 | #ifndef CONFIG_IP_NF_NAT_NEEDED |
792 | return -EINVAL; | |
793 | #else | |
080774a2 HW |
794 | struct ip_nat_range range; |
795 | ||
3726add7 PM |
796 | if (cda[CTA_NAT_DST-1]) { |
797 | if (ctnetlink_parse_nat(cda[CTA_NAT_DST-1], ct, | |
798 | &range) < 0) | |
799 | return -EINVAL; | |
800 | if (ip_nat_initialized(ct, | |
801 | HOOK2MANIP(NF_IP_PRE_ROUTING))) | |
802 | return -EEXIST; | |
803 | ip_nat_setup_info(ct, &range, NF_IP_PRE_ROUTING); | |
804 | } | |
805 | if (cda[CTA_NAT_SRC-1]) { | |
806 | if (ctnetlink_parse_nat(cda[CTA_NAT_SRC-1], ct, | |
807 | &range) < 0) | |
808 | return -EINVAL; | |
809 | if (ip_nat_initialized(ct, | |
810 | HOOK2MANIP(NF_IP_POST_ROUTING))) | |
811 | return -EEXIST; | |
812 | ip_nat_setup_info(ct, &range, NF_IP_POST_ROUTING); | |
813 | } | |
080774a2 HW |
814 | #endif |
815 | } | |
816 | ||
817 | /* Be careful here, modifying NAT bits can screw up things, | |
818 | * so don't let users modify them directly if they don't pass | |
819 | * ip_nat_range. */ | |
820 | ct->status |= status & ~(IPS_NAT_DONE_MASK | IPS_NAT_MASK); | |
821 | return 0; | |
822 | } | |
823 | ||
824 | ||
825 | static inline int | |
826 | ctnetlink_change_helper(struct ip_conntrack *ct, struct nfattr *cda[]) | |
827 | { | |
828 | struct ip_conntrack_helper *helper; | |
829 | char *helpname; | |
830 | int err; | |
831 | ||
080774a2 HW |
832 | /* don't change helper of sibling connections */ |
833 | if (ct->master) | |
834 | return -EINVAL; | |
835 | ||
836 | err = ctnetlink_parse_help(cda[CTA_HELP-1], &helpname); | |
837 | if (err < 0) | |
838 | return err; | |
839 | ||
840 | helper = __ip_conntrack_helper_find_byname(helpname); | |
841 | if (!helper) { | |
842 | if (!strcmp(helpname, "")) | |
843 | helper = NULL; | |
844 | else | |
845 | return -EINVAL; | |
846 | } | |
847 | ||
848 | if (ct->helper) { | |
849 | if (!helper) { | |
850 | /* we had a helper before ... */ | |
851 | ip_ct_remove_expectations(ct); | |
852 | ct->helper = NULL; | |
853 | } else { | |
854 | /* need to zero data of old helper */ | |
855 | memset(&ct->help, 0, sizeof(ct->help)); | |
856 | } | |
857 | } | |
858 | ||
859 | ct->helper = helper; | |
860 | ||
861 | return 0; | |
862 | } | |
863 | ||
864 | static inline int | |
865 | ctnetlink_change_timeout(struct ip_conntrack *ct, struct nfattr *cda[]) | |
866 | { | |
cdcb71bf | 867 | u_int32_t timeout = ntohl(*(__be32 *)NFA_DATA(cda[CTA_TIMEOUT-1])); |
080774a2 HW |
868 | |
869 | if (!del_timer(&ct->timeout)) | |
870 | return -ETIME; | |
871 | ||
872 | ct->timeout.expires = jiffies + timeout * HZ; | |
873 | add_timer(&ct->timeout); | |
874 | ||
875 | return 0; | |
876 | } | |
877 | ||
061cb4a0 PNA |
878 | static inline int |
879 | ctnetlink_change_protoinfo(struct ip_conntrack *ct, struct nfattr *cda[]) | |
880 | { | |
881 | struct nfattr *tb[CTA_PROTOINFO_MAX], *attr = cda[CTA_PROTOINFO-1]; | |
882 | struct ip_conntrack_protocol *proto; | |
883 | u_int16_t npt = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum; | |
884 | int err = 0; | |
885 | ||
a2506c04 | 886 | nfattr_parse_nested(tb, CTA_PROTOINFO_MAX, attr); |
061cb4a0 PNA |
887 | |
888 | proto = ip_conntrack_proto_find_get(npt); | |
061cb4a0 PNA |
889 | |
890 | if (proto->from_nfattr) | |
891 | err = proto->from_nfattr(tb, ct); | |
892 | ip_conntrack_proto_put(proto); | |
893 | ||
894 | return err; | |
061cb4a0 PNA |
895 | } |
896 | ||
080774a2 HW |
897 | static int |
898 | ctnetlink_change_conntrack(struct ip_conntrack *ct, struct nfattr *cda[]) | |
899 | { | |
900 | int err; | |
901 | ||
080774a2 HW |
902 | if (cda[CTA_HELP-1]) { |
903 | err = ctnetlink_change_helper(ct, cda); | |
904 | if (err < 0) | |
905 | return err; | |
906 | } | |
907 | ||
908 | if (cda[CTA_TIMEOUT-1]) { | |
909 | err = ctnetlink_change_timeout(ct, cda); | |
910 | if (err < 0) | |
911 | return err; | |
912 | } | |
913 | ||
914 | if (cda[CTA_STATUS-1]) { | |
915 | err = ctnetlink_change_status(ct, cda); | |
916 | if (err < 0) | |
917 | return err; | |
918 | } | |
919 | ||
061cb4a0 PNA |
920 | if (cda[CTA_PROTOINFO-1]) { |
921 | err = ctnetlink_change_protoinfo(ct, cda); | |
922 | if (err < 0) | |
923 | return err; | |
924 | } | |
925 | ||
02a78cdf PNA |
926 | #if defined(CONFIG_IP_NF_CONNTRACK_MARK) |
927 | if (cda[CTA_MARK-1]) | |
cdcb71bf | 928 | ct->mark = ntohl(*(__be32 *)NFA_DATA(cda[CTA_MARK-1])); |
02a78cdf PNA |
929 | #endif |
930 | ||
080774a2 HW |
931 | return 0; |
932 | } | |
933 | ||
934 | static int | |
935 | ctnetlink_create_conntrack(struct nfattr *cda[], | |
936 | struct ip_conntrack_tuple *otuple, | |
937 | struct ip_conntrack_tuple *rtuple) | |
938 | { | |
939 | struct ip_conntrack *ct; | |
940 | int err = -EINVAL; | |
941 | ||
080774a2 HW |
942 | ct = ip_conntrack_alloc(otuple, rtuple); |
943 | if (ct == NULL || IS_ERR(ct)) | |
944 | return -ENOMEM; | |
945 | ||
946 | if (!cda[CTA_TIMEOUT-1]) | |
947 | goto err; | |
cdcb71bf | 948 | ct->timeout.expires = ntohl(*(__be32 *)NFA_DATA(cda[CTA_TIMEOUT-1])); |
080774a2 HW |
949 | |
950 | ct->timeout.expires = jiffies + ct->timeout.expires * HZ; | |
951 | ct->status |= IPS_CONFIRMED; | |
952 | ||
bbb3357d PNA |
953 | if (cda[CTA_STATUS-1]) { |
954 | err = ctnetlink_change_status(ct, cda); | |
955 | if (err < 0) | |
956 | goto err; | |
957 | } | |
080774a2 | 958 | |
061cb4a0 PNA |
959 | if (cda[CTA_PROTOINFO-1]) { |
960 | err = ctnetlink_change_protoinfo(ct, cda); | |
961 | if (err < 0) | |
962 | return err; | |
963 | } | |
964 | ||
d4d6bb41 PNA |
965 | #if defined(CONFIG_IP_NF_CONNTRACK_MARK) |
966 | if (cda[CTA_MARK-1]) | |
cdcb71bf | 967 | ct->mark = ntohl(*(__be32 *)NFA_DATA(cda[CTA_MARK-1])); |
d4d6bb41 PNA |
968 | #endif |
969 | ||
080774a2 HW |
970 | ct->helper = ip_conntrack_helper_find_get(rtuple); |
971 | ||
972 | add_timer(&ct->timeout); | |
973 | ip_conntrack_hash_insert(ct); | |
974 | ||
975 | if (ct->helper) | |
976 | ip_conntrack_helper_put(ct->helper); | |
977 | ||
080774a2 HW |
978 | return 0; |
979 | ||
980 | err: | |
981 | ip_conntrack_free(ct); | |
982 | return err; | |
983 | } | |
984 | ||
985 | static int | |
986 | ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb, | |
987 | struct nlmsghdr *nlh, struct nfattr *cda[], int *errp) | |
988 | { | |
989 | struct ip_conntrack_tuple otuple, rtuple; | |
990 | struct ip_conntrack_tuple_hash *h = NULL; | |
991 | int err = 0; | |
992 | ||
56558208 PNA |
993 | if (nfattr_bad_size(cda, CTA_MAX, cta_min)) |
994 | return -EINVAL; | |
995 | ||
080774a2 HW |
996 | if (cda[CTA_TUPLE_ORIG-1]) { |
997 | err = ctnetlink_parse_tuple(cda, &otuple, CTA_TUPLE_ORIG); | |
998 | if (err < 0) | |
999 | return err; | |
1000 | } | |
1001 | ||
1002 | if (cda[CTA_TUPLE_REPLY-1]) { | |
1003 | err = ctnetlink_parse_tuple(cda, &rtuple, CTA_TUPLE_REPLY); | |
1004 | if (err < 0) | |
1005 | return err; | |
1006 | } | |
1007 | ||
1008 | write_lock_bh(&ip_conntrack_lock); | |
1009 | if (cda[CTA_TUPLE_ORIG-1]) | |
1010 | h = __ip_conntrack_find(&otuple, NULL); | |
1011 | else if (cda[CTA_TUPLE_REPLY-1]) | |
1012 | h = __ip_conntrack_find(&rtuple, NULL); | |
1013 | ||
1014 | if (h == NULL) { | |
1015 | write_unlock_bh(&ip_conntrack_lock); | |
080774a2 HW |
1016 | err = -ENOENT; |
1017 | if (nlh->nlmsg_flags & NLM_F_CREATE) | |
1018 | err = ctnetlink_create_conntrack(cda, &otuple, &rtuple); | |
88aa0429 PN |
1019 | return err; |
1020 | } | |
1021 | /* implicit 'else' */ | |
1022 | ||
1023 | /* we only allow nat config for new conntracks */ | |
3726add7 | 1024 | if (cda[CTA_NAT_SRC-1] || cda[CTA_NAT_DST-1]) { |
88aa0429 | 1025 | err = -EINVAL; |
080774a2 | 1026 | goto out_unlock; |
080774a2 HW |
1027 | } |
1028 | ||
1029 | /* We manipulate the conntrack inside the global conntrack table lock, | |
1030 | * so there's no need to increase the refcount */ | |
080774a2 HW |
1031 | err = -EEXIST; |
1032 | if (!(nlh->nlmsg_flags & NLM_F_EXCL)) | |
1033 | err = ctnetlink_change_conntrack(tuplehash_to_ctrack(h), cda); | |
1034 | ||
1035 | out_unlock: | |
1036 | write_unlock_bh(&ip_conntrack_lock); | |
1037 | return err; | |
1038 | } | |
1039 | ||
1040 | /*********************************************************************** | |
1041 | * EXPECT | |
1042 | ***********************************************************************/ | |
1043 | ||
1044 | static inline int | |
1045 | ctnetlink_exp_dump_tuple(struct sk_buff *skb, | |
1046 | const struct ip_conntrack_tuple *tuple, | |
1047 | enum ctattr_expect type) | |
1048 | { | |
1049 | struct nfattr *nest_parms = NFA_NEST(skb, type); | |
1050 | ||
1051 | if (ctnetlink_dump_tuples(skb, tuple) < 0) | |
1052 | goto nfattr_failure; | |
1053 | ||
1054 | NFA_NEST_END(skb, nest_parms); | |
1055 | ||
1056 | return 0; | |
1057 | ||
1058 | nfattr_failure: | |
1059 | return -1; | |
1060 | } | |
1061 | ||
1cde6436 PNA |
1062 | static inline int |
1063 | ctnetlink_exp_dump_mask(struct sk_buff *skb, | |
1064 | const struct ip_conntrack_tuple *tuple, | |
1065 | const struct ip_conntrack_tuple *mask) | |
1066 | { | |
1067 | int ret; | |
1068 | struct ip_conntrack_protocol *proto; | |
1069 | struct nfattr *nest_parms = NFA_NEST(skb, CTA_EXPECT_MASK); | |
1070 | ||
1071 | ret = ctnetlink_dump_tuples_ip(skb, mask); | |
1072 | if (unlikely(ret < 0)) | |
1073 | goto nfattr_failure; | |
1074 | ||
1075 | proto = ip_conntrack_proto_find_get(tuple->dst.protonum); | |
1076 | ret = ctnetlink_dump_tuples_proto(skb, mask, proto); | |
1077 | ip_conntrack_proto_put(proto); | |
1078 | if (unlikely(ret < 0)) | |
1079 | goto nfattr_failure; | |
1080 | ||
1081 | NFA_NEST_END(skb, nest_parms); | |
1082 | ||
1083 | return 0; | |
1084 | ||
1085 | nfattr_failure: | |
1086 | return -1; | |
1087 | } | |
1088 | ||
080774a2 HW |
1089 | static inline int |
1090 | ctnetlink_exp_dump_expect(struct sk_buff *skb, | |
1091 | const struct ip_conntrack_expect *exp) | |
1092 | { | |
1444fc55 | 1093 | struct ip_conntrack *master = exp->master; |
cdcb71bf AV |
1094 | __be32 timeout = htonl((exp->timeout.expires - jiffies) / HZ); |
1095 | __be32 id = htonl(exp->id); | |
080774a2 HW |
1096 | |
1097 | if (ctnetlink_exp_dump_tuple(skb, &exp->tuple, CTA_EXPECT_TUPLE) < 0) | |
1098 | goto nfattr_failure; | |
1cde6436 | 1099 | if (ctnetlink_exp_dump_mask(skb, &exp->tuple, &exp->mask) < 0) |
080774a2 | 1100 | goto nfattr_failure; |
1444fc55 HW |
1101 | if (ctnetlink_exp_dump_tuple(skb, |
1102 | &master->tuplehash[IP_CT_DIR_ORIGINAL].tuple, | |
1103 | CTA_EXPECT_MASTER) < 0) | |
1104 | goto nfattr_failure; | |
080774a2 | 1105 | |
cdcb71bf AV |
1106 | NFA_PUT(skb, CTA_EXPECT_TIMEOUT, sizeof(__be32), &timeout); |
1107 | NFA_PUT(skb, CTA_EXPECT_ID, sizeof(__be32), &id); | |
080774a2 HW |
1108 | |
1109 | return 0; | |
1110 | ||
1111 | nfattr_failure: | |
1112 | return -1; | |
1113 | } | |
1114 | ||
1115 | static int | |
1116 | ctnetlink_exp_fill_info(struct sk_buff *skb, u32 pid, u32 seq, | |
1117 | int event, | |
1118 | int nowait, | |
1119 | const struct ip_conntrack_expect *exp) | |
1120 | { | |
1121 | struct nlmsghdr *nlh; | |
1122 | struct nfgenmsg *nfmsg; | |
1123 | unsigned char *b; | |
1124 | ||
1125 | b = skb->tail; | |
1126 | ||
1127 | event |= NFNL_SUBSYS_CTNETLINK_EXP << 8; | |
1128 | nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(struct nfgenmsg)); | |
1129 | nfmsg = NLMSG_DATA(nlh); | |
1130 | ||
1131 | nlh->nlmsg_flags = (nowait && pid) ? NLM_F_MULTI : 0; | |
1132 | nfmsg->nfgen_family = AF_INET; | |
1133 | nfmsg->version = NFNETLINK_V0; | |
1134 | nfmsg->res_id = 0; | |
1135 | ||
1136 | if (ctnetlink_exp_dump_expect(skb, exp) < 0) | |
1137 | goto nfattr_failure; | |
1138 | ||
1139 | nlh->nlmsg_len = skb->tail - b; | |
1140 | return skb->len; | |
1141 | ||
1142 | nlmsg_failure: | |
1143 | nfattr_failure: | |
1144 | skb_trim(skb, b - skb->data); | |
1145 | return -1; | |
1146 | } | |
1147 | ||
1148 | #ifdef CONFIG_IP_NF_CONNTRACK_EVENTS | |
1149 | static int ctnetlink_expect_event(struct notifier_block *this, | |
1150 | unsigned long events, void *ptr) | |
1151 | { | |
1152 | struct nlmsghdr *nlh; | |
1153 | struct nfgenmsg *nfmsg; | |
1154 | struct ip_conntrack_expect *exp = (struct ip_conntrack_expect *)ptr; | |
1155 | struct sk_buff *skb; | |
1156 | unsigned int type; | |
1157 | unsigned char *b; | |
1158 | int flags = 0; | |
080774a2 HW |
1159 | |
1160 | if (events & IPEXP_NEW) { | |
1161 | type = IPCTNL_MSG_EXP_NEW; | |
1162 | flags = NLM_F_CREATE|NLM_F_EXCL; | |
1163 | } else | |
1164 | return NOTIFY_DONE; | |
1165 | ||
b3a27bfb PNA |
1166 | if (!nfnetlink_has_listeners(NFNLGRP_CONNTRACK_EXP_NEW)) |
1167 | return NOTIFY_DONE; | |
1168 | ||
080774a2 HW |
1169 | skb = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC); |
1170 | if (!skb) | |
1171 | return NOTIFY_DONE; | |
1172 | ||
1173 | b = skb->tail; | |
1174 | ||
b633ad5f | 1175 | type |= NFNL_SUBSYS_CTNETLINK_EXP << 8; |
080774a2 HW |
1176 | nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(struct nfgenmsg)); |
1177 | nfmsg = NLMSG_DATA(nlh); | |
1178 | ||
1179 | nlh->nlmsg_flags = flags; | |
1180 | nfmsg->nfgen_family = AF_INET; | |
1181 | nfmsg->version = NFNETLINK_V0; | |
1182 | nfmsg->res_id = 0; | |
1183 | ||
1184 | if (ctnetlink_exp_dump_expect(skb, exp) < 0) | |
1185 | goto nfattr_failure; | |
1186 | ||
1187 | nlh->nlmsg_len = skb->tail - b; | |
ac6d439d | 1188 | nfnetlink_send(skb, 0, NFNLGRP_CONNTRACK_EXP_NEW, 0); |
080774a2 HW |
1189 | return NOTIFY_DONE; |
1190 | ||
1191 | nlmsg_failure: | |
1192 | nfattr_failure: | |
1193 | kfree_skb(skb); | |
1194 | return NOTIFY_DONE; | |
1195 | } | |
1196 | #endif | |
1197 | ||
1198 | static int | |
1199 | ctnetlink_exp_dump_table(struct sk_buff *skb, struct netlink_callback *cb) | |
1200 | { | |
1201 | struct ip_conntrack_expect *exp = NULL; | |
1202 | struct list_head *i; | |
1203 | u_int32_t *id = (u_int32_t *) &cb->args[0]; | |
1204 | ||
080774a2 | 1205 | read_lock_bh(&ip_conntrack_lock); |
ff21d577 | 1206 | list_for_each_prev(i, &ip_conntrack_expect_list) { |
080774a2 HW |
1207 | exp = (struct ip_conntrack_expect *) i; |
1208 | if (exp->id <= *id) | |
1209 | continue; | |
1210 | if (ctnetlink_exp_fill_info(skb, NETLINK_CB(cb->skb).pid, | |
1211 | cb->nlh->nlmsg_seq, | |
1212 | IPCTNL_MSG_EXP_NEW, | |
1213 | 1, exp) < 0) | |
1214 | goto out; | |
1215 | *id = exp->id; | |
1216 | } | |
1217 | out: | |
1218 | read_unlock_bh(&ip_conntrack_lock); | |
1219 | ||
080774a2 HW |
1220 | return skb->len; |
1221 | } | |
1222 | ||
56558208 | 1223 | static const size_t cta_min_exp[CTA_EXPECT_MAX] = { |
cdcb71bf AV |
1224 | [CTA_EXPECT_TIMEOUT-1] = sizeof(__be32), |
1225 | [CTA_EXPECT_ID-1] = sizeof(__be32) | |
56558208 PNA |
1226 | }; |
1227 | ||
080774a2 HW |
1228 | static int |
1229 | ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb, | |
1230 | struct nlmsghdr *nlh, struct nfattr *cda[], int *errp) | |
1231 | { | |
1232 | struct ip_conntrack_tuple tuple; | |
1233 | struct ip_conntrack_expect *exp; | |
1234 | struct sk_buff *skb2; | |
1235 | int err = 0; | |
1236 | ||
56558208 PNA |
1237 | if (nfattr_bad_size(cda, CTA_EXPECT_MAX, cta_min_exp)) |
1238 | return -EINVAL; | |
1239 | ||
080774a2 HW |
1240 | if (nlh->nlmsg_flags & NLM_F_DUMP) { |
1241 | struct nfgenmsg *msg = NLMSG_DATA(nlh); | |
1242 | u32 rlen; | |
1243 | ||
1244 | if (msg->nfgen_family != AF_INET) | |
1245 | return -EAFNOSUPPORT; | |
1246 | ||
1247 | if ((*errp = netlink_dump_start(ctnl, skb, nlh, | |
1248 | ctnetlink_exp_dump_table, | |
1249 | ctnetlink_done)) != 0) | |
1250 | return -EINVAL; | |
1251 | rlen = NLMSG_ALIGN(nlh->nlmsg_len); | |
1252 | if (rlen > skb->len) | |
1253 | rlen = skb->len; | |
1254 | skb_pull(skb, rlen); | |
1255 | return 0; | |
1256 | } | |
1257 | ||
1444fc55 HW |
1258 | if (cda[CTA_EXPECT_MASTER-1]) |
1259 | err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_MASTER); | |
080774a2 HW |
1260 | else |
1261 | return -EINVAL; | |
1262 | ||
1263 | if (err < 0) | |
1264 | return err; | |
1265 | ||
468ec44b | 1266 | exp = ip_conntrack_expect_find_get(&tuple); |
080774a2 HW |
1267 | if (!exp) |
1268 | return -ENOENT; | |
1269 | ||
a856a19a | 1270 | if (cda[CTA_EXPECT_ID-1]) { |
cdcb71bf | 1271 | __be32 id = *(__be32 *)NFA_DATA(cda[CTA_EXPECT_ID-1]); |
a856a19a PNA |
1272 | if (exp->id != ntohl(id)) { |
1273 | ip_conntrack_expect_put(exp); | |
1274 | return -ENOENT; | |
1275 | } | |
1276 | } | |
1277 | ||
080774a2 HW |
1278 | err = -ENOMEM; |
1279 | skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); | |
1280 | if (!skb2) | |
1281 | goto out; | |
4e9b8269 | 1282 | |
080774a2 HW |
1283 | err = ctnetlink_exp_fill_info(skb2, NETLINK_CB(skb).pid, |
1284 | nlh->nlmsg_seq, IPCTNL_MSG_EXP_NEW, | |
1285 | 1, exp); | |
1286 | if (err <= 0) | |
0f81eb4d | 1287 | goto free; |
080774a2 HW |
1288 | |
1289 | ip_conntrack_expect_put(exp); | |
1290 | ||
0f81eb4d | 1291 | return netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT); |
080774a2 | 1292 | |
0f81eb4d HW |
1293 | free: |
1294 | kfree_skb(skb2); | |
080774a2 HW |
1295 | out: |
1296 | ip_conntrack_expect_put(exp); | |
080774a2 HW |
1297 | return err; |
1298 | } | |
1299 | ||
1300 | static int | |
1301 | ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb, | |
1302 | struct nlmsghdr *nlh, struct nfattr *cda[], int *errp) | |
1303 | { | |
1304 | struct ip_conntrack_expect *exp, *tmp; | |
1305 | struct ip_conntrack_tuple tuple; | |
1306 | struct ip_conntrack_helper *h; | |
1307 | int err; | |
1308 | ||
56558208 PNA |
1309 | if (nfattr_bad_size(cda, CTA_EXPECT_MAX, cta_min_exp)) |
1310 | return -EINVAL; | |
1311 | ||
1444fc55 HW |
1312 | if (cda[CTA_EXPECT_TUPLE-1]) { |
1313 | /* delete a single expect by tuple */ | |
1314 | err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_TUPLE); | |
1315 | if (err < 0) | |
1316 | return err; | |
1317 | ||
1318 | /* bump usage count to 2 */ | |
468ec44b | 1319 | exp = ip_conntrack_expect_find_get(&tuple); |
1444fc55 HW |
1320 | if (!exp) |
1321 | return -ENOENT; | |
1322 | ||
1323 | if (cda[CTA_EXPECT_ID-1]) { | |
cdcb71bf AV |
1324 | __be32 id = |
1325 | *(__be32 *)NFA_DATA(cda[CTA_EXPECT_ID-1]); | |
1444fc55 HW |
1326 | if (exp->id != ntohl(id)) { |
1327 | ip_conntrack_expect_put(exp); | |
1328 | return -ENOENT; | |
1329 | } | |
1330 | } | |
1331 | ||
1332 | /* after list removal, usage count == 1 */ | |
1333 | ip_conntrack_unexpect_related(exp); | |
1334 | /* have to put what we 'get' above. | |
1335 | * after this line usage count == 0 */ | |
1336 | ip_conntrack_expect_put(exp); | |
1337 | } else if (cda[CTA_EXPECT_HELP_NAME-1]) { | |
1338 | char *name = NFA_DATA(cda[CTA_EXPECT_HELP_NAME-1]); | |
080774a2 HW |
1339 | |
1340 | /* delete all expectations for this helper */ | |
1341 | write_lock_bh(&ip_conntrack_lock); | |
1342 | h = __ip_conntrack_helper_find_byname(name); | |
1343 | if (!h) { | |
1344 | write_unlock_bh(&ip_conntrack_lock); | |
1345 | return -EINVAL; | |
1346 | } | |
1347 | list_for_each_entry_safe(exp, tmp, &ip_conntrack_expect_list, | |
1348 | list) { | |
1349 | if (exp->master->helper == h | |
49719eb3 PNA |
1350 | && del_timer(&exp->timeout)) { |
1351 | ip_ct_unlink_expect(exp); | |
1352 | ip_conntrack_expect_put(exp); | |
1353 | } | |
080774a2 | 1354 | } |
9fb9cbb1 | 1355 | write_unlock_bh(&ip_conntrack_lock); |
080774a2 HW |
1356 | } else { |
1357 | /* This basically means we have to flush everything*/ | |
1358 | write_lock_bh(&ip_conntrack_lock); | |
1359 | list_for_each_entry_safe(exp, tmp, &ip_conntrack_expect_list, | |
1360 | list) { | |
49719eb3 PNA |
1361 | if (del_timer(&exp->timeout)) { |
1362 | ip_ct_unlink_expect(exp); | |
1363 | ip_conntrack_expect_put(exp); | |
1364 | } | |
080774a2 HW |
1365 | } |
1366 | write_unlock_bh(&ip_conntrack_lock); | |
080774a2 HW |
1367 | } |
1368 | ||
080774a2 HW |
1369 | return 0; |
1370 | } | |
1371 | static int | |
1372 | ctnetlink_change_expect(struct ip_conntrack_expect *x, struct nfattr *cda[]) | |
1373 | { | |
1374 | return -EOPNOTSUPP; | |
1375 | } | |
1376 | ||
1377 | static int | |
1378 | ctnetlink_create_expect(struct nfattr *cda[]) | |
1379 | { | |
1380 | struct ip_conntrack_tuple tuple, mask, master_tuple; | |
1381 | struct ip_conntrack_tuple_hash *h = NULL; | |
1382 | struct ip_conntrack_expect *exp; | |
1383 | struct ip_conntrack *ct; | |
1384 | int err = 0; | |
1385 | ||
1444fc55 | 1386 | /* caller guarantees that those three CTA_EXPECT_* exist */ |
080774a2 HW |
1387 | err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_TUPLE); |
1388 | if (err < 0) | |
1389 | return err; | |
bd9a26b7 | 1390 | err = ctnetlink_parse_tuple(cda, &mask, CTA_EXPECT_MASK); |
080774a2 HW |
1391 | if (err < 0) |
1392 | return err; | |
1444fc55 | 1393 | err = ctnetlink_parse_tuple(cda, &master_tuple, CTA_EXPECT_MASTER); |
080774a2 HW |
1394 | if (err < 0) |
1395 | return err; | |
1396 | ||
1397 | /* Look for master conntrack of this expectation */ | |
1398 | h = ip_conntrack_find_get(&master_tuple, NULL); | |
1399 | if (!h) | |
1400 | return -ENOENT; | |
1401 | ct = tuplehash_to_ctrack(h); | |
1402 | ||
1403 | if (!ct->helper) { | |
1404 | /* such conntrack hasn't got any helper, abort */ | |
1405 | err = -EINVAL; | |
1406 | goto out; | |
1407 | } | |
1408 | ||
1409 | exp = ip_conntrack_expect_alloc(ct); | |
1410 | if (!exp) { | |
1411 | err = -ENOMEM; | |
1412 | goto out; | |
1413 | } | |
1414 | ||
1415 | exp->expectfn = NULL; | |
2248bcfc | 1416 | exp->flags = 0; |
080774a2 HW |
1417 | exp->master = ct; |
1418 | memcpy(&exp->tuple, &tuple, sizeof(struct ip_conntrack_tuple)); | |
1419 | memcpy(&exp->mask, &mask, sizeof(struct ip_conntrack_tuple)); | |
1420 | ||
1421 | err = ip_conntrack_expect_related(exp); | |
1422 | ip_conntrack_expect_put(exp); | |
1423 | ||
1424 | out: | |
1425 | ip_conntrack_put(tuplehash_to_ctrack(h)); | |
1426 | return err; | |
1427 | } | |
1428 | ||
1429 | static int | |
1430 | ctnetlink_new_expect(struct sock *ctnl, struct sk_buff *skb, | |
1431 | struct nlmsghdr *nlh, struct nfattr *cda[], int *errp) | |
1432 | { | |
1433 | struct ip_conntrack_tuple tuple; | |
1434 | struct ip_conntrack_expect *exp; | |
1435 | int err = 0; | |
1436 | ||
56558208 PNA |
1437 | if (nfattr_bad_size(cda, CTA_EXPECT_MAX, cta_min_exp)) |
1438 | return -EINVAL; | |
1439 | ||
1444fc55 HW |
1440 | if (!cda[CTA_EXPECT_TUPLE-1] |
1441 | || !cda[CTA_EXPECT_MASK-1] | |
1442 | || !cda[CTA_EXPECT_MASTER-1]) | |
080774a2 HW |
1443 | return -EINVAL; |
1444 | ||
1445 | err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_TUPLE); | |
1446 | if (err < 0) | |
1447 | return err; | |
1448 | ||
1449 | write_lock_bh(&ip_conntrack_lock); | |
1450 | exp = __ip_conntrack_expect_find(&tuple); | |
1451 | ||
1452 | if (!exp) { | |
1453 | write_unlock_bh(&ip_conntrack_lock); | |
1454 | err = -ENOENT; | |
1455 | if (nlh->nlmsg_flags & NLM_F_CREATE) | |
1456 | err = ctnetlink_create_expect(cda); | |
1457 | return err; | |
1458 | } | |
1459 | ||
1460 | err = -EEXIST; | |
1461 | if (!(nlh->nlmsg_flags & NLM_F_EXCL)) | |
1462 | err = ctnetlink_change_expect(exp, cda); | |
1463 | write_unlock_bh(&ip_conntrack_lock); | |
1464 | ||
080774a2 HW |
1465 | return err; |
1466 | } | |
1467 | ||
1468 | #ifdef CONFIG_IP_NF_CONNTRACK_EVENTS | |
1469 | static struct notifier_block ctnl_notifier = { | |
1470 | .notifier_call = ctnetlink_conntrack_event, | |
1471 | }; | |
1472 | ||
1473 | static struct notifier_block ctnl_notifier_exp = { | |
1474 | .notifier_call = ctnetlink_expect_event, | |
1475 | }; | |
1476 | #endif | |
1477 | ||
1478 | static struct nfnl_callback ctnl_cb[IPCTNL_MSG_MAX] = { | |
1479 | [IPCTNL_MSG_CT_NEW] = { .call = ctnetlink_new_conntrack, | |
37d2e7a2 | 1480 | .attr_count = CTA_MAX, }, |
080774a2 | 1481 | [IPCTNL_MSG_CT_GET] = { .call = ctnetlink_get_conntrack, |
37d2e7a2 | 1482 | .attr_count = CTA_MAX, }, |
080774a2 | 1483 | [IPCTNL_MSG_CT_DELETE] = { .call = ctnetlink_del_conntrack, |
37d2e7a2 | 1484 | .attr_count = CTA_MAX, }, |
080774a2 | 1485 | [IPCTNL_MSG_CT_GET_CTRZERO] = { .call = ctnetlink_get_conntrack, |
37d2e7a2 | 1486 | .attr_count = CTA_MAX, }, |
080774a2 HW |
1487 | }; |
1488 | ||
28b19d99 | 1489 | static struct nfnl_callback ctnl_exp_cb[IPCTNL_MSG_EXP_MAX] = { |
080774a2 | 1490 | [IPCTNL_MSG_EXP_GET] = { .call = ctnetlink_get_expect, |
37d2e7a2 | 1491 | .attr_count = CTA_EXPECT_MAX, }, |
080774a2 | 1492 | [IPCTNL_MSG_EXP_NEW] = { .call = ctnetlink_new_expect, |
37d2e7a2 | 1493 | .attr_count = CTA_EXPECT_MAX, }, |
080774a2 | 1494 | [IPCTNL_MSG_EXP_DELETE] = { .call = ctnetlink_del_expect, |
37d2e7a2 | 1495 | .attr_count = CTA_EXPECT_MAX, }, |
080774a2 HW |
1496 | }; |
1497 | ||
1498 | static struct nfnetlink_subsystem ctnl_subsys = { | |
1499 | .name = "conntrack", | |
1500 | .subsys_id = NFNL_SUBSYS_CTNETLINK, | |
1501 | .cb_count = IPCTNL_MSG_MAX, | |
080774a2 HW |
1502 | .cb = ctnl_cb, |
1503 | }; | |
1504 | ||
1505 | static struct nfnetlink_subsystem ctnl_exp_subsys = { | |
1506 | .name = "conntrack_expect", | |
1507 | .subsys_id = NFNL_SUBSYS_CTNETLINK_EXP, | |
1508 | .cb_count = IPCTNL_MSG_EXP_MAX, | |
080774a2 HW |
1509 | .cb = ctnl_exp_cb, |
1510 | }; | |
1511 | ||
119a3184 | 1512 | MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTNETLINK); |
34f9a2e4 | 1513 | MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTNETLINK_EXP); |
119a3184 | 1514 | |
080774a2 HW |
1515 | static int __init ctnetlink_init(void) |
1516 | { | |
1517 | int ret; | |
1518 | ||
1519 | printk("ctnetlink v%s: registering with nfnetlink.\n", version); | |
1520 | ret = nfnetlink_subsys_register(&ctnl_subsys); | |
1521 | if (ret < 0) { | |
1522 | printk("ctnetlink_init: cannot register with nfnetlink.\n"); | |
1523 | goto err_out; | |
1524 | } | |
1525 | ||
1526 | ret = nfnetlink_subsys_register(&ctnl_exp_subsys); | |
1527 | if (ret < 0) { | |
1528 | printk("ctnetlink_init: cannot register exp with nfnetlink.\n"); | |
1529 | goto err_unreg_subsys; | |
1530 | } | |
1531 | ||
1532 | #ifdef CONFIG_IP_NF_CONNTRACK_EVENTS | |
1533 | ret = ip_conntrack_register_notifier(&ctnl_notifier); | |
1534 | if (ret < 0) { | |
1535 | printk("ctnetlink_init: cannot register notifier.\n"); | |
1536 | goto err_unreg_exp_subsys; | |
1537 | } | |
1538 | ||
1539 | ret = ip_conntrack_expect_register_notifier(&ctnl_notifier_exp); | |
1540 | if (ret < 0) { | |
1541 | printk("ctnetlink_init: cannot expect register notifier.\n"); | |
1542 | goto err_unreg_notifier; | |
1543 | } | |
1544 | #endif | |
1545 | ||
1546 | return 0; | |
1547 | ||
1548 | #ifdef CONFIG_IP_NF_CONNTRACK_EVENTS | |
1549 | err_unreg_notifier: | |
1550 | ip_conntrack_unregister_notifier(&ctnl_notifier); | |
1551 | err_unreg_exp_subsys: | |
1552 | nfnetlink_subsys_unregister(&ctnl_exp_subsys); | |
1553 | #endif | |
1554 | err_unreg_subsys: | |
1555 | nfnetlink_subsys_unregister(&ctnl_subsys); | |
1556 | err_out: | |
1557 | return ret; | |
1558 | } | |
1559 | ||
1560 | static void __exit ctnetlink_exit(void) | |
1561 | { | |
1562 | printk("ctnetlink: unregistering from nfnetlink.\n"); | |
1563 | ||
1564 | #ifdef CONFIG_IP_NF_CONNTRACK_EVENTS | |
e64a70be | 1565 | ip_conntrack_expect_unregister_notifier(&ctnl_notifier_exp); |
080774a2 HW |
1566 | ip_conntrack_unregister_notifier(&ctnl_notifier); |
1567 | #endif | |
1568 | ||
1569 | nfnetlink_subsys_unregister(&ctnl_exp_subsys); | |
1570 | nfnetlink_subsys_unregister(&ctnl_subsys); | |
1571 | return; | |
1572 | } | |
1573 | ||
1574 | module_init(ctnetlink_init); | |
1575 | module_exit(ctnetlink_exit); |