blob: 262d0d44ec1b5924aff8a3d7efe4ec6a1fbb2733 [file] [log] [blame]
Harald Welte080774a2005-08-09 19:32:58 -07001/* 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>
Pablo Neira Ayuso1cde6432006-03-22 13:54:15 -08007 * (C) 2005-2006 by Pablo Neira Ayuso <pablo@eurodev.net>
Harald Welte080774a2005-08-09 19:32:58 -07008 *
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>
Benoit Boissinotde919822005-11-23 19:03:46 -080030#include <linux/interrupt.h>
Harald Welte080774a2005-08-09 19:32:58 -070031#include <linux/notifier.h>
Harald Welte080774a2005-08-09 19:32:58 -070032
33#include <linux/netfilter.h>
Harald Welte080774a2005-08-09 19:32:58 -070034#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
43MODULE_LICENSE("GPL");
44
45static char __initdata version[] = "0.90";
46
Harald Welte080774a2005-08-09 19:32:58 -070047static inline int
48ctnetlink_dump_tuples_proto(struct sk_buff *skb,
Pablo Neira Ayuso1cde6432006-03-22 13:54:15 -080049 const struct ip_conntrack_tuple *tuple,
50 struct ip_conntrack_protocol *proto)
Harald Welte080774a2005-08-09 19:32:58 -070051{
Yasuyuki Kozakaieaae4fa2005-11-09 12:58:46 -080052 int ret = 0;
Pablo Neira Ayuso1cde6432006-03-22 13:54:15 -080053 struct nfattr *nest_parms = NFA_NEST(skb, CTA_TUPLE_PROTO);
Harald Welte080774a2005-08-09 19:32:58 -070054
55 NFA_PUT(skb, CTA_PROTO_NUM, sizeof(u_int8_t), &tuple->dst.protonum);
56
Pablo Neira Ayuso00cb2772005-11-22 14:54:34 -080057 if (likely(proto->tuple_to_nfattr))
Yasuyuki Kozakaieaae4fa2005-11-09 12:58:46 -080058 ret = proto->tuple_to_nfattr(skb, tuple);
Pablo Neira Ayuso00cb2772005-11-22 14:54:34 -080059
Pablo Neira Ayuso1cde6432006-03-22 13:54:15 -080060 NFA_NEST_END(skb, nest_parms);
Harald Welte080774a2005-08-09 19:32:58 -070061
Yasuyuki Kozakaieaae4fa2005-11-09 12:58:46 -080062 return ret;
Harald Welte080774a2005-08-09 19:32:58 -070063
64nfattr_failure:
65 return -1;
66}
67
68static inline int
Pablo Neira Ayuso1cde6432006-03-22 13:54:15 -080069ctnetlink_dump_tuples_ip(struct sk_buff *skb,
70 const struct ip_conntrack_tuple *tuple)
Harald Welte080774a2005-08-09 19:32:58 -070071{
Pablo Neira Ayuso1cde6432006-03-22 13:54:15 -080072 struct nfattr *nest_parms = NFA_NEST(skb, CTA_TUPLE_IP);
Harald Welte080774a2005-08-09 19:32:58 -070073
Al Virocdcb71b2006-09-28 14:21:37 -070074 NFA_PUT(skb, CTA_IP_V4_SRC, sizeof(__be32), &tuple->src.ip);
75 NFA_PUT(skb, CTA_IP_V4_DST, sizeof(__be32), &tuple->dst.ip);
Pablo Neira Ayuso1cde6432006-03-22 13:54:15 -080076
Harald Welte080774a2005-08-09 19:32:58 -070077 NFA_NEST_END(skb, nest_parms);
78
Pablo Neira Ayuso1cde6432006-03-22 13:54:15 -080079 return 0;
Harald Welte080774a2005-08-09 19:32:58 -070080
81nfattr_failure:
82 return -1;
83}
84
85static inline int
Pablo Neira Ayuso1cde6432006-03-22 13:54:15 -080086ctnetlink_dump_tuples(struct sk_buff *skb,
87 const struct ip_conntrack_tuple *tuple)
88{
89 int ret;
90 struct ip_conntrack_protocol *proto;
91
92 ret = ctnetlink_dump_tuples_ip(skb, tuple);
93 if (unlikely(ret < 0))
94 return ret;
95
96 proto = ip_conntrack_proto_find_get(tuple->dst.protonum);
97 ret = ctnetlink_dump_tuples_proto(skb, tuple, proto);
98 ip_conntrack_proto_put(proto);
99
100 return ret;
101}
102
103static inline int
Harald Welte080774a2005-08-09 19:32:58 -0700104ctnetlink_dump_status(struct sk_buff *skb, const struct ip_conntrack *ct)
105{
Al Virocdcb71b2006-09-28 14:21:37 -0700106 __be32 status = htonl((u_int32_t) ct->status);
Harald Welte080774a2005-08-09 19:32:58 -0700107 NFA_PUT(skb, CTA_STATUS, sizeof(status), &status);
108 return 0;
109
110nfattr_failure:
111 return -1;
112}
113
114static inline int
115ctnetlink_dump_timeout(struct sk_buff *skb, const struct ip_conntrack *ct)
116{
117 long timeout_l = ct->timeout.expires - jiffies;
Al Virocdcb71b2006-09-28 14:21:37 -0700118 __be32 timeout;
Harald Welte080774a2005-08-09 19:32:58 -0700119
120 if (timeout_l < 0)
121 timeout = 0;
122 else
123 timeout = htonl(timeout_l / HZ);
124
125 NFA_PUT(skb, CTA_TIMEOUT, sizeof(timeout), &timeout);
126 return 0;
127
128nfattr_failure:
129 return -1;
130}
131
132static inline int
133ctnetlink_dump_protoinfo(struct sk_buff *skb, const struct ip_conntrack *ct)
134{
135 struct ip_conntrack_protocol *proto = ip_conntrack_proto_find_get(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum);
136
137 struct nfattr *nest_proto;
138 int ret;
Pablo Neira Ayuso00cb2772005-11-22 14:54:34 -0800139
140 if (!proto->to_nfattr) {
141 ip_conntrack_proto_put(proto);
Harald Welte080774a2005-08-09 19:32:58 -0700142 return 0;
Pablo Neira Ayuso00cb2772005-11-22 14:54:34 -0800143 }
Harald Welte080774a2005-08-09 19:32:58 -0700144
145 nest_proto = NFA_NEST(skb, CTA_PROTOINFO);
146
147 ret = proto->to_nfattr(skb, nest_proto, ct);
148
149 ip_conntrack_proto_put(proto);
150
151 NFA_NEST_END(skb, nest_proto);
152
153 return ret;
154
155nfattr_failure:
156 return -1;
157}
158
159static inline int
160ctnetlink_dump_helpinfo(struct sk_buff *skb, const struct ip_conntrack *ct)
161{
162 struct nfattr *nest_helper;
163
164 if (!ct->helper)
165 return 0;
166
167 nest_helper = NFA_NEST(skb, CTA_HELP);
Patrick McHardya9b305c2006-01-05 12:20:02 -0800168 NFA_PUT(skb, CTA_HELP_NAME, strlen(ct->helper->name), ct->helper->name);
Harald Welte080774a2005-08-09 19:32:58 -0700169
170 if (ct->helper->to_nfattr)
171 ct->helper->to_nfattr(skb, ct);
172
173 NFA_NEST_END(skb, nest_helper);
174
175 return 0;
176
177nfattr_failure:
178 return -1;
179}
180
181#ifdef CONFIG_IP_NF_CT_ACCT
182static inline int
183ctnetlink_dump_counters(struct sk_buff *skb, const struct ip_conntrack *ct,
184 enum ip_conntrack_dir dir)
185{
186 enum ctattr_type type = dir ? CTA_COUNTERS_REPLY: CTA_COUNTERS_ORIG;
187 struct nfattr *nest_count = NFA_NEST(skb, type);
Al Virocdcb71b2006-09-28 14:21:37 -0700188 __be32 tmp;
Harald Welte080774a2005-08-09 19:32:58 -0700189
Harald Weltea051a8f2005-10-10 21:21:10 -0700190 tmp = htonl(ct->counters[dir].packets);
Al Virocdcb71b2006-09-28 14:21:37 -0700191 NFA_PUT(skb, CTA_COUNTERS32_PACKETS, sizeof(__be32), &tmp);
Harald Welte080774a2005-08-09 19:32:58 -0700192
Harald Weltea051a8f2005-10-10 21:21:10 -0700193 tmp = htonl(ct->counters[dir].bytes);
Al Virocdcb71b2006-09-28 14:21:37 -0700194 NFA_PUT(skb, CTA_COUNTERS32_BYTES, sizeof(__be32), &tmp);
Harald Welte080774a2005-08-09 19:32:58 -0700195
196 NFA_NEST_END(skb, nest_count);
197
198 return 0;
199
200nfattr_failure:
201 return -1;
202}
203#else
204#define ctnetlink_dump_counters(a, b, c) (0)
205#endif
206
207#ifdef CONFIG_IP_NF_CONNTRACK_MARK
208static inline int
209ctnetlink_dump_mark(struct sk_buff *skb, const struct ip_conntrack *ct)
210{
Al Virocdcb71b2006-09-28 14:21:37 -0700211 __be32 mark = htonl(ct->mark);
Harald Welte080774a2005-08-09 19:32:58 -0700212
Al Virocdcb71b2006-09-28 14:21:37 -0700213 NFA_PUT(skb, CTA_MARK, sizeof(__be32), &mark);
Harald Welte080774a2005-08-09 19:32:58 -0700214 return 0;
215
216nfattr_failure:
217 return -1;
218}
219#else
220#define ctnetlink_dump_mark(a, b) (0)
221#endif
222
223static inline int
224ctnetlink_dump_id(struct sk_buff *skb, const struct ip_conntrack *ct)
225{
Al Virocdcb71b2006-09-28 14:21:37 -0700226 __be32 id = htonl(ct->id);
227 NFA_PUT(skb, CTA_ID, sizeof(__be32), &id);
Harald Welte080774a2005-08-09 19:32:58 -0700228 return 0;
229
230nfattr_failure:
231 return -1;
232}
233
234static inline int
235ctnetlink_dump_use(struct sk_buff *skb, const struct ip_conntrack *ct)
236{
Al Virocdcb71b2006-09-28 14:21:37 -0700237 __be32 use = htonl(atomic_read(&ct->ct_general.use));
Harald Welte080774a2005-08-09 19:32:58 -0700238
Al Virocdcb71b2006-09-28 14:21:37 -0700239 NFA_PUT(skb, CTA_USE, sizeof(__be32), &use);
Harald Welte080774a2005-08-09 19:32:58 -0700240 return 0;
241
242nfattr_failure:
243 return -1;
244}
245
246#define tuple(ct, dir) (&(ct)->tuplehash[dir].tuple)
247
248static int
249ctnetlink_fill_info(struct sk_buff *skb, u32 pid, u32 seq,
250 int event, int nowait,
251 const struct ip_conntrack *ct)
252{
253 struct nlmsghdr *nlh;
254 struct nfgenmsg *nfmsg;
255 struct nfattr *nest_parms;
256 unsigned char *b;
257
258 b = skb->tail;
259
260 event |= NFNL_SUBSYS_CTNETLINK << 8;
261 nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(struct nfgenmsg));
262 nfmsg = NLMSG_DATA(nlh);
263
264 nlh->nlmsg_flags = (nowait && pid) ? NLM_F_MULTI : 0;
265 nfmsg->nfgen_family = AF_INET;
266 nfmsg->version = NFNETLINK_V0;
267 nfmsg->res_id = 0;
268
269 nest_parms = NFA_NEST(skb, CTA_TUPLE_ORIG);
270 if (ctnetlink_dump_tuples(skb, tuple(ct, IP_CT_DIR_ORIGINAL)) < 0)
271 goto nfattr_failure;
272 NFA_NEST_END(skb, nest_parms);
273
274 nest_parms = NFA_NEST(skb, CTA_TUPLE_REPLY);
275 if (ctnetlink_dump_tuples(skb, tuple(ct, IP_CT_DIR_REPLY)) < 0)
276 goto nfattr_failure;
277 NFA_NEST_END(skb, nest_parms);
278
279 if (ctnetlink_dump_status(skb, ct) < 0 ||
280 ctnetlink_dump_timeout(skb, ct) < 0 ||
281 ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 ||
282 ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0 ||
283 ctnetlink_dump_protoinfo(skb, ct) < 0 ||
284 ctnetlink_dump_helpinfo(skb, ct) < 0 ||
285 ctnetlink_dump_mark(skb, ct) < 0 ||
286 ctnetlink_dump_id(skb, ct) < 0 ||
287 ctnetlink_dump_use(skb, ct) < 0)
288 goto nfattr_failure;
289
290 nlh->nlmsg_len = skb->tail - b;
291 return skb->len;
292
293nlmsg_failure:
294nfattr_failure:
295 skb_trim(skb, b - skb->data);
296 return -1;
297}
298
299#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS
300static int ctnetlink_conntrack_event(struct notifier_block *this,
301 unsigned long events, void *ptr)
302{
303 struct nlmsghdr *nlh;
304 struct nfgenmsg *nfmsg;
305 struct nfattr *nest_parms;
306 struct ip_conntrack *ct = (struct ip_conntrack *)ptr;
307 struct sk_buff *skb;
308 unsigned int type;
309 unsigned char *b;
Patrick McHardyac6d4392005-08-14 19:29:52 -0700310 unsigned int flags = 0, group;
Harald Welte080774a2005-08-09 19:32:58 -0700311
312 /* ignore our fake conntrack entry */
313 if (ct == &ip_conntrack_untracked)
314 return NOTIFY_DONE;
315
316 if (events & IPCT_DESTROY) {
317 type = IPCTNL_MSG_CT_DELETE;
Patrick McHardyac6d4392005-08-14 19:29:52 -0700318 group = NFNLGRP_CONNTRACK_DESTROY;
Pablo Neira Ayuso03683092006-01-05 12:18:08 -0800319 } else if (events & (IPCT_NEW | IPCT_RELATED)) {
Harald Welte080774a2005-08-09 19:32:58 -0700320 type = IPCTNL_MSG_CT_NEW;
321 flags = NLM_F_CREATE|NLM_F_EXCL;
322 /* dump everything */
323 events = ~0UL;
Patrick McHardyac6d4392005-08-14 19:29:52 -0700324 group = NFNLGRP_CONNTRACK_NEW;
Pablo Neira Ayuso1a315262006-08-22 00:32:23 -0700325 } else if (events & (IPCT_STATUS | IPCT_PROTOINFO)) {
Harald Welte080774a2005-08-09 19:32:58 -0700326 type = IPCTNL_MSG_CT_NEW;
Patrick McHardyac6d4392005-08-14 19:29:52 -0700327 group = NFNLGRP_CONNTRACK_UPDATE;
Pablo Neira Ayuso03683092006-01-05 12:18:08 -0800328 } else
329 return NOTIFY_DONE;
Patrick McHardya2427692006-03-20 18:03:59 -0800330
331 if (!nfnetlink_has_listeners(group))
332 return NOTIFY_DONE;
333
Harald Welte080774a2005-08-09 19:32:58 -0700334 skb = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC);
335 if (!skb)
336 return NOTIFY_DONE;
337
338 b = skb->tail;
339
340 type |= NFNL_SUBSYS_CTNETLINK << 8;
341 nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(struct nfgenmsg));
342 nfmsg = NLMSG_DATA(nlh);
343
344 nlh->nlmsg_flags = flags;
345 nfmsg->nfgen_family = AF_INET;
346 nfmsg->version = NFNETLINK_V0;
347 nfmsg->res_id = 0;
348
349 nest_parms = NFA_NEST(skb, CTA_TUPLE_ORIG);
350 if (ctnetlink_dump_tuples(skb, tuple(ct, IP_CT_DIR_ORIGINAL)) < 0)
351 goto nfattr_failure;
352 NFA_NEST_END(skb, nest_parms);
353
354 nest_parms = NFA_NEST(skb, CTA_TUPLE_REPLY);
355 if (ctnetlink_dump_tuples(skb, tuple(ct, IP_CT_DIR_REPLY)) < 0)
356 goto nfattr_failure;
357 NFA_NEST_END(skb, nest_parms);
358
359 /* NAT stuff is now a status flag */
360 if ((events & IPCT_STATUS || events & IPCT_NATINFO)
361 && ctnetlink_dump_status(skb, ct) < 0)
362 goto nfattr_failure;
363 if (events & IPCT_REFRESH
364 && ctnetlink_dump_timeout(skb, ct) < 0)
365 goto nfattr_failure;
366 if (events & IPCT_PROTOINFO
367 && ctnetlink_dump_protoinfo(skb, ct) < 0)
368 goto nfattr_failure;
369 if (events & IPCT_HELPINFO
370 && ctnetlink_dump_helpinfo(skb, ct) < 0)
371 goto nfattr_failure;
372
373 if (ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 ||
374 ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0)
375 goto nfattr_failure;
376
Pablo Neira Ayusob9a37e02006-08-22 00:31:49 -0700377 if (events & IPCT_MARK
378 && ctnetlink_dump_mark(skb, ct) < 0)
379 goto nfattr_failure;
380
Harald Welte080774a2005-08-09 19:32:58 -0700381 nlh->nlmsg_len = skb->tail - b;
Patrick McHardyac6d4392005-08-14 19:29:52 -0700382 nfnetlink_send(skb, 0, group, 0);
Harald Welte080774a2005-08-09 19:32:58 -0700383 return NOTIFY_DONE;
384
385nlmsg_failure:
386nfattr_failure:
387 kfree_skb(skb);
388 return NOTIFY_DONE;
389}
390#endif /* CONFIG_IP_NF_CONNTRACK_EVENTS */
391
392static int ctnetlink_done(struct netlink_callback *cb)
393{
Patrick McHardy89f2e212006-05-29 18:24:58 -0700394 if (cb->args[1])
395 ip_conntrack_put((struct ip_conntrack *)cb->args[1]);
Harald Welte080774a2005-08-09 19:32:58 -0700396 return 0;
397}
398
399static int
400ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
401{
Patrick McHardy89f2e212006-05-29 18:24:58 -0700402 struct ip_conntrack *ct, *last;
Harald Welte080774a2005-08-09 19:32:58 -0700403 struct ip_conntrack_tuple_hash *h;
404 struct list_head *i;
Harald Welte080774a2005-08-09 19:32:58 -0700405
Harald Welte080774a2005-08-09 19:32:58 -0700406 read_lock_bh(&ip_conntrack_lock);
Patrick McHardyd205dc42006-08-17 18:12:38 -0700407 last = (struct ip_conntrack *)cb->args[1];
Patrick McHardy89f2e212006-05-29 18:24:58 -0700408 for (; cb->args[0] < ip_conntrack_htable_size; cb->args[0]++) {
409restart:
Pablo Neira Ayusoff21d572005-08-09 20:06:42 -0700410 list_for_each_prev(i, &ip_conntrack_hash[cb->args[0]]) {
Harald Welte080774a2005-08-09 19:32:58 -0700411 h = (struct ip_conntrack_tuple_hash *) i;
412 if (DIRECTION(h) != IP_CT_DIR_ORIGINAL)
413 continue;
414 ct = tuplehash_to_ctrack(h);
Patrick McHardyd205dc42006-08-17 18:12:38 -0700415 if (cb->args[1]) {
416 if (ct != last)
Patrick McHardy89f2e212006-05-29 18:24:58 -0700417 continue;
Patrick McHardyd205dc42006-08-17 18:12:38 -0700418 cb->args[1] = 0;
Patrick McHardy89f2e212006-05-29 18:24:58 -0700419 }
Harald Welte080774a2005-08-09 19:32:58 -0700420 if (ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).pid,
421 cb->nlh->nlmsg_seq,
422 IPCTNL_MSG_CT_NEW,
Patrick McHardy89f2e212006-05-29 18:24:58 -0700423 1, ct) < 0) {
424 nf_conntrack_get(&ct->ct_general);
425 cb->args[1] = (unsigned long)ct;
Harald Welte080774a2005-08-09 19:32:58 -0700426 goto out;
Patrick McHardy89f2e212006-05-29 18:24:58 -0700427 }
Pablo Neira Ayuso01f34842006-09-20 12:00:45 -0700428#ifdef CONFIG_NF_CT_ACCT
429 if (NFNL_MSG_TYPE(cb->nlh->nlmsg_type) ==
430 IPCTNL_MSG_CT_GET_CTRZERO)
431 memset(&ct->counters, 0, sizeof(ct->counters));
432#endif
Patrick McHardy89f2e212006-05-29 18:24:58 -0700433 }
Patrick McHardyd205dc42006-08-17 18:12:38 -0700434 if (cb->args[1]) {
Patrick McHardy89f2e212006-05-29 18:24:58 -0700435 cb->args[1] = 0;
436 goto restart;
Harald Welte080774a2005-08-09 19:32:58 -0700437 }
438 }
Patrick McHardy89f2e212006-05-29 18:24:58 -0700439out:
Harald Welte080774a2005-08-09 19:32:58 -0700440 read_unlock_bh(&ip_conntrack_lock);
Patrick McHardyd205dc42006-08-17 18:12:38 -0700441 if (last)
442 ip_conntrack_put(last);
Harald Welte080774a2005-08-09 19:32:58 -0700443
Harald Welte080774a2005-08-09 19:32:58 -0700444 return skb->len;
445}
446
Pablo Neira Ayusodbd36ea2005-11-14 15:21:01 -0800447static const size_t cta_min_ip[CTA_IP_MAX] = {
Al Virocdcb71b2006-09-28 14:21:37 -0700448 [CTA_IP_V4_SRC-1] = sizeof(__be32),
449 [CTA_IP_V4_DST-1] = sizeof(__be32),
Harald Welte080774a2005-08-09 19:32:58 -0700450};
451
452static inline int
453ctnetlink_parse_tuple_ip(struct nfattr *attr, struct ip_conntrack_tuple *tuple)
454{
455 struct nfattr *tb[CTA_IP_MAX];
456
Harald Weltea2506c02005-11-09 12:59:13 -0800457 nfattr_parse_nested(tb, CTA_IP_MAX, attr);
Harald Welte080774a2005-08-09 19:32:58 -0700458
459 if (nfattr_bad_size(tb, CTA_IP_MAX, cta_min_ip))
460 return -EINVAL;
461
462 if (!tb[CTA_IP_V4_SRC-1])
463 return -EINVAL;
Al Virocdcb71b2006-09-28 14:21:37 -0700464 tuple->src.ip = *(__be32 *)NFA_DATA(tb[CTA_IP_V4_SRC-1]);
Harald Welte080774a2005-08-09 19:32:58 -0700465
466 if (!tb[CTA_IP_V4_DST-1])
467 return -EINVAL;
Al Virocdcb71b2006-09-28 14:21:37 -0700468 tuple->dst.ip = *(__be32 *)NFA_DATA(tb[CTA_IP_V4_DST-1]);
Harald Welte080774a2005-08-09 19:32:58 -0700469
Harald Welte080774a2005-08-09 19:32:58 -0700470 return 0;
Harald Welte080774a2005-08-09 19:32:58 -0700471}
472
Pablo Neira Ayusodbd36ea2005-11-14 15:21:01 -0800473static const size_t cta_min_proto[CTA_PROTO_MAX] = {
Patrick McHardy0be7fa92005-12-05 13:34:51 -0800474 [CTA_PROTO_NUM-1] = sizeof(u_int8_t),
Harald Welte080774a2005-08-09 19:32:58 -0700475 [CTA_PROTO_SRC_PORT-1] = sizeof(u_int16_t),
476 [CTA_PROTO_DST_PORT-1] = sizeof(u_int16_t),
477 [CTA_PROTO_ICMP_TYPE-1] = sizeof(u_int8_t),
478 [CTA_PROTO_ICMP_CODE-1] = sizeof(u_int8_t),
479 [CTA_PROTO_ICMP_ID-1] = sizeof(u_int16_t),
480};
481
482static inline int
483ctnetlink_parse_tuple_proto(struct nfattr *attr,
484 struct ip_conntrack_tuple *tuple)
485{
486 struct nfattr *tb[CTA_PROTO_MAX];
487 struct ip_conntrack_protocol *proto;
488 int ret = 0;
489
Harald Weltea2506c02005-11-09 12:59:13 -0800490 nfattr_parse_nested(tb, CTA_PROTO_MAX, attr);
Harald Welte080774a2005-08-09 19:32:58 -0700491
492 if (nfattr_bad_size(tb, CTA_PROTO_MAX, cta_min_proto))
493 return -EINVAL;
494
495 if (!tb[CTA_PROTO_NUM-1])
496 return -EINVAL;
Patrick McHardy0be7fa92005-12-05 13:34:51 -0800497 tuple->dst.protonum = *(u_int8_t *)NFA_DATA(tb[CTA_PROTO_NUM-1]);
Harald Welte080774a2005-08-09 19:32:58 -0700498
499 proto = ip_conntrack_proto_find_get(tuple->dst.protonum);
500
Pablo Neira Ayuso00cb2772005-11-22 14:54:34 -0800501 if (likely(proto->nfattr_to_tuple))
Harald Welte080774a2005-08-09 19:32:58 -0700502 ret = proto->nfattr_to_tuple(tb, tuple);
Pablo Neira Ayuso00cb2772005-11-22 14:54:34 -0800503
504 ip_conntrack_proto_put(proto);
Harald Welte080774a2005-08-09 19:32:58 -0700505
506 return ret;
Harald Welte080774a2005-08-09 19:32:58 -0700507}
508
509static inline int
510ctnetlink_parse_tuple(struct nfattr *cda[], struct ip_conntrack_tuple *tuple,
511 enum ctattr_tuple type)
512{
513 struct nfattr *tb[CTA_TUPLE_MAX];
514 int err;
515
Harald Welte080774a2005-08-09 19:32:58 -0700516 memset(tuple, 0, sizeof(*tuple));
517
Harald Weltea2506c02005-11-09 12:59:13 -0800518 nfattr_parse_nested(tb, CTA_TUPLE_MAX, cda[type-1]);
Harald Welte080774a2005-08-09 19:32:58 -0700519
520 if (!tb[CTA_TUPLE_IP-1])
521 return -EINVAL;
522
523 err = ctnetlink_parse_tuple_ip(tb[CTA_TUPLE_IP-1], tuple);
524 if (err < 0)
525 return err;
526
527 if (!tb[CTA_TUPLE_PROTO-1])
528 return -EINVAL;
529
530 err = ctnetlink_parse_tuple_proto(tb[CTA_TUPLE_PROTO-1], tuple);
531 if (err < 0)
532 return err;
533
534 /* orig and expect tuples get DIR_ORIGINAL */
535 if (type == CTA_TUPLE_REPLY)
536 tuple->dst.dir = IP_CT_DIR_REPLY;
537 else
538 tuple->dst.dir = IP_CT_DIR_ORIGINAL;
539
Harald Welte080774a2005-08-09 19:32:58 -0700540 return 0;
Harald Welte080774a2005-08-09 19:32:58 -0700541}
542
543#ifdef CONFIG_IP_NF_NAT_NEEDED
Pablo Neira Ayusodbd36ea2005-11-14 15:21:01 -0800544static const size_t cta_min_protonat[CTA_PROTONAT_MAX] = {
Harald Welte080774a2005-08-09 19:32:58 -0700545 [CTA_PROTONAT_PORT_MIN-1] = sizeof(u_int16_t),
546 [CTA_PROTONAT_PORT_MAX-1] = sizeof(u_int16_t),
547};
548
549static int ctnetlink_parse_nat_proto(struct nfattr *attr,
550 const struct ip_conntrack *ct,
551 struct ip_nat_range *range)
552{
553 struct nfattr *tb[CTA_PROTONAT_MAX];
554 struct ip_nat_protocol *npt;
555
Harald Weltea2506c02005-11-09 12:59:13 -0800556 nfattr_parse_nested(tb, CTA_PROTONAT_MAX, attr);
Harald Welte080774a2005-08-09 19:32:58 -0700557
558 if (nfattr_bad_size(tb, CTA_PROTONAT_MAX, cta_min_protonat))
Pablo Neira Ayusofe902a92005-11-09 13:03:09 -0800559 return -EINVAL;
Harald Welte080774a2005-08-09 19:32:58 -0700560
561 npt = ip_nat_proto_find_get(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum);
Harald Welte080774a2005-08-09 19:32:58 -0700562
563 if (!npt->nfattr_to_range) {
564 ip_nat_proto_put(npt);
565 return 0;
566 }
567
568 /* nfattr_to_range returns 1 if it parsed, 0 if not, neg. on error */
569 if (npt->nfattr_to_range(tb, range) > 0)
570 range->flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
571
572 ip_nat_proto_put(npt);
573
Harald Welte080774a2005-08-09 19:32:58 -0700574 return 0;
Harald Welte080774a2005-08-09 19:32:58 -0700575}
576
Pablo Neira Ayuso56558202005-11-14 15:22:11 -0800577static const size_t cta_min_nat[CTA_NAT_MAX] = {
Al Virocdcb71b2006-09-28 14:21:37 -0700578 [CTA_NAT_MINIP-1] = sizeof(__be32),
579 [CTA_NAT_MAXIP-1] = sizeof(__be32),
Pablo Neira Ayuso56558202005-11-14 15:22:11 -0800580};
581
Harald Welte080774a2005-08-09 19:32:58 -0700582static inline int
Patrick McHardy3726add2006-05-29 18:24:39 -0700583ctnetlink_parse_nat(struct nfattr *nat,
Harald Welte080774a2005-08-09 19:32:58 -0700584 const struct ip_conntrack *ct, struct ip_nat_range *range)
585{
586 struct nfattr *tb[CTA_NAT_MAX];
587 int err;
588
Harald Welte080774a2005-08-09 19:32:58 -0700589 memset(range, 0, sizeof(*range));
590
Patrick McHardy3726add2006-05-29 18:24:39 -0700591 nfattr_parse_nested(tb, CTA_NAT_MAX, nat);
Harald Welte080774a2005-08-09 19:32:58 -0700592
Pablo Neira Ayuso56558202005-11-14 15:22:11 -0800593 if (nfattr_bad_size(tb, CTA_NAT_MAX, cta_min_nat))
594 return -EINVAL;
595
Harald Welte080774a2005-08-09 19:32:58 -0700596 if (tb[CTA_NAT_MINIP-1])
Al Virocdcb71b2006-09-28 14:21:37 -0700597 range->min_ip = *(__be32 *)NFA_DATA(tb[CTA_NAT_MINIP-1]);
Harald Welte080774a2005-08-09 19:32:58 -0700598
599 if (!tb[CTA_NAT_MAXIP-1])
600 range->max_ip = range->min_ip;
601 else
Al Virocdcb71b2006-09-28 14:21:37 -0700602 range->max_ip = *(__be32 *)NFA_DATA(tb[CTA_NAT_MAXIP-1]);
Harald Welte080774a2005-08-09 19:32:58 -0700603
604 if (range->min_ip)
605 range->flags |= IP_NAT_RANGE_MAP_IPS;
606
607 if (!tb[CTA_NAT_PROTO-1])
608 return 0;
609
610 err = ctnetlink_parse_nat_proto(tb[CTA_NAT_PROTO-1], ct, range);
611 if (err < 0)
612 return err;
613
Harald Welte080774a2005-08-09 19:32:58 -0700614 return 0;
Harald Welte080774a2005-08-09 19:32:58 -0700615}
616#endif
617
618static inline int
619ctnetlink_parse_help(struct nfattr *attr, char **helper_name)
620{
621 struct nfattr *tb[CTA_HELP_MAX];
622
Harald Weltea2506c02005-11-09 12:59:13 -0800623 nfattr_parse_nested(tb, CTA_HELP_MAX, attr);
Harald Welte080774a2005-08-09 19:32:58 -0700624
625 if (!tb[CTA_HELP_NAME-1])
626 return -EINVAL;
627
628 *helper_name = NFA_DATA(tb[CTA_HELP_NAME-1]);
629
630 return 0;
Harald Welte080774a2005-08-09 19:32:58 -0700631}
632
Pablo Neira Ayuso56558202005-11-14 15:22:11 -0800633static const size_t cta_min[CTA_MAX] = {
Al Virocdcb71b2006-09-28 14:21:37 -0700634 [CTA_STATUS-1] = sizeof(__be32),
635 [CTA_TIMEOUT-1] = sizeof(__be32),
636 [CTA_MARK-1] = sizeof(__be32),
637 [CTA_USE-1] = sizeof(__be32),
638 [CTA_ID-1] = sizeof(__be32)
Pablo Neira Ayuso56558202005-11-14 15:22:11 -0800639};
640
Harald Welte080774a2005-08-09 19:32:58 -0700641static int
642ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb,
643 struct nlmsghdr *nlh, struct nfattr *cda[], int *errp)
644{
645 struct ip_conntrack_tuple_hash *h;
646 struct ip_conntrack_tuple tuple;
647 struct ip_conntrack *ct;
648 int err = 0;
649
Pablo Neira Ayuso56558202005-11-14 15:22:11 -0800650 if (nfattr_bad_size(cda, CTA_MAX, cta_min))
651 return -EINVAL;
652
Harald Welte080774a2005-08-09 19:32:58 -0700653 if (cda[CTA_TUPLE_ORIG-1])
654 err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_ORIG);
655 else if (cda[CTA_TUPLE_REPLY-1])
656 err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_REPLY);
657 else {
658 /* Flush the whole table */
659 ip_conntrack_flush();
660 return 0;
661 }
662
663 if (err < 0)
664 return err;
665
666 h = ip_conntrack_find_get(&tuple, NULL);
Pablo Neira Ayuso9ea8cfd2006-10-12 14:09:16 -0700667 if (!h)
Harald Welte080774a2005-08-09 19:32:58 -0700668 return -ENOENT;
Harald Welte080774a2005-08-09 19:32:58 -0700669
670 ct = tuplehash_to_ctrack(h);
671
672 if (cda[CTA_ID-1]) {
Al Virocdcb71b2006-09-28 14:21:37 -0700673 u_int32_t id = ntohl(*(__be32 *)NFA_DATA(cda[CTA_ID-1]));
Harald Welte080774a2005-08-09 19:32:58 -0700674 if (ct->id != id) {
675 ip_conntrack_put(ct);
676 return -ENOENT;
677 }
678 }
Patrick McHardy2fdf1fa2005-12-05 13:38:16 -0800679 if (del_timer(&ct->timeout))
Harald Welte080774a2005-08-09 19:32:58 -0700680 ct->timeout.function((unsigned long)ct);
Patrick McHardy2fdf1fa2005-12-05 13:38:16 -0800681
Harald Welte080774a2005-08-09 19:32:58 -0700682 ip_conntrack_put(ct);
Harald Welte080774a2005-08-09 19:32:58 -0700683
684 return 0;
685}
686
687static int
688ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb,
689 struct nlmsghdr *nlh, struct nfattr *cda[], int *errp)
690{
691 struct ip_conntrack_tuple_hash *h;
692 struct ip_conntrack_tuple tuple;
693 struct ip_conntrack *ct;
694 struct sk_buff *skb2 = NULL;
695 int err = 0;
696
Harald Welte080774a2005-08-09 19:32:58 -0700697 if (nlh->nlmsg_flags & NLM_F_DUMP) {
698 struct nfgenmsg *msg = NLMSG_DATA(nlh);
699 u32 rlen;
700
701 if (msg->nfgen_family != AF_INET)
702 return -EAFNOSUPPORT;
703
Pablo Neira Ayuso01f34842006-09-20 12:00:45 -0700704#ifndef CONFIG_IP_NF_CT_ACCT
705 if (NFNL_MSG_TYPE(nlh->nlmsg_type) == IPCTNL_MSG_CT_GET_CTRZERO)
Harald Welte080774a2005-08-09 19:32:58 -0700706 return -ENOTSUPP;
707#endif
Pablo Neira Ayuso01f34842006-09-20 12:00:45 -0700708 if ((*errp = netlink_dump_start(ctnl, skb, nlh,
709 ctnetlink_dump_table,
710 ctnetlink_done)) != 0)
Harald Welte080774a2005-08-09 19:32:58 -0700711 return -EINVAL;
Harald Welte080774a2005-08-09 19:32:58 -0700712
713 rlen = NLMSG_ALIGN(nlh->nlmsg_len);
714 if (rlen > skb->len)
715 rlen = skb->len;
716 skb_pull(skb, rlen);
717 return 0;
718 }
719
Pablo Neira Ayuso56558202005-11-14 15:22:11 -0800720 if (nfattr_bad_size(cda, CTA_MAX, cta_min))
721 return -EINVAL;
722
Harald Welte080774a2005-08-09 19:32:58 -0700723 if (cda[CTA_TUPLE_ORIG-1])
724 err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_ORIG);
725 else if (cda[CTA_TUPLE_REPLY-1])
726 err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_REPLY);
727 else
728 return -EINVAL;
729
730 if (err < 0)
731 return err;
732
733 h = ip_conntrack_find_get(&tuple, NULL);
Pablo Neira Ayuso9ea8cfd2006-10-12 14:09:16 -0700734 if (!h)
Harald Welte080774a2005-08-09 19:32:58 -0700735 return -ENOENT;
Pablo Neira Ayuso9ea8cfd2006-10-12 14:09:16 -0700736
Harald Welte080774a2005-08-09 19:32:58 -0700737 ct = tuplehash_to_ctrack(h);
738
739 err = -ENOMEM;
Pablo Neira Ayuso81e5c272005-11-09 13:01:19 -0800740 skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
Harald Welte080774a2005-08-09 19:32:58 -0700741 if (!skb2) {
742 ip_conntrack_put(ct);
743 return -ENOMEM;
744 }
745 NETLINK_CB(skb2).dst_pid = NETLINK_CB(skb).pid;
746
747 err = ctnetlink_fill_info(skb2, NETLINK_CB(skb).pid, nlh->nlmsg_seq,
748 IPCTNL_MSG_CT_NEW, 1, ct);
749 ip_conntrack_put(ct);
750 if (err <= 0)
Harald Welte0f81eb42005-11-03 19:05:37 +0100751 goto free;
Harald Welte080774a2005-08-09 19:32:58 -0700752
753 err = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT);
754 if (err < 0)
755 goto out;
756
Harald Welte080774a2005-08-09 19:32:58 -0700757 return 0;
758
Harald Welte0f81eb42005-11-03 19:05:37 +0100759free:
760 kfree_skb(skb2);
Harald Welte080774a2005-08-09 19:32:58 -0700761out:
Pablo Neira Ayusofcda4612005-11-09 13:03:26 -0800762 return err;
Harald Welte080774a2005-08-09 19:32:58 -0700763}
764
765static inline int
766ctnetlink_change_status(struct ip_conntrack *ct, struct nfattr *cda[])
767{
Harald Welted000eaf2005-10-10 20:52:51 -0700768 unsigned long d;
Al Virocdcb71b2006-09-28 14:21:37 -0700769 unsigned status = ntohl(*(__be32 *)NFA_DATA(cda[CTA_STATUS-1]));
Harald Welte080774a2005-08-09 19:32:58 -0700770 d = ct->status ^ status;
771
772 if (d & (IPS_EXPECTED|IPS_CONFIRMED|IPS_DYING))
773 /* unchangeable */
774 return -EINVAL;
775
776 if (d & IPS_SEEN_REPLY && !(status & IPS_SEEN_REPLY))
777 /* SEEN_REPLY bit can only be set */
778 return -EINVAL;
779
780
781 if (d & IPS_ASSURED && !(status & IPS_ASSURED))
782 /* ASSURED bit can only be set */
783 return -EINVAL;
784
Patrick McHardy3726add2006-05-29 18:24:39 -0700785 if (cda[CTA_NAT_SRC-1] || cda[CTA_NAT_DST-1]) {
Harald Welte080774a2005-08-09 19:32:58 -0700786#ifndef CONFIG_IP_NF_NAT_NEEDED
787 return -EINVAL;
788#else
Harald Welte080774a2005-08-09 19:32:58 -0700789 struct ip_nat_range range;
790
Patrick McHardy3726add2006-05-29 18:24:39 -0700791 if (cda[CTA_NAT_DST-1]) {
792 if (ctnetlink_parse_nat(cda[CTA_NAT_DST-1], ct,
793 &range) < 0)
794 return -EINVAL;
795 if (ip_nat_initialized(ct,
796 HOOK2MANIP(NF_IP_PRE_ROUTING)))
797 return -EEXIST;
798 ip_nat_setup_info(ct, &range, NF_IP_PRE_ROUTING);
799 }
800 if (cda[CTA_NAT_SRC-1]) {
801 if (ctnetlink_parse_nat(cda[CTA_NAT_SRC-1], ct,
802 &range) < 0)
803 return -EINVAL;
804 if (ip_nat_initialized(ct,
805 HOOK2MANIP(NF_IP_POST_ROUTING)))
806 return -EEXIST;
807 ip_nat_setup_info(ct, &range, NF_IP_POST_ROUTING);
808 }
Harald Welte080774a2005-08-09 19:32:58 -0700809#endif
810 }
811
812 /* Be careful here, modifying NAT bits can screw up things,
813 * so don't let users modify them directly if they don't pass
814 * ip_nat_range. */
815 ct->status |= status & ~(IPS_NAT_DONE_MASK | IPS_NAT_MASK);
816 return 0;
817}
818
819
820static inline int
821ctnetlink_change_helper(struct ip_conntrack *ct, struct nfattr *cda[])
822{
823 struct ip_conntrack_helper *helper;
824 char *helpname;
825 int err;
826
Harald Welte080774a2005-08-09 19:32:58 -0700827 /* don't change helper of sibling connections */
828 if (ct->master)
829 return -EINVAL;
830
831 err = ctnetlink_parse_help(cda[CTA_HELP-1], &helpname);
832 if (err < 0)
833 return err;
834
835 helper = __ip_conntrack_helper_find_byname(helpname);
836 if (!helper) {
837 if (!strcmp(helpname, ""))
838 helper = NULL;
839 else
840 return -EINVAL;
841 }
842
843 if (ct->helper) {
844 if (!helper) {
845 /* we had a helper before ... */
846 ip_ct_remove_expectations(ct);
847 ct->helper = NULL;
848 } else {
849 /* need to zero data of old helper */
850 memset(&ct->help, 0, sizeof(ct->help));
851 }
852 }
853
854 ct->helper = helper;
855
856 return 0;
857}
858
859static inline int
860ctnetlink_change_timeout(struct ip_conntrack *ct, struct nfattr *cda[])
861{
Al Virocdcb71b2006-09-28 14:21:37 -0700862 u_int32_t timeout = ntohl(*(__be32 *)NFA_DATA(cda[CTA_TIMEOUT-1]));
Harald Welte080774a2005-08-09 19:32:58 -0700863
864 if (!del_timer(&ct->timeout))
865 return -ETIME;
866
867 ct->timeout.expires = jiffies + timeout * HZ;
868 add_timer(&ct->timeout);
869
870 return 0;
871}
872
Pablo Neira Ayuso061cb4a2005-10-10 21:23:46 -0700873static inline int
874ctnetlink_change_protoinfo(struct ip_conntrack *ct, struct nfattr *cda[])
875{
876 struct nfattr *tb[CTA_PROTOINFO_MAX], *attr = cda[CTA_PROTOINFO-1];
877 struct ip_conntrack_protocol *proto;
878 u_int16_t npt = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum;
879 int err = 0;
880
Harald Weltea2506c02005-11-09 12:59:13 -0800881 nfattr_parse_nested(tb, CTA_PROTOINFO_MAX, attr);
Pablo Neira Ayuso061cb4a2005-10-10 21:23:46 -0700882
883 proto = ip_conntrack_proto_find_get(npt);
Pablo Neira Ayuso061cb4a2005-10-10 21:23:46 -0700884
885 if (proto->from_nfattr)
886 err = proto->from_nfattr(tb, ct);
887 ip_conntrack_proto_put(proto);
888
889 return err;
Pablo Neira Ayuso061cb4a2005-10-10 21:23:46 -0700890}
891
Harald Welte080774a2005-08-09 19:32:58 -0700892static int
893ctnetlink_change_conntrack(struct ip_conntrack *ct, struct nfattr *cda[])
894{
895 int err;
896
Harald Welte080774a2005-08-09 19:32:58 -0700897 if (cda[CTA_HELP-1]) {
898 err = ctnetlink_change_helper(ct, cda);
899 if (err < 0)
900 return err;
901 }
902
903 if (cda[CTA_TIMEOUT-1]) {
904 err = ctnetlink_change_timeout(ct, cda);
905 if (err < 0)
906 return err;
907 }
908
909 if (cda[CTA_STATUS-1]) {
910 err = ctnetlink_change_status(ct, cda);
911 if (err < 0)
912 return err;
913 }
914
Pablo Neira Ayuso061cb4a2005-10-10 21:23:46 -0700915 if (cda[CTA_PROTOINFO-1]) {
916 err = ctnetlink_change_protoinfo(ct, cda);
917 if (err < 0)
918 return err;
919 }
920
Pablo Neira Ayuso02a78cd2005-11-09 13:00:04 -0800921#if defined(CONFIG_IP_NF_CONNTRACK_MARK)
922 if (cda[CTA_MARK-1])
Al Virocdcb71b2006-09-28 14:21:37 -0700923 ct->mark = ntohl(*(__be32 *)NFA_DATA(cda[CTA_MARK-1]));
Pablo Neira Ayuso02a78cd2005-11-09 13:00:04 -0800924#endif
925
Harald Welte080774a2005-08-09 19:32:58 -0700926 return 0;
927}
928
929static int
930ctnetlink_create_conntrack(struct nfattr *cda[],
931 struct ip_conntrack_tuple *otuple,
932 struct ip_conntrack_tuple *rtuple)
933{
934 struct ip_conntrack *ct;
935 int err = -EINVAL;
936
Harald Welte080774a2005-08-09 19:32:58 -0700937 ct = ip_conntrack_alloc(otuple, rtuple);
938 if (ct == NULL || IS_ERR(ct))
939 return -ENOMEM;
940
941 if (!cda[CTA_TIMEOUT-1])
942 goto err;
Al Virocdcb71b2006-09-28 14:21:37 -0700943 ct->timeout.expires = ntohl(*(__be32 *)NFA_DATA(cda[CTA_TIMEOUT-1]));
Harald Welte080774a2005-08-09 19:32:58 -0700944
945 ct->timeout.expires = jiffies + ct->timeout.expires * HZ;
946 ct->status |= IPS_CONFIRMED;
947
948 err = ctnetlink_change_status(ct, cda);
949 if (err < 0)
950 goto err;
951
Pablo Neira Ayuso061cb4a2005-10-10 21:23:46 -0700952 if (cda[CTA_PROTOINFO-1]) {
953 err = ctnetlink_change_protoinfo(ct, cda);
954 if (err < 0)
955 return err;
956 }
957
Pablo Neira Ayusod4d6bb42006-01-05 12:18:25 -0800958#if defined(CONFIG_IP_NF_CONNTRACK_MARK)
959 if (cda[CTA_MARK-1])
Al Virocdcb71b2006-09-28 14:21:37 -0700960 ct->mark = ntohl(*(__be32 *)NFA_DATA(cda[CTA_MARK-1]));
Pablo Neira Ayusod4d6bb42006-01-05 12:18:25 -0800961#endif
962
Harald Welte080774a2005-08-09 19:32:58 -0700963 ct->helper = ip_conntrack_helper_find_get(rtuple);
964
965 add_timer(&ct->timeout);
966 ip_conntrack_hash_insert(ct);
967
968 if (ct->helper)
969 ip_conntrack_helper_put(ct->helper);
970
Harald Welte080774a2005-08-09 19:32:58 -0700971 return 0;
972
973err:
974 ip_conntrack_free(ct);
975 return err;
976}
977
978static int
979ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
980 struct nlmsghdr *nlh, struct nfattr *cda[], int *errp)
981{
982 struct ip_conntrack_tuple otuple, rtuple;
983 struct ip_conntrack_tuple_hash *h = NULL;
984 int err = 0;
985
Pablo Neira Ayuso56558202005-11-14 15:22:11 -0800986 if (nfattr_bad_size(cda, CTA_MAX, cta_min))
987 return -EINVAL;
988
Harald Welte080774a2005-08-09 19:32:58 -0700989 if (cda[CTA_TUPLE_ORIG-1]) {
990 err = ctnetlink_parse_tuple(cda, &otuple, CTA_TUPLE_ORIG);
991 if (err < 0)
992 return err;
993 }
994
995 if (cda[CTA_TUPLE_REPLY-1]) {
996 err = ctnetlink_parse_tuple(cda, &rtuple, CTA_TUPLE_REPLY);
997 if (err < 0)
998 return err;
999 }
1000
1001 write_lock_bh(&ip_conntrack_lock);
1002 if (cda[CTA_TUPLE_ORIG-1])
1003 h = __ip_conntrack_find(&otuple, NULL);
1004 else if (cda[CTA_TUPLE_REPLY-1])
1005 h = __ip_conntrack_find(&rtuple, NULL);
1006
1007 if (h == NULL) {
1008 write_unlock_bh(&ip_conntrack_lock);
Harald Welte080774a2005-08-09 19:32:58 -07001009 err = -ENOENT;
1010 if (nlh->nlmsg_flags & NLM_F_CREATE)
1011 err = ctnetlink_create_conntrack(cda, &otuple, &rtuple);
Pablo Neira88aa0422005-08-09 20:02:55 -07001012 return err;
1013 }
1014 /* implicit 'else' */
1015
1016 /* we only allow nat config for new conntracks */
Patrick McHardy3726add2006-05-29 18:24:39 -07001017 if (cda[CTA_NAT_SRC-1] || cda[CTA_NAT_DST-1]) {
Pablo Neira88aa0422005-08-09 20:02:55 -07001018 err = -EINVAL;
Harald Welte080774a2005-08-09 19:32:58 -07001019 goto out_unlock;
Harald Welte080774a2005-08-09 19:32:58 -07001020 }
1021
1022 /* We manipulate the conntrack inside the global conntrack table lock,
1023 * so there's no need to increase the refcount */
Harald Welte080774a2005-08-09 19:32:58 -07001024 err = -EEXIST;
1025 if (!(nlh->nlmsg_flags & NLM_F_EXCL))
1026 err = ctnetlink_change_conntrack(tuplehash_to_ctrack(h), cda);
1027
1028out_unlock:
1029 write_unlock_bh(&ip_conntrack_lock);
1030 return err;
1031}
1032
1033/***********************************************************************
1034 * EXPECT
1035 ***********************************************************************/
1036
1037static inline int
1038ctnetlink_exp_dump_tuple(struct sk_buff *skb,
1039 const struct ip_conntrack_tuple *tuple,
1040 enum ctattr_expect type)
1041{
1042 struct nfattr *nest_parms = NFA_NEST(skb, type);
1043
1044 if (ctnetlink_dump_tuples(skb, tuple) < 0)
1045 goto nfattr_failure;
1046
1047 NFA_NEST_END(skb, nest_parms);
1048
1049 return 0;
1050
1051nfattr_failure:
1052 return -1;
1053}
1054
1055static inline int
Pablo Neira Ayuso1cde6432006-03-22 13:54:15 -08001056ctnetlink_exp_dump_mask(struct sk_buff *skb,
1057 const struct ip_conntrack_tuple *tuple,
1058 const struct ip_conntrack_tuple *mask)
1059{
1060 int ret;
1061 struct ip_conntrack_protocol *proto;
1062 struct nfattr *nest_parms = NFA_NEST(skb, CTA_EXPECT_MASK);
1063
1064 ret = ctnetlink_dump_tuples_ip(skb, mask);
1065 if (unlikely(ret < 0))
1066 goto nfattr_failure;
1067
1068 proto = ip_conntrack_proto_find_get(tuple->dst.protonum);
1069 ret = ctnetlink_dump_tuples_proto(skb, mask, proto);
1070 ip_conntrack_proto_put(proto);
1071 if (unlikely(ret < 0))
1072 goto nfattr_failure;
1073
1074 NFA_NEST_END(skb, nest_parms);
1075
1076 return 0;
1077
1078nfattr_failure:
1079 return -1;
1080}
1081
1082static inline int
Harald Welte080774a2005-08-09 19:32:58 -07001083ctnetlink_exp_dump_expect(struct sk_buff *skb,
1084 const struct ip_conntrack_expect *exp)
1085{
Harald Welte1444fc52005-08-09 20:04:07 -07001086 struct ip_conntrack *master = exp->master;
Al Virocdcb71b2006-09-28 14:21:37 -07001087 __be32 timeout = htonl((exp->timeout.expires - jiffies) / HZ);
1088 __be32 id = htonl(exp->id);
Harald Welte080774a2005-08-09 19:32:58 -07001089
1090 if (ctnetlink_exp_dump_tuple(skb, &exp->tuple, CTA_EXPECT_TUPLE) < 0)
1091 goto nfattr_failure;
Pablo Neira Ayuso1cde6432006-03-22 13:54:15 -08001092 if (ctnetlink_exp_dump_mask(skb, &exp->tuple, &exp->mask) < 0)
Harald Welte080774a2005-08-09 19:32:58 -07001093 goto nfattr_failure;
Harald Welte1444fc52005-08-09 20:04:07 -07001094 if (ctnetlink_exp_dump_tuple(skb,
1095 &master->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
1096 CTA_EXPECT_MASTER) < 0)
1097 goto nfattr_failure;
Harald Welte080774a2005-08-09 19:32:58 -07001098
Al Virocdcb71b2006-09-28 14:21:37 -07001099 NFA_PUT(skb, CTA_EXPECT_TIMEOUT, sizeof(__be32), &timeout);
1100 NFA_PUT(skb, CTA_EXPECT_ID, sizeof(__be32), &id);
Harald Welte080774a2005-08-09 19:32:58 -07001101
1102 return 0;
1103
1104nfattr_failure:
1105 return -1;
1106}
1107
1108static int
1109ctnetlink_exp_fill_info(struct sk_buff *skb, u32 pid, u32 seq,
1110 int event,
1111 int nowait,
1112 const struct ip_conntrack_expect *exp)
1113{
1114 struct nlmsghdr *nlh;
1115 struct nfgenmsg *nfmsg;
1116 unsigned char *b;
1117
1118 b = skb->tail;
1119
1120 event |= NFNL_SUBSYS_CTNETLINK_EXP << 8;
1121 nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(struct nfgenmsg));
1122 nfmsg = NLMSG_DATA(nlh);
1123
1124 nlh->nlmsg_flags = (nowait && pid) ? NLM_F_MULTI : 0;
1125 nfmsg->nfgen_family = AF_INET;
1126 nfmsg->version = NFNETLINK_V0;
1127 nfmsg->res_id = 0;
1128
1129 if (ctnetlink_exp_dump_expect(skb, exp) < 0)
1130 goto nfattr_failure;
1131
1132 nlh->nlmsg_len = skb->tail - b;
1133 return skb->len;
1134
1135nlmsg_failure:
1136nfattr_failure:
1137 skb_trim(skb, b - skb->data);
1138 return -1;
1139}
1140
1141#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS
1142static int ctnetlink_expect_event(struct notifier_block *this,
1143 unsigned long events, void *ptr)
1144{
1145 struct nlmsghdr *nlh;
1146 struct nfgenmsg *nfmsg;
1147 struct ip_conntrack_expect *exp = (struct ip_conntrack_expect *)ptr;
1148 struct sk_buff *skb;
1149 unsigned int type;
1150 unsigned char *b;
1151 int flags = 0;
Harald Welte080774a2005-08-09 19:32:58 -07001152
1153 if (events & IPEXP_NEW) {
1154 type = IPCTNL_MSG_EXP_NEW;
1155 flags = NLM_F_CREATE|NLM_F_EXCL;
1156 } else
1157 return NOTIFY_DONE;
1158
Pablo Neira Ayusob3a27bf2006-08-22 00:32:05 -07001159 if (!nfnetlink_has_listeners(NFNLGRP_CONNTRACK_EXP_NEW))
1160 return NOTIFY_DONE;
1161
Harald Welte080774a2005-08-09 19:32:58 -07001162 skb = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC);
1163 if (!skb)
1164 return NOTIFY_DONE;
1165
1166 b = skb->tail;
1167
Marcus Sundbergb633ad52006-02-04 02:11:09 -08001168 type |= NFNL_SUBSYS_CTNETLINK_EXP << 8;
Harald Welte080774a2005-08-09 19:32:58 -07001169 nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(struct nfgenmsg));
1170 nfmsg = NLMSG_DATA(nlh);
1171
1172 nlh->nlmsg_flags = flags;
1173 nfmsg->nfgen_family = AF_INET;
1174 nfmsg->version = NFNETLINK_V0;
1175 nfmsg->res_id = 0;
1176
1177 if (ctnetlink_exp_dump_expect(skb, exp) < 0)
1178 goto nfattr_failure;
1179
1180 nlh->nlmsg_len = skb->tail - b;
Patrick McHardyac6d4392005-08-14 19:29:52 -07001181 nfnetlink_send(skb, 0, NFNLGRP_CONNTRACK_EXP_NEW, 0);
Harald Welte080774a2005-08-09 19:32:58 -07001182 return NOTIFY_DONE;
1183
1184nlmsg_failure:
1185nfattr_failure:
1186 kfree_skb(skb);
1187 return NOTIFY_DONE;
1188}
1189#endif
1190
1191static int
1192ctnetlink_exp_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
1193{
1194 struct ip_conntrack_expect *exp = NULL;
1195 struct list_head *i;
1196 u_int32_t *id = (u_int32_t *) &cb->args[0];
1197
Harald Welte080774a2005-08-09 19:32:58 -07001198 read_lock_bh(&ip_conntrack_lock);
Pablo Neira Ayusoff21d572005-08-09 20:06:42 -07001199 list_for_each_prev(i, &ip_conntrack_expect_list) {
Harald Welte080774a2005-08-09 19:32:58 -07001200 exp = (struct ip_conntrack_expect *) i;
1201 if (exp->id <= *id)
1202 continue;
1203 if (ctnetlink_exp_fill_info(skb, NETLINK_CB(cb->skb).pid,
1204 cb->nlh->nlmsg_seq,
1205 IPCTNL_MSG_EXP_NEW,
1206 1, exp) < 0)
1207 goto out;
1208 *id = exp->id;
1209 }
1210out:
1211 read_unlock_bh(&ip_conntrack_lock);
1212
Harald Welte080774a2005-08-09 19:32:58 -07001213 return skb->len;
1214}
1215
Pablo Neira Ayuso56558202005-11-14 15:22:11 -08001216static const size_t cta_min_exp[CTA_EXPECT_MAX] = {
Al Virocdcb71b2006-09-28 14:21:37 -07001217 [CTA_EXPECT_TIMEOUT-1] = sizeof(__be32),
1218 [CTA_EXPECT_ID-1] = sizeof(__be32)
Pablo Neira Ayuso56558202005-11-14 15:22:11 -08001219};
1220
Harald Welte080774a2005-08-09 19:32:58 -07001221static int
1222ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb,
1223 struct nlmsghdr *nlh, struct nfattr *cda[], int *errp)
1224{
1225 struct ip_conntrack_tuple tuple;
1226 struct ip_conntrack_expect *exp;
1227 struct sk_buff *skb2;
1228 int err = 0;
1229
Pablo Neira Ayuso56558202005-11-14 15:22:11 -08001230 if (nfattr_bad_size(cda, CTA_EXPECT_MAX, cta_min_exp))
1231 return -EINVAL;
1232
Harald Welte080774a2005-08-09 19:32:58 -07001233 if (nlh->nlmsg_flags & NLM_F_DUMP) {
1234 struct nfgenmsg *msg = NLMSG_DATA(nlh);
1235 u32 rlen;
1236
1237 if (msg->nfgen_family != AF_INET)
1238 return -EAFNOSUPPORT;
1239
1240 if ((*errp = netlink_dump_start(ctnl, skb, nlh,
1241 ctnetlink_exp_dump_table,
1242 ctnetlink_done)) != 0)
1243 return -EINVAL;
1244 rlen = NLMSG_ALIGN(nlh->nlmsg_len);
1245 if (rlen > skb->len)
1246 rlen = skb->len;
1247 skb_pull(skb, rlen);
1248 return 0;
1249 }
1250
Harald Welte1444fc52005-08-09 20:04:07 -07001251 if (cda[CTA_EXPECT_MASTER-1])
1252 err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_MASTER);
Harald Welte080774a2005-08-09 19:32:58 -07001253 else
1254 return -EINVAL;
1255
1256 if (err < 0)
1257 return err;
1258
Patrick McHardya41bc002005-09-19 15:35:31 -07001259 exp = ip_conntrack_expect_find(&tuple);
Harald Welte080774a2005-08-09 19:32:58 -07001260 if (!exp)
1261 return -ENOENT;
1262
Pablo Neira Ayusoa856a192005-11-09 13:03:42 -08001263 if (cda[CTA_EXPECT_ID-1]) {
Al Virocdcb71b2006-09-28 14:21:37 -07001264 __be32 id = *(__be32 *)NFA_DATA(cda[CTA_EXPECT_ID-1]);
Pablo Neira Ayusoa856a192005-11-09 13:03:42 -08001265 if (exp->id != ntohl(id)) {
1266 ip_conntrack_expect_put(exp);
1267 return -ENOENT;
1268 }
1269 }
1270
Harald Welte080774a2005-08-09 19:32:58 -07001271 err = -ENOMEM;
1272 skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
1273 if (!skb2)
1274 goto out;
1275 NETLINK_CB(skb2).dst_pid = NETLINK_CB(skb).pid;
1276
1277 err = ctnetlink_exp_fill_info(skb2, NETLINK_CB(skb).pid,
1278 nlh->nlmsg_seq, IPCTNL_MSG_EXP_NEW,
1279 1, exp);
1280 if (err <= 0)
Harald Welte080774a2005-08-09 19:32:58 -07001281 goto free;
1282
Harald Welte0f81eb42005-11-03 19:05:37 +01001283 ip_conntrack_expect_put(exp);
Harald Welte080774a2005-08-09 19:32:58 -07001284
Harald Welte0f81eb42005-11-03 19:05:37 +01001285 return netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT);
1286
1287free:
1288 kfree_skb(skb2);
Harald Welte080774a2005-08-09 19:32:58 -07001289out:
1290 ip_conntrack_expect_put(exp);
Harald Welte080774a2005-08-09 19:32:58 -07001291 return err;
1292}
1293
1294static int
1295ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb,
1296 struct nlmsghdr *nlh, struct nfattr *cda[], int *errp)
1297{
1298 struct ip_conntrack_expect *exp, *tmp;
1299 struct ip_conntrack_tuple tuple;
1300 struct ip_conntrack_helper *h;
1301 int err;
1302
Pablo Neira Ayuso56558202005-11-14 15:22:11 -08001303 if (nfattr_bad_size(cda, CTA_EXPECT_MAX, cta_min_exp))
1304 return -EINVAL;
1305
Harald Welte1444fc52005-08-09 20:04:07 -07001306 if (cda[CTA_EXPECT_TUPLE-1]) {
1307 /* delete a single expect by tuple */
1308 err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_TUPLE);
1309 if (err < 0)
1310 return err;
1311
1312 /* bump usage count to 2 */
Patrick McHardya41bc002005-09-19 15:35:31 -07001313 exp = ip_conntrack_expect_find(&tuple);
Harald Welte1444fc52005-08-09 20:04:07 -07001314 if (!exp)
1315 return -ENOENT;
1316
1317 if (cda[CTA_EXPECT_ID-1]) {
Al Virocdcb71b2006-09-28 14:21:37 -07001318 __be32 id =
1319 *(__be32 *)NFA_DATA(cda[CTA_EXPECT_ID-1]);
Harald Welte1444fc52005-08-09 20:04:07 -07001320 if (exp->id != ntohl(id)) {
1321 ip_conntrack_expect_put(exp);
1322 return -ENOENT;
1323 }
1324 }
1325
1326 /* after list removal, usage count == 1 */
1327 ip_conntrack_unexpect_related(exp);
1328 /* have to put what we 'get' above.
1329 * after this line usage count == 0 */
1330 ip_conntrack_expect_put(exp);
1331 } else if (cda[CTA_EXPECT_HELP_NAME-1]) {
1332 char *name = NFA_DATA(cda[CTA_EXPECT_HELP_NAME-1]);
Harald Welte080774a2005-08-09 19:32:58 -07001333
1334 /* delete all expectations for this helper */
1335 write_lock_bh(&ip_conntrack_lock);
1336 h = __ip_conntrack_helper_find_byname(name);
1337 if (!h) {
1338 write_unlock_bh(&ip_conntrack_lock);
1339 return -EINVAL;
1340 }
1341 list_for_each_entry_safe(exp, tmp, &ip_conntrack_expect_list,
1342 list) {
1343 if (exp->master->helper == h
Pablo Neira Ayuso49719eb2005-09-06 15:10:46 -07001344 && del_timer(&exp->timeout)) {
1345 ip_ct_unlink_expect(exp);
1346 ip_conntrack_expect_put(exp);
1347 }
Harald Welte080774a2005-08-09 19:32:58 -07001348 }
Yasuyuki Kozakai9fb9cbb2005-11-09 16:38:16 -08001349 write_unlock_bh(&ip_conntrack_lock);
Harald Welte080774a2005-08-09 19:32:58 -07001350 } else {
1351 /* This basically means we have to flush everything*/
1352 write_lock_bh(&ip_conntrack_lock);
1353 list_for_each_entry_safe(exp, tmp, &ip_conntrack_expect_list,
1354 list) {
Pablo Neira Ayuso49719eb2005-09-06 15:10:46 -07001355 if (del_timer(&exp->timeout)) {
1356 ip_ct_unlink_expect(exp);
1357 ip_conntrack_expect_put(exp);
1358 }
Harald Welte080774a2005-08-09 19:32:58 -07001359 }
1360 write_unlock_bh(&ip_conntrack_lock);
Harald Welte080774a2005-08-09 19:32:58 -07001361 }
1362
Harald Welte080774a2005-08-09 19:32:58 -07001363 return 0;
1364}
1365static int
1366ctnetlink_change_expect(struct ip_conntrack_expect *x, struct nfattr *cda[])
1367{
1368 return -EOPNOTSUPP;
1369}
1370
1371static int
1372ctnetlink_create_expect(struct nfattr *cda[])
1373{
1374 struct ip_conntrack_tuple tuple, mask, master_tuple;
1375 struct ip_conntrack_tuple_hash *h = NULL;
1376 struct ip_conntrack_expect *exp;
1377 struct ip_conntrack *ct;
1378 int err = 0;
1379
Harald Welte1444fc52005-08-09 20:04:07 -07001380 /* caller guarantees that those three CTA_EXPECT_* exist */
Harald Welte080774a2005-08-09 19:32:58 -07001381 err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_TUPLE);
1382 if (err < 0)
1383 return err;
Harald Weltebd9a26b2005-08-09 20:03:22 -07001384 err = ctnetlink_parse_tuple(cda, &mask, CTA_EXPECT_MASK);
Harald Welte080774a2005-08-09 19:32:58 -07001385 if (err < 0)
1386 return err;
Harald Welte1444fc52005-08-09 20:04:07 -07001387 err = ctnetlink_parse_tuple(cda, &master_tuple, CTA_EXPECT_MASTER);
Harald Welte080774a2005-08-09 19:32:58 -07001388 if (err < 0)
1389 return err;
1390
1391 /* Look for master conntrack of this expectation */
1392 h = ip_conntrack_find_get(&master_tuple, NULL);
1393 if (!h)
1394 return -ENOENT;
1395 ct = tuplehash_to_ctrack(h);
1396
1397 if (!ct->helper) {
1398 /* such conntrack hasn't got any helper, abort */
1399 err = -EINVAL;
1400 goto out;
1401 }
1402
1403 exp = ip_conntrack_expect_alloc(ct);
1404 if (!exp) {
1405 err = -ENOMEM;
1406 goto out;
1407 }
1408
1409 exp->expectfn = NULL;
Patrick McHardy2248bcf2005-09-06 15:06:42 -07001410 exp->flags = 0;
Harald Welte080774a2005-08-09 19:32:58 -07001411 exp->master = ct;
1412 memcpy(&exp->tuple, &tuple, sizeof(struct ip_conntrack_tuple));
1413 memcpy(&exp->mask, &mask, sizeof(struct ip_conntrack_tuple));
1414
1415 err = ip_conntrack_expect_related(exp);
1416 ip_conntrack_expect_put(exp);
1417
1418out:
1419 ip_conntrack_put(tuplehash_to_ctrack(h));
1420 return err;
1421}
1422
1423static int
1424ctnetlink_new_expect(struct sock *ctnl, struct sk_buff *skb,
1425 struct nlmsghdr *nlh, struct nfattr *cda[], int *errp)
1426{
1427 struct ip_conntrack_tuple tuple;
1428 struct ip_conntrack_expect *exp;
1429 int err = 0;
1430
Pablo Neira Ayuso56558202005-11-14 15:22:11 -08001431 if (nfattr_bad_size(cda, CTA_EXPECT_MAX, cta_min_exp))
1432 return -EINVAL;
1433
Harald Welte1444fc52005-08-09 20:04:07 -07001434 if (!cda[CTA_EXPECT_TUPLE-1]
1435 || !cda[CTA_EXPECT_MASK-1]
1436 || !cda[CTA_EXPECT_MASTER-1])
Harald Welte080774a2005-08-09 19:32:58 -07001437 return -EINVAL;
1438
1439 err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_TUPLE);
1440 if (err < 0)
1441 return err;
1442
1443 write_lock_bh(&ip_conntrack_lock);
1444 exp = __ip_conntrack_expect_find(&tuple);
1445
1446 if (!exp) {
1447 write_unlock_bh(&ip_conntrack_lock);
1448 err = -ENOENT;
1449 if (nlh->nlmsg_flags & NLM_F_CREATE)
1450 err = ctnetlink_create_expect(cda);
1451 return err;
1452 }
1453
1454 err = -EEXIST;
1455 if (!(nlh->nlmsg_flags & NLM_F_EXCL))
1456 err = ctnetlink_change_expect(exp, cda);
1457 write_unlock_bh(&ip_conntrack_lock);
1458
Harald Welte080774a2005-08-09 19:32:58 -07001459 return err;
1460}
1461
1462#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS
1463static struct notifier_block ctnl_notifier = {
1464 .notifier_call = ctnetlink_conntrack_event,
1465};
1466
1467static struct notifier_block ctnl_notifier_exp = {
1468 .notifier_call = ctnetlink_expect_event,
1469};
1470#endif
1471
1472static struct nfnl_callback ctnl_cb[IPCTNL_MSG_MAX] = {
1473 [IPCTNL_MSG_CT_NEW] = { .call = ctnetlink_new_conntrack,
Harald Welte37d2e7a2005-11-14 15:24:59 -08001474 .attr_count = CTA_MAX, },
Harald Welte080774a2005-08-09 19:32:58 -07001475 [IPCTNL_MSG_CT_GET] = { .call = ctnetlink_get_conntrack,
Harald Welte37d2e7a2005-11-14 15:24:59 -08001476 .attr_count = CTA_MAX, },
Harald Welte080774a2005-08-09 19:32:58 -07001477 [IPCTNL_MSG_CT_DELETE] = { .call = ctnetlink_del_conntrack,
Harald Welte37d2e7a2005-11-14 15:24:59 -08001478 .attr_count = CTA_MAX, },
Harald Welte080774a2005-08-09 19:32:58 -07001479 [IPCTNL_MSG_CT_GET_CTRZERO] = { .call = ctnetlink_get_conntrack,
Harald Welte37d2e7a2005-11-14 15:24:59 -08001480 .attr_count = CTA_MAX, },
Harald Welte080774a2005-08-09 19:32:58 -07001481};
1482
Pablo Neira Ayuso28b19d92005-08-09 20:06:27 -07001483static struct nfnl_callback ctnl_exp_cb[IPCTNL_MSG_EXP_MAX] = {
Harald Welte080774a2005-08-09 19:32:58 -07001484 [IPCTNL_MSG_EXP_GET] = { .call = ctnetlink_get_expect,
Harald Welte37d2e7a2005-11-14 15:24:59 -08001485 .attr_count = CTA_EXPECT_MAX, },
Harald Welte080774a2005-08-09 19:32:58 -07001486 [IPCTNL_MSG_EXP_NEW] = { .call = ctnetlink_new_expect,
Harald Welte37d2e7a2005-11-14 15:24:59 -08001487 .attr_count = CTA_EXPECT_MAX, },
Harald Welte080774a2005-08-09 19:32:58 -07001488 [IPCTNL_MSG_EXP_DELETE] = { .call = ctnetlink_del_expect,
Harald Welte37d2e7a2005-11-14 15:24:59 -08001489 .attr_count = CTA_EXPECT_MAX, },
Harald Welte080774a2005-08-09 19:32:58 -07001490};
1491
1492static struct nfnetlink_subsystem ctnl_subsys = {
1493 .name = "conntrack",
1494 .subsys_id = NFNL_SUBSYS_CTNETLINK,
1495 .cb_count = IPCTNL_MSG_MAX,
Harald Welte080774a2005-08-09 19:32:58 -07001496 .cb = ctnl_cb,
1497};
1498
1499static struct nfnetlink_subsystem ctnl_exp_subsys = {
1500 .name = "conntrack_expect",
1501 .subsys_id = NFNL_SUBSYS_CTNETLINK_EXP,
1502 .cb_count = IPCTNL_MSG_EXP_MAX,
Harald Welte080774a2005-08-09 19:32:58 -07001503 .cb = ctnl_exp_cb,
1504};
1505
Pablo Neira Ayuso119a3182005-11-09 13:00:29 -08001506MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTNETLINK);
Pablo Neira Ayuso34f9a2e2006-02-04 02:11:41 -08001507MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTNETLINK_EXP);
Pablo Neira Ayuso119a3182005-11-09 13:00:29 -08001508
Harald Welte080774a2005-08-09 19:32:58 -07001509static int __init ctnetlink_init(void)
1510{
1511 int ret;
1512
1513 printk("ctnetlink v%s: registering with nfnetlink.\n", version);
1514 ret = nfnetlink_subsys_register(&ctnl_subsys);
1515 if (ret < 0) {
1516 printk("ctnetlink_init: cannot register with nfnetlink.\n");
1517 goto err_out;
1518 }
1519
1520 ret = nfnetlink_subsys_register(&ctnl_exp_subsys);
1521 if (ret < 0) {
1522 printk("ctnetlink_init: cannot register exp with nfnetlink.\n");
1523 goto err_unreg_subsys;
1524 }
1525
1526#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS
1527 ret = ip_conntrack_register_notifier(&ctnl_notifier);
1528 if (ret < 0) {
1529 printk("ctnetlink_init: cannot register notifier.\n");
1530 goto err_unreg_exp_subsys;
1531 }
1532
1533 ret = ip_conntrack_expect_register_notifier(&ctnl_notifier_exp);
1534 if (ret < 0) {
1535 printk("ctnetlink_init: cannot expect register notifier.\n");
1536 goto err_unreg_notifier;
1537 }
1538#endif
1539
1540 return 0;
1541
1542#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS
1543err_unreg_notifier:
1544 ip_conntrack_unregister_notifier(&ctnl_notifier);
1545err_unreg_exp_subsys:
1546 nfnetlink_subsys_unregister(&ctnl_exp_subsys);
1547#endif
1548err_unreg_subsys:
1549 nfnetlink_subsys_unregister(&ctnl_subsys);
1550err_out:
1551 return ret;
1552}
1553
1554static void __exit ctnetlink_exit(void)
1555{
1556 printk("ctnetlink: unregistering from nfnetlink.\n");
1557
1558#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS
Martin Josefssone64a70b2006-04-01 02:24:48 -08001559 ip_conntrack_expect_unregister_notifier(&ctnl_notifier_exp);
Harald Welte080774a2005-08-09 19:32:58 -07001560 ip_conntrack_unregister_notifier(&ctnl_notifier);
1561#endif
1562
1563 nfnetlink_subsys_unregister(&ctnl_exp_subsys);
1564 nfnetlink_subsys_unregister(&ctnl_subsys);
1565 return;
1566}
1567
1568module_init(ctnetlink_init);
1569module_exit(ctnetlink_exit);