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

reassembly.c (13162B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*	6LoWPAN fragment reassembly
      3 *
      4 *	Authors:
      5 *	Alexander Aring		<aar@pengutronix.de>
      6 *
      7 *	Based on: net/ipv6/reassembly.c
      8 */
      9
     10#define pr_fmt(fmt) "6LoWPAN: " fmt
     11
     12#include <linux/net.h>
     13#include <linux/list.h>
     14#include <linux/netdevice.h>
     15#include <linux/random.h>
     16#include <linux/jhash.h>
     17#include <linux/skbuff.h>
     18#include <linux/slab.h>
     19#include <linux/export.h>
     20
     21#include <net/ieee802154_netdev.h>
     22#include <net/6lowpan.h>
     23#include <net/ipv6_frag.h>
     24#include <net/inet_frag.h>
     25#include <net/ip.h>
     26
     27#include "6lowpan_i.h"
     28
     29static const char lowpan_frags_cache_name[] = "lowpan-frags";
     30
     31static struct inet_frags lowpan_frags;
     32
     33static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, struct sk_buff *skb,
     34			     struct sk_buff *prev,  struct net_device *ldev);
     35
     36static void lowpan_frag_init(struct inet_frag_queue *q, const void *a)
     37{
     38	const struct frag_lowpan_compare_key *key = a;
     39
     40	BUILD_BUG_ON(sizeof(*key) > sizeof(q->key));
     41	memcpy(&q->key, key, sizeof(*key));
     42}
     43
     44static void lowpan_frag_expire(struct timer_list *t)
     45{
     46	struct inet_frag_queue *frag = from_timer(frag, t, timer);
     47	struct frag_queue *fq;
     48
     49	fq = container_of(frag, struct frag_queue, q);
     50
     51	spin_lock(&fq->q.lock);
     52
     53	if (fq->q.flags & INET_FRAG_COMPLETE)
     54		goto out;
     55
     56	inet_frag_kill(&fq->q);
     57out:
     58	spin_unlock(&fq->q.lock);
     59	inet_frag_put(&fq->q);
     60}
     61
     62static inline struct lowpan_frag_queue *
     63fq_find(struct net *net, const struct lowpan_802154_cb *cb,
     64	const struct ieee802154_addr *src,
     65	const struct ieee802154_addr *dst)
     66{
     67	struct netns_ieee802154_lowpan *ieee802154_lowpan =
     68		net_ieee802154_lowpan(net);
     69	struct frag_lowpan_compare_key key = {};
     70	struct inet_frag_queue *q;
     71
     72	key.tag = cb->d_tag;
     73	key.d_size = cb->d_size;
     74	key.src = *src;
     75	key.dst = *dst;
     76
     77	q = inet_frag_find(ieee802154_lowpan->fqdir, &key);
     78	if (!q)
     79		return NULL;
     80
     81	return container_of(q, struct lowpan_frag_queue, q);
     82}
     83
     84static int lowpan_frag_queue(struct lowpan_frag_queue *fq,
     85			     struct sk_buff *skb, u8 frag_type)
     86{
     87	struct sk_buff *prev_tail;
     88	struct net_device *ldev;
     89	int end, offset, err;
     90
     91	/* inet_frag_queue_* functions use skb->cb; see struct ipfrag_skb_cb
     92	 * in inet_fragment.c
     93	 */
     94	BUILD_BUG_ON(sizeof(struct lowpan_802154_cb) > sizeof(struct inet_skb_parm));
     95	BUILD_BUG_ON(sizeof(struct lowpan_802154_cb) > sizeof(struct inet6_skb_parm));
     96
     97	if (fq->q.flags & INET_FRAG_COMPLETE)
     98		goto err;
     99
    100	offset = lowpan_802154_cb(skb)->d_offset << 3;
    101	end = lowpan_802154_cb(skb)->d_size;
    102
    103	/* Is this the final fragment? */
    104	if (offset + skb->len == end) {
    105		/* If we already have some bits beyond end
    106		 * or have different end, the segment is corrupted.
    107		 */
    108		if (end < fq->q.len ||
    109		    ((fq->q.flags & INET_FRAG_LAST_IN) && end != fq->q.len))
    110			goto err;
    111		fq->q.flags |= INET_FRAG_LAST_IN;
    112		fq->q.len = end;
    113	} else {
    114		if (end > fq->q.len) {
    115			/* Some bits beyond end -> corruption. */
    116			if (fq->q.flags & INET_FRAG_LAST_IN)
    117				goto err;
    118			fq->q.len = end;
    119		}
    120	}
    121
    122	ldev = skb->dev;
    123	if (ldev)
    124		skb->dev = NULL;
    125	barrier();
    126
    127	prev_tail = fq->q.fragments_tail;
    128	err = inet_frag_queue_insert(&fq->q, skb, offset, end);
    129	if (err)
    130		goto err;
    131
    132	fq->q.stamp = skb->tstamp;
    133	fq->q.mono_delivery_time = skb->mono_delivery_time;
    134	if (frag_type == LOWPAN_DISPATCH_FRAG1)
    135		fq->q.flags |= INET_FRAG_FIRST_IN;
    136
    137	fq->q.meat += skb->len;
    138	add_frag_mem_limit(fq->q.fqdir, skb->truesize);
    139
    140	if (fq->q.flags == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) &&
    141	    fq->q.meat == fq->q.len) {
    142		int res;
    143		unsigned long orefdst = skb->_skb_refdst;
    144
    145		skb->_skb_refdst = 0UL;
    146		res = lowpan_frag_reasm(fq, skb, prev_tail, ldev);
    147		skb->_skb_refdst = orefdst;
    148		return res;
    149	}
    150	skb_dst_drop(skb);
    151
    152	return -1;
    153err:
    154	kfree_skb(skb);
    155	return -1;
    156}
    157
    158/*	Check if this packet is complete.
    159 *
    160 *	It is called with locked fq, and caller must check that
    161 *	queue is eligible for reassembly i.e. it is not COMPLETE,
    162 *	the last and the first frames arrived and all the bits are here.
    163 */
    164static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, struct sk_buff *skb,
    165			     struct sk_buff *prev_tail, struct net_device *ldev)
    166{
    167	void *reasm_data;
    168
    169	inet_frag_kill(&fq->q);
    170
    171	reasm_data = inet_frag_reasm_prepare(&fq->q, skb, prev_tail);
    172	if (!reasm_data)
    173		goto out_oom;
    174	inet_frag_reasm_finish(&fq->q, skb, reasm_data, false);
    175
    176	skb->dev = ldev;
    177	skb->tstamp = fq->q.stamp;
    178	fq->q.rb_fragments = RB_ROOT;
    179	fq->q.fragments_tail = NULL;
    180	fq->q.last_run_head = NULL;
    181
    182	return 1;
    183out_oom:
    184	net_dbg_ratelimited("lowpan_frag_reasm: no memory for reassembly\n");
    185	return -1;
    186}
    187
    188static int lowpan_frag_rx_handlers_result(struct sk_buff *skb,
    189					  lowpan_rx_result res)
    190{
    191	switch (res) {
    192	case RX_QUEUED:
    193		return NET_RX_SUCCESS;
    194	case RX_CONTINUE:
    195		/* nobody cared about this packet */
    196		net_warn_ratelimited("%s: received unknown dispatch\n",
    197				     __func__);
    198
    199		fallthrough;
    200	default:
    201		/* all others failure */
    202		return NET_RX_DROP;
    203	}
    204}
    205
    206static lowpan_rx_result lowpan_frag_rx_h_iphc(struct sk_buff *skb)
    207{
    208	int ret;
    209
    210	if (!lowpan_is_iphc(*skb_network_header(skb)))
    211		return RX_CONTINUE;
    212
    213	ret = lowpan_iphc_decompress(skb);
    214	if (ret < 0)
    215		return RX_DROP;
    216
    217	return RX_QUEUED;
    218}
    219
    220static int lowpan_invoke_frag_rx_handlers(struct sk_buff *skb)
    221{
    222	lowpan_rx_result res;
    223
    224#define CALL_RXH(rxh)			\
    225	do {				\
    226		res = rxh(skb);	\
    227		if (res != RX_CONTINUE)	\
    228			goto rxh_next;	\
    229	} while (0)
    230
    231	/* likely at first */
    232	CALL_RXH(lowpan_frag_rx_h_iphc);
    233	CALL_RXH(lowpan_rx_h_ipv6);
    234
    235rxh_next:
    236	return lowpan_frag_rx_handlers_result(skb, res);
    237#undef CALL_RXH
    238}
    239
    240#define LOWPAN_FRAG_DGRAM_SIZE_HIGH_MASK	0x07
    241#define LOWPAN_FRAG_DGRAM_SIZE_HIGH_SHIFT	8
    242
    243static int lowpan_get_cb(struct sk_buff *skb, u8 frag_type,
    244			 struct lowpan_802154_cb *cb)
    245{
    246	bool fail;
    247	u8 high = 0, low = 0;
    248	__be16 d_tag = 0;
    249
    250	fail = lowpan_fetch_skb(skb, &high, 1);
    251	fail |= lowpan_fetch_skb(skb, &low, 1);
    252	/* remove the dispatch value and use first three bits as high value
    253	 * for the datagram size
    254	 */
    255	cb->d_size = (high & LOWPAN_FRAG_DGRAM_SIZE_HIGH_MASK) <<
    256		LOWPAN_FRAG_DGRAM_SIZE_HIGH_SHIFT | low;
    257	fail |= lowpan_fetch_skb(skb, &d_tag, 2);
    258	cb->d_tag = ntohs(d_tag);
    259
    260	if (frag_type == LOWPAN_DISPATCH_FRAGN) {
    261		fail |= lowpan_fetch_skb(skb, &cb->d_offset, 1);
    262	} else {
    263		skb_reset_network_header(skb);
    264		cb->d_offset = 0;
    265		/* check if datagram_size has ipv6hdr on FRAG1 */
    266		fail |= cb->d_size < sizeof(struct ipv6hdr);
    267		/* check if we can dereference the dispatch value */
    268		fail |= !skb->len;
    269	}
    270
    271	if (unlikely(fail))
    272		return -EIO;
    273
    274	return 0;
    275}
    276
    277int lowpan_frag_rcv(struct sk_buff *skb, u8 frag_type)
    278{
    279	struct lowpan_frag_queue *fq;
    280	struct net *net = dev_net(skb->dev);
    281	struct lowpan_802154_cb *cb = lowpan_802154_cb(skb);
    282	struct ieee802154_hdr hdr = {};
    283	int err;
    284
    285	if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0)
    286		goto err;
    287
    288	err = lowpan_get_cb(skb, frag_type, cb);
    289	if (err < 0)
    290		goto err;
    291
    292	if (frag_type == LOWPAN_DISPATCH_FRAG1) {
    293		err = lowpan_invoke_frag_rx_handlers(skb);
    294		if (err == NET_RX_DROP)
    295			goto err;
    296	}
    297
    298	if (cb->d_size > IPV6_MIN_MTU) {
    299		net_warn_ratelimited("lowpan_frag_rcv: datagram size exceeds MTU\n");
    300		goto err;
    301	}
    302
    303	fq = fq_find(net, cb, &hdr.source, &hdr.dest);
    304	if (fq != NULL) {
    305		int ret;
    306
    307		spin_lock(&fq->q.lock);
    308		ret = lowpan_frag_queue(fq, skb, frag_type);
    309		spin_unlock(&fq->q.lock);
    310
    311		inet_frag_put(&fq->q);
    312		return ret;
    313	}
    314
    315err:
    316	kfree_skb(skb);
    317	return -1;
    318}
    319
    320#ifdef CONFIG_SYSCTL
    321
    322static struct ctl_table lowpan_frags_ns_ctl_table[] = {
    323	{
    324		.procname	= "6lowpanfrag_high_thresh",
    325		.maxlen		= sizeof(unsigned long),
    326		.mode		= 0644,
    327		.proc_handler	= proc_doulongvec_minmax,
    328	},
    329	{
    330		.procname	= "6lowpanfrag_low_thresh",
    331		.maxlen		= sizeof(unsigned long),
    332		.mode		= 0644,
    333		.proc_handler	= proc_doulongvec_minmax,
    334	},
    335	{
    336		.procname	= "6lowpanfrag_time",
    337		.maxlen		= sizeof(int),
    338		.mode		= 0644,
    339		.proc_handler	= proc_dointvec_jiffies,
    340	},
    341	{ }
    342};
    343
    344/* secret interval has been deprecated */
    345static int lowpan_frags_secret_interval_unused;
    346static struct ctl_table lowpan_frags_ctl_table[] = {
    347	{
    348		.procname	= "6lowpanfrag_secret_interval",
    349		.data		= &lowpan_frags_secret_interval_unused,
    350		.maxlen		= sizeof(int),
    351		.mode		= 0644,
    352		.proc_handler	= proc_dointvec_jiffies,
    353	},
    354	{ }
    355};
    356
    357static int __net_init lowpan_frags_ns_sysctl_register(struct net *net)
    358{
    359	struct ctl_table *table;
    360	struct ctl_table_header *hdr;
    361	struct netns_ieee802154_lowpan *ieee802154_lowpan =
    362		net_ieee802154_lowpan(net);
    363
    364	table = lowpan_frags_ns_ctl_table;
    365	if (!net_eq(net, &init_net)) {
    366		table = kmemdup(table, sizeof(lowpan_frags_ns_ctl_table),
    367				GFP_KERNEL);
    368		if (table == NULL)
    369			goto err_alloc;
    370
    371		/* Don't export sysctls to unprivileged users */
    372		if (net->user_ns != &init_user_ns)
    373			table[0].procname = NULL;
    374	}
    375
    376	table[0].data	= &ieee802154_lowpan->fqdir->high_thresh;
    377	table[0].extra1	= &ieee802154_lowpan->fqdir->low_thresh;
    378	table[1].data	= &ieee802154_lowpan->fqdir->low_thresh;
    379	table[1].extra2	= &ieee802154_lowpan->fqdir->high_thresh;
    380	table[2].data	= &ieee802154_lowpan->fqdir->timeout;
    381
    382	hdr = register_net_sysctl(net, "net/ieee802154/6lowpan", table);
    383	if (hdr == NULL)
    384		goto err_reg;
    385
    386	ieee802154_lowpan->sysctl.frags_hdr = hdr;
    387	return 0;
    388
    389err_reg:
    390	if (!net_eq(net, &init_net))
    391		kfree(table);
    392err_alloc:
    393	return -ENOMEM;
    394}
    395
    396static void __net_exit lowpan_frags_ns_sysctl_unregister(struct net *net)
    397{
    398	struct ctl_table *table;
    399	struct netns_ieee802154_lowpan *ieee802154_lowpan =
    400		net_ieee802154_lowpan(net);
    401
    402	table = ieee802154_lowpan->sysctl.frags_hdr->ctl_table_arg;
    403	unregister_net_sysctl_table(ieee802154_lowpan->sysctl.frags_hdr);
    404	if (!net_eq(net, &init_net))
    405		kfree(table);
    406}
    407
    408static struct ctl_table_header *lowpan_ctl_header;
    409
    410static int __init lowpan_frags_sysctl_register(void)
    411{
    412	lowpan_ctl_header = register_net_sysctl(&init_net,
    413						"net/ieee802154/6lowpan",
    414						lowpan_frags_ctl_table);
    415	return lowpan_ctl_header == NULL ? -ENOMEM : 0;
    416}
    417
    418static void lowpan_frags_sysctl_unregister(void)
    419{
    420	unregister_net_sysctl_table(lowpan_ctl_header);
    421}
    422#else
    423static inline int lowpan_frags_ns_sysctl_register(struct net *net)
    424{
    425	return 0;
    426}
    427
    428static inline void lowpan_frags_ns_sysctl_unregister(struct net *net)
    429{
    430}
    431
    432static inline int __init lowpan_frags_sysctl_register(void)
    433{
    434	return 0;
    435}
    436
    437static inline void lowpan_frags_sysctl_unregister(void)
    438{
    439}
    440#endif
    441
    442static int __net_init lowpan_frags_init_net(struct net *net)
    443{
    444	struct netns_ieee802154_lowpan *ieee802154_lowpan =
    445		net_ieee802154_lowpan(net);
    446	int res;
    447
    448
    449	res = fqdir_init(&ieee802154_lowpan->fqdir, &lowpan_frags, net);
    450	if (res < 0)
    451		return res;
    452
    453	ieee802154_lowpan->fqdir->high_thresh = IPV6_FRAG_HIGH_THRESH;
    454	ieee802154_lowpan->fqdir->low_thresh = IPV6_FRAG_LOW_THRESH;
    455	ieee802154_lowpan->fqdir->timeout = IPV6_FRAG_TIMEOUT;
    456
    457	res = lowpan_frags_ns_sysctl_register(net);
    458	if (res < 0)
    459		fqdir_exit(ieee802154_lowpan->fqdir);
    460	return res;
    461}
    462
    463static void __net_exit lowpan_frags_pre_exit_net(struct net *net)
    464{
    465	struct netns_ieee802154_lowpan *ieee802154_lowpan =
    466		net_ieee802154_lowpan(net);
    467
    468	fqdir_pre_exit(ieee802154_lowpan->fqdir);
    469}
    470
    471static void __net_exit lowpan_frags_exit_net(struct net *net)
    472{
    473	struct netns_ieee802154_lowpan *ieee802154_lowpan =
    474		net_ieee802154_lowpan(net);
    475
    476	lowpan_frags_ns_sysctl_unregister(net);
    477	fqdir_exit(ieee802154_lowpan->fqdir);
    478}
    479
    480static struct pernet_operations lowpan_frags_ops = {
    481	.init		= lowpan_frags_init_net,
    482	.pre_exit	= lowpan_frags_pre_exit_net,
    483	.exit		= lowpan_frags_exit_net,
    484};
    485
    486static u32 lowpan_key_hashfn(const void *data, u32 len, u32 seed)
    487{
    488	return jhash2(data,
    489		      sizeof(struct frag_lowpan_compare_key) / sizeof(u32), seed);
    490}
    491
    492static u32 lowpan_obj_hashfn(const void *data, u32 len, u32 seed)
    493{
    494	const struct inet_frag_queue *fq = data;
    495
    496	return jhash2((const u32 *)&fq->key,
    497		      sizeof(struct frag_lowpan_compare_key) / sizeof(u32), seed);
    498}
    499
    500static int lowpan_obj_cmpfn(struct rhashtable_compare_arg *arg, const void *ptr)
    501{
    502	const struct frag_lowpan_compare_key *key = arg->key;
    503	const struct inet_frag_queue *fq = ptr;
    504
    505	return !!memcmp(&fq->key, key, sizeof(*key));
    506}
    507
    508static const struct rhashtable_params lowpan_rhash_params = {
    509	.head_offset		= offsetof(struct inet_frag_queue, node),
    510	.hashfn			= lowpan_key_hashfn,
    511	.obj_hashfn		= lowpan_obj_hashfn,
    512	.obj_cmpfn		= lowpan_obj_cmpfn,
    513	.automatic_shrinking	= true,
    514};
    515
    516int __init lowpan_net_frag_init(void)
    517{
    518	int ret;
    519
    520	lowpan_frags.constructor = lowpan_frag_init;
    521	lowpan_frags.destructor = NULL;
    522	lowpan_frags.qsize = sizeof(struct frag_queue);
    523	lowpan_frags.frag_expire = lowpan_frag_expire;
    524	lowpan_frags.frags_cache_name = lowpan_frags_cache_name;
    525	lowpan_frags.rhash_params = lowpan_rhash_params;
    526	ret = inet_frags_init(&lowpan_frags);
    527	if (ret)
    528		goto out;
    529
    530	ret = lowpan_frags_sysctl_register();
    531	if (ret)
    532		goto err_sysctl;
    533
    534	ret = register_pernet_subsys(&lowpan_frags_ops);
    535	if (ret)
    536		goto err_pernet;
    537out:
    538	return ret;
    539err_pernet:
    540	lowpan_frags_sysctl_unregister();
    541err_sysctl:
    542	inet_frags_fini(&lowpan_frags);
    543	return ret;
    544}
    545
    546void lowpan_net_frag_exit(void)
    547{
    548	lowpan_frags_sysctl_unregister();
    549	unregister_pernet_subsys(&lowpan_frags_ops);
    550	inet_frags_fini(&lowpan_frags);
    551}