18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/* net/sched/act_ctinfo.c  netfilter ctinfo connmark actions
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (c) 2019 Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/module.h>
88c2ecf20Sopenharmony_ci#include <linux/init.h>
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
118c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h>
128c2ecf20Sopenharmony_ci#include <linux/pkt_cls.h>
138c2ecf20Sopenharmony_ci#include <linux/ip.h>
148c2ecf20Sopenharmony_ci#include <linux/ipv6.h>
158c2ecf20Sopenharmony_ci#include <net/netlink.h>
168c2ecf20Sopenharmony_ci#include <net/pkt_sched.h>
178c2ecf20Sopenharmony_ci#include <net/act_api.h>
188c2ecf20Sopenharmony_ci#include <net/pkt_cls.h>
198c2ecf20Sopenharmony_ci#include <uapi/linux/tc_act/tc_ctinfo.h>
208c2ecf20Sopenharmony_ci#include <net/tc_act/tc_ctinfo.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include <net/netfilter/nf_conntrack.h>
238c2ecf20Sopenharmony_ci#include <net/netfilter/nf_conntrack_core.h>
248c2ecf20Sopenharmony_ci#include <net/netfilter/nf_conntrack_ecache.h>
258c2ecf20Sopenharmony_ci#include <net/netfilter/nf_conntrack_zones.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic struct tc_action_ops act_ctinfo_ops;
288c2ecf20Sopenharmony_cistatic unsigned int ctinfo_net_id;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic void tcf_ctinfo_dscp_set(struct nf_conn *ct, struct tcf_ctinfo *ca,
318c2ecf20Sopenharmony_ci				struct tcf_ctinfo_params *cp,
328c2ecf20Sopenharmony_ci				struct sk_buff *skb, int wlen, int proto)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	u8 dscp, newdscp;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	newdscp = (((READ_ONCE(ct->mark) & cp->dscpmask) >> cp->dscpmaskshift) << 2) &
378c2ecf20Sopenharmony_ci		     ~INET_ECN_MASK;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	switch (proto) {
408c2ecf20Sopenharmony_ci	case NFPROTO_IPV4:
418c2ecf20Sopenharmony_ci		dscp = ipv4_get_dsfield(ip_hdr(skb)) & ~INET_ECN_MASK;
428c2ecf20Sopenharmony_ci		if (dscp != newdscp) {
438c2ecf20Sopenharmony_ci			if (likely(!skb_try_make_writable(skb, wlen))) {
448c2ecf20Sopenharmony_ci				ipv4_change_dsfield(ip_hdr(skb),
458c2ecf20Sopenharmony_ci						    INET_ECN_MASK,
468c2ecf20Sopenharmony_ci						    newdscp);
478c2ecf20Sopenharmony_ci				ca->stats_dscp_set++;
488c2ecf20Sopenharmony_ci			} else {
498c2ecf20Sopenharmony_ci				ca->stats_dscp_error++;
508c2ecf20Sopenharmony_ci			}
518c2ecf20Sopenharmony_ci		}
528c2ecf20Sopenharmony_ci		break;
538c2ecf20Sopenharmony_ci	case NFPROTO_IPV6:
548c2ecf20Sopenharmony_ci		dscp = ipv6_get_dsfield(ipv6_hdr(skb)) & ~INET_ECN_MASK;
558c2ecf20Sopenharmony_ci		if (dscp != newdscp) {
568c2ecf20Sopenharmony_ci			if (likely(!skb_try_make_writable(skb, wlen))) {
578c2ecf20Sopenharmony_ci				ipv6_change_dsfield(ipv6_hdr(skb),
588c2ecf20Sopenharmony_ci						    INET_ECN_MASK,
598c2ecf20Sopenharmony_ci						    newdscp);
608c2ecf20Sopenharmony_ci				ca->stats_dscp_set++;
618c2ecf20Sopenharmony_ci			} else {
628c2ecf20Sopenharmony_ci				ca->stats_dscp_error++;
638c2ecf20Sopenharmony_ci			}
648c2ecf20Sopenharmony_ci		}
658c2ecf20Sopenharmony_ci		break;
668c2ecf20Sopenharmony_ci	default:
678c2ecf20Sopenharmony_ci		break;
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic void tcf_ctinfo_cpmark_set(struct nf_conn *ct, struct tcf_ctinfo *ca,
728c2ecf20Sopenharmony_ci				  struct tcf_ctinfo_params *cp,
738c2ecf20Sopenharmony_ci				  struct sk_buff *skb)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	ca->stats_cpmark_set++;
768c2ecf20Sopenharmony_ci	skb->mark = READ_ONCE(ct->mark) & cp->cpmarkmask;
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic int tcf_ctinfo_act(struct sk_buff *skb, const struct tc_action *a,
808c2ecf20Sopenharmony_ci			  struct tcf_result *res)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	const struct nf_conntrack_tuple_hash *thash = NULL;
838c2ecf20Sopenharmony_ci	struct tcf_ctinfo *ca = to_ctinfo(a);
848c2ecf20Sopenharmony_ci	struct nf_conntrack_tuple tuple;
858c2ecf20Sopenharmony_ci	struct nf_conntrack_zone zone;
868c2ecf20Sopenharmony_ci	enum ip_conntrack_info ctinfo;
878c2ecf20Sopenharmony_ci	struct tcf_ctinfo_params *cp;
888c2ecf20Sopenharmony_ci	struct nf_conn *ct;
898c2ecf20Sopenharmony_ci	int proto, wlen;
908c2ecf20Sopenharmony_ci	int action;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	cp = rcu_dereference_bh(ca->params);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	tcf_lastuse_update(&ca->tcf_tm);
958c2ecf20Sopenharmony_ci	tcf_action_update_bstats(&ca->common, skb);
968c2ecf20Sopenharmony_ci	action = READ_ONCE(ca->tcf_action);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	wlen = skb_network_offset(skb);
998c2ecf20Sopenharmony_ci	switch (skb_protocol(skb, true)) {
1008c2ecf20Sopenharmony_ci	case htons(ETH_P_IP):
1018c2ecf20Sopenharmony_ci		wlen += sizeof(struct iphdr);
1028c2ecf20Sopenharmony_ci		if (!pskb_may_pull(skb, wlen))
1038c2ecf20Sopenharmony_ci			goto out;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci		proto = NFPROTO_IPV4;
1068c2ecf20Sopenharmony_ci		break;
1078c2ecf20Sopenharmony_ci	case htons(ETH_P_IPV6):
1088c2ecf20Sopenharmony_ci		wlen += sizeof(struct ipv6hdr);
1098c2ecf20Sopenharmony_ci		if (!pskb_may_pull(skb, wlen))
1108c2ecf20Sopenharmony_ci			goto out;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci		proto = NFPROTO_IPV6;
1138c2ecf20Sopenharmony_ci		break;
1148c2ecf20Sopenharmony_ci	default:
1158c2ecf20Sopenharmony_ci		goto out;
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	ct = nf_ct_get(skb, &ctinfo);
1198c2ecf20Sopenharmony_ci	if (!ct) { /* look harder, usually ingress */
1208c2ecf20Sopenharmony_ci		if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb),
1218c2ecf20Sopenharmony_ci				       proto, cp->net, &tuple))
1228c2ecf20Sopenharmony_ci			goto out;
1238c2ecf20Sopenharmony_ci		zone.id = cp->zone;
1248c2ecf20Sopenharmony_ci		zone.dir = NF_CT_DEFAULT_ZONE_DIR;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci		thash = nf_conntrack_find_get(cp->net, &zone, &tuple);
1278c2ecf20Sopenharmony_ci		if (!thash)
1288c2ecf20Sopenharmony_ci			goto out;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci		ct = nf_ct_tuplehash_to_ctrack(thash);
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	if (cp->mode & CTINFO_MODE_DSCP)
1348c2ecf20Sopenharmony_ci		if (!cp->dscpstatemask || (READ_ONCE(ct->mark) & cp->dscpstatemask))
1358c2ecf20Sopenharmony_ci			tcf_ctinfo_dscp_set(ct, ca, cp, skb, wlen, proto);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	if (cp->mode & CTINFO_MODE_CPMARK)
1388c2ecf20Sopenharmony_ci		tcf_ctinfo_cpmark_set(ct, ca, cp, skb);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	if (thash)
1418c2ecf20Sopenharmony_ci		nf_ct_put(ct);
1428c2ecf20Sopenharmony_ciout:
1438c2ecf20Sopenharmony_ci	return action;
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic const struct nla_policy ctinfo_policy[TCA_CTINFO_MAX + 1] = {
1478c2ecf20Sopenharmony_ci	[TCA_CTINFO_ACT]		  =
1488c2ecf20Sopenharmony_ci		NLA_POLICY_EXACT_LEN(sizeof(struct tc_ctinfo)),
1498c2ecf20Sopenharmony_ci	[TCA_CTINFO_ZONE]		  = { .type = NLA_U16 },
1508c2ecf20Sopenharmony_ci	[TCA_CTINFO_PARMS_DSCP_MASK]	  = { .type = NLA_U32 },
1518c2ecf20Sopenharmony_ci	[TCA_CTINFO_PARMS_DSCP_STATEMASK] = { .type = NLA_U32 },
1528c2ecf20Sopenharmony_ci	[TCA_CTINFO_PARMS_CPMARK_MASK]	  = { .type = NLA_U32 },
1538c2ecf20Sopenharmony_ci};
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic int tcf_ctinfo_init(struct net *net, struct nlattr *nla,
1568c2ecf20Sopenharmony_ci			   struct nlattr *est, struct tc_action **a,
1578c2ecf20Sopenharmony_ci			   int ovr, int bind, bool rtnl_held,
1588c2ecf20Sopenharmony_ci			   struct tcf_proto *tp, u32 flags,
1598c2ecf20Sopenharmony_ci			   struct netlink_ext_ack *extack)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	struct tc_action_net *tn = net_generic(net, ctinfo_net_id);
1628c2ecf20Sopenharmony_ci	u32 dscpmask = 0, dscpstatemask, index;
1638c2ecf20Sopenharmony_ci	struct nlattr *tb[TCA_CTINFO_MAX + 1];
1648c2ecf20Sopenharmony_ci	struct tcf_ctinfo_params *cp_new;
1658c2ecf20Sopenharmony_ci	struct tcf_chain *goto_ch = NULL;
1668c2ecf20Sopenharmony_ci	struct tc_ctinfo *actparm;
1678c2ecf20Sopenharmony_ci	struct tcf_ctinfo *ci;
1688c2ecf20Sopenharmony_ci	u8 dscpmaskshift;
1698c2ecf20Sopenharmony_ci	int ret = 0, err;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	if (!nla) {
1728c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "ctinfo requires attributes to be passed");
1738c2ecf20Sopenharmony_ci		return -EINVAL;
1748c2ecf20Sopenharmony_ci	}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	err = nla_parse_nested(tb, TCA_CTINFO_MAX, nla, ctinfo_policy, extack);
1778c2ecf20Sopenharmony_ci	if (err < 0)
1788c2ecf20Sopenharmony_ci		return err;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	if (!tb[TCA_CTINFO_ACT]) {
1818c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack,
1828c2ecf20Sopenharmony_ci				   "Missing required TCA_CTINFO_ACT attribute");
1838c2ecf20Sopenharmony_ci		return -EINVAL;
1848c2ecf20Sopenharmony_ci	}
1858c2ecf20Sopenharmony_ci	actparm = nla_data(tb[TCA_CTINFO_ACT]);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	/* do some basic validation here before dynamically allocating things */
1888c2ecf20Sopenharmony_ci	/* that we would otherwise have to clean up.			      */
1898c2ecf20Sopenharmony_ci	if (tb[TCA_CTINFO_PARMS_DSCP_MASK]) {
1908c2ecf20Sopenharmony_ci		dscpmask = nla_get_u32(tb[TCA_CTINFO_PARMS_DSCP_MASK]);
1918c2ecf20Sopenharmony_ci		/* need contiguous 6 bit mask */
1928c2ecf20Sopenharmony_ci		dscpmaskshift = dscpmask ? __ffs(dscpmask) : 0;
1938c2ecf20Sopenharmony_ci		if ((~0 & (dscpmask >> dscpmaskshift)) != 0x3f) {
1948c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_ATTR(extack,
1958c2ecf20Sopenharmony_ci					    tb[TCA_CTINFO_PARMS_DSCP_MASK],
1968c2ecf20Sopenharmony_ci					    "dscp mask must be 6 contiguous bits");
1978c2ecf20Sopenharmony_ci			return -EINVAL;
1988c2ecf20Sopenharmony_ci		}
1998c2ecf20Sopenharmony_ci		dscpstatemask = tb[TCA_CTINFO_PARMS_DSCP_STATEMASK] ?
2008c2ecf20Sopenharmony_ci			nla_get_u32(tb[TCA_CTINFO_PARMS_DSCP_STATEMASK]) : 0;
2018c2ecf20Sopenharmony_ci		/* mask & statemask must not overlap */
2028c2ecf20Sopenharmony_ci		if (dscpmask & dscpstatemask) {
2038c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_ATTR(extack,
2048c2ecf20Sopenharmony_ci					    tb[TCA_CTINFO_PARMS_DSCP_STATEMASK],
2058c2ecf20Sopenharmony_ci					    "dscp statemask must not overlap dscp mask");
2068c2ecf20Sopenharmony_ci			return -EINVAL;
2078c2ecf20Sopenharmony_ci		}
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	/* done the validation:now to the actual action allocation */
2118c2ecf20Sopenharmony_ci	index = actparm->index;
2128c2ecf20Sopenharmony_ci	err = tcf_idr_check_alloc(tn, &index, a, bind);
2138c2ecf20Sopenharmony_ci	if (!err) {
2148c2ecf20Sopenharmony_ci		ret = tcf_idr_create_from_flags(tn, index, est, a,
2158c2ecf20Sopenharmony_ci						&act_ctinfo_ops, bind, flags);
2168c2ecf20Sopenharmony_ci		if (ret) {
2178c2ecf20Sopenharmony_ci			tcf_idr_cleanup(tn, index);
2188c2ecf20Sopenharmony_ci			return ret;
2198c2ecf20Sopenharmony_ci		}
2208c2ecf20Sopenharmony_ci		ret = ACT_P_CREATED;
2218c2ecf20Sopenharmony_ci	} else if (err > 0) {
2228c2ecf20Sopenharmony_ci		if (bind) /* don't override defaults */
2238c2ecf20Sopenharmony_ci			return 0;
2248c2ecf20Sopenharmony_ci		if (!ovr) {
2258c2ecf20Sopenharmony_ci			tcf_idr_release(*a, bind);
2268c2ecf20Sopenharmony_ci			return -EEXIST;
2278c2ecf20Sopenharmony_ci		}
2288c2ecf20Sopenharmony_ci	} else {
2298c2ecf20Sopenharmony_ci		return err;
2308c2ecf20Sopenharmony_ci	}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	err = tcf_action_check_ctrlact(actparm->action, tp, &goto_ch, extack);
2338c2ecf20Sopenharmony_ci	if (err < 0)
2348c2ecf20Sopenharmony_ci		goto release_idr;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	ci = to_ctinfo(*a);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	cp_new = kzalloc(sizeof(*cp_new), GFP_KERNEL);
2398c2ecf20Sopenharmony_ci	if (unlikely(!cp_new)) {
2408c2ecf20Sopenharmony_ci		err = -ENOMEM;
2418c2ecf20Sopenharmony_ci		goto put_chain;
2428c2ecf20Sopenharmony_ci	}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	cp_new->net = net;
2458c2ecf20Sopenharmony_ci	cp_new->zone = tb[TCA_CTINFO_ZONE] ?
2468c2ecf20Sopenharmony_ci			nla_get_u16(tb[TCA_CTINFO_ZONE]) : 0;
2478c2ecf20Sopenharmony_ci	if (dscpmask) {
2488c2ecf20Sopenharmony_ci		cp_new->dscpmask = dscpmask;
2498c2ecf20Sopenharmony_ci		cp_new->dscpmaskshift = dscpmaskshift;
2508c2ecf20Sopenharmony_ci		cp_new->dscpstatemask = dscpstatemask;
2518c2ecf20Sopenharmony_ci		cp_new->mode |= CTINFO_MODE_DSCP;
2528c2ecf20Sopenharmony_ci	}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	if (tb[TCA_CTINFO_PARMS_CPMARK_MASK]) {
2558c2ecf20Sopenharmony_ci		cp_new->cpmarkmask =
2568c2ecf20Sopenharmony_ci				nla_get_u32(tb[TCA_CTINFO_PARMS_CPMARK_MASK]);
2578c2ecf20Sopenharmony_ci		cp_new->mode |= CTINFO_MODE_CPMARK;
2588c2ecf20Sopenharmony_ci	}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	spin_lock_bh(&ci->tcf_lock);
2618c2ecf20Sopenharmony_ci	goto_ch = tcf_action_set_ctrlact(*a, actparm->action, goto_ch);
2628c2ecf20Sopenharmony_ci	cp_new = rcu_replace_pointer(ci->params, cp_new,
2638c2ecf20Sopenharmony_ci				     lockdep_is_held(&ci->tcf_lock));
2648c2ecf20Sopenharmony_ci	spin_unlock_bh(&ci->tcf_lock);
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	if (goto_ch)
2678c2ecf20Sopenharmony_ci		tcf_chain_put_by_act(goto_ch);
2688c2ecf20Sopenharmony_ci	if (cp_new)
2698c2ecf20Sopenharmony_ci		kfree_rcu(cp_new, rcu);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	return ret;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ciput_chain:
2748c2ecf20Sopenharmony_ci	if (goto_ch)
2758c2ecf20Sopenharmony_ci		tcf_chain_put_by_act(goto_ch);
2768c2ecf20Sopenharmony_cirelease_idr:
2778c2ecf20Sopenharmony_ci	tcf_idr_release(*a, bind);
2788c2ecf20Sopenharmony_ci	return err;
2798c2ecf20Sopenharmony_ci}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_cistatic int tcf_ctinfo_dump(struct sk_buff *skb, struct tc_action *a,
2828c2ecf20Sopenharmony_ci			   int bind, int ref)
2838c2ecf20Sopenharmony_ci{
2848c2ecf20Sopenharmony_ci	struct tcf_ctinfo *ci = to_ctinfo(a);
2858c2ecf20Sopenharmony_ci	struct tc_ctinfo opt = {
2868c2ecf20Sopenharmony_ci		.index   = ci->tcf_index,
2878c2ecf20Sopenharmony_ci		.refcnt  = refcount_read(&ci->tcf_refcnt) - ref,
2888c2ecf20Sopenharmony_ci		.bindcnt = atomic_read(&ci->tcf_bindcnt) - bind,
2898c2ecf20Sopenharmony_ci	};
2908c2ecf20Sopenharmony_ci	unsigned char *b = skb_tail_pointer(skb);
2918c2ecf20Sopenharmony_ci	struct tcf_ctinfo_params *cp;
2928c2ecf20Sopenharmony_ci	struct tcf_t t;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	spin_lock_bh(&ci->tcf_lock);
2958c2ecf20Sopenharmony_ci	cp = rcu_dereference_protected(ci->params,
2968c2ecf20Sopenharmony_ci				       lockdep_is_held(&ci->tcf_lock));
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	tcf_tm_dump(&t, &ci->tcf_tm);
2998c2ecf20Sopenharmony_ci	if (nla_put_64bit(skb, TCA_CTINFO_TM, sizeof(t), &t, TCA_CTINFO_PAD))
3008c2ecf20Sopenharmony_ci		goto nla_put_failure;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	opt.action = ci->tcf_action;
3038c2ecf20Sopenharmony_ci	if (nla_put(skb, TCA_CTINFO_ACT, sizeof(opt), &opt))
3048c2ecf20Sopenharmony_ci		goto nla_put_failure;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	if (nla_put_u16(skb, TCA_CTINFO_ZONE, cp->zone))
3078c2ecf20Sopenharmony_ci		goto nla_put_failure;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	if (cp->mode & CTINFO_MODE_DSCP) {
3108c2ecf20Sopenharmony_ci		if (nla_put_u32(skb, TCA_CTINFO_PARMS_DSCP_MASK,
3118c2ecf20Sopenharmony_ci				cp->dscpmask))
3128c2ecf20Sopenharmony_ci			goto nla_put_failure;
3138c2ecf20Sopenharmony_ci		if (nla_put_u32(skb, TCA_CTINFO_PARMS_DSCP_STATEMASK,
3148c2ecf20Sopenharmony_ci				cp->dscpstatemask))
3158c2ecf20Sopenharmony_ci			goto nla_put_failure;
3168c2ecf20Sopenharmony_ci	}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	if (cp->mode & CTINFO_MODE_CPMARK) {
3198c2ecf20Sopenharmony_ci		if (nla_put_u32(skb, TCA_CTINFO_PARMS_CPMARK_MASK,
3208c2ecf20Sopenharmony_ci				cp->cpmarkmask))
3218c2ecf20Sopenharmony_ci			goto nla_put_failure;
3228c2ecf20Sopenharmony_ci	}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	if (nla_put_u64_64bit(skb, TCA_CTINFO_STATS_DSCP_SET,
3258c2ecf20Sopenharmony_ci			      ci->stats_dscp_set, TCA_CTINFO_PAD))
3268c2ecf20Sopenharmony_ci		goto nla_put_failure;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	if (nla_put_u64_64bit(skb, TCA_CTINFO_STATS_DSCP_ERROR,
3298c2ecf20Sopenharmony_ci			      ci->stats_dscp_error, TCA_CTINFO_PAD))
3308c2ecf20Sopenharmony_ci		goto nla_put_failure;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	if (nla_put_u64_64bit(skb, TCA_CTINFO_STATS_CPMARK_SET,
3338c2ecf20Sopenharmony_ci			      ci->stats_cpmark_set, TCA_CTINFO_PAD))
3348c2ecf20Sopenharmony_ci		goto nla_put_failure;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	spin_unlock_bh(&ci->tcf_lock);
3378c2ecf20Sopenharmony_ci	return skb->len;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_cinla_put_failure:
3408c2ecf20Sopenharmony_ci	spin_unlock_bh(&ci->tcf_lock);
3418c2ecf20Sopenharmony_ci	nlmsg_trim(skb, b);
3428c2ecf20Sopenharmony_ci	return -1;
3438c2ecf20Sopenharmony_ci}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_cistatic int tcf_ctinfo_walker(struct net *net, struct sk_buff *skb,
3468c2ecf20Sopenharmony_ci			     struct netlink_callback *cb, int type,
3478c2ecf20Sopenharmony_ci			     const struct tc_action_ops *ops,
3488c2ecf20Sopenharmony_ci			     struct netlink_ext_ack *extack)
3498c2ecf20Sopenharmony_ci{
3508c2ecf20Sopenharmony_ci	struct tc_action_net *tn = net_generic(net, ctinfo_net_id);
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	return tcf_generic_walker(tn, skb, cb, type, ops, extack);
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_cistatic int tcf_ctinfo_search(struct net *net, struct tc_action **a, u32 index)
3568c2ecf20Sopenharmony_ci{
3578c2ecf20Sopenharmony_ci	struct tc_action_net *tn = net_generic(net, ctinfo_net_id);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	return tcf_idr_search(tn, a, index);
3608c2ecf20Sopenharmony_ci}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_cistatic void tcf_ctinfo_cleanup(struct tc_action *a)
3638c2ecf20Sopenharmony_ci{
3648c2ecf20Sopenharmony_ci	struct tcf_ctinfo *ci = to_ctinfo(a);
3658c2ecf20Sopenharmony_ci	struct tcf_ctinfo_params *cp;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	cp = rcu_dereference_protected(ci->params, 1);
3688c2ecf20Sopenharmony_ci	if (cp)
3698c2ecf20Sopenharmony_ci		kfree_rcu(cp, rcu);
3708c2ecf20Sopenharmony_ci}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_cistatic struct tc_action_ops act_ctinfo_ops = {
3738c2ecf20Sopenharmony_ci	.kind	= "ctinfo",
3748c2ecf20Sopenharmony_ci	.id	= TCA_ID_CTINFO,
3758c2ecf20Sopenharmony_ci	.owner	= THIS_MODULE,
3768c2ecf20Sopenharmony_ci	.act	= tcf_ctinfo_act,
3778c2ecf20Sopenharmony_ci	.dump	= tcf_ctinfo_dump,
3788c2ecf20Sopenharmony_ci	.init	= tcf_ctinfo_init,
3798c2ecf20Sopenharmony_ci	.cleanup= tcf_ctinfo_cleanup,
3808c2ecf20Sopenharmony_ci	.walk	= tcf_ctinfo_walker,
3818c2ecf20Sopenharmony_ci	.lookup	= tcf_ctinfo_search,
3828c2ecf20Sopenharmony_ci	.size	= sizeof(struct tcf_ctinfo),
3838c2ecf20Sopenharmony_ci};
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_cistatic __net_init int ctinfo_init_net(struct net *net)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	struct tc_action_net *tn = net_generic(net, ctinfo_net_id);
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	return tc_action_net_init(net, tn, &act_ctinfo_ops);
3908c2ecf20Sopenharmony_ci}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_cistatic void __net_exit ctinfo_exit_net(struct list_head *net_list)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	tc_action_net_exit(net_list, ctinfo_net_id);
3958c2ecf20Sopenharmony_ci}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_cistatic struct pernet_operations ctinfo_net_ops = {
3988c2ecf20Sopenharmony_ci	.init		= ctinfo_init_net,
3998c2ecf20Sopenharmony_ci	.exit_batch	= ctinfo_exit_net,
4008c2ecf20Sopenharmony_ci	.id		= &ctinfo_net_id,
4018c2ecf20Sopenharmony_ci	.size		= sizeof(struct tc_action_net),
4028c2ecf20Sopenharmony_ci};
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_cistatic int __init ctinfo_init_module(void)
4058c2ecf20Sopenharmony_ci{
4068c2ecf20Sopenharmony_ci	return tcf_register_action(&act_ctinfo_ops, &ctinfo_net_ops);
4078c2ecf20Sopenharmony_ci}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_cistatic void __exit ctinfo_cleanup_module(void)
4108c2ecf20Sopenharmony_ci{
4118c2ecf20Sopenharmony_ci	tcf_unregister_action(&act_ctinfo_ops, &ctinfo_net_ops);
4128c2ecf20Sopenharmony_ci}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_cimodule_init(ctinfo_init_module);
4158c2ecf20Sopenharmony_cimodule_exit(ctinfo_cleanup_module);
4168c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>");
4178c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Connection tracking mark actions");
4188c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
419