1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * 6LoWPAN next header compression
6 * Alexander Aring <aar@pengutronix.de>
9 #include <linux/netdevice.h>
15 static struct rb_root rb_root
= RB_ROOT
;
16 static struct lowpan_nhc
*lowpan_nexthdr_nhcs
[NEXTHDR_MAX
+ 1];
17 static DEFINE_SPINLOCK(lowpan_nhc_lock
);
19 static int lowpan_nhc_insert(struct lowpan_nhc
*nhc
)
21 struct rb_node
**new = &rb_root
.rb_node
, *parent
= NULL
;
23 /* Figure out where to put new node */
25 struct lowpan_nhc
*this = rb_entry(*new, struct lowpan_nhc
,
27 int result
, len_dif
, len
;
29 len_dif
= nhc
->idlen
- this->idlen
;
31 if (nhc
->idlen
< this->idlen
)
36 result
= memcmp(nhc
->id
, this->id
, len
);
42 new = &((*new)->rb_left
);
44 new = &((*new)->rb_right
);
49 /* Add new node and rebalance tree. */
50 rb_link_node(&nhc
->node
, parent
, new);
51 rb_insert_color(&nhc
->node
, &rb_root
);
56 static void lowpan_nhc_remove(struct lowpan_nhc
*nhc
)
58 rb_erase(&nhc
->node
, &rb_root
);
61 static struct lowpan_nhc
*lowpan_nhc_by_nhcid(const struct sk_buff
*skb
)
63 struct rb_node
*node
= rb_root
.rb_node
;
64 const u8
*nhcid_skb_ptr
= skb
->data
;
67 struct lowpan_nhc
*nhc
= rb_entry(node
, struct lowpan_nhc
,
69 u8 nhcid_skb_ptr_masked
[LOWPAN_NHC_MAX_ID_LEN
];
72 if (nhcid_skb_ptr
+ nhc
->idlen
> skb
->data
+ skb
->len
)
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
];
80 result
= memcmp(nhcid_skb_ptr_masked
, nhc
->id
, nhc
->idlen
);
84 node
= node
->rb_right
;
92 int lowpan_nhc_check_compression(struct sk_buff
*skb
,
93 const struct ipv6hdr
*hdr
, u8
**hc_ptr
)
95 struct lowpan_nhc
*nhc
;
98 spin_lock_bh(&lowpan_nhc_lock
);
100 nhc
= lowpan_nexthdr_nhcs
[hdr
->nexthdr
];
101 if (!(nhc
&& nhc
->compress
))
104 spin_unlock_bh(&lowpan_nhc_lock
);
109 int lowpan_nhc_do_compression(struct sk_buff
*skb
, const struct ipv6hdr
*hdr
,
113 struct lowpan_nhc
*nhc
;
115 spin_lock_bh(&lowpan_nhc_lock
);
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.
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
128 if (unlikely(!nhc
|| !nhc
->compress
)) {
133 /* In the case of RAW sockets the transport header is not set by
134 * the ip6 stack so we must set it ourselves
136 if (skb
->transport_header
== skb
->network_header
)
137 skb_set_transport_header(skb
, sizeof(struct ipv6hdr
));
139 ret
= nhc
->compress(skb
, hc_ptr
);
143 /* skip the transport header */
144 skb_pull(skb
, nhc
->nexthdrlen
);
147 spin_unlock_bh(&lowpan_nhc_lock
);
152 int lowpan_nhc_do_uncompression(struct sk_buff
*skb
,
153 const struct net_device
*dev
,
156 struct lowpan_nhc
*nhc
;
159 spin_lock_bh(&lowpan_nhc_lock
);
161 nhc
= lowpan_nhc_by_nhcid(skb
);
163 if (nhc
->uncompress
) {
164 ret
= nhc
->uncompress(skb
, sizeof(struct ipv6hdr
) +
167 spin_unlock_bh(&lowpan_nhc_lock
);
171 spin_unlock_bh(&lowpan_nhc_lock
);
172 netdev_warn(dev
, "received nhc id for %s which is not implemented.\n",
177 spin_unlock_bh(&lowpan_nhc_lock
);
178 netdev_warn(dev
, "received unknown nhc id which was not found.\n");
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
);
187 spin_unlock_bh(&lowpan_nhc_lock
);
192 int lowpan_nhc_add(struct lowpan_nhc
*nhc
)
196 if (!nhc
->idlen
|| !nhc
->idsetup
)
199 WARN_ONCE(nhc
->idlen
> LOWPAN_NHC_MAX_ID_LEN
,
200 "LOWPAN_NHC_MAX_ID_LEN should be updated to %zd.\n",
205 spin_lock_bh(&lowpan_nhc_lock
);
207 if (lowpan_nexthdr_nhcs
[nhc
->nexthdr
]) {
212 ret
= lowpan_nhc_insert(nhc
);
216 lowpan_nexthdr_nhcs
[nhc
->nexthdr
] = nhc
;
218 spin_unlock_bh(&lowpan_nhc_lock
);
221 EXPORT_SYMBOL(lowpan_nhc_add
);
223 void lowpan_nhc_del(struct lowpan_nhc
*nhc
)
225 spin_lock_bh(&lowpan_nhc_lock
);
227 lowpan_nhc_remove(nhc
);
228 lowpan_nexthdr_nhcs
[nhc
->nexthdr
] = NULL
;
230 spin_unlock_bh(&lowpan_nhc_lock
);
234 EXPORT_SYMBOL(lowpan_nhc_del
);