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