18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * net/sched/act_skbmod.c  skb data modifier
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2016 Jamal Hadi Salim <jhs@mojatatu.com>
68c2ecf20Sopenharmony_ci*/
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/if_arp.h>
108c2ecf20Sopenharmony_ci#include <linux/init.h>
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
138c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h>
148c2ecf20Sopenharmony_ci#include <net/netlink.h>
158c2ecf20Sopenharmony_ci#include <net/pkt_sched.h>
168c2ecf20Sopenharmony_ci#include <net/pkt_cls.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <linux/tc_act/tc_skbmod.h>
198c2ecf20Sopenharmony_ci#include <net/tc_act/tc_skbmod.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic unsigned int skbmod_net_id;
228c2ecf20Sopenharmony_cistatic struct tc_action_ops act_skbmod_ops;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define MAX_EDIT_LEN ETH_HLEN
258c2ecf20Sopenharmony_cistatic int tcf_skbmod_act(struct sk_buff *skb, const struct tc_action *a,
268c2ecf20Sopenharmony_ci			  struct tcf_result *res)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	struct tcf_skbmod *d = to_skbmod(a);
298c2ecf20Sopenharmony_ci	int action;
308c2ecf20Sopenharmony_ci	struct tcf_skbmod_params *p;
318c2ecf20Sopenharmony_ci	u64 flags;
328c2ecf20Sopenharmony_ci	int err;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	tcf_lastuse_update(&d->tcf_tm);
358c2ecf20Sopenharmony_ci	bstats_cpu_update(this_cpu_ptr(d->common.cpu_bstats), skb);
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	action = READ_ONCE(d->tcf_action);
388c2ecf20Sopenharmony_ci	if (unlikely(action == TC_ACT_SHOT))
398c2ecf20Sopenharmony_ci		goto drop;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	if (!skb->dev || skb->dev->type != ARPHRD_ETHER)
428c2ecf20Sopenharmony_ci		return action;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	/* XXX: if you are going to edit more fields beyond ethernet header
458c2ecf20Sopenharmony_ci	 * (example when you add IP header replacement or vlan swap)
468c2ecf20Sopenharmony_ci	 * then MAX_EDIT_LEN needs to change appropriately
478c2ecf20Sopenharmony_ci	*/
488c2ecf20Sopenharmony_ci	err = skb_ensure_writable(skb, MAX_EDIT_LEN);
498c2ecf20Sopenharmony_ci	if (unlikely(err)) /* best policy is to drop on the floor */
508c2ecf20Sopenharmony_ci		goto drop;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	p = rcu_dereference_bh(d->skbmod_p);
538c2ecf20Sopenharmony_ci	flags = p->flags;
548c2ecf20Sopenharmony_ci	if (flags & SKBMOD_F_DMAC)
558c2ecf20Sopenharmony_ci		ether_addr_copy(eth_hdr(skb)->h_dest, p->eth_dst);
568c2ecf20Sopenharmony_ci	if (flags & SKBMOD_F_SMAC)
578c2ecf20Sopenharmony_ci		ether_addr_copy(eth_hdr(skb)->h_source, p->eth_src);
588c2ecf20Sopenharmony_ci	if (flags & SKBMOD_F_ETYPE)
598c2ecf20Sopenharmony_ci		eth_hdr(skb)->h_proto = p->eth_type;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	if (flags & SKBMOD_F_SWAPMAC) {
628c2ecf20Sopenharmony_ci		u16 tmpaddr[ETH_ALEN / 2]; /* ether_addr_copy() requirement */
638c2ecf20Sopenharmony_ci		/*XXX: I am sure we can come up with more efficient swapping*/
648c2ecf20Sopenharmony_ci		ether_addr_copy((u8 *)tmpaddr, eth_hdr(skb)->h_dest);
658c2ecf20Sopenharmony_ci		ether_addr_copy(eth_hdr(skb)->h_dest, eth_hdr(skb)->h_source);
668c2ecf20Sopenharmony_ci		ether_addr_copy(eth_hdr(skb)->h_source, (u8 *)tmpaddr);
678c2ecf20Sopenharmony_ci	}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	return action;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cidrop:
728c2ecf20Sopenharmony_ci	qstats_overlimit_inc(this_cpu_ptr(d->common.cpu_qstats));
738c2ecf20Sopenharmony_ci	return TC_ACT_SHOT;
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic const struct nla_policy skbmod_policy[TCA_SKBMOD_MAX + 1] = {
778c2ecf20Sopenharmony_ci	[TCA_SKBMOD_PARMS]		= { .len = sizeof(struct tc_skbmod) },
788c2ecf20Sopenharmony_ci	[TCA_SKBMOD_DMAC]		= { .len = ETH_ALEN },
798c2ecf20Sopenharmony_ci	[TCA_SKBMOD_SMAC]		= { .len = ETH_ALEN },
808c2ecf20Sopenharmony_ci	[TCA_SKBMOD_ETYPE]		= { .type = NLA_U16 },
818c2ecf20Sopenharmony_ci};
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic int tcf_skbmod_init(struct net *net, struct nlattr *nla,
848c2ecf20Sopenharmony_ci			   struct nlattr *est, struct tc_action **a,
858c2ecf20Sopenharmony_ci			   int ovr, int bind, bool rtnl_held,
868c2ecf20Sopenharmony_ci			   struct tcf_proto *tp, u32 flags,
878c2ecf20Sopenharmony_ci			   struct netlink_ext_ack *extack)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	struct tc_action_net *tn = net_generic(net, skbmod_net_id);
908c2ecf20Sopenharmony_ci	struct nlattr *tb[TCA_SKBMOD_MAX + 1];
918c2ecf20Sopenharmony_ci	struct tcf_skbmod_params *p, *p_old;
928c2ecf20Sopenharmony_ci	struct tcf_chain *goto_ch = NULL;
938c2ecf20Sopenharmony_ci	struct tc_skbmod *parm;
948c2ecf20Sopenharmony_ci	u32 lflags = 0, index;
958c2ecf20Sopenharmony_ci	struct tcf_skbmod *d;
968c2ecf20Sopenharmony_ci	bool exists = false;
978c2ecf20Sopenharmony_ci	u8 *daddr = NULL;
988c2ecf20Sopenharmony_ci	u8 *saddr = NULL;
998c2ecf20Sopenharmony_ci	u16 eth_type = 0;
1008c2ecf20Sopenharmony_ci	int ret = 0, err;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	if (!nla)
1038c2ecf20Sopenharmony_ci		return -EINVAL;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	err = nla_parse_nested_deprecated(tb, TCA_SKBMOD_MAX, nla,
1068c2ecf20Sopenharmony_ci					  skbmod_policy, NULL);
1078c2ecf20Sopenharmony_ci	if (err < 0)
1088c2ecf20Sopenharmony_ci		return err;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	if (!tb[TCA_SKBMOD_PARMS])
1118c2ecf20Sopenharmony_ci		return -EINVAL;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	if (tb[TCA_SKBMOD_DMAC]) {
1148c2ecf20Sopenharmony_ci		daddr = nla_data(tb[TCA_SKBMOD_DMAC]);
1158c2ecf20Sopenharmony_ci		lflags |= SKBMOD_F_DMAC;
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	if (tb[TCA_SKBMOD_SMAC]) {
1198c2ecf20Sopenharmony_ci		saddr = nla_data(tb[TCA_SKBMOD_SMAC]);
1208c2ecf20Sopenharmony_ci		lflags |= SKBMOD_F_SMAC;
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	if (tb[TCA_SKBMOD_ETYPE]) {
1248c2ecf20Sopenharmony_ci		eth_type = nla_get_u16(tb[TCA_SKBMOD_ETYPE]);
1258c2ecf20Sopenharmony_ci		lflags |= SKBMOD_F_ETYPE;
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	parm = nla_data(tb[TCA_SKBMOD_PARMS]);
1298c2ecf20Sopenharmony_ci	index = parm->index;
1308c2ecf20Sopenharmony_ci	if (parm->flags & SKBMOD_F_SWAPMAC)
1318c2ecf20Sopenharmony_ci		lflags = SKBMOD_F_SWAPMAC;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	err = tcf_idr_check_alloc(tn, &index, a, bind);
1348c2ecf20Sopenharmony_ci	if (err < 0)
1358c2ecf20Sopenharmony_ci		return err;
1368c2ecf20Sopenharmony_ci	exists = err;
1378c2ecf20Sopenharmony_ci	if (exists && bind)
1388c2ecf20Sopenharmony_ci		return 0;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	if (!lflags) {
1418c2ecf20Sopenharmony_ci		if (exists)
1428c2ecf20Sopenharmony_ci			tcf_idr_release(*a, bind);
1438c2ecf20Sopenharmony_ci		else
1448c2ecf20Sopenharmony_ci			tcf_idr_cleanup(tn, index);
1458c2ecf20Sopenharmony_ci		return -EINVAL;
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	if (!exists) {
1498c2ecf20Sopenharmony_ci		ret = tcf_idr_create(tn, index, est, a,
1508c2ecf20Sopenharmony_ci				     &act_skbmod_ops, bind, true, flags);
1518c2ecf20Sopenharmony_ci		if (ret) {
1528c2ecf20Sopenharmony_ci			tcf_idr_cleanup(tn, index);
1538c2ecf20Sopenharmony_ci			return ret;
1548c2ecf20Sopenharmony_ci		}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci		ret = ACT_P_CREATED;
1578c2ecf20Sopenharmony_ci	} else if (!ovr) {
1588c2ecf20Sopenharmony_ci		tcf_idr_release(*a, bind);
1598c2ecf20Sopenharmony_ci		return -EEXIST;
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci	err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
1628c2ecf20Sopenharmony_ci	if (err < 0)
1638c2ecf20Sopenharmony_ci		goto release_idr;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	d = to_skbmod(*a);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	p = kzalloc(sizeof(struct tcf_skbmod_params), GFP_KERNEL);
1688c2ecf20Sopenharmony_ci	if (unlikely(!p)) {
1698c2ecf20Sopenharmony_ci		err = -ENOMEM;
1708c2ecf20Sopenharmony_ci		goto put_chain;
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	p->flags = lflags;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	if (ovr)
1768c2ecf20Sopenharmony_ci		spin_lock_bh(&d->tcf_lock);
1778c2ecf20Sopenharmony_ci	/* Protected by tcf_lock if overwriting existing action. */
1788c2ecf20Sopenharmony_ci	goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
1798c2ecf20Sopenharmony_ci	p_old = rcu_dereference_protected(d->skbmod_p, 1);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	if (lflags & SKBMOD_F_DMAC)
1828c2ecf20Sopenharmony_ci		ether_addr_copy(p->eth_dst, daddr);
1838c2ecf20Sopenharmony_ci	if (lflags & SKBMOD_F_SMAC)
1848c2ecf20Sopenharmony_ci		ether_addr_copy(p->eth_src, saddr);
1858c2ecf20Sopenharmony_ci	if (lflags & SKBMOD_F_ETYPE)
1868c2ecf20Sopenharmony_ci		p->eth_type = htons(eth_type);
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	rcu_assign_pointer(d->skbmod_p, p);
1898c2ecf20Sopenharmony_ci	if (ovr)
1908c2ecf20Sopenharmony_ci		spin_unlock_bh(&d->tcf_lock);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	if (p_old)
1938c2ecf20Sopenharmony_ci		kfree_rcu(p_old, rcu);
1948c2ecf20Sopenharmony_ci	if (goto_ch)
1958c2ecf20Sopenharmony_ci		tcf_chain_put_by_act(goto_ch);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	return ret;
1988c2ecf20Sopenharmony_ciput_chain:
1998c2ecf20Sopenharmony_ci	if (goto_ch)
2008c2ecf20Sopenharmony_ci		tcf_chain_put_by_act(goto_ch);
2018c2ecf20Sopenharmony_cirelease_idr:
2028c2ecf20Sopenharmony_ci	tcf_idr_release(*a, bind);
2038c2ecf20Sopenharmony_ci	return err;
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_cistatic void tcf_skbmod_cleanup(struct tc_action *a)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	struct tcf_skbmod *d = to_skbmod(a);
2098c2ecf20Sopenharmony_ci	struct tcf_skbmod_params  *p;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	p = rcu_dereference_protected(d->skbmod_p, 1);
2128c2ecf20Sopenharmony_ci	if (p)
2138c2ecf20Sopenharmony_ci		kfree_rcu(p, rcu);
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_cistatic int tcf_skbmod_dump(struct sk_buff *skb, struct tc_action *a,
2178c2ecf20Sopenharmony_ci			   int bind, int ref)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	struct tcf_skbmod *d = to_skbmod(a);
2208c2ecf20Sopenharmony_ci	unsigned char *b = skb_tail_pointer(skb);
2218c2ecf20Sopenharmony_ci	struct tcf_skbmod_params  *p;
2228c2ecf20Sopenharmony_ci	struct tc_skbmod opt = {
2238c2ecf20Sopenharmony_ci		.index   = d->tcf_index,
2248c2ecf20Sopenharmony_ci		.refcnt  = refcount_read(&d->tcf_refcnt) - ref,
2258c2ecf20Sopenharmony_ci		.bindcnt = atomic_read(&d->tcf_bindcnt) - bind,
2268c2ecf20Sopenharmony_ci	};
2278c2ecf20Sopenharmony_ci	struct tcf_t t;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	spin_lock_bh(&d->tcf_lock);
2308c2ecf20Sopenharmony_ci	opt.action = d->tcf_action;
2318c2ecf20Sopenharmony_ci	p = rcu_dereference_protected(d->skbmod_p,
2328c2ecf20Sopenharmony_ci				      lockdep_is_held(&d->tcf_lock));
2338c2ecf20Sopenharmony_ci	opt.flags  = p->flags;
2348c2ecf20Sopenharmony_ci	if (nla_put(skb, TCA_SKBMOD_PARMS, sizeof(opt), &opt))
2358c2ecf20Sopenharmony_ci		goto nla_put_failure;
2368c2ecf20Sopenharmony_ci	if ((p->flags & SKBMOD_F_DMAC) &&
2378c2ecf20Sopenharmony_ci	    nla_put(skb, TCA_SKBMOD_DMAC, ETH_ALEN, p->eth_dst))
2388c2ecf20Sopenharmony_ci		goto nla_put_failure;
2398c2ecf20Sopenharmony_ci	if ((p->flags & SKBMOD_F_SMAC) &&
2408c2ecf20Sopenharmony_ci	    nla_put(skb, TCA_SKBMOD_SMAC, ETH_ALEN, p->eth_src))
2418c2ecf20Sopenharmony_ci		goto nla_put_failure;
2428c2ecf20Sopenharmony_ci	if ((p->flags & SKBMOD_F_ETYPE) &&
2438c2ecf20Sopenharmony_ci	    nla_put_u16(skb, TCA_SKBMOD_ETYPE, ntohs(p->eth_type)))
2448c2ecf20Sopenharmony_ci		goto nla_put_failure;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	tcf_tm_dump(&t, &d->tcf_tm);
2478c2ecf20Sopenharmony_ci	if (nla_put_64bit(skb, TCA_SKBMOD_TM, sizeof(t), &t, TCA_SKBMOD_PAD))
2488c2ecf20Sopenharmony_ci		goto nla_put_failure;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	spin_unlock_bh(&d->tcf_lock);
2518c2ecf20Sopenharmony_ci	return skb->len;
2528c2ecf20Sopenharmony_cinla_put_failure:
2538c2ecf20Sopenharmony_ci	spin_unlock_bh(&d->tcf_lock);
2548c2ecf20Sopenharmony_ci	nlmsg_trim(skb, b);
2558c2ecf20Sopenharmony_ci	return -1;
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic int tcf_skbmod_walker(struct net *net, struct sk_buff *skb,
2598c2ecf20Sopenharmony_ci			     struct netlink_callback *cb, int type,
2608c2ecf20Sopenharmony_ci			     const struct tc_action_ops *ops,
2618c2ecf20Sopenharmony_ci			     struct netlink_ext_ack *extack)
2628c2ecf20Sopenharmony_ci{
2638c2ecf20Sopenharmony_ci	struct tc_action_net *tn = net_generic(net, skbmod_net_id);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	return tcf_generic_walker(tn, skb, cb, type, ops, extack);
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_cistatic int tcf_skbmod_search(struct net *net, struct tc_action **a, u32 index)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	struct tc_action_net *tn = net_generic(net, skbmod_net_id);
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	return tcf_idr_search(tn, a, index);
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_cistatic struct tc_action_ops act_skbmod_ops = {
2768c2ecf20Sopenharmony_ci	.kind		=	"skbmod",
2778c2ecf20Sopenharmony_ci	.id		=	TCA_ACT_SKBMOD,
2788c2ecf20Sopenharmony_ci	.owner		=	THIS_MODULE,
2798c2ecf20Sopenharmony_ci	.act		=	tcf_skbmod_act,
2808c2ecf20Sopenharmony_ci	.dump		=	tcf_skbmod_dump,
2818c2ecf20Sopenharmony_ci	.init		=	tcf_skbmod_init,
2828c2ecf20Sopenharmony_ci	.cleanup	=	tcf_skbmod_cleanup,
2838c2ecf20Sopenharmony_ci	.walk		=	tcf_skbmod_walker,
2848c2ecf20Sopenharmony_ci	.lookup		=	tcf_skbmod_search,
2858c2ecf20Sopenharmony_ci	.size		=	sizeof(struct tcf_skbmod),
2868c2ecf20Sopenharmony_ci};
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_cistatic __net_init int skbmod_init_net(struct net *net)
2898c2ecf20Sopenharmony_ci{
2908c2ecf20Sopenharmony_ci	struct tc_action_net *tn = net_generic(net, skbmod_net_id);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	return tc_action_net_init(net, tn, &act_skbmod_ops);
2938c2ecf20Sopenharmony_ci}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_cistatic void __net_exit skbmod_exit_net(struct list_head *net_list)
2968c2ecf20Sopenharmony_ci{
2978c2ecf20Sopenharmony_ci	tc_action_net_exit(net_list, skbmod_net_id);
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cistatic struct pernet_operations skbmod_net_ops = {
3018c2ecf20Sopenharmony_ci	.init = skbmod_init_net,
3028c2ecf20Sopenharmony_ci	.exit_batch = skbmod_exit_net,
3038c2ecf20Sopenharmony_ci	.id   = &skbmod_net_id,
3048c2ecf20Sopenharmony_ci	.size = sizeof(struct tc_action_net),
3058c2ecf20Sopenharmony_ci};
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jamal Hadi Salim, <jhs@mojatatu.com>");
3088c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SKB data mod-ing");
3098c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_cistatic int __init skbmod_init_module(void)
3128c2ecf20Sopenharmony_ci{
3138c2ecf20Sopenharmony_ci	return tcf_register_action(&act_skbmod_ops, &skbmod_net_ops);
3148c2ecf20Sopenharmony_ci}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_cistatic void __exit skbmod_cleanup_module(void)
3178c2ecf20Sopenharmony_ci{
3188c2ecf20Sopenharmony_ci	tcf_unregister_action(&act_skbmod_ops, &skbmod_net_ops);
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cimodule_init(skbmod_init_module);
3228c2ecf20Sopenharmony_cimodule_exit(skbmod_cleanup_module);
323