162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * net/sched/act_ipt.c		iptables target interface
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *TODO: Add other tables. For now we only support the ipv4 table targets
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright:	Jamal Hadi Salim (2002-13)
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/types.h>
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/string.h>
1362306a36Sopenharmony_ci#include <linux/errno.h>
1462306a36Sopenharmony_ci#include <linux/skbuff.h>
1562306a36Sopenharmony_ci#include <linux/rtnetlink.h>
1662306a36Sopenharmony_ci#include <linux/module.h>
1762306a36Sopenharmony_ci#include <linux/init.h>
1862306a36Sopenharmony_ci#include <linux/slab.h>
1962306a36Sopenharmony_ci#include <net/netlink.h>
2062306a36Sopenharmony_ci#include <net/pkt_sched.h>
2162306a36Sopenharmony_ci#include <linux/tc_act/tc_ipt.h>
2262306a36Sopenharmony_ci#include <net/tc_act/tc_ipt.h>
2362306a36Sopenharmony_ci#include <net/tc_wrapper.h>
2462306a36Sopenharmony_ci#include <net/ip.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include <linux/netfilter_ipv4/ip_tables.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic struct tc_action_ops act_ipt_ops;
3062306a36Sopenharmony_cistatic struct tc_action_ops act_xt_ops;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic int ipt_init_target(struct net *net, struct xt_entry_target *t,
3362306a36Sopenharmony_ci			   char *table, unsigned int hook)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	struct xt_tgchk_param par;
3662306a36Sopenharmony_ci	struct xt_target *target;
3762306a36Sopenharmony_ci	struct ipt_entry e = {};
3862306a36Sopenharmony_ci	int ret = 0;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	target = xt_request_find_target(AF_INET, t->u.user.name,
4162306a36Sopenharmony_ci					t->u.user.revision);
4262306a36Sopenharmony_ci	if (IS_ERR(target))
4362306a36Sopenharmony_ci		return PTR_ERR(target);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	t->u.kernel.target = target;
4662306a36Sopenharmony_ci	memset(&par, 0, sizeof(par));
4762306a36Sopenharmony_ci	par.net       = net;
4862306a36Sopenharmony_ci	par.table     = table;
4962306a36Sopenharmony_ci	par.entryinfo = &e;
5062306a36Sopenharmony_ci	par.target    = target;
5162306a36Sopenharmony_ci	par.targinfo  = t->data;
5262306a36Sopenharmony_ci	par.hook_mask = 1 << hook;
5362306a36Sopenharmony_ci	par.family    = NFPROTO_IPV4;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	ret = xt_check_target(&par, t->u.target_size - sizeof(*t), 0, false);
5662306a36Sopenharmony_ci	if (ret < 0) {
5762306a36Sopenharmony_ci		module_put(t->u.kernel.target->me);
5862306a36Sopenharmony_ci		return ret;
5962306a36Sopenharmony_ci	}
6062306a36Sopenharmony_ci	return 0;
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic void ipt_destroy_target(struct xt_entry_target *t, struct net *net)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	struct xt_tgdtor_param par = {
6662306a36Sopenharmony_ci		.target   = t->u.kernel.target,
6762306a36Sopenharmony_ci		.targinfo = t->data,
6862306a36Sopenharmony_ci		.family   = NFPROTO_IPV4,
6962306a36Sopenharmony_ci		.net      = net,
7062306a36Sopenharmony_ci	};
7162306a36Sopenharmony_ci	if (par.target->destroy != NULL)
7262306a36Sopenharmony_ci		par.target->destroy(&par);
7362306a36Sopenharmony_ci	module_put(par.target->me);
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic void tcf_ipt_release(struct tc_action *a)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	struct tcf_ipt *ipt = to_ipt(a);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	if (ipt->tcfi_t) {
8162306a36Sopenharmony_ci		ipt_destroy_target(ipt->tcfi_t, a->idrinfo->net);
8262306a36Sopenharmony_ci		kfree(ipt->tcfi_t);
8362306a36Sopenharmony_ci	}
8462306a36Sopenharmony_ci	kfree(ipt->tcfi_tname);
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic const struct nla_policy ipt_policy[TCA_IPT_MAX + 1] = {
8862306a36Sopenharmony_ci	[TCA_IPT_TABLE]	= { .type = NLA_STRING, .len = IFNAMSIZ },
8962306a36Sopenharmony_ci	[TCA_IPT_HOOK]	= NLA_POLICY_RANGE(NLA_U32, NF_INET_PRE_ROUTING,
9062306a36Sopenharmony_ci					   NF_INET_NUMHOOKS),
9162306a36Sopenharmony_ci	[TCA_IPT_INDEX]	= { .type = NLA_U32 },
9262306a36Sopenharmony_ci	[TCA_IPT_TARG]	= { .len = sizeof(struct xt_entry_target) },
9362306a36Sopenharmony_ci};
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic int __tcf_ipt_init(struct net *net, unsigned int id, struct nlattr *nla,
9662306a36Sopenharmony_ci			  struct nlattr *est, struct tc_action **a,
9762306a36Sopenharmony_ci			  const struct tc_action_ops *ops,
9862306a36Sopenharmony_ci			  struct tcf_proto *tp, u32 flags)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	struct tc_action_net *tn = net_generic(net, id);
10162306a36Sopenharmony_ci	bool bind = flags & TCA_ACT_FLAGS_BIND;
10262306a36Sopenharmony_ci	struct nlattr *tb[TCA_IPT_MAX + 1];
10362306a36Sopenharmony_ci	struct tcf_ipt *ipt;
10462306a36Sopenharmony_ci	struct xt_entry_target *td, *t;
10562306a36Sopenharmony_ci	char *tname;
10662306a36Sopenharmony_ci	bool exists = false;
10762306a36Sopenharmony_ci	int ret = 0, err;
10862306a36Sopenharmony_ci	u32 hook = 0;
10962306a36Sopenharmony_ci	u32 index = 0;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	if (nla == NULL)
11262306a36Sopenharmony_ci		return -EINVAL;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	err = nla_parse_nested_deprecated(tb, TCA_IPT_MAX, nla, ipt_policy,
11562306a36Sopenharmony_ci					  NULL);
11662306a36Sopenharmony_ci	if (err < 0)
11762306a36Sopenharmony_ci		return err;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	if (tb[TCA_IPT_INDEX] != NULL)
12062306a36Sopenharmony_ci		index = nla_get_u32(tb[TCA_IPT_INDEX]);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	err = tcf_idr_check_alloc(tn, &index, a, bind);
12362306a36Sopenharmony_ci	if (err < 0)
12462306a36Sopenharmony_ci		return err;
12562306a36Sopenharmony_ci	exists = err;
12662306a36Sopenharmony_ci	if (exists && bind)
12762306a36Sopenharmony_ci		return 0;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	if (tb[TCA_IPT_HOOK] == NULL || tb[TCA_IPT_TARG] == NULL) {
13062306a36Sopenharmony_ci		if (exists)
13162306a36Sopenharmony_ci			tcf_idr_release(*a, bind);
13262306a36Sopenharmony_ci		else
13362306a36Sopenharmony_ci			tcf_idr_cleanup(tn, index);
13462306a36Sopenharmony_ci		return -EINVAL;
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	td = (struct xt_entry_target *)nla_data(tb[TCA_IPT_TARG]);
13862306a36Sopenharmony_ci	if (nla_len(tb[TCA_IPT_TARG]) != td->u.target_size) {
13962306a36Sopenharmony_ci		if (exists)
14062306a36Sopenharmony_ci			tcf_idr_release(*a, bind);
14162306a36Sopenharmony_ci		else
14262306a36Sopenharmony_ci			tcf_idr_cleanup(tn, index);
14362306a36Sopenharmony_ci		return -EINVAL;
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	if (!exists) {
14762306a36Sopenharmony_ci		ret = tcf_idr_create(tn, index, est, a, ops, bind,
14862306a36Sopenharmony_ci				     false, flags);
14962306a36Sopenharmony_ci		if (ret) {
15062306a36Sopenharmony_ci			tcf_idr_cleanup(tn, index);
15162306a36Sopenharmony_ci			return ret;
15262306a36Sopenharmony_ci		}
15362306a36Sopenharmony_ci		ret = ACT_P_CREATED;
15462306a36Sopenharmony_ci	} else {
15562306a36Sopenharmony_ci		if (bind)/* dont override defaults */
15662306a36Sopenharmony_ci			return 0;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci		if (!(flags & TCA_ACT_FLAGS_REPLACE)) {
15962306a36Sopenharmony_ci			tcf_idr_release(*a, bind);
16062306a36Sopenharmony_ci			return -EEXIST;
16162306a36Sopenharmony_ci		}
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	err = -EINVAL;
16562306a36Sopenharmony_ci	hook = nla_get_u32(tb[TCA_IPT_HOOK]);
16662306a36Sopenharmony_ci	switch (hook) {
16762306a36Sopenharmony_ci	case NF_INET_PRE_ROUTING:
16862306a36Sopenharmony_ci		break;
16962306a36Sopenharmony_ci	case NF_INET_POST_ROUTING:
17062306a36Sopenharmony_ci		break;
17162306a36Sopenharmony_ci	default:
17262306a36Sopenharmony_ci		goto err1;
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	if (tb[TCA_IPT_TABLE]) {
17662306a36Sopenharmony_ci		/* mangle only for now */
17762306a36Sopenharmony_ci		if (nla_strcmp(tb[TCA_IPT_TABLE], "mangle"))
17862306a36Sopenharmony_ci			goto err1;
17962306a36Sopenharmony_ci	}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	tname = kstrdup("mangle", GFP_KERNEL);
18262306a36Sopenharmony_ci	if (unlikely(!tname))
18362306a36Sopenharmony_ci		goto err1;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	t = kmemdup(td, td->u.target_size, GFP_KERNEL);
18662306a36Sopenharmony_ci	if (unlikely(!t))
18762306a36Sopenharmony_ci		goto err2;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	err = ipt_init_target(net, t, tname, hook);
19062306a36Sopenharmony_ci	if (err < 0)
19162306a36Sopenharmony_ci		goto err3;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	ipt = to_ipt(*a);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	spin_lock_bh(&ipt->tcf_lock);
19662306a36Sopenharmony_ci	if (ret != ACT_P_CREATED) {
19762306a36Sopenharmony_ci		ipt_destroy_target(ipt->tcfi_t, net);
19862306a36Sopenharmony_ci		kfree(ipt->tcfi_tname);
19962306a36Sopenharmony_ci		kfree(ipt->tcfi_t);
20062306a36Sopenharmony_ci	}
20162306a36Sopenharmony_ci	ipt->tcfi_tname = tname;
20262306a36Sopenharmony_ci	ipt->tcfi_t     = t;
20362306a36Sopenharmony_ci	ipt->tcfi_hook  = hook;
20462306a36Sopenharmony_ci	spin_unlock_bh(&ipt->tcf_lock);
20562306a36Sopenharmony_ci	return ret;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cierr3:
20862306a36Sopenharmony_ci	kfree(t);
20962306a36Sopenharmony_cierr2:
21062306a36Sopenharmony_ci	kfree(tname);
21162306a36Sopenharmony_cierr1:
21262306a36Sopenharmony_ci	tcf_idr_release(*a, bind);
21362306a36Sopenharmony_ci	return err;
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cistatic int tcf_ipt_init(struct net *net, struct nlattr *nla,
21762306a36Sopenharmony_ci			struct nlattr *est, struct tc_action **a,
21862306a36Sopenharmony_ci			struct tcf_proto *tp,
21962306a36Sopenharmony_ci			u32 flags, struct netlink_ext_ack *extack)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	return __tcf_ipt_init(net, act_ipt_ops.net_id, nla, est,
22262306a36Sopenharmony_ci			      a, &act_ipt_ops, tp, flags);
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_cistatic int tcf_xt_init(struct net *net, struct nlattr *nla,
22662306a36Sopenharmony_ci		       struct nlattr *est, struct tc_action **a,
22762306a36Sopenharmony_ci		       struct tcf_proto *tp,
22862306a36Sopenharmony_ci		       u32 flags, struct netlink_ext_ack *extack)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	return __tcf_ipt_init(net, act_xt_ops.net_id, nla, est,
23162306a36Sopenharmony_ci			      a, &act_xt_ops, tp, flags);
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_cistatic bool tcf_ipt_act_check(struct sk_buff *skb)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	const struct iphdr *iph;
23762306a36Sopenharmony_ci	unsigned int nhoff, len;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
24062306a36Sopenharmony_ci		return false;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	nhoff = skb_network_offset(skb);
24362306a36Sopenharmony_ci	iph = ip_hdr(skb);
24462306a36Sopenharmony_ci	if (iph->ihl < 5 || iph->version != 4)
24562306a36Sopenharmony_ci		return false;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	len = skb_ip_totlen(skb);
24862306a36Sopenharmony_ci	if (skb->len < nhoff + len || len < (iph->ihl * 4u))
24962306a36Sopenharmony_ci		return false;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	return pskb_may_pull(skb, iph->ihl * 4u);
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ciTC_INDIRECT_SCOPE int tcf_ipt_act(struct sk_buff *skb,
25562306a36Sopenharmony_ci				  const struct tc_action *a,
25662306a36Sopenharmony_ci				  struct tcf_result *res)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	char saved_cb[sizeof_field(struct sk_buff, cb)];
25962306a36Sopenharmony_ci	int ret = 0, result = 0;
26062306a36Sopenharmony_ci	struct tcf_ipt *ipt = to_ipt(a);
26162306a36Sopenharmony_ci	struct xt_action_param par;
26262306a36Sopenharmony_ci	struct nf_hook_state state = {
26362306a36Sopenharmony_ci		.net	= dev_net(skb->dev),
26462306a36Sopenharmony_ci		.in	= skb->dev,
26562306a36Sopenharmony_ci		.hook	= ipt->tcfi_hook,
26662306a36Sopenharmony_ci		.pf	= NFPROTO_IPV4,
26762306a36Sopenharmony_ci	};
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	if (skb_protocol(skb, false) != htons(ETH_P_IP))
27062306a36Sopenharmony_ci		return TC_ACT_UNSPEC;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	if (skb_unclone(skb, GFP_ATOMIC))
27362306a36Sopenharmony_ci		return TC_ACT_UNSPEC;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	if (!tcf_ipt_act_check(skb))
27662306a36Sopenharmony_ci		return TC_ACT_UNSPEC;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	if (state.hook == NF_INET_POST_ROUTING) {
27962306a36Sopenharmony_ci		if (!skb_dst(skb))
28062306a36Sopenharmony_ci			return TC_ACT_UNSPEC;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci		state.out = skb->dev;
28362306a36Sopenharmony_ci	}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	memcpy(saved_cb, skb->cb, sizeof(saved_cb));
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	spin_lock(&ipt->tcf_lock);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	tcf_lastuse_update(&ipt->tcf_tm);
29062306a36Sopenharmony_ci	bstats_update(&ipt->tcf_bstats, skb);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	/* yes, we have to worry about both in and out dev
29362306a36Sopenharmony_ci	 * worry later - danger - this API seems to have changed
29462306a36Sopenharmony_ci	 * from earlier kernels
29562306a36Sopenharmony_ci	 */
29662306a36Sopenharmony_ci	par.state    = &state;
29762306a36Sopenharmony_ci	par.target   = ipt->tcfi_t->u.kernel.target;
29862306a36Sopenharmony_ci	par.targinfo = ipt->tcfi_t->data;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	ret = par.target->target(skb, &par);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	switch (ret) {
30562306a36Sopenharmony_ci	case NF_ACCEPT:
30662306a36Sopenharmony_ci		result = TC_ACT_OK;
30762306a36Sopenharmony_ci		break;
30862306a36Sopenharmony_ci	case NF_DROP:
30962306a36Sopenharmony_ci		result = TC_ACT_SHOT;
31062306a36Sopenharmony_ci		ipt->tcf_qstats.drops++;
31162306a36Sopenharmony_ci		break;
31262306a36Sopenharmony_ci	case XT_CONTINUE:
31362306a36Sopenharmony_ci		result = TC_ACT_PIPE;
31462306a36Sopenharmony_ci		break;
31562306a36Sopenharmony_ci	default:
31662306a36Sopenharmony_ci		net_notice_ratelimited("tc filter: Bogus netfilter code %d assume ACCEPT\n",
31762306a36Sopenharmony_ci				       ret);
31862306a36Sopenharmony_ci		result = TC_ACT_OK;
31962306a36Sopenharmony_ci		break;
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci	spin_unlock(&ipt->tcf_lock);
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	memcpy(skb->cb, saved_cb, sizeof(skb->cb));
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	return result;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_cistatic int tcf_ipt_dump(struct sk_buff *skb, struct tc_action *a, int bind,
33062306a36Sopenharmony_ci			int ref)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	unsigned char *b = skb_tail_pointer(skb);
33362306a36Sopenharmony_ci	struct tcf_ipt *ipt = to_ipt(a);
33462306a36Sopenharmony_ci	struct xt_entry_target *t;
33562306a36Sopenharmony_ci	struct tcf_t tm;
33662306a36Sopenharmony_ci	struct tc_cnt c;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	/* for simple targets kernel size == user size
33962306a36Sopenharmony_ci	 * user name = target name
34062306a36Sopenharmony_ci	 * for foolproof you need to not assume this
34162306a36Sopenharmony_ci	 */
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	spin_lock_bh(&ipt->tcf_lock);
34462306a36Sopenharmony_ci	t = kmemdup(ipt->tcfi_t, ipt->tcfi_t->u.user.target_size, GFP_ATOMIC);
34562306a36Sopenharmony_ci	if (unlikely(!t))
34662306a36Sopenharmony_ci		goto nla_put_failure;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	c.bindcnt = atomic_read(&ipt->tcf_bindcnt) - bind;
34962306a36Sopenharmony_ci	c.refcnt = refcount_read(&ipt->tcf_refcnt) - ref;
35062306a36Sopenharmony_ci	strcpy(t->u.user.name, ipt->tcfi_t->u.kernel.target->name);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	if (nla_put(skb, TCA_IPT_TARG, ipt->tcfi_t->u.user.target_size, t) ||
35362306a36Sopenharmony_ci	    nla_put_u32(skb, TCA_IPT_INDEX, ipt->tcf_index) ||
35462306a36Sopenharmony_ci	    nla_put_u32(skb, TCA_IPT_HOOK, ipt->tcfi_hook) ||
35562306a36Sopenharmony_ci	    nla_put(skb, TCA_IPT_CNT, sizeof(struct tc_cnt), &c) ||
35662306a36Sopenharmony_ci	    nla_put_string(skb, TCA_IPT_TABLE, ipt->tcfi_tname))
35762306a36Sopenharmony_ci		goto nla_put_failure;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	tcf_tm_dump(&tm, &ipt->tcf_tm);
36062306a36Sopenharmony_ci	if (nla_put_64bit(skb, TCA_IPT_TM, sizeof(tm), &tm, TCA_IPT_PAD))
36162306a36Sopenharmony_ci		goto nla_put_failure;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	spin_unlock_bh(&ipt->tcf_lock);
36462306a36Sopenharmony_ci	kfree(t);
36562306a36Sopenharmony_ci	return skb->len;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_cinla_put_failure:
36862306a36Sopenharmony_ci	spin_unlock_bh(&ipt->tcf_lock);
36962306a36Sopenharmony_ci	nlmsg_trim(skb, b);
37062306a36Sopenharmony_ci	kfree(t);
37162306a36Sopenharmony_ci	return -1;
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_cistatic struct tc_action_ops act_ipt_ops = {
37562306a36Sopenharmony_ci	.kind		=	"ipt",
37662306a36Sopenharmony_ci	.id		=	TCA_ID_IPT,
37762306a36Sopenharmony_ci	.owner		=	THIS_MODULE,
37862306a36Sopenharmony_ci	.act		=	tcf_ipt_act,
37962306a36Sopenharmony_ci	.dump		=	tcf_ipt_dump,
38062306a36Sopenharmony_ci	.cleanup	=	tcf_ipt_release,
38162306a36Sopenharmony_ci	.init		=	tcf_ipt_init,
38262306a36Sopenharmony_ci	.size		=	sizeof(struct tcf_ipt),
38362306a36Sopenharmony_ci};
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_cistatic __net_init int ipt_init_net(struct net *net)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	struct tc_action_net *tn = net_generic(net, act_ipt_ops.net_id);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	return tc_action_net_init(net, tn, &act_ipt_ops);
39062306a36Sopenharmony_ci}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_cistatic void __net_exit ipt_exit_net(struct list_head *net_list)
39362306a36Sopenharmony_ci{
39462306a36Sopenharmony_ci	tc_action_net_exit(net_list, act_ipt_ops.net_id);
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cistatic struct pernet_operations ipt_net_ops = {
39862306a36Sopenharmony_ci	.init = ipt_init_net,
39962306a36Sopenharmony_ci	.exit_batch = ipt_exit_net,
40062306a36Sopenharmony_ci	.id   = &act_ipt_ops.net_id,
40162306a36Sopenharmony_ci	.size = sizeof(struct tc_action_net),
40262306a36Sopenharmony_ci};
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_cistatic struct tc_action_ops act_xt_ops = {
40562306a36Sopenharmony_ci	.kind		=	"xt",
40662306a36Sopenharmony_ci	.id		=	TCA_ID_XT,
40762306a36Sopenharmony_ci	.owner		=	THIS_MODULE,
40862306a36Sopenharmony_ci	.act		=	tcf_ipt_act,
40962306a36Sopenharmony_ci	.dump		=	tcf_ipt_dump,
41062306a36Sopenharmony_ci	.cleanup	=	tcf_ipt_release,
41162306a36Sopenharmony_ci	.init		=	tcf_xt_init,
41262306a36Sopenharmony_ci	.size		=	sizeof(struct tcf_ipt),
41362306a36Sopenharmony_ci};
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_cistatic __net_init int xt_init_net(struct net *net)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	struct tc_action_net *tn = net_generic(net, act_xt_ops.net_id);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	return tc_action_net_init(net, tn, &act_xt_ops);
42062306a36Sopenharmony_ci}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cistatic void __net_exit xt_exit_net(struct list_head *net_list)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	tc_action_net_exit(net_list, act_xt_ops.net_id);
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_cistatic struct pernet_operations xt_net_ops = {
42862306a36Sopenharmony_ci	.init = xt_init_net,
42962306a36Sopenharmony_ci	.exit_batch = xt_exit_net,
43062306a36Sopenharmony_ci	.id   = &act_xt_ops.net_id,
43162306a36Sopenharmony_ci	.size = sizeof(struct tc_action_net),
43262306a36Sopenharmony_ci};
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ciMODULE_AUTHOR("Jamal Hadi Salim(2002-13)");
43562306a36Sopenharmony_ciMODULE_DESCRIPTION("Iptables target actions");
43662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
43762306a36Sopenharmony_ciMODULE_ALIAS("act_xt");
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cistatic int __init ipt_init_module(void)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	int ret1, ret2;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	ret1 = tcf_register_action(&act_xt_ops, &xt_net_ops);
44462306a36Sopenharmony_ci	if (ret1 < 0)
44562306a36Sopenharmony_ci		pr_err("Failed to load xt action\n");
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	ret2 = tcf_register_action(&act_ipt_ops, &ipt_net_ops);
44862306a36Sopenharmony_ci	if (ret2 < 0)
44962306a36Sopenharmony_ci		pr_err("Failed to load ipt action\n");
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	if (ret1 < 0 && ret2 < 0) {
45262306a36Sopenharmony_ci		return ret1;
45362306a36Sopenharmony_ci	} else
45462306a36Sopenharmony_ci		return 0;
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_cistatic void __exit ipt_cleanup_module(void)
45862306a36Sopenharmony_ci{
45962306a36Sopenharmony_ci	tcf_unregister_action(&act_ipt_ops, &ipt_net_ops);
46062306a36Sopenharmony_ci	tcf_unregister_action(&act_xt_ops, &xt_net_ops);
46162306a36Sopenharmony_ci}
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_cimodule_init(ipt_init_module);
46462306a36Sopenharmony_cimodule_exit(ipt_cleanup_module);
465