18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2008, Intel Corporation.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Author: Alexander Duyck <alexander.h.duyck@intel.com>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/init.h>
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
128c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h>
138c2ecf20Sopenharmony_ci#include <net/netlink.h>
148c2ecf20Sopenharmony_ci#include <net/pkt_sched.h>
158c2ecf20Sopenharmony_ci#include <net/ip.h>
168c2ecf20Sopenharmony_ci#include <net/ipv6.h>
178c2ecf20Sopenharmony_ci#include <net/dsfield.h>
188c2ecf20Sopenharmony_ci#include <net/pkt_cls.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include <linux/tc_act/tc_skbedit.h>
218c2ecf20Sopenharmony_ci#include <net/tc_act/tc_skbedit.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistatic unsigned int skbedit_net_id;
248c2ecf20Sopenharmony_cistatic struct tc_action_ops act_skbedit_ops;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic int tcf_skbedit_act(struct sk_buff *skb, const struct tc_action *a,
278c2ecf20Sopenharmony_ci			   struct tcf_result *res)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	struct tcf_skbedit *d = to_skbedit(a);
308c2ecf20Sopenharmony_ci	struct tcf_skbedit_params *params;
318c2ecf20Sopenharmony_ci	int action;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	tcf_lastuse_update(&d->tcf_tm);
348c2ecf20Sopenharmony_ci	bstats_cpu_update(this_cpu_ptr(d->common.cpu_bstats), skb);
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	params = rcu_dereference_bh(d->params);
378c2ecf20Sopenharmony_ci	action = READ_ONCE(d->tcf_action);
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	if (params->flags & SKBEDIT_F_PRIORITY)
408c2ecf20Sopenharmony_ci		skb->priority = params->priority;
418c2ecf20Sopenharmony_ci	if (params->flags & SKBEDIT_F_INHERITDSFIELD) {
428c2ecf20Sopenharmony_ci		int wlen = skb_network_offset(skb);
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci		switch (skb_protocol(skb, true)) {
458c2ecf20Sopenharmony_ci		case htons(ETH_P_IP):
468c2ecf20Sopenharmony_ci			wlen += sizeof(struct iphdr);
478c2ecf20Sopenharmony_ci			if (!pskb_may_pull(skb, wlen))
488c2ecf20Sopenharmony_ci				goto err;
498c2ecf20Sopenharmony_ci			skb->priority = ipv4_get_dsfield(ip_hdr(skb)) >> 2;
508c2ecf20Sopenharmony_ci			break;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci		case htons(ETH_P_IPV6):
538c2ecf20Sopenharmony_ci			wlen += sizeof(struct ipv6hdr);
548c2ecf20Sopenharmony_ci			if (!pskb_may_pull(skb, wlen))
558c2ecf20Sopenharmony_ci				goto err;
568c2ecf20Sopenharmony_ci			skb->priority = ipv6_get_dsfield(ipv6_hdr(skb)) >> 2;
578c2ecf20Sopenharmony_ci			break;
588c2ecf20Sopenharmony_ci		}
598c2ecf20Sopenharmony_ci	}
608c2ecf20Sopenharmony_ci	if (params->flags & SKBEDIT_F_QUEUE_MAPPING &&
618c2ecf20Sopenharmony_ci	    skb->dev->real_num_tx_queues > params->queue_mapping)
628c2ecf20Sopenharmony_ci		skb_set_queue_mapping(skb, params->queue_mapping);
638c2ecf20Sopenharmony_ci	if (params->flags & SKBEDIT_F_MARK) {
648c2ecf20Sopenharmony_ci		skb->mark &= ~params->mask;
658c2ecf20Sopenharmony_ci		skb->mark |= params->mark & params->mask;
668c2ecf20Sopenharmony_ci	}
678c2ecf20Sopenharmony_ci	if (params->flags & SKBEDIT_F_PTYPE)
688c2ecf20Sopenharmony_ci		skb->pkt_type = params->ptype;
698c2ecf20Sopenharmony_ci	return action;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cierr:
728c2ecf20Sopenharmony_ci	qstats_drop_inc(this_cpu_ptr(d->common.cpu_qstats));
738c2ecf20Sopenharmony_ci	return TC_ACT_SHOT;
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic void tcf_skbedit_stats_update(struct tc_action *a, u64 bytes,
778c2ecf20Sopenharmony_ci				     u64 packets, u64 drops,
788c2ecf20Sopenharmony_ci				     u64 lastuse, bool hw)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	struct tcf_skbedit *d = to_skbedit(a);
818c2ecf20Sopenharmony_ci	struct tcf_t *tm = &d->tcf_tm;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	tcf_action_update_stats(a, bytes, packets, drops, hw);
848c2ecf20Sopenharmony_ci	tm->lastuse = max_t(u64, tm->lastuse, lastuse);
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic const struct nla_policy skbedit_policy[TCA_SKBEDIT_MAX + 1] = {
888c2ecf20Sopenharmony_ci	[TCA_SKBEDIT_PARMS]		= { .len = sizeof(struct tc_skbedit) },
898c2ecf20Sopenharmony_ci	[TCA_SKBEDIT_PRIORITY]		= { .len = sizeof(u32) },
908c2ecf20Sopenharmony_ci	[TCA_SKBEDIT_QUEUE_MAPPING]	= { .len = sizeof(u16) },
918c2ecf20Sopenharmony_ci	[TCA_SKBEDIT_MARK]		= { .len = sizeof(u32) },
928c2ecf20Sopenharmony_ci	[TCA_SKBEDIT_PTYPE]		= { .len = sizeof(u16) },
938c2ecf20Sopenharmony_ci	[TCA_SKBEDIT_MASK]		= { .len = sizeof(u32) },
948c2ecf20Sopenharmony_ci	[TCA_SKBEDIT_FLAGS]		= { .len = sizeof(u64) },
958c2ecf20Sopenharmony_ci};
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic int tcf_skbedit_init(struct net *net, struct nlattr *nla,
988c2ecf20Sopenharmony_ci			    struct nlattr *est, struct tc_action **a,
998c2ecf20Sopenharmony_ci			    int ovr, int bind, bool rtnl_held,
1008c2ecf20Sopenharmony_ci			    struct tcf_proto *tp, u32 act_flags,
1018c2ecf20Sopenharmony_ci			    struct netlink_ext_ack *extack)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	struct tc_action_net *tn = net_generic(net, skbedit_net_id);
1048c2ecf20Sopenharmony_ci	struct tcf_skbedit_params *params_new;
1058c2ecf20Sopenharmony_ci	struct nlattr *tb[TCA_SKBEDIT_MAX + 1];
1068c2ecf20Sopenharmony_ci	struct tcf_chain *goto_ch = NULL;
1078c2ecf20Sopenharmony_ci	struct tc_skbedit *parm;
1088c2ecf20Sopenharmony_ci	struct tcf_skbedit *d;
1098c2ecf20Sopenharmony_ci	u32 flags = 0, *priority = NULL, *mark = NULL, *mask = NULL;
1108c2ecf20Sopenharmony_ci	u16 *queue_mapping = NULL, *ptype = NULL;
1118c2ecf20Sopenharmony_ci	bool exists = false;
1128c2ecf20Sopenharmony_ci	int ret = 0, err;
1138c2ecf20Sopenharmony_ci	u32 index;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	if (nla == NULL)
1168c2ecf20Sopenharmony_ci		return -EINVAL;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	err = nla_parse_nested_deprecated(tb, TCA_SKBEDIT_MAX, nla,
1198c2ecf20Sopenharmony_ci					  skbedit_policy, NULL);
1208c2ecf20Sopenharmony_ci	if (err < 0)
1218c2ecf20Sopenharmony_ci		return err;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	if (tb[TCA_SKBEDIT_PARMS] == NULL)
1248c2ecf20Sopenharmony_ci		return -EINVAL;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	if (tb[TCA_SKBEDIT_PRIORITY] != NULL) {
1278c2ecf20Sopenharmony_ci		flags |= SKBEDIT_F_PRIORITY;
1288c2ecf20Sopenharmony_ci		priority = nla_data(tb[TCA_SKBEDIT_PRIORITY]);
1298c2ecf20Sopenharmony_ci	}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	if (tb[TCA_SKBEDIT_QUEUE_MAPPING] != NULL) {
1328c2ecf20Sopenharmony_ci		flags |= SKBEDIT_F_QUEUE_MAPPING;
1338c2ecf20Sopenharmony_ci		queue_mapping = nla_data(tb[TCA_SKBEDIT_QUEUE_MAPPING]);
1348c2ecf20Sopenharmony_ci	}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	if (tb[TCA_SKBEDIT_PTYPE] != NULL) {
1378c2ecf20Sopenharmony_ci		ptype = nla_data(tb[TCA_SKBEDIT_PTYPE]);
1388c2ecf20Sopenharmony_ci		if (!skb_pkt_type_ok(*ptype))
1398c2ecf20Sopenharmony_ci			return -EINVAL;
1408c2ecf20Sopenharmony_ci		flags |= SKBEDIT_F_PTYPE;
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	if (tb[TCA_SKBEDIT_MARK] != NULL) {
1448c2ecf20Sopenharmony_ci		flags |= SKBEDIT_F_MARK;
1458c2ecf20Sopenharmony_ci		mark = nla_data(tb[TCA_SKBEDIT_MARK]);
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	if (tb[TCA_SKBEDIT_MASK] != NULL) {
1498c2ecf20Sopenharmony_ci		flags |= SKBEDIT_F_MASK;
1508c2ecf20Sopenharmony_ci		mask = nla_data(tb[TCA_SKBEDIT_MASK]);
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	if (tb[TCA_SKBEDIT_FLAGS] != NULL) {
1548c2ecf20Sopenharmony_ci		u64 *pure_flags = nla_data(tb[TCA_SKBEDIT_FLAGS]);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci		if (*pure_flags & SKBEDIT_F_INHERITDSFIELD)
1578c2ecf20Sopenharmony_ci			flags |= SKBEDIT_F_INHERITDSFIELD;
1588c2ecf20Sopenharmony_ci	}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	parm = nla_data(tb[TCA_SKBEDIT_PARMS]);
1618c2ecf20Sopenharmony_ci	index = parm->index;
1628c2ecf20Sopenharmony_ci	err = tcf_idr_check_alloc(tn, &index, a, bind);
1638c2ecf20Sopenharmony_ci	if (err < 0)
1648c2ecf20Sopenharmony_ci		return err;
1658c2ecf20Sopenharmony_ci	exists = err;
1668c2ecf20Sopenharmony_ci	if (exists && bind)
1678c2ecf20Sopenharmony_ci		return 0;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	if (!flags) {
1708c2ecf20Sopenharmony_ci		if (exists)
1718c2ecf20Sopenharmony_ci			tcf_idr_release(*a, bind);
1728c2ecf20Sopenharmony_ci		else
1738c2ecf20Sopenharmony_ci			tcf_idr_cleanup(tn, index);
1748c2ecf20Sopenharmony_ci		return -EINVAL;
1758c2ecf20Sopenharmony_ci	}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	if (!exists) {
1788c2ecf20Sopenharmony_ci		ret = tcf_idr_create(tn, index, est, a,
1798c2ecf20Sopenharmony_ci				     &act_skbedit_ops, bind, true, act_flags);
1808c2ecf20Sopenharmony_ci		if (ret) {
1818c2ecf20Sopenharmony_ci			tcf_idr_cleanup(tn, index);
1828c2ecf20Sopenharmony_ci			return ret;
1838c2ecf20Sopenharmony_ci		}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci		d = to_skbedit(*a);
1868c2ecf20Sopenharmony_ci		ret = ACT_P_CREATED;
1878c2ecf20Sopenharmony_ci	} else {
1888c2ecf20Sopenharmony_ci		d = to_skbedit(*a);
1898c2ecf20Sopenharmony_ci		if (!ovr) {
1908c2ecf20Sopenharmony_ci			tcf_idr_release(*a, bind);
1918c2ecf20Sopenharmony_ci			return -EEXIST;
1928c2ecf20Sopenharmony_ci		}
1938c2ecf20Sopenharmony_ci	}
1948c2ecf20Sopenharmony_ci	err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
1958c2ecf20Sopenharmony_ci	if (err < 0)
1968c2ecf20Sopenharmony_ci		goto release_idr;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	params_new = kzalloc(sizeof(*params_new), GFP_KERNEL);
1998c2ecf20Sopenharmony_ci	if (unlikely(!params_new)) {
2008c2ecf20Sopenharmony_ci		err = -ENOMEM;
2018c2ecf20Sopenharmony_ci		goto put_chain;
2028c2ecf20Sopenharmony_ci	}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	params_new->flags = flags;
2058c2ecf20Sopenharmony_ci	if (flags & SKBEDIT_F_PRIORITY)
2068c2ecf20Sopenharmony_ci		params_new->priority = *priority;
2078c2ecf20Sopenharmony_ci	if (flags & SKBEDIT_F_QUEUE_MAPPING)
2088c2ecf20Sopenharmony_ci		params_new->queue_mapping = *queue_mapping;
2098c2ecf20Sopenharmony_ci	if (flags & SKBEDIT_F_MARK)
2108c2ecf20Sopenharmony_ci		params_new->mark = *mark;
2118c2ecf20Sopenharmony_ci	if (flags & SKBEDIT_F_PTYPE)
2128c2ecf20Sopenharmony_ci		params_new->ptype = *ptype;
2138c2ecf20Sopenharmony_ci	/* default behaviour is to use all the bits */
2148c2ecf20Sopenharmony_ci	params_new->mask = 0xffffffff;
2158c2ecf20Sopenharmony_ci	if (flags & SKBEDIT_F_MASK)
2168c2ecf20Sopenharmony_ci		params_new->mask = *mask;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	spin_lock_bh(&d->tcf_lock);
2198c2ecf20Sopenharmony_ci	goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
2208c2ecf20Sopenharmony_ci	params_new = rcu_replace_pointer(d->params, params_new,
2218c2ecf20Sopenharmony_ci					 lockdep_is_held(&d->tcf_lock));
2228c2ecf20Sopenharmony_ci	spin_unlock_bh(&d->tcf_lock);
2238c2ecf20Sopenharmony_ci	if (params_new)
2248c2ecf20Sopenharmony_ci		kfree_rcu(params_new, rcu);
2258c2ecf20Sopenharmony_ci	if (goto_ch)
2268c2ecf20Sopenharmony_ci		tcf_chain_put_by_act(goto_ch);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	return ret;
2298c2ecf20Sopenharmony_ciput_chain:
2308c2ecf20Sopenharmony_ci	if (goto_ch)
2318c2ecf20Sopenharmony_ci		tcf_chain_put_by_act(goto_ch);
2328c2ecf20Sopenharmony_cirelease_idr:
2338c2ecf20Sopenharmony_ci	tcf_idr_release(*a, bind);
2348c2ecf20Sopenharmony_ci	return err;
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cistatic int tcf_skbedit_dump(struct sk_buff *skb, struct tc_action *a,
2388c2ecf20Sopenharmony_ci			    int bind, int ref)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	unsigned char *b = skb_tail_pointer(skb);
2418c2ecf20Sopenharmony_ci	struct tcf_skbedit *d = to_skbedit(a);
2428c2ecf20Sopenharmony_ci	struct tcf_skbedit_params *params;
2438c2ecf20Sopenharmony_ci	struct tc_skbedit opt = {
2448c2ecf20Sopenharmony_ci		.index   = d->tcf_index,
2458c2ecf20Sopenharmony_ci		.refcnt  = refcount_read(&d->tcf_refcnt) - ref,
2468c2ecf20Sopenharmony_ci		.bindcnt = atomic_read(&d->tcf_bindcnt) - bind,
2478c2ecf20Sopenharmony_ci	};
2488c2ecf20Sopenharmony_ci	u64 pure_flags = 0;
2498c2ecf20Sopenharmony_ci	struct tcf_t t;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	spin_lock_bh(&d->tcf_lock);
2528c2ecf20Sopenharmony_ci	params = rcu_dereference_protected(d->params,
2538c2ecf20Sopenharmony_ci					   lockdep_is_held(&d->tcf_lock));
2548c2ecf20Sopenharmony_ci	opt.action = d->tcf_action;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	if (nla_put(skb, TCA_SKBEDIT_PARMS, sizeof(opt), &opt))
2578c2ecf20Sopenharmony_ci		goto nla_put_failure;
2588c2ecf20Sopenharmony_ci	if ((params->flags & SKBEDIT_F_PRIORITY) &&
2598c2ecf20Sopenharmony_ci	    nla_put_u32(skb, TCA_SKBEDIT_PRIORITY, params->priority))
2608c2ecf20Sopenharmony_ci		goto nla_put_failure;
2618c2ecf20Sopenharmony_ci	if ((params->flags & SKBEDIT_F_QUEUE_MAPPING) &&
2628c2ecf20Sopenharmony_ci	    nla_put_u16(skb, TCA_SKBEDIT_QUEUE_MAPPING, params->queue_mapping))
2638c2ecf20Sopenharmony_ci		goto nla_put_failure;
2648c2ecf20Sopenharmony_ci	if ((params->flags & SKBEDIT_F_MARK) &&
2658c2ecf20Sopenharmony_ci	    nla_put_u32(skb, TCA_SKBEDIT_MARK, params->mark))
2668c2ecf20Sopenharmony_ci		goto nla_put_failure;
2678c2ecf20Sopenharmony_ci	if ((params->flags & SKBEDIT_F_PTYPE) &&
2688c2ecf20Sopenharmony_ci	    nla_put_u16(skb, TCA_SKBEDIT_PTYPE, params->ptype))
2698c2ecf20Sopenharmony_ci		goto nla_put_failure;
2708c2ecf20Sopenharmony_ci	if ((params->flags & SKBEDIT_F_MASK) &&
2718c2ecf20Sopenharmony_ci	    nla_put_u32(skb, TCA_SKBEDIT_MASK, params->mask))
2728c2ecf20Sopenharmony_ci		goto nla_put_failure;
2738c2ecf20Sopenharmony_ci	if (params->flags & SKBEDIT_F_INHERITDSFIELD)
2748c2ecf20Sopenharmony_ci		pure_flags |= SKBEDIT_F_INHERITDSFIELD;
2758c2ecf20Sopenharmony_ci	if (pure_flags != 0 &&
2768c2ecf20Sopenharmony_ci	    nla_put(skb, TCA_SKBEDIT_FLAGS, sizeof(pure_flags), &pure_flags))
2778c2ecf20Sopenharmony_ci		goto nla_put_failure;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	tcf_tm_dump(&t, &d->tcf_tm);
2808c2ecf20Sopenharmony_ci	if (nla_put_64bit(skb, TCA_SKBEDIT_TM, sizeof(t), &t, TCA_SKBEDIT_PAD))
2818c2ecf20Sopenharmony_ci		goto nla_put_failure;
2828c2ecf20Sopenharmony_ci	spin_unlock_bh(&d->tcf_lock);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	return skb->len;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_cinla_put_failure:
2878c2ecf20Sopenharmony_ci	spin_unlock_bh(&d->tcf_lock);
2888c2ecf20Sopenharmony_ci	nlmsg_trim(skb, b);
2898c2ecf20Sopenharmony_ci	return -1;
2908c2ecf20Sopenharmony_ci}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_cistatic void tcf_skbedit_cleanup(struct tc_action *a)
2938c2ecf20Sopenharmony_ci{
2948c2ecf20Sopenharmony_ci	struct tcf_skbedit *d = to_skbedit(a);
2958c2ecf20Sopenharmony_ci	struct tcf_skbedit_params *params;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	params = rcu_dereference_protected(d->params, 1);
2988c2ecf20Sopenharmony_ci	if (params)
2998c2ecf20Sopenharmony_ci		kfree_rcu(params, rcu);
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistatic int tcf_skbedit_walker(struct net *net, struct sk_buff *skb,
3038c2ecf20Sopenharmony_ci			      struct netlink_callback *cb, int type,
3048c2ecf20Sopenharmony_ci			      const struct tc_action_ops *ops,
3058c2ecf20Sopenharmony_ci			      struct netlink_ext_ack *extack)
3068c2ecf20Sopenharmony_ci{
3078c2ecf20Sopenharmony_ci	struct tc_action_net *tn = net_generic(net, skbedit_net_id);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	return tcf_generic_walker(tn, skb, cb, type, ops, extack);
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic int tcf_skbedit_search(struct net *net, struct tc_action **a, u32 index)
3138c2ecf20Sopenharmony_ci{
3148c2ecf20Sopenharmony_ci	struct tc_action_net *tn = net_generic(net, skbedit_net_id);
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	return tcf_idr_search(tn, a, index);
3178c2ecf20Sopenharmony_ci}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_cistatic size_t tcf_skbedit_get_fill_size(const struct tc_action *act)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	return nla_total_size(sizeof(struct tc_skbedit))
3228c2ecf20Sopenharmony_ci		+ nla_total_size(sizeof(u32)) /* TCA_SKBEDIT_PRIORITY */
3238c2ecf20Sopenharmony_ci		+ nla_total_size(sizeof(u16)) /* TCA_SKBEDIT_QUEUE_MAPPING */
3248c2ecf20Sopenharmony_ci		+ nla_total_size(sizeof(u32)) /* TCA_SKBEDIT_MARK */
3258c2ecf20Sopenharmony_ci		+ nla_total_size(sizeof(u16)) /* TCA_SKBEDIT_PTYPE */
3268c2ecf20Sopenharmony_ci		+ nla_total_size(sizeof(u32)) /* TCA_SKBEDIT_MASK */
3278c2ecf20Sopenharmony_ci		+ nla_total_size_64bit(sizeof(u64)); /* TCA_SKBEDIT_FLAGS */
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cistatic struct tc_action_ops act_skbedit_ops = {
3318c2ecf20Sopenharmony_ci	.kind		=	"skbedit",
3328c2ecf20Sopenharmony_ci	.id		=	TCA_ID_SKBEDIT,
3338c2ecf20Sopenharmony_ci	.owner		=	THIS_MODULE,
3348c2ecf20Sopenharmony_ci	.act		=	tcf_skbedit_act,
3358c2ecf20Sopenharmony_ci	.stats_update	=	tcf_skbedit_stats_update,
3368c2ecf20Sopenharmony_ci	.dump		=	tcf_skbedit_dump,
3378c2ecf20Sopenharmony_ci	.init		=	tcf_skbedit_init,
3388c2ecf20Sopenharmony_ci	.cleanup	=	tcf_skbedit_cleanup,
3398c2ecf20Sopenharmony_ci	.walk		=	tcf_skbedit_walker,
3408c2ecf20Sopenharmony_ci	.get_fill_size	=	tcf_skbedit_get_fill_size,
3418c2ecf20Sopenharmony_ci	.lookup		=	tcf_skbedit_search,
3428c2ecf20Sopenharmony_ci	.size		=	sizeof(struct tcf_skbedit),
3438c2ecf20Sopenharmony_ci};
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_cistatic __net_init int skbedit_init_net(struct net *net)
3468c2ecf20Sopenharmony_ci{
3478c2ecf20Sopenharmony_ci	struct tc_action_net *tn = net_generic(net, skbedit_net_id);
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	return tc_action_net_init(net, tn, &act_skbedit_ops);
3508c2ecf20Sopenharmony_ci}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_cistatic void __net_exit skbedit_exit_net(struct list_head *net_list)
3538c2ecf20Sopenharmony_ci{
3548c2ecf20Sopenharmony_ci	tc_action_net_exit(net_list, skbedit_net_id);
3558c2ecf20Sopenharmony_ci}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_cistatic struct pernet_operations skbedit_net_ops = {
3588c2ecf20Sopenharmony_ci	.init = skbedit_init_net,
3598c2ecf20Sopenharmony_ci	.exit_batch = skbedit_exit_net,
3608c2ecf20Sopenharmony_ci	.id   = &skbedit_net_id,
3618c2ecf20Sopenharmony_ci	.size = sizeof(struct tc_action_net),
3628c2ecf20Sopenharmony_ci};
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ciMODULE_AUTHOR("Alexander Duyck, <alexander.h.duyck@intel.com>");
3658c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SKB Editing");
3668c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_cistatic int __init skbedit_init_module(void)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	return tcf_register_action(&act_skbedit_ops, &skbedit_net_ops);
3718c2ecf20Sopenharmony_ci}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_cistatic void __exit skbedit_cleanup_module(void)
3748c2ecf20Sopenharmony_ci{
3758c2ecf20Sopenharmony_ci	tcf_unregister_action(&act_skbedit_ops, &skbedit_net_ops);
3768c2ecf20Sopenharmony_ci}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_cimodule_init(skbedit_init_module);
3798c2ecf20Sopenharmony_cimodule_exit(skbedit_cleanup_module);
380