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

nhc.c (5182B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *	6LoWPAN next header compression
      4 *
      5 *	Authors:
      6 *	Alexander Aring		<aar@pengutronix.de>
      7 */
      8
      9#include <linux/netdevice.h>
     10
     11#include <net/ipv6.h>
     12
     13#include "nhc.h"
     14
     15static struct rb_root rb_root = RB_ROOT;
     16static struct lowpan_nhc *lowpan_nexthdr_nhcs[NEXTHDR_MAX + 1];
     17static DEFINE_SPINLOCK(lowpan_nhc_lock);
     18
     19static int lowpan_nhc_insert(struct lowpan_nhc *nhc)
     20{
     21	struct rb_node **new = &rb_root.rb_node, *parent = NULL;
     22
     23	/* Figure out where to put new node */
     24	while (*new) {
     25		struct lowpan_nhc *this = rb_entry(*new, struct lowpan_nhc,
     26						   node);
     27		int result, len_dif, len;
     28
     29		len_dif = nhc->idlen - this->idlen;
     30
     31		if (nhc->idlen < this->idlen)
     32			len = nhc->idlen;
     33		else
     34			len = this->idlen;
     35
     36		result = memcmp(nhc->id, this->id, len);
     37		if (!result)
     38			result = len_dif;
     39
     40		parent = *new;
     41		if (result < 0)
     42			new = &((*new)->rb_left);
     43		else if (result > 0)
     44			new = &((*new)->rb_right);
     45		else
     46			return -EEXIST;
     47	}
     48
     49	/* Add new node and rebalance tree. */
     50	rb_link_node(&nhc->node, parent, new);
     51	rb_insert_color(&nhc->node, &rb_root);
     52
     53	return 0;
     54}
     55
     56static void lowpan_nhc_remove(struct lowpan_nhc *nhc)
     57{
     58	rb_erase(&nhc->node, &rb_root);
     59}
     60
     61static struct lowpan_nhc *lowpan_nhc_by_nhcid(const struct sk_buff *skb)
     62{
     63	struct rb_node *node = rb_root.rb_node;
     64	const u8 *nhcid_skb_ptr = skb->data;
     65
     66	while (node) {
     67		struct lowpan_nhc *nhc = rb_entry(node, struct lowpan_nhc,
     68						  node);
     69		u8 nhcid_skb_ptr_masked[LOWPAN_NHC_MAX_ID_LEN];
     70		int result, i;
     71
     72		if (nhcid_skb_ptr + nhc->idlen > skb->data + skb->len)
     73			return NULL;
     74
     75		/* copy and mask afterwards the nhid value from skb */
     76		memcpy(nhcid_skb_ptr_masked, nhcid_skb_ptr, nhc->idlen);
     77		for (i = 0; i < nhc->idlen; i++)
     78			nhcid_skb_ptr_masked[i] &= nhc->idmask[i];
     79
     80		result = memcmp(nhcid_skb_ptr_masked, nhc->id, nhc->idlen);
     81		if (result < 0)
     82			node = node->rb_left;
     83		else if (result > 0)
     84			node = node->rb_right;
     85		else
     86			return nhc;
     87	}
     88
     89	return NULL;
     90}
     91
     92int lowpan_nhc_check_compression(struct sk_buff *skb,
     93				 const struct ipv6hdr *hdr, u8 **hc_ptr)
     94{
     95	struct lowpan_nhc *nhc;
     96	int ret = 0;
     97
     98	spin_lock_bh(&lowpan_nhc_lock);
     99
    100	nhc = lowpan_nexthdr_nhcs[hdr->nexthdr];
    101	if (!(nhc && nhc->compress))
    102		ret = -ENOENT;
    103
    104	spin_unlock_bh(&lowpan_nhc_lock);
    105
    106	return ret;
    107}
    108
    109int lowpan_nhc_do_compression(struct sk_buff *skb, const struct ipv6hdr *hdr,
    110			      u8 **hc_ptr)
    111{
    112	int ret;
    113	struct lowpan_nhc *nhc;
    114
    115	spin_lock_bh(&lowpan_nhc_lock);
    116
    117	nhc = lowpan_nexthdr_nhcs[hdr->nexthdr];
    118	/* check if the nhc module was removed in unlocked part.
    119	 * TODO: this is a workaround we should prevent unloading
    120	 * of nhc modules while unlocked part, this will always drop
    121	 * the lowpan packet but it's very unlikely.
    122	 *
    123	 * Solution isn't easy because we need to decide at
    124	 * lowpan_nhc_check_compression if we do a compression or not.
    125	 * Because the inline data which is added to skb, we can't move this
    126	 * handling.
    127	 */
    128	if (unlikely(!nhc || !nhc->compress)) {
    129		ret = -EINVAL;
    130		goto out;
    131	}
    132
    133	/* In the case of RAW sockets the transport header is not set by
    134	 * the ip6 stack so we must set it ourselves
    135	 */
    136	if (skb->transport_header == skb->network_header)
    137		skb_set_transport_header(skb, sizeof(struct ipv6hdr));
    138
    139	ret = nhc->compress(skb, hc_ptr);
    140	if (ret < 0)
    141		goto out;
    142
    143	/* skip the transport header */
    144	skb_pull(skb, nhc->nexthdrlen);
    145
    146out:
    147	spin_unlock_bh(&lowpan_nhc_lock);
    148
    149	return ret;
    150}
    151
    152int lowpan_nhc_do_uncompression(struct sk_buff *skb,
    153				const struct net_device *dev,
    154				struct ipv6hdr *hdr)
    155{
    156	struct lowpan_nhc *nhc;
    157	int ret;
    158
    159	spin_lock_bh(&lowpan_nhc_lock);
    160
    161	nhc = lowpan_nhc_by_nhcid(skb);
    162	if (nhc) {
    163		if (nhc->uncompress) {
    164			ret = nhc->uncompress(skb, sizeof(struct ipv6hdr) +
    165					      nhc->nexthdrlen);
    166			if (ret < 0) {
    167				spin_unlock_bh(&lowpan_nhc_lock);
    168				return ret;
    169			}
    170		} else {
    171			spin_unlock_bh(&lowpan_nhc_lock);
    172			netdev_warn(dev, "received nhc id for %s which is not implemented.\n",
    173				    nhc->name);
    174			return -ENOTSUPP;
    175		}
    176	} else {
    177		spin_unlock_bh(&lowpan_nhc_lock);
    178		netdev_warn(dev, "received unknown nhc id which was not found.\n");
    179		return -ENOENT;
    180	}
    181
    182	hdr->nexthdr = nhc->nexthdr;
    183	skb_reset_transport_header(skb);
    184	raw_dump_table(__func__, "raw transport header dump",
    185		       skb_transport_header(skb), nhc->nexthdrlen);
    186
    187	spin_unlock_bh(&lowpan_nhc_lock);
    188
    189	return 0;
    190}
    191
    192int lowpan_nhc_add(struct lowpan_nhc *nhc)
    193{
    194	int ret;
    195
    196	if (!nhc->idlen || !nhc->idsetup)
    197		return -EINVAL;
    198
    199	WARN_ONCE(nhc->idlen > LOWPAN_NHC_MAX_ID_LEN,
    200		  "LOWPAN_NHC_MAX_ID_LEN should be updated to %zd.\n",
    201		  nhc->idlen);
    202
    203	nhc->idsetup(nhc);
    204
    205	spin_lock_bh(&lowpan_nhc_lock);
    206
    207	if (lowpan_nexthdr_nhcs[nhc->nexthdr]) {
    208		ret = -EEXIST;
    209		goto out;
    210	}
    211
    212	ret = lowpan_nhc_insert(nhc);
    213	if (ret < 0)
    214		goto out;
    215
    216	lowpan_nexthdr_nhcs[nhc->nexthdr] = nhc;
    217out:
    218	spin_unlock_bh(&lowpan_nhc_lock);
    219	return ret;
    220}
    221EXPORT_SYMBOL(lowpan_nhc_add);
    222
    223void lowpan_nhc_del(struct lowpan_nhc *nhc)
    224{
    225	spin_lock_bh(&lowpan_nhc_lock);
    226
    227	lowpan_nhc_remove(nhc);
    228	lowpan_nexthdr_nhcs[nhc->nexthdr] = NULL;
    229
    230	spin_unlock_bh(&lowpan_nhc_lock);
    231
    232	synchronize_net();
    233}
    234EXPORT_SYMBOL(lowpan_nhc_del);