cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

act_skbmod.c (8705B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * net/sched/act_skbmod.c  skb data modifier
      4 *
      5 * Copyright (c) 2016 Jamal Hadi Salim <jhs@mojatatu.com>
      6*/
      7
      8#include <linux/module.h>
      9#include <linux/if_arp.h>
     10#include <linux/init.h>
     11#include <linux/kernel.h>
     12#include <linux/skbuff.h>
     13#include <linux/rtnetlink.h>
     14#include <net/inet_ecn.h>
     15#include <net/netlink.h>
     16#include <net/pkt_sched.h>
     17#include <net/pkt_cls.h>
     18
     19#include <linux/tc_act/tc_skbmod.h>
     20#include <net/tc_act/tc_skbmod.h>
     21
     22static unsigned int skbmod_net_id;
     23static struct tc_action_ops act_skbmod_ops;
     24
     25static int tcf_skbmod_act(struct sk_buff *skb, const struct tc_action *a,
     26			  struct tcf_result *res)
     27{
     28	struct tcf_skbmod *d = to_skbmod(a);
     29	int action, max_edit_len, err;
     30	struct tcf_skbmod_params *p;
     31	u64 flags;
     32
     33	tcf_lastuse_update(&d->tcf_tm);
     34	bstats_update(this_cpu_ptr(d->common.cpu_bstats), skb);
     35
     36	action = READ_ONCE(d->tcf_action);
     37	if (unlikely(action == TC_ACT_SHOT))
     38		goto drop;
     39
     40	max_edit_len = skb_mac_header_len(skb);
     41	p = rcu_dereference_bh(d->skbmod_p);
     42	flags = p->flags;
     43
     44	/* tcf_skbmod_init() guarantees "flags" to be one of the following:
     45	 *	1. a combination of SKBMOD_F_{DMAC,SMAC,ETYPE}
     46	 *	2. SKBMOD_F_SWAPMAC
     47	 *	3. SKBMOD_F_ECN
     48	 * SKBMOD_F_ECN only works with IP packets; all other flags only work with Ethernet
     49	 * packets.
     50	 */
     51	if (flags == SKBMOD_F_ECN) {
     52		switch (skb_protocol(skb, true)) {
     53		case cpu_to_be16(ETH_P_IP):
     54		case cpu_to_be16(ETH_P_IPV6):
     55			max_edit_len += skb_network_header_len(skb);
     56			break;
     57		default:
     58			goto out;
     59		}
     60	} else if (!skb->dev || skb->dev->type != ARPHRD_ETHER) {
     61		goto out;
     62	}
     63
     64	err = skb_ensure_writable(skb, max_edit_len);
     65	if (unlikely(err)) /* best policy is to drop on the floor */
     66		goto drop;
     67
     68	if (flags & SKBMOD_F_DMAC)
     69		ether_addr_copy(eth_hdr(skb)->h_dest, p->eth_dst);
     70	if (flags & SKBMOD_F_SMAC)
     71		ether_addr_copy(eth_hdr(skb)->h_source, p->eth_src);
     72	if (flags & SKBMOD_F_ETYPE)
     73		eth_hdr(skb)->h_proto = p->eth_type;
     74
     75	if (flags & SKBMOD_F_SWAPMAC) {
     76		u16 tmpaddr[ETH_ALEN / 2]; /* ether_addr_copy() requirement */
     77		/*XXX: I am sure we can come up with more efficient swapping*/
     78		ether_addr_copy((u8 *)tmpaddr, eth_hdr(skb)->h_dest);
     79		ether_addr_copy(eth_hdr(skb)->h_dest, eth_hdr(skb)->h_source);
     80		ether_addr_copy(eth_hdr(skb)->h_source, (u8 *)tmpaddr);
     81	}
     82
     83	if (flags & SKBMOD_F_ECN)
     84		INET_ECN_set_ce(skb);
     85
     86out:
     87	return action;
     88
     89drop:
     90	qstats_overlimit_inc(this_cpu_ptr(d->common.cpu_qstats));
     91	return TC_ACT_SHOT;
     92}
     93
     94static const struct nla_policy skbmod_policy[TCA_SKBMOD_MAX + 1] = {
     95	[TCA_SKBMOD_PARMS]		= { .len = sizeof(struct tc_skbmod) },
     96	[TCA_SKBMOD_DMAC]		= { .len = ETH_ALEN },
     97	[TCA_SKBMOD_SMAC]		= { .len = ETH_ALEN },
     98	[TCA_SKBMOD_ETYPE]		= { .type = NLA_U16 },
     99};
    100
    101static int tcf_skbmod_init(struct net *net, struct nlattr *nla,
    102			   struct nlattr *est, struct tc_action **a,
    103			   struct tcf_proto *tp, u32 flags,
    104			   struct netlink_ext_ack *extack)
    105{
    106	struct tc_action_net *tn = net_generic(net, skbmod_net_id);
    107	bool ovr = flags & TCA_ACT_FLAGS_REPLACE;
    108	bool bind = flags & TCA_ACT_FLAGS_BIND;
    109	struct nlattr *tb[TCA_SKBMOD_MAX + 1];
    110	struct tcf_skbmod_params *p, *p_old;
    111	struct tcf_chain *goto_ch = NULL;
    112	struct tc_skbmod *parm;
    113	u32 lflags = 0, index;
    114	struct tcf_skbmod *d;
    115	bool exists = false;
    116	u8 *daddr = NULL;
    117	u8 *saddr = NULL;
    118	u16 eth_type = 0;
    119	int ret = 0, err;
    120
    121	if (!nla)
    122		return -EINVAL;
    123
    124	err = nla_parse_nested_deprecated(tb, TCA_SKBMOD_MAX, nla,
    125					  skbmod_policy, NULL);
    126	if (err < 0)
    127		return err;
    128
    129	if (!tb[TCA_SKBMOD_PARMS])
    130		return -EINVAL;
    131
    132	if (tb[TCA_SKBMOD_DMAC]) {
    133		daddr = nla_data(tb[TCA_SKBMOD_DMAC]);
    134		lflags |= SKBMOD_F_DMAC;
    135	}
    136
    137	if (tb[TCA_SKBMOD_SMAC]) {
    138		saddr = nla_data(tb[TCA_SKBMOD_SMAC]);
    139		lflags |= SKBMOD_F_SMAC;
    140	}
    141
    142	if (tb[TCA_SKBMOD_ETYPE]) {
    143		eth_type = nla_get_u16(tb[TCA_SKBMOD_ETYPE]);
    144		lflags |= SKBMOD_F_ETYPE;
    145	}
    146
    147	parm = nla_data(tb[TCA_SKBMOD_PARMS]);
    148	index = parm->index;
    149	if (parm->flags & SKBMOD_F_SWAPMAC)
    150		lflags = SKBMOD_F_SWAPMAC;
    151	if (parm->flags & SKBMOD_F_ECN)
    152		lflags = SKBMOD_F_ECN;
    153
    154	err = tcf_idr_check_alloc(tn, &index, a, bind);
    155	if (err < 0)
    156		return err;
    157	exists = err;
    158	if (exists && bind)
    159		return 0;
    160
    161	if (!lflags) {
    162		if (exists)
    163			tcf_idr_release(*a, bind);
    164		else
    165			tcf_idr_cleanup(tn, index);
    166		return -EINVAL;
    167	}
    168
    169	if (!exists) {
    170		ret = tcf_idr_create(tn, index, est, a,
    171				     &act_skbmod_ops, bind, true, flags);
    172		if (ret) {
    173			tcf_idr_cleanup(tn, index);
    174			return ret;
    175		}
    176
    177		ret = ACT_P_CREATED;
    178	} else if (!ovr) {
    179		tcf_idr_release(*a, bind);
    180		return -EEXIST;
    181	}
    182	err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
    183	if (err < 0)
    184		goto release_idr;
    185
    186	d = to_skbmod(*a);
    187
    188	p = kzalloc(sizeof(struct tcf_skbmod_params), GFP_KERNEL);
    189	if (unlikely(!p)) {
    190		err = -ENOMEM;
    191		goto put_chain;
    192	}
    193
    194	p->flags = lflags;
    195
    196	if (ovr)
    197		spin_lock_bh(&d->tcf_lock);
    198	/* Protected by tcf_lock if overwriting existing action. */
    199	goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
    200	p_old = rcu_dereference_protected(d->skbmod_p, 1);
    201
    202	if (lflags & SKBMOD_F_DMAC)
    203		ether_addr_copy(p->eth_dst, daddr);
    204	if (lflags & SKBMOD_F_SMAC)
    205		ether_addr_copy(p->eth_src, saddr);
    206	if (lflags & SKBMOD_F_ETYPE)
    207		p->eth_type = htons(eth_type);
    208
    209	rcu_assign_pointer(d->skbmod_p, p);
    210	if (ovr)
    211		spin_unlock_bh(&d->tcf_lock);
    212
    213	if (p_old)
    214		kfree_rcu(p_old, rcu);
    215	if (goto_ch)
    216		tcf_chain_put_by_act(goto_ch);
    217
    218	return ret;
    219put_chain:
    220	if (goto_ch)
    221		tcf_chain_put_by_act(goto_ch);
    222release_idr:
    223	tcf_idr_release(*a, bind);
    224	return err;
    225}
    226
    227static void tcf_skbmod_cleanup(struct tc_action *a)
    228{
    229	struct tcf_skbmod *d = to_skbmod(a);
    230	struct tcf_skbmod_params  *p;
    231
    232	p = rcu_dereference_protected(d->skbmod_p, 1);
    233	if (p)
    234		kfree_rcu(p, rcu);
    235}
    236
    237static int tcf_skbmod_dump(struct sk_buff *skb, struct tc_action *a,
    238			   int bind, int ref)
    239{
    240	struct tcf_skbmod *d = to_skbmod(a);
    241	unsigned char *b = skb_tail_pointer(skb);
    242	struct tcf_skbmod_params  *p;
    243	struct tc_skbmod opt = {
    244		.index   = d->tcf_index,
    245		.refcnt  = refcount_read(&d->tcf_refcnt) - ref,
    246		.bindcnt = atomic_read(&d->tcf_bindcnt) - bind,
    247	};
    248	struct tcf_t t;
    249
    250	spin_lock_bh(&d->tcf_lock);
    251	opt.action = d->tcf_action;
    252	p = rcu_dereference_protected(d->skbmod_p,
    253				      lockdep_is_held(&d->tcf_lock));
    254	opt.flags  = p->flags;
    255	if (nla_put(skb, TCA_SKBMOD_PARMS, sizeof(opt), &opt))
    256		goto nla_put_failure;
    257	if ((p->flags & SKBMOD_F_DMAC) &&
    258	    nla_put(skb, TCA_SKBMOD_DMAC, ETH_ALEN, p->eth_dst))
    259		goto nla_put_failure;
    260	if ((p->flags & SKBMOD_F_SMAC) &&
    261	    nla_put(skb, TCA_SKBMOD_SMAC, ETH_ALEN, p->eth_src))
    262		goto nla_put_failure;
    263	if ((p->flags & SKBMOD_F_ETYPE) &&
    264	    nla_put_u16(skb, TCA_SKBMOD_ETYPE, ntohs(p->eth_type)))
    265		goto nla_put_failure;
    266
    267	tcf_tm_dump(&t, &d->tcf_tm);
    268	if (nla_put_64bit(skb, TCA_SKBMOD_TM, sizeof(t), &t, TCA_SKBMOD_PAD))
    269		goto nla_put_failure;
    270
    271	spin_unlock_bh(&d->tcf_lock);
    272	return skb->len;
    273nla_put_failure:
    274	spin_unlock_bh(&d->tcf_lock);
    275	nlmsg_trim(skb, b);
    276	return -1;
    277}
    278
    279static int tcf_skbmod_walker(struct net *net, struct sk_buff *skb,
    280			     struct netlink_callback *cb, int type,
    281			     const struct tc_action_ops *ops,
    282			     struct netlink_ext_ack *extack)
    283{
    284	struct tc_action_net *tn = net_generic(net, skbmod_net_id);
    285
    286	return tcf_generic_walker(tn, skb, cb, type, ops, extack);
    287}
    288
    289static int tcf_skbmod_search(struct net *net, struct tc_action **a, u32 index)
    290{
    291	struct tc_action_net *tn = net_generic(net, skbmod_net_id);
    292
    293	return tcf_idr_search(tn, a, index);
    294}
    295
    296static struct tc_action_ops act_skbmod_ops = {
    297	.kind		=	"skbmod",
    298	.id		=	TCA_ACT_SKBMOD,
    299	.owner		=	THIS_MODULE,
    300	.act		=	tcf_skbmod_act,
    301	.dump		=	tcf_skbmod_dump,
    302	.init		=	tcf_skbmod_init,
    303	.cleanup	=	tcf_skbmod_cleanup,
    304	.walk		=	tcf_skbmod_walker,
    305	.lookup		=	tcf_skbmod_search,
    306	.size		=	sizeof(struct tcf_skbmod),
    307};
    308
    309static __net_init int skbmod_init_net(struct net *net)
    310{
    311	struct tc_action_net *tn = net_generic(net, skbmod_net_id);
    312
    313	return tc_action_net_init(net, tn, &act_skbmod_ops);
    314}
    315
    316static void __net_exit skbmod_exit_net(struct list_head *net_list)
    317{
    318	tc_action_net_exit(net_list, skbmod_net_id);
    319}
    320
    321static struct pernet_operations skbmod_net_ops = {
    322	.init = skbmod_init_net,
    323	.exit_batch = skbmod_exit_net,
    324	.id   = &skbmod_net_id,
    325	.size = sizeof(struct tc_action_net),
    326};
    327
    328MODULE_AUTHOR("Jamal Hadi Salim, <jhs@mojatatu.com>");
    329MODULE_DESCRIPTION("SKB data mod-ing");
    330MODULE_LICENSE("GPL");
    331
    332static int __init skbmod_init_module(void)
    333{
    334	return tcf_register_action(&act_skbmod_ops, &skbmod_net_ops);
    335}
    336
    337static void __exit skbmod_cleanup_module(void)
    338{
    339	tcf_unregister_action(&act_skbmod_ops, &skbmod_net_ops);
    340}
    341
    342module_init(skbmod_init_module);
    343module_exit(skbmod_cleanup_module);