xref: /linux/net/6lowpan/nhc.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
292aa7c65SAlexander Aring /*
392aa7c65SAlexander Aring  *	6LoWPAN next header compression
492aa7c65SAlexander Aring  *
592aa7c65SAlexander Aring  *	Authors:
692aa7c65SAlexander Aring  *	Alexander Aring		<aar@pengutronix.de>
792aa7c65SAlexander Aring  */
892aa7c65SAlexander Aring 
992aa7c65SAlexander Aring #include <linux/netdevice.h>
1092aa7c65SAlexander Aring 
1192aa7c65SAlexander Aring #include <net/ipv6.h>
1292aa7c65SAlexander Aring 
1392aa7c65SAlexander Aring #include "nhc.h"
1492aa7c65SAlexander Aring 
15*f3de6f4eSAlexander Aring static const struct lowpan_nhc *lowpan_nexthdr_nhcs[NEXTHDR_MAX + 1];
1692aa7c65SAlexander Aring static DEFINE_SPINLOCK(lowpan_nhc_lock);
1792aa7c65SAlexander Aring 
lowpan_nhc_by_nhcid(struct sk_buff * skb)18*f3de6f4eSAlexander Aring static const struct lowpan_nhc *lowpan_nhc_by_nhcid(struct sk_buff *skb)
1992aa7c65SAlexander Aring {
20*f3de6f4eSAlexander Aring 	const struct lowpan_nhc *nhc;
2131264f95SAlexander Aring 	int i;
2231264f95SAlexander Aring 	u8 id;
2392aa7c65SAlexander Aring 
2431264f95SAlexander Aring 	if (!pskb_may_pull(skb, 1))
2592aa7c65SAlexander Aring 		return NULL;
2692aa7c65SAlexander Aring 
2731264f95SAlexander Aring 	id = *skb->data;
2892aa7c65SAlexander Aring 
2931264f95SAlexander Aring 	for (i = 0; i < NEXTHDR_MAX + 1; i++) {
3031264f95SAlexander Aring 		nhc = lowpan_nexthdr_nhcs[i];
3131264f95SAlexander Aring 		if (!nhc)
3231264f95SAlexander Aring 			continue;
3331264f95SAlexander Aring 
3431264f95SAlexander Aring 		if ((id & nhc->idmask) == nhc->id)
3592aa7c65SAlexander Aring 			return nhc;
3692aa7c65SAlexander Aring 	}
3792aa7c65SAlexander Aring 
3892aa7c65SAlexander Aring 	return NULL;
3992aa7c65SAlexander Aring }
4092aa7c65SAlexander Aring 
lowpan_nhc_check_compression(struct sk_buff * skb,const struct ipv6hdr * hdr,u8 ** hc_ptr)4192aa7c65SAlexander Aring int lowpan_nhc_check_compression(struct sk_buff *skb,
42607b0bd3SAlexander Aring 				 const struct ipv6hdr *hdr, u8 **hc_ptr)
4392aa7c65SAlexander Aring {
44*f3de6f4eSAlexander Aring 	const struct lowpan_nhc *nhc;
45607b0bd3SAlexander Aring 	int ret = 0;
4692aa7c65SAlexander Aring 
4792aa7c65SAlexander Aring 	spin_lock_bh(&lowpan_nhc_lock);
4892aa7c65SAlexander Aring 
4992aa7c65SAlexander Aring 	nhc = lowpan_nexthdr_nhcs[hdr->nexthdr];
50607b0bd3SAlexander Aring 	if (!(nhc && nhc->compress))
51607b0bd3SAlexander Aring 		ret = -ENOENT;
5292aa7c65SAlexander Aring 
5392aa7c65SAlexander Aring 	spin_unlock_bh(&lowpan_nhc_lock);
5492aa7c65SAlexander Aring 
55607b0bd3SAlexander Aring 	return ret;
5692aa7c65SAlexander Aring }
5792aa7c65SAlexander Aring 
lowpan_nhc_do_compression(struct sk_buff * skb,const struct ipv6hdr * hdr,u8 ** hc_ptr)5892aa7c65SAlexander Aring int lowpan_nhc_do_compression(struct sk_buff *skb, const struct ipv6hdr *hdr,
5992aa7c65SAlexander Aring 			      u8 **hc_ptr)
6092aa7c65SAlexander Aring {
6192aa7c65SAlexander Aring 	int ret;
62*f3de6f4eSAlexander Aring 	const struct lowpan_nhc *nhc;
6392aa7c65SAlexander Aring 
6492aa7c65SAlexander Aring 	spin_lock_bh(&lowpan_nhc_lock);
6592aa7c65SAlexander Aring 
6692aa7c65SAlexander Aring 	nhc = lowpan_nexthdr_nhcs[hdr->nexthdr];
6792aa7c65SAlexander Aring 	/* check if the nhc module was removed in unlocked part.
6892aa7c65SAlexander Aring 	 * TODO: this is a workaround we should prevent unloading
6992aa7c65SAlexander Aring 	 * of nhc modules while unlocked part, this will always drop
7092aa7c65SAlexander Aring 	 * the lowpan packet but it's very unlikely.
7192aa7c65SAlexander Aring 	 *
7292aa7c65SAlexander Aring 	 * Solution isn't easy because we need to decide at
7392aa7c65SAlexander Aring 	 * lowpan_nhc_check_compression if we do a compression or not.
7492aa7c65SAlexander Aring 	 * Because the inline data which is added to skb, we can't move this
7592aa7c65SAlexander Aring 	 * handling.
7692aa7c65SAlexander Aring 	 */
7792aa7c65SAlexander Aring 	if (unlikely(!nhc || !nhc->compress)) {
7892aa7c65SAlexander Aring 		ret = -EINVAL;
7992aa7c65SAlexander Aring 		goto out;
8092aa7c65SAlexander Aring 	}
8192aa7c65SAlexander Aring 
8292aa7c65SAlexander Aring 	/* In the case of RAW sockets the transport header is not set by
8392aa7c65SAlexander Aring 	 * the ip6 stack so we must set it ourselves
8492aa7c65SAlexander Aring 	 */
8592aa7c65SAlexander Aring 	if (skb->transport_header == skb->network_header)
8692aa7c65SAlexander Aring 		skb_set_transport_header(skb, sizeof(struct ipv6hdr));
8792aa7c65SAlexander Aring 
8892aa7c65SAlexander Aring 	ret = nhc->compress(skb, hc_ptr);
8992aa7c65SAlexander Aring 	if (ret < 0)
9092aa7c65SAlexander Aring 		goto out;
9192aa7c65SAlexander Aring 
9292aa7c65SAlexander Aring 	/* skip the transport header */
9392aa7c65SAlexander Aring 	skb_pull(skb, nhc->nexthdrlen);
9492aa7c65SAlexander Aring 
9592aa7c65SAlexander Aring out:
9692aa7c65SAlexander Aring 	spin_unlock_bh(&lowpan_nhc_lock);
9792aa7c65SAlexander Aring 
9892aa7c65SAlexander Aring 	return ret;
9992aa7c65SAlexander Aring }
10092aa7c65SAlexander Aring 
lowpan_nhc_do_uncompression(struct sk_buff * skb,const struct net_device * dev,struct ipv6hdr * hdr)1018911d774SAlexander Aring int lowpan_nhc_do_uncompression(struct sk_buff *skb,
1028911d774SAlexander Aring 				const struct net_device *dev,
10392aa7c65SAlexander Aring 				struct ipv6hdr *hdr)
10492aa7c65SAlexander Aring {
105*f3de6f4eSAlexander Aring 	const struct lowpan_nhc *nhc;
10692aa7c65SAlexander Aring 	int ret;
10792aa7c65SAlexander Aring 
10892aa7c65SAlexander Aring 	spin_lock_bh(&lowpan_nhc_lock);
10992aa7c65SAlexander Aring 
11092aa7c65SAlexander Aring 	nhc = lowpan_nhc_by_nhcid(skb);
11192aa7c65SAlexander Aring 	if (nhc) {
11292aa7c65SAlexander Aring 		if (nhc->uncompress) {
11392aa7c65SAlexander Aring 			ret = nhc->uncompress(skb, sizeof(struct ipv6hdr) +
11492aa7c65SAlexander Aring 					      nhc->nexthdrlen);
11592aa7c65SAlexander Aring 			if (ret < 0) {
11692aa7c65SAlexander Aring 				spin_unlock_bh(&lowpan_nhc_lock);
11792aa7c65SAlexander Aring 				return ret;
11892aa7c65SAlexander Aring 			}
11992aa7c65SAlexander Aring 		} else {
12092aa7c65SAlexander Aring 			spin_unlock_bh(&lowpan_nhc_lock);
12192aa7c65SAlexander Aring 			netdev_warn(dev, "received nhc id for %s which is not implemented.\n",
12292aa7c65SAlexander Aring 				    nhc->name);
12392aa7c65SAlexander Aring 			return -ENOTSUPP;
12492aa7c65SAlexander Aring 		}
12592aa7c65SAlexander Aring 	} else {
12692aa7c65SAlexander Aring 		spin_unlock_bh(&lowpan_nhc_lock);
12792aa7c65SAlexander Aring 		netdev_warn(dev, "received unknown nhc id which was not found.\n");
12892aa7c65SAlexander Aring 		return -ENOENT;
12992aa7c65SAlexander Aring 	}
13092aa7c65SAlexander Aring 
13192aa7c65SAlexander Aring 	hdr->nexthdr = nhc->nexthdr;
13292aa7c65SAlexander Aring 	skb_reset_transport_header(skb);
13392aa7c65SAlexander Aring 	raw_dump_table(__func__, "raw transport header dump",
13492aa7c65SAlexander Aring 		       skb_transport_header(skb), nhc->nexthdrlen);
13592aa7c65SAlexander Aring 
13692aa7c65SAlexander Aring 	spin_unlock_bh(&lowpan_nhc_lock);
13792aa7c65SAlexander Aring 
13892aa7c65SAlexander Aring 	return 0;
13992aa7c65SAlexander Aring }
14092aa7c65SAlexander Aring 
lowpan_nhc_add(const struct lowpan_nhc * nhc)141*f3de6f4eSAlexander Aring int lowpan_nhc_add(const struct lowpan_nhc *nhc)
14292aa7c65SAlexander Aring {
14331264f95SAlexander Aring 	int ret = 0;
14492aa7c65SAlexander Aring 
14592aa7c65SAlexander Aring 	spin_lock_bh(&lowpan_nhc_lock);
14692aa7c65SAlexander Aring 
14792aa7c65SAlexander Aring 	if (lowpan_nexthdr_nhcs[nhc->nexthdr]) {
14892aa7c65SAlexander Aring 		ret = -EEXIST;
14992aa7c65SAlexander Aring 		goto out;
15092aa7c65SAlexander Aring 	}
15192aa7c65SAlexander Aring 
15292aa7c65SAlexander Aring 	lowpan_nexthdr_nhcs[nhc->nexthdr] = nhc;
15392aa7c65SAlexander Aring out:
15492aa7c65SAlexander Aring 	spin_unlock_bh(&lowpan_nhc_lock);
15592aa7c65SAlexander Aring 	return ret;
15692aa7c65SAlexander Aring }
15792aa7c65SAlexander Aring EXPORT_SYMBOL(lowpan_nhc_add);
15892aa7c65SAlexander Aring 
lowpan_nhc_del(const struct lowpan_nhc * nhc)159*f3de6f4eSAlexander Aring void lowpan_nhc_del(const struct lowpan_nhc *nhc)
16092aa7c65SAlexander Aring {
16192aa7c65SAlexander Aring 	spin_lock_bh(&lowpan_nhc_lock);
16292aa7c65SAlexander Aring 
16392aa7c65SAlexander Aring 	lowpan_nexthdr_nhcs[nhc->nexthdr] = NULL;
16492aa7c65SAlexander Aring 
16592aa7c65SAlexander Aring 	spin_unlock_bh(&lowpan_nhc_lock);
16692aa7c65SAlexander Aring 
16792aa7c65SAlexander Aring 	synchronize_net();
16892aa7c65SAlexander Aring }
16992aa7c65SAlexander Aring EXPORT_SYMBOL(lowpan_nhc_del);
170