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_nat.c (7927B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Stateless NAT actions
      4 *
      5 * Copyright (c) 2007 Herbert Xu <herbert@gondor.apana.org.au>
      6 */
      7
      8#include <linux/errno.h>
      9#include <linux/init.h>
     10#include <linux/kernel.h>
     11#include <linux/module.h>
     12#include <linux/netfilter.h>
     13#include <linux/rtnetlink.h>
     14#include <linux/skbuff.h>
     15#include <linux/slab.h>
     16#include <linux/spinlock.h>
     17#include <linux/string.h>
     18#include <linux/tc_act/tc_nat.h>
     19#include <net/act_api.h>
     20#include <net/pkt_cls.h>
     21#include <net/icmp.h>
     22#include <net/ip.h>
     23#include <net/netlink.h>
     24#include <net/tc_act/tc_nat.h>
     25#include <net/tcp.h>
     26#include <net/udp.h>
     27
     28
     29static unsigned int nat_net_id;
     30static struct tc_action_ops act_nat_ops;
     31
     32static const struct nla_policy nat_policy[TCA_NAT_MAX + 1] = {
     33	[TCA_NAT_PARMS]	= { .len = sizeof(struct tc_nat) },
     34};
     35
     36static int tcf_nat_init(struct net *net, struct nlattr *nla, struct nlattr *est,
     37			struct tc_action **a, struct tcf_proto *tp,
     38			u32 flags, struct netlink_ext_ack *extack)
     39{
     40	struct tc_action_net *tn = net_generic(net, nat_net_id);
     41	bool bind = flags & TCA_ACT_FLAGS_BIND;
     42	struct nlattr *tb[TCA_NAT_MAX + 1];
     43	struct tcf_chain *goto_ch = NULL;
     44	struct tc_nat *parm;
     45	int ret = 0, err;
     46	struct tcf_nat *p;
     47	u32 index;
     48
     49	if (nla == NULL)
     50		return -EINVAL;
     51
     52	err = nla_parse_nested_deprecated(tb, TCA_NAT_MAX, nla, nat_policy,
     53					  NULL);
     54	if (err < 0)
     55		return err;
     56
     57	if (tb[TCA_NAT_PARMS] == NULL)
     58		return -EINVAL;
     59	parm = nla_data(tb[TCA_NAT_PARMS]);
     60	index = parm->index;
     61	err = tcf_idr_check_alloc(tn, &index, a, bind);
     62	if (!err) {
     63		ret = tcf_idr_create(tn, index, est, a,
     64				     &act_nat_ops, bind, false, flags);
     65		if (ret) {
     66			tcf_idr_cleanup(tn, index);
     67			return ret;
     68		}
     69		ret = ACT_P_CREATED;
     70	} else if (err > 0) {
     71		if (bind)
     72			return 0;
     73		if (!(flags & TCA_ACT_FLAGS_REPLACE)) {
     74			tcf_idr_release(*a, bind);
     75			return -EEXIST;
     76		}
     77	} else {
     78		return err;
     79	}
     80	err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
     81	if (err < 0)
     82		goto release_idr;
     83	p = to_tcf_nat(*a);
     84
     85	spin_lock_bh(&p->tcf_lock);
     86	p->old_addr = parm->old_addr;
     87	p->new_addr = parm->new_addr;
     88	p->mask = parm->mask;
     89	p->flags = parm->flags;
     90
     91	goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
     92	spin_unlock_bh(&p->tcf_lock);
     93	if (goto_ch)
     94		tcf_chain_put_by_act(goto_ch);
     95
     96	return ret;
     97release_idr:
     98	tcf_idr_release(*a, bind);
     99	return err;
    100}
    101
    102static int tcf_nat_act(struct sk_buff *skb, const struct tc_action *a,
    103		       struct tcf_result *res)
    104{
    105	struct tcf_nat *p = to_tcf_nat(a);
    106	struct iphdr *iph;
    107	__be32 old_addr;
    108	__be32 new_addr;
    109	__be32 mask;
    110	__be32 addr;
    111	int egress;
    112	int action;
    113	int ihl;
    114	int noff;
    115
    116	spin_lock(&p->tcf_lock);
    117
    118	tcf_lastuse_update(&p->tcf_tm);
    119	old_addr = p->old_addr;
    120	new_addr = p->new_addr;
    121	mask = p->mask;
    122	egress = p->flags & TCA_NAT_FLAG_EGRESS;
    123	action = p->tcf_action;
    124
    125	bstats_update(&p->tcf_bstats, skb);
    126
    127	spin_unlock(&p->tcf_lock);
    128
    129	if (unlikely(action == TC_ACT_SHOT))
    130		goto drop;
    131
    132	noff = skb_network_offset(skb);
    133	if (!pskb_may_pull(skb, sizeof(*iph) + noff))
    134		goto drop;
    135
    136	iph = ip_hdr(skb);
    137
    138	if (egress)
    139		addr = iph->saddr;
    140	else
    141		addr = iph->daddr;
    142
    143	if (!((old_addr ^ addr) & mask)) {
    144		if (skb_try_make_writable(skb, sizeof(*iph) + noff))
    145			goto drop;
    146
    147		new_addr &= mask;
    148		new_addr |= addr & ~mask;
    149
    150		/* Rewrite IP header */
    151		iph = ip_hdr(skb);
    152		if (egress)
    153			iph->saddr = new_addr;
    154		else
    155			iph->daddr = new_addr;
    156
    157		csum_replace4(&iph->check, addr, new_addr);
    158	} else if ((iph->frag_off & htons(IP_OFFSET)) ||
    159		   iph->protocol != IPPROTO_ICMP) {
    160		goto out;
    161	}
    162
    163	ihl = iph->ihl * 4;
    164
    165	/* It would be nice to share code with stateful NAT. */
    166	switch (iph->frag_off & htons(IP_OFFSET) ? 0 : iph->protocol) {
    167	case IPPROTO_TCP:
    168	{
    169		struct tcphdr *tcph;
    170
    171		if (!pskb_may_pull(skb, ihl + sizeof(*tcph) + noff) ||
    172		    skb_try_make_writable(skb, ihl + sizeof(*tcph) + noff))
    173			goto drop;
    174
    175		tcph = (void *)(skb_network_header(skb) + ihl);
    176		inet_proto_csum_replace4(&tcph->check, skb, addr, new_addr,
    177					 true);
    178		break;
    179	}
    180	case IPPROTO_UDP:
    181	{
    182		struct udphdr *udph;
    183
    184		if (!pskb_may_pull(skb, ihl + sizeof(*udph) + noff) ||
    185		    skb_try_make_writable(skb, ihl + sizeof(*udph) + noff))
    186			goto drop;
    187
    188		udph = (void *)(skb_network_header(skb) + ihl);
    189		if (udph->check || skb->ip_summed == CHECKSUM_PARTIAL) {
    190			inet_proto_csum_replace4(&udph->check, skb, addr,
    191						 new_addr, true);
    192			if (!udph->check)
    193				udph->check = CSUM_MANGLED_0;
    194		}
    195		break;
    196	}
    197	case IPPROTO_ICMP:
    198	{
    199		struct icmphdr *icmph;
    200
    201		if (!pskb_may_pull(skb, ihl + sizeof(*icmph) + noff))
    202			goto drop;
    203
    204		icmph = (void *)(skb_network_header(skb) + ihl);
    205
    206		if (!icmp_is_err(icmph->type))
    207			break;
    208
    209		if (!pskb_may_pull(skb, ihl + sizeof(*icmph) + sizeof(*iph) +
    210					noff))
    211			goto drop;
    212
    213		icmph = (void *)(skb_network_header(skb) + ihl);
    214		iph = (void *)(icmph + 1);
    215		if (egress)
    216			addr = iph->daddr;
    217		else
    218			addr = iph->saddr;
    219
    220		if ((old_addr ^ addr) & mask)
    221			break;
    222
    223		if (skb_try_make_writable(skb, ihl + sizeof(*icmph) +
    224					  sizeof(*iph) + noff))
    225			goto drop;
    226
    227		icmph = (void *)(skb_network_header(skb) + ihl);
    228		iph = (void *)(icmph + 1);
    229
    230		new_addr &= mask;
    231		new_addr |= addr & ~mask;
    232
    233		/* XXX Fix up the inner checksums. */
    234		if (egress)
    235			iph->daddr = new_addr;
    236		else
    237			iph->saddr = new_addr;
    238
    239		inet_proto_csum_replace4(&icmph->checksum, skb, addr, new_addr,
    240					 false);
    241		break;
    242	}
    243	default:
    244		break;
    245	}
    246
    247out:
    248	return action;
    249
    250drop:
    251	spin_lock(&p->tcf_lock);
    252	p->tcf_qstats.drops++;
    253	spin_unlock(&p->tcf_lock);
    254	return TC_ACT_SHOT;
    255}
    256
    257static int tcf_nat_dump(struct sk_buff *skb, struct tc_action *a,
    258			int bind, int ref)
    259{
    260	unsigned char *b = skb_tail_pointer(skb);
    261	struct tcf_nat *p = to_tcf_nat(a);
    262	struct tc_nat opt = {
    263		.index    = p->tcf_index,
    264		.refcnt   = refcount_read(&p->tcf_refcnt) - ref,
    265		.bindcnt  = atomic_read(&p->tcf_bindcnt) - bind,
    266	};
    267	struct tcf_t t;
    268
    269	spin_lock_bh(&p->tcf_lock);
    270	opt.old_addr = p->old_addr;
    271	opt.new_addr = p->new_addr;
    272	opt.mask = p->mask;
    273	opt.flags = p->flags;
    274	opt.action = p->tcf_action;
    275
    276	if (nla_put(skb, TCA_NAT_PARMS, sizeof(opt), &opt))
    277		goto nla_put_failure;
    278
    279	tcf_tm_dump(&t, &p->tcf_tm);
    280	if (nla_put_64bit(skb, TCA_NAT_TM, sizeof(t), &t, TCA_NAT_PAD))
    281		goto nla_put_failure;
    282	spin_unlock_bh(&p->tcf_lock);
    283
    284	return skb->len;
    285
    286nla_put_failure:
    287	spin_unlock_bh(&p->tcf_lock);
    288	nlmsg_trim(skb, b);
    289	return -1;
    290}
    291
    292static int tcf_nat_walker(struct net *net, struct sk_buff *skb,
    293			  struct netlink_callback *cb, int type,
    294			  const struct tc_action_ops *ops,
    295			  struct netlink_ext_ack *extack)
    296{
    297	struct tc_action_net *tn = net_generic(net, nat_net_id);
    298
    299	return tcf_generic_walker(tn, skb, cb, type, ops, extack);
    300}
    301
    302static int tcf_nat_search(struct net *net, struct tc_action **a, u32 index)
    303{
    304	struct tc_action_net *tn = net_generic(net, nat_net_id);
    305
    306	return tcf_idr_search(tn, a, index);
    307}
    308
    309static struct tc_action_ops act_nat_ops = {
    310	.kind		=	"nat",
    311	.id		=	TCA_ID_NAT,
    312	.owner		=	THIS_MODULE,
    313	.act		=	tcf_nat_act,
    314	.dump		=	tcf_nat_dump,
    315	.init		=	tcf_nat_init,
    316	.walk		=	tcf_nat_walker,
    317	.lookup		=	tcf_nat_search,
    318	.size		=	sizeof(struct tcf_nat),
    319};
    320
    321static __net_init int nat_init_net(struct net *net)
    322{
    323	struct tc_action_net *tn = net_generic(net, nat_net_id);
    324
    325	return tc_action_net_init(net, tn, &act_nat_ops);
    326}
    327
    328static void __net_exit nat_exit_net(struct list_head *net_list)
    329{
    330	tc_action_net_exit(net_list, nat_net_id);
    331}
    332
    333static struct pernet_operations nat_net_ops = {
    334	.init = nat_init_net,
    335	.exit_batch = nat_exit_net,
    336	.id   = &nat_net_id,
    337	.size = sizeof(struct tc_action_net),
    338};
    339
    340MODULE_DESCRIPTION("Stateless NAT actions");
    341MODULE_LICENSE("GPL");
    342
    343static int __init nat_init_module(void)
    344{
    345	return tcf_register_action(&act_nat_ops, &nat_net_ops);
    346}
    347
    348static void __exit nat_cleanup_module(void)
    349{
    350	tcf_unregister_action(&act_nat_ops, &nat_net_ops);
    351}
    352
    353module_init(nat_init_module);
    354module_exit(nat_cleanup_module);