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