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