act_ipt.c 10.6 KB
Newer Older
L
Linus Torvalds 已提交
1
/*
2
 * net/sched/act_ipt.c		iptables target interface
L
Linus Torvalds 已提交
3 4 5 6 7 8 9 10
 *
 *TODO: Add other tables. For now we only support the ipv4 table targets
 *
 *		This program is free software; you can redistribute it and/or
 *		modify it under the terms of the GNU General Public License
 *		as published by the Free Software Foundation; either version
 *		2 of the License, or (at your option) any later version.
 *
11
 * Copyright:	Jamal Hadi Salim (2002-13)
L
Linus Torvalds 已提交
12 13 14 15 16 17 18 19 20 21
 */

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/skbuff.h>
#include <linux/rtnetlink.h>
#include <linux/module.h>
#include <linux/init.h>
22
#include <linux/slab.h>
23
#include <net/netlink.h>
L
Linus Torvalds 已提交
24 25 26 27 28 29 30
#include <net/pkt_sched.h>
#include <linux/tc_act/tc_ipt.h>
#include <net/tc_act/tc_ipt.h>

#include <linux/netfilter_ipv4/ip_tables.h>


31
static unsigned int ipt_net_id;
32
static struct tc_action_ops act_ipt_ops;
33

34
static unsigned int xt_net_id;
35
static struct tc_action_ops act_xt_ops;
36

37 38
static int ipt_init_target(struct net *net, struct xt_entry_target *t,
			   char *table, unsigned int hook)
L
Linus Torvalds 已提交
39
{
40
	struct xt_tgchk_param par;
41
	struct xt_target *target;
42
	struct ipt_entry e = {};
L
Linus Torvalds 已提交
43 44
	int ret = 0;

45 46
	target = xt_request_find_target(AF_INET, t->u.user.name,
					t->u.user.revision);
47 48
	if (IS_ERR(target))
		return PTR_ERR(target);
L
Linus Torvalds 已提交
49 50

	t->u.kernel.target = target;
51
	memset(&par, 0, sizeof(par));
52
	par.net       = net;
53
	par.table     = table;
54
	par.entryinfo = &e;
55 56 57
	par.target    = target;
	par.targinfo  = t->data;
	par.hook_mask = hook;
58
	par.family    = NFPROTO_IPV4;
L
Linus Torvalds 已提交
59

60
	ret = xt_check_target(&par, t->u.target_size - sizeof(*t), 0, false);
61
	if (ret < 0) {
62
		module_put(t->u.kernel.target->me);
63
		return ret;
64
	}
65
	return 0;
L
Linus Torvalds 已提交
66 67
}

68
static void ipt_destroy_target(struct xt_entry_target *t)
L
Linus Torvalds 已提交
69
{
70 71 72
	struct xt_tgdtor_param par = {
		.target   = t->u.kernel.target,
		.targinfo = t->data,
73
		.family   = NFPROTO_IPV4,
74 75 76 77
	};
	if (par.target->destroy != NULL)
		par.target->destroy(&par);
	module_put(par.target->me);
L
Linus Torvalds 已提交
78 79
}

80
static void tcf_ipt_release(struct tc_action *a)
L
Linus Torvalds 已提交
81
{
82
	struct tcf_ipt *ipt = to_ipt(a);
83 84 85 86 87

	if (ipt->tcfi_t) {
		ipt_destroy_target(ipt->tcfi_t);
		kfree(ipt->tcfi_t);
	}
W
WANG Cong 已提交
88
	kfree(ipt->tcfi_tname);
L
Linus Torvalds 已提交
89 90
}

91 92 93 94
static const struct nla_policy ipt_policy[TCA_IPT_MAX + 1] = {
	[TCA_IPT_TABLE]	= { .type = NLA_STRING, .len = IFNAMSIZ },
	[TCA_IPT_HOOK]	= { .type = NLA_U32 },
	[TCA_IPT_INDEX]	= { .type = NLA_U32 },
95
	[TCA_IPT_TARG]	= { .len = sizeof(struct xt_entry_target) },
96 97
};

98
static int __tcf_ipt_init(struct net *net, unsigned int id, struct nlattr *nla,
99 100
			  struct nlattr *est, struct tc_action **a,
			  const struct tc_action_ops *ops, int ovr, int bind)
L
Linus Torvalds 已提交
101
{
102
	struct tc_action_net *tn = net_generic(net, id);
103
	struct nlattr *tb[TCA_IPT_MAX + 1];
104
	struct tcf_ipt *ipt;
105
	struct xt_entry_target *td, *t;
L
Linus Torvalds 已提交
106
	char *tname;
107 108
	bool exists = false;
	int ret = 0, err;
L
Linus Torvalds 已提交
109 110 111
	u32 hook = 0;
	u32 index = 0;

112
	if (nla == NULL)
L
Linus Torvalds 已提交
113 114
		return -EINVAL;

115
	err = nla_parse_nested(tb, TCA_IPT_MAX, nla, ipt_policy, NULL);
116 117 118
	if (err < 0)
		return err;

119 120 121
	if (tb[TCA_IPT_INDEX] != NULL)
		index = nla_get_u32(tb[TCA_IPT_INDEX]);

122 123 124 125
	err = tcf_idr_check_alloc(tn, &index, a, bind);
	if (err < 0)
		return err;
	exists = err;
126 127 128 129 130
	if (exists && bind)
		return 0;

	if (tb[TCA_IPT_HOOK] == NULL || tb[TCA_IPT_TARG] == NULL) {
		if (exists)
131
			tcf_idr_release(*a, bind);
132 133
		else
			tcf_idr_cleanup(tn, index);
L
Linus Torvalds 已提交
134
		return -EINVAL;
135
	}
136

137
	td = (struct xt_entry_target *)nla_data(tb[TCA_IPT_TARG]);
138
	if (nla_len(tb[TCA_IPT_TARG]) != td->u.target_size) {
W
WANG Cong 已提交
139
		if (exists)
140
			tcf_idr_release(*a, bind);
141 142
		else
			tcf_idr_cleanup(tn, index);
L
Linus Torvalds 已提交
143
		return -EINVAL;
W
WANG Cong 已提交
144
	}
L
Linus Torvalds 已提交
145

W
WANG Cong 已提交
146
	if (!exists) {
147 148
		ret = tcf_idr_create(tn, index, est, a, ops, bind,
				     false);
149 150
		if (ret) {
			tcf_idr_cleanup(tn, index);
151
			return ret;
152
		}
L
Linus Torvalds 已提交
153 154
		ret = ACT_P_CREATED;
	} else {
155 156 157
		if (bind)/* dont override defaults */
			return 0;

158 159
		if (!ovr) {
			tcf_idr_release(*a, bind);
L
Linus Torvalds 已提交
160
			return -EEXIST;
161
		}
L
Linus Torvalds 已提交
162
	}
163
	hook = nla_get_u32(tb[TCA_IPT_HOOK]);
L
Linus Torvalds 已提交
164 165 166

	err = -ENOMEM;
	tname = kmalloc(IFNAMSIZ, GFP_KERNEL);
167
	if (unlikely(!tname))
L
Linus Torvalds 已提交
168
		goto err1;
169 170
	if (tb[TCA_IPT_TABLE] == NULL ||
	    nla_strlcpy(tname, tb[TCA_IPT_TABLE], IFNAMSIZ) >= IFNAMSIZ)
L
Linus Torvalds 已提交
171 172
		strcpy(tname, "mangle");

173
	t = kmemdup(td, td->u.target_size, GFP_KERNEL);
174
	if (unlikely(!t))
L
Linus Torvalds 已提交
175 176
		goto err2;

177
	err = ipt_init_target(net, t, tname, hook);
E
Eric Dumazet 已提交
178
	if (err < 0)
L
Linus Torvalds 已提交
179 180
		goto err3;

181 182
	ipt = to_ipt(*a);

183
	spin_lock_bh(&ipt->tcf_lock);
L
Linus Torvalds 已提交
184
	if (ret != ACT_P_CREATED) {
185 186 187
		ipt_destroy_target(ipt->tcfi_t);
		kfree(ipt->tcfi_tname);
		kfree(ipt->tcfi_t);
L
Linus Torvalds 已提交
188
	}
189 190 191 192
	ipt->tcfi_tname = tname;
	ipt->tcfi_t     = t;
	ipt->tcfi_hook  = hook;
	spin_unlock_bh(&ipt->tcf_lock);
L
Linus Torvalds 已提交
193
	if (ret == ACT_P_CREATED)
194
		tcf_idr_insert(tn, *a);
L
Linus Torvalds 已提交
195 196 197 198 199 200 201
	return ret;

err3:
	kfree(t);
err2:
	kfree(tname);
err1:
202
	if (ret == ACT_P_CREATED)
203
		tcf_idr_release(*a, bind);
L
Linus Torvalds 已提交
204 205 206
	return err;
}

207
static int tcf_ipt_init(struct net *net, struct nlattr *nla,
208
			struct nlattr *est, struct tc_action **a, int ovr,
209 210
			int bind, bool rtnl_held,
			struct netlink_ext_ack *extack)
211
{
212 213
	return __tcf_ipt_init(net, ipt_net_id, nla, est, a, &act_ipt_ops, ovr,
			      bind);
214 215 216
}

static int tcf_xt_init(struct net *net, struct nlattr *nla,
217
		       struct nlattr *est, struct tc_action **a, int ovr,
218 219
		       int bind, bool unlocked,
		       struct netlink_ext_ack *extack)
220
{
221 222
	return __tcf_ipt_init(net, xt_net_id, nla, est, a, &act_xt_ops, ovr,
			      bind);
223 224
}

225 226
static int tcf_ipt_act(struct sk_buff *skb, const struct tc_action *a,
		       struct tcf_result *res)
L
Linus Torvalds 已提交
227 228
{
	int ret = 0, result = 0;
229
	struct tcf_ipt *ipt = to_ipt(a);
230
	struct xt_action_param par;
231 232 233 234 235 236
	struct nf_hook_state state = {
		.net	= dev_net(skb->dev),
		.in	= skb->dev,
		.hook	= ipt->tcfi_hook,
		.pf	= NFPROTO_IPV4,
	};
L
Linus Torvalds 已提交
237

238 239
	if (skb_unclone(skb, GFP_ATOMIC))
		return TC_ACT_UNSPEC;
L
Linus Torvalds 已提交
240

241
	spin_lock(&ipt->tcf_lock);
L
Linus Torvalds 已提交
242

243
	tcf_lastuse_update(&ipt->tcf_tm);
244
	bstats_update(&ipt->tcf_bstats, skb);
L
Linus Torvalds 已提交
245 246

	/* yes, we have to worry about both in and out dev
E
Eric Dumazet 已提交
247 248 249
	 * worry later - danger - this API seems to have changed
	 * from earlier kernels
	 */
250
	par.state    = &state;
251 252 253 254
	par.target   = ipt->tcfi_t->u.kernel.target;
	par.targinfo = ipt->tcfi_t->data;
	ret = par.target->target(skb, &par);

L
Linus Torvalds 已提交
255 256 257 258 259 260
	switch (ret) {
	case NF_ACCEPT:
		result = TC_ACT_OK;
		break;
	case NF_DROP:
		result = TC_ACT_SHOT;
261
		ipt->tcf_qstats.drops++;
L
Linus Torvalds 已提交
262
		break;
263
	case XT_CONTINUE:
L
Linus Torvalds 已提交
264 265 266
		result = TC_ACT_PIPE;
		break;
	default:
267 268
		net_notice_ratelimited("tc filter: Bogus netfilter code %d assume ACCEPT\n",
				       ret);
269
		result = TC_ACT_OK;
L
Linus Torvalds 已提交
270 271
		break;
	}
272
	spin_unlock(&ipt->tcf_lock);
L
Linus Torvalds 已提交
273 274 275 276
	return result;

}

277 278
static int tcf_ipt_dump(struct sk_buff *skb, struct tc_action *a, int bind,
			int ref)
L
Linus Torvalds 已提交
279
{
280
	unsigned char *b = skb_tail_pointer(skb);
281
	struct tcf_ipt *ipt = to_ipt(a);
282
	struct xt_entry_target *t;
L
Linus Torvalds 已提交
283 284 285 286
	struct tcf_t tm;
	struct tc_cnt c;

	/* for simple targets kernel size == user size
E
Eric Dumazet 已提交
287 288 289
	 * user name = target name
	 * for foolproof you need to not assume this
	 */
L
Linus Torvalds 已提交
290

291
	spin_lock_bh(&ipt->tcf_lock);
292
	t = kmemdup(ipt->tcfi_t, ipt->tcfi_t->u.user.target_size, GFP_ATOMIC);
293
	if (unlikely(!t))
294
		goto nla_put_failure;
L
Linus Torvalds 已提交
295

296 297
	c.bindcnt = atomic_read(&ipt->tcf_bindcnt) - bind;
	c.refcnt = refcount_read(&ipt->tcf_refcnt) - ref;
298 299
	strcpy(t->u.user.name, ipt->tcfi_t->u.kernel.target->name);

300 301 302 303 304 305
	if (nla_put(skb, TCA_IPT_TARG, ipt->tcfi_t->u.user.target_size, t) ||
	    nla_put_u32(skb, TCA_IPT_INDEX, ipt->tcf_index) ||
	    nla_put_u32(skb, TCA_IPT_HOOK, ipt->tcfi_hook) ||
	    nla_put(skb, TCA_IPT_CNT, sizeof(struct tc_cnt), &c) ||
	    nla_put_string(skb, TCA_IPT_TABLE, ipt->tcfi_tname))
		goto nla_put_failure;
306 307

	tcf_tm_dump(&tm, &ipt->tcf_tm);
308
	if (nla_put_64bit(skb, TCA_IPT_TM, sizeof(tm), &tm, TCA_IPT_PAD))
309
		goto nla_put_failure;
310

311
	spin_unlock_bh(&ipt->tcf_lock);
L
Linus Torvalds 已提交
312 313 314
	kfree(t);
	return skb->len;

315
nla_put_failure:
316
	spin_unlock_bh(&ipt->tcf_lock);
317
	nlmsg_trim(skb, b);
L
Linus Torvalds 已提交
318 319 320 321
	kfree(t);
	return -1;
}

322 323
static int tcf_ipt_walker(struct net *net, struct sk_buff *skb,
			  struct netlink_callback *cb, int type,
324 325
			  const struct tc_action_ops *ops,
			  struct netlink_ext_ack *extack)
326 327 328
{
	struct tc_action_net *tn = net_generic(net, ipt_net_id);

329
	return tcf_generic_walker(tn, skb, cb, type, ops, extack);
330 331
}

332
static int tcf_ipt_search(struct net *net, struct tc_action **a, u32 index)
333 334 335
{
	struct tc_action_net *tn = net_generic(net, ipt_net_id);

336
	return tcf_idr_search(tn, a, index);
337 338
}

L
Linus Torvalds 已提交
339 340
static struct tc_action_ops act_ipt_ops = {
	.kind		=	"ipt",
341
	.id		=	TCA_ID_IPT,
L
Linus Torvalds 已提交
342
	.owner		=	THIS_MODULE,
343
	.act		=	tcf_ipt_act,
L
Linus Torvalds 已提交
344
	.dump		=	tcf_ipt_dump,
345
	.cleanup	=	tcf_ipt_release,
L
Linus Torvalds 已提交
346
	.init		=	tcf_ipt_init,
347 348
	.walk		=	tcf_ipt_walker,
	.lookup		=	tcf_ipt_search,
349
	.size		=	sizeof(struct tcf_ipt),
350 351 352 353 354 355
};

static __net_init int ipt_init_net(struct net *net)
{
	struct tc_action_net *tn = net_generic(net, ipt_net_id);

356
	return tc_action_net_init(tn, &act_ipt_ops);
357 358
}

359
static void __net_exit ipt_exit_net(struct list_head *net_list)
360
{
361
	tc_action_net_exit(net_list, ipt_net_id);
362 363 364 365
}

static struct pernet_operations ipt_net_ops = {
	.init = ipt_init_net,
366
	.exit_batch = ipt_exit_net,
367 368
	.id   = &ipt_net_id,
	.size = sizeof(struct tc_action_net),
L
Linus Torvalds 已提交
369 370
};

371 372
static int tcf_xt_walker(struct net *net, struct sk_buff *skb,
			 struct netlink_callback *cb, int type,
373 374
			 const struct tc_action_ops *ops,
			 struct netlink_ext_ack *extack)
375 376 377
{
	struct tc_action_net *tn = net_generic(net, xt_net_id);

378
	return tcf_generic_walker(tn, skb, cb, type, ops, extack);
379 380
}

381
static int tcf_xt_search(struct net *net, struct tc_action **a, u32 index)
382 383 384
{
	struct tc_action_net *tn = net_generic(net, xt_net_id);

385
	return tcf_idr_search(tn, a, index);
386 387
}

388 389
static struct tc_action_ops act_xt_ops = {
	.kind		=	"xt",
390
	.id		=	TCA_ID_XT,
391
	.owner		=	THIS_MODULE,
392
	.act		=	tcf_ipt_act,
393
	.dump		=	tcf_ipt_dump,
394
	.cleanup	=	tcf_ipt_release,
395 396 397
	.init		=	tcf_xt_init,
	.walk		=	tcf_xt_walker,
	.lookup		=	tcf_xt_search,
398
	.size		=	sizeof(struct tcf_ipt),
399 400 401 402 403 404
};

static __net_init int xt_init_net(struct net *net)
{
	struct tc_action_net *tn = net_generic(net, xt_net_id);

405
	return tc_action_net_init(tn, &act_xt_ops);
406 407
}

408
static void __net_exit xt_exit_net(struct list_head *net_list)
409
{
410
	tc_action_net_exit(net_list, xt_net_id);
411 412 413 414
}

static struct pernet_operations xt_net_ops = {
	.init = xt_init_net,
415
	.exit_batch = xt_exit_net,
416 417
	.id   = &xt_net_id,
	.size = sizeof(struct tc_action_net),
418 419 420
};

MODULE_AUTHOR("Jamal Hadi Salim(2002-13)");
L
Linus Torvalds 已提交
421 422
MODULE_DESCRIPTION("Iptables target actions");
MODULE_LICENSE("GPL");
423
MODULE_ALIAS("act_xt");
L
Linus Torvalds 已提交
424

425
static int __init ipt_init_module(void)
L
Linus Torvalds 已提交
426
{
427
	int ret1, ret2;
428

429
	ret1 = tcf_register_action(&act_xt_ops, &xt_net_ops);
430
	if (ret1 < 0)
431 432 433
		pr_err("Failed to load xt action\n");

	ret2 = tcf_register_action(&act_ipt_ops, &ipt_net_ops);
434
	if (ret2 < 0)
435
		pr_err("Failed to load ipt action\n");
436

437
	if (ret1 < 0 && ret2 < 0) {
438
		return ret1;
439
	} else
440
		return 0;
L
Linus Torvalds 已提交
441 442
}

443
static void __exit ipt_cleanup_module(void)
L
Linus Torvalds 已提交
444
{
445 446
	tcf_unregister_action(&act_ipt_ops, &ipt_net_ops);
	tcf_unregister_action(&act_xt_ops, &xt_net_ops);
L
Linus Torvalds 已提交
447 448 449 450
}

module_init(ipt_init_module);
module_exit(ipt_cleanup_module);