Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.17.
  1// SPDX-License-Identifier: GPL-2.0+
  2/* net/sched/act_ctinfo.c  netfilter ctinfo connmark actions
  3 *
  4 * Copyright (c) 2019 Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
  5 */
  6
  7#include <linux/module.h>
  8#include <linux/init.h>
  9#include <linux/kernel.h>
 10#include <linux/skbuff.h>
 11#include <linux/rtnetlink.h>
 12#include <linux/pkt_cls.h>
 13#include <linux/ip.h>
 14#include <linux/ipv6.h>
 15#include <net/netlink.h>
 16#include <net/pkt_sched.h>
 17#include <net/act_api.h>
 18#include <net/pkt_cls.h>
 19#include <uapi/linux/tc_act/tc_ctinfo.h>
 20#include <net/tc_act/tc_ctinfo.h>
 21#include <net/tc_wrapper.h>
 22
 23#include <net/netfilter/nf_conntrack.h>
 24#include <net/netfilter/nf_conntrack_core.h>
 25#include <net/netfilter/nf_conntrack_ecache.h>
 26#include <net/netfilter/nf_conntrack_zones.h>
 27
 28static struct tc_action_ops act_ctinfo_ops;
 29
 30static void tcf_ctinfo_dscp_set(struct nf_conn *ct, struct tcf_ctinfo *ca,
 31				struct tcf_ctinfo_params *cp,
 32				struct sk_buff *skb, int wlen, int proto)
 33{
 34	u8 dscp, newdscp;
 35
 36	newdscp = (((READ_ONCE(ct->mark) & cp->dscpmask) >> cp->dscpmaskshift) << 2) &
 37		     ~INET_ECN_MASK;
 38
 39	switch (proto) {
 40	case NFPROTO_IPV4:
 41		dscp = ipv4_get_dsfield(ip_hdr(skb)) & ~INET_ECN_MASK;
 42		if (dscp != newdscp) {
 43			if (likely(!skb_try_make_writable(skb, wlen))) {
 44				ipv4_change_dsfield(ip_hdr(skb),
 45						    INET_ECN_MASK,
 46						    newdscp);
 47				ca->stats_dscp_set++;
 48			} else {
 49				ca->stats_dscp_error++;
 50			}
 51		}
 52		break;
 53	case NFPROTO_IPV6:
 54		dscp = ipv6_get_dsfield(ipv6_hdr(skb)) & ~INET_ECN_MASK;
 55		if (dscp != newdscp) {
 56			if (likely(!skb_try_make_writable(skb, wlen))) {
 57				ipv6_change_dsfield(ipv6_hdr(skb),
 58						    INET_ECN_MASK,
 59						    newdscp);
 60				ca->stats_dscp_set++;
 61			} else {
 62				ca->stats_dscp_error++;
 63			}
 64		}
 65		break;
 66	default:
 67		break;
 68	}
 69}
 70
 71static void tcf_ctinfo_cpmark_set(struct nf_conn *ct, struct tcf_ctinfo *ca,
 72				  struct tcf_ctinfo_params *cp,
 73				  struct sk_buff *skb)
 74{
 75	ca->stats_cpmark_set++;
 76	skb->mark = READ_ONCE(ct->mark) & cp->cpmarkmask;
 77}
 78
 79TC_INDIRECT_SCOPE int tcf_ctinfo_act(struct sk_buff *skb,
 80				     const struct tc_action *a,
 81				     struct tcf_result *res)
 82{
 83	const struct nf_conntrack_tuple_hash *thash = NULL;
 84	struct tcf_ctinfo *ca = to_ctinfo(a);
 85	struct nf_conntrack_tuple tuple;
 86	struct nf_conntrack_zone zone;
 87	enum ip_conntrack_info ctinfo;
 88	struct tcf_ctinfo_params *cp;
 89	struct nf_conn *ct;
 90	int proto, wlen;
 91	int action;
 92
 93	cp = rcu_dereference_bh(ca->params);
 94
 95	tcf_lastuse_update(&ca->tcf_tm);
 96	tcf_action_update_bstats(&ca->common, skb);
 97	action = READ_ONCE(ca->tcf_action);
 98
 99	wlen = skb_network_offset(skb);
100	switch (skb_protocol(skb, true)) {
101	case htons(ETH_P_IP):
102		wlen += sizeof(struct iphdr);
103		if (!pskb_may_pull(skb, wlen))
104			goto out;
105
106		proto = NFPROTO_IPV4;
107		break;
108	case htons(ETH_P_IPV6):
109		wlen += sizeof(struct ipv6hdr);
110		if (!pskb_may_pull(skb, wlen))
111			goto out;
112
113		proto = NFPROTO_IPV6;
114		break;
115	default:
116		goto out;
117	}
118
119	ct = nf_ct_get(skb, &ctinfo);
120	if (!ct) { /* look harder, usually ingress */
121		if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb),
122				       proto, cp->net, &tuple))
123			goto out;
124		zone.id = cp->zone;
125		zone.dir = NF_CT_DEFAULT_ZONE_DIR;
126
127		thash = nf_conntrack_find_get(cp->net, &zone, &tuple);
128		if (!thash)
129			goto out;
130
131		ct = nf_ct_tuplehash_to_ctrack(thash);
132	}
133
134	if (cp->mode & CTINFO_MODE_DSCP)
135		if (!cp->dscpstatemask || (READ_ONCE(ct->mark) & cp->dscpstatemask))
136			tcf_ctinfo_dscp_set(ct, ca, cp, skb, wlen, proto);
137
138	if (cp->mode & CTINFO_MODE_CPMARK)
139		tcf_ctinfo_cpmark_set(ct, ca, cp, skb);
140
141	if (thash)
142		nf_ct_put(ct);
143out:
144	return action;
145}
146
147static const struct nla_policy ctinfo_policy[TCA_CTINFO_MAX + 1] = {
148	[TCA_CTINFO_ACT]		  =
149		NLA_POLICY_EXACT_LEN(sizeof(struct tc_ctinfo)),
150	[TCA_CTINFO_ZONE]		  = { .type = NLA_U16 },
151	[TCA_CTINFO_PARMS_DSCP_MASK]	  = { .type = NLA_U32 },
152	[TCA_CTINFO_PARMS_DSCP_STATEMASK] = { .type = NLA_U32 },
153	[TCA_CTINFO_PARMS_CPMARK_MASK]	  = { .type = NLA_U32 },
154};
155
156static int tcf_ctinfo_init(struct net *net, struct nlattr *nla,
157			   struct nlattr *est, struct tc_action **a,
158			   struct tcf_proto *tp, u32 flags,
159			   struct netlink_ext_ack *extack)
160{
161	struct tc_action_net *tn = net_generic(net, act_ctinfo_ops.net_id);
162	bool bind = flags & TCA_ACT_FLAGS_BIND;
163	u32 dscpmask = 0, dscpstatemask, index;
164	struct nlattr *tb[TCA_CTINFO_MAX + 1];
165	struct tcf_ctinfo_params *cp_new;
166	struct tcf_chain *goto_ch = NULL;
167	struct tc_ctinfo *actparm;
168	struct tcf_ctinfo *ci;
169	u8 dscpmaskshift;
170	int ret = 0, err;
171
172	if (!nla) {
173		NL_SET_ERR_MSG_MOD(extack, "ctinfo requires attributes to be passed");
174		return -EINVAL;
175	}
176
177	err = nla_parse_nested(tb, TCA_CTINFO_MAX, nla, ctinfo_policy, extack);
178	if (err < 0)
179		return err;
180
181	if (!tb[TCA_CTINFO_ACT]) {
182		NL_SET_ERR_MSG_MOD(extack,
183				   "Missing required TCA_CTINFO_ACT attribute");
184		return -EINVAL;
185	}
186	actparm = nla_data(tb[TCA_CTINFO_ACT]);
187
188	/* do some basic validation here before dynamically allocating things */
189	/* that we would otherwise have to clean up.			      */
190	if (tb[TCA_CTINFO_PARMS_DSCP_MASK]) {
191		dscpmask = nla_get_u32(tb[TCA_CTINFO_PARMS_DSCP_MASK]);
192		/* need contiguous 6 bit mask */
193		dscpmaskshift = dscpmask ? __ffs(dscpmask) : 0;
194		if ((~0 & (dscpmask >> dscpmaskshift)) != 0x3f) {
195			NL_SET_ERR_MSG_ATTR(extack,
196					    tb[TCA_CTINFO_PARMS_DSCP_MASK],
197					    "dscp mask must be 6 contiguous bits");
198			return -EINVAL;
199		}
200		dscpstatemask =
201			nla_get_u32_default(tb[TCA_CTINFO_PARMS_DSCP_STATEMASK],
202					    0);
203		/* mask & statemask must not overlap */
204		if (dscpmask & dscpstatemask) {
205			NL_SET_ERR_MSG_ATTR(extack,
206					    tb[TCA_CTINFO_PARMS_DSCP_STATEMASK],
207					    "dscp statemask must not overlap dscp mask");
208			return -EINVAL;
209		}
210	}
211
212	/* done the validation:now to the actual action allocation */
213	index = actparm->index;
214	err = tcf_idr_check_alloc(tn, &index, a, bind);
215	if (!err) {
216		ret = tcf_idr_create_from_flags(tn, index, est, a,
217						&act_ctinfo_ops, bind, flags);
218		if (ret) {
219			tcf_idr_cleanup(tn, index);
220			return ret;
221		}
222		ret = ACT_P_CREATED;
223	} else if (err > 0) {
224		if (bind) /* don't override defaults */
225			return ACT_P_BOUND;
226		if (!(flags & TCA_ACT_FLAGS_REPLACE)) {
227			tcf_idr_release(*a, bind);
228			return -EEXIST;
229		}
230	} else {
231		return err;
232	}
233
234	err = tcf_action_check_ctrlact(actparm->action, tp, &goto_ch, extack);
235	if (err < 0)
236		goto release_idr;
237
238	ci = to_ctinfo(*a);
239
240	cp_new = kzalloc(sizeof(*cp_new), GFP_KERNEL);
241	if (unlikely(!cp_new)) {
242		err = -ENOMEM;
243		goto put_chain;
244	}
245
246	cp_new->net = net;
247	cp_new->zone = nla_get_u16_default(tb[TCA_CTINFO_ZONE], 0);
248	if (dscpmask) {
249		cp_new->dscpmask = dscpmask;
250		cp_new->dscpmaskshift = dscpmaskshift;
251		cp_new->dscpstatemask = dscpstatemask;
252		cp_new->mode |= CTINFO_MODE_DSCP;
253	}
254
255	if (tb[TCA_CTINFO_PARMS_CPMARK_MASK]) {
256		cp_new->cpmarkmask =
257				nla_get_u32(tb[TCA_CTINFO_PARMS_CPMARK_MASK]);
258		cp_new->mode |= CTINFO_MODE_CPMARK;
259	}
260
261	spin_lock_bh(&ci->tcf_lock);
262	goto_ch = tcf_action_set_ctrlact(*a, actparm->action, goto_ch);
263	cp_new = rcu_replace_pointer(ci->params, cp_new,
264				     lockdep_is_held(&ci->tcf_lock));
265	spin_unlock_bh(&ci->tcf_lock);
266
267	if (goto_ch)
268		tcf_chain_put_by_act(goto_ch);
269	if (cp_new)
270		kfree_rcu(cp_new, rcu);
271
272	return ret;
273
274put_chain:
275	if (goto_ch)
276		tcf_chain_put_by_act(goto_ch);
277release_idr:
278	tcf_idr_release(*a, bind);
279	return err;
280}
281
282static int tcf_ctinfo_dump(struct sk_buff *skb, struct tc_action *a,
283			   int bind, int ref)
284{
285	struct tcf_ctinfo *ci = to_ctinfo(a);
286	struct tc_ctinfo opt = {
287		.index   = ci->tcf_index,
288		.refcnt  = refcount_read(&ci->tcf_refcnt) - ref,
289		.bindcnt = atomic_read(&ci->tcf_bindcnt) - bind,
290	};
291	unsigned char *b = skb_tail_pointer(skb);
292	struct tcf_ctinfo_params *cp;
293	struct tcf_t t;
294
295	spin_lock_bh(&ci->tcf_lock);
296	cp = rcu_dereference_protected(ci->params,
297				       lockdep_is_held(&ci->tcf_lock));
298
299	tcf_tm_dump(&t, &ci->tcf_tm);
300	if (nla_put_64bit(skb, TCA_CTINFO_TM, sizeof(t), &t, TCA_CTINFO_PAD))
301		goto nla_put_failure;
302
303	opt.action = ci->tcf_action;
304	if (nla_put(skb, TCA_CTINFO_ACT, sizeof(opt), &opt))
305		goto nla_put_failure;
306
307	if (nla_put_u16(skb, TCA_CTINFO_ZONE, cp->zone))
308		goto nla_put_failure;
309
310	if (cp->mode & CTINFO_MODE_DSCP) {
311		if (nla_put_u32(skb, TCA_CTINFO_PARMS_DSCP_MASK,
312				cp->dscpmask))
313			goto nla_put_failure;
314		if (nla_put_u32(skb, TCA_CTINFO_PARMS_DSCP_STATEMASK,
315				cp->dscpstatemask))
316			goto nla_put_failure;
317	}
318
319	if (cp->mode & CTINFO_MODE_CPMARK) {
320		if (nla_put_u32(skb, TCA_CTINFO_PARMS_CPMARK_MASK,
321				cp->cpmarkmask))
322			goto nla_put_failure;
323	}
324
325	if (nla_put_u64_64bit(skb, TCA_CTINFO_STATS_DSCP_SET,
326			      ci->stats_dscp_set, TCA_CTINFO_PAD))
327		goto nla_put_failure;
328
329	if (nla_put_u64_64bit(skb, TCA_CTINFO_STATS_DSCP_ERROR,
330			      ci->stats_dscp_error, TCA_CTINFO_PAD))
331		goto nla_put_failure;
332
333	if (nla_put_u64_64bit(skb, TCA_CTINFO_STATS_CPMARK_SET,
334			      ci->stats_cpmark_set, TCA_CTINFO_PAD))
335		goto nla_put_failure;
336
337	spin_unlock_bh(&ci->tcf_lock);
338	return skb->len;
339
340nla_put_failure:
341	spin_unlock_bh(&ci->tcf_lock);
342	nlmsg_trim(skb, b);
343	return -1;
344}
345
346static void tcf_ctinfo_cleanup(struct tc_action *a)
347{
348	struct tcf_ctinfo *ci = to_ctinfo(a);
349	struct tcf_ctinfo_params *cp;
350
351	cp = rcu_dereference_protected(ci->params, 1);
352	if (cp)
353		kfree_rcu(cp, rcu);
354}
355
356static struct tc_action_ops act_ctinfo_ops = {
357	.kind	= "ctinfo",
358	.id	= TCA_ID_CTINFO,
359	.owner	= THIS_MODULE,
360	.act	= tcf_ctinfo_act,
361	.dump	= tcf_ctinfo_dump,
362	.init	= tcf_ctinfo_init,
363	.cleanup= tcf_ctinfo_cleanup,
364	.size	= sizeof(struct tcf_ctinfo),
365};
366MODULE_ALIAS_NET_ACT("ctinfo");
367
368static __net_init int ctinfo_init_net(struct net *net)
369{
370	struct tc_action_net *tn = net_generic(net, act_ctinfo_ops.net_id);
371
372	return tc_action_net_init(net, tn, &act_ctinfo_ops);
373}
374
375static void __net_exit ctinfo_exit_net(struct list_head *net_list)
376{
377	tc_action_net_exit(net_list, act_ctinfo_ops.net_id);
378}
379
380static struct pernet_operations ctinfo_net_ops = {
381	.init		= ctinfo_init_net,
382	.exit_batch	= ctinfo_exit_net,
383	.id		= &act_ctinfo_ops.net_id,
384	.size		= sizeof(struct tc_action_net),
385};
386
387static int __init ctinfo_init_module(void)
388{
389	return tcf_register_action(&act_ctinfo_ops, &ctinfo_net_ops);
390}
391
392static void __exit ctinfo_cleanup_module(void)
393{
394	tcf_unregister_action(&act_ctinfo_ops, &ctinfo_net_ops);
395}
396
397module_init(ctinfo_init_module);
398module_exit(ctinfo_cleanup_module);
399MODULE_AUTHOR("Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>");
400MODULE_DESCRIPTION("Connection tracking mark actions");
401MODULE_LICENSE("GPL");