2 * 6LoWPAN next header compression
6 * Alexander Aring <aar@pengutronix.de>
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version
11 * 2 of the License, or (at your option) any later version.
14 #include <linux/netdevice.h>
20 static struct rb_root rb_root
= RB_ROOT
;
21 static struct lowpan_nhc
*lowpan_nexthdr_nhcs
[NEXTHDR_MAX
];
22 static DEFINE_SPINLOCK(lowpan_nhc_lock
);
24 static int lowpan_nhc_insert(struct lowpan_nhc
*nhc
)
26 struct rb_node
**new = &rb_root
.rb_node
, *parent
= NULL
;
28 /* Figure out where to put new node */
30 struct lowpan_nhc
*this = container_of(*new, struct lowpan_nhc
,
32 int result
, len_dif
, len
;
34 len_dif
= nhc
->idlen
- this->idlen
;
36 if (nhc
->idlen
< this->idlen
)
41 result
= memcmp(nhc
->id
, this->id
, len
);
47 new = &((*new)->rb_left
);
49 new = &((*new)->rb_right
);
54 /* Add new node and rebalance tree. */
55 rb_link_node(&nhc
->node
, parent
, new);
56 rb_insert_color(&nhc
->node
, &rb_root
);
61 static void lowpan_nhc_remove(struct lowpan_nhc
*nhc
)
63 rb_erase(&nhc
->node
, &rb_root
);
66 static struct lowpan_nhc
*lowpan_nhc_by_nhcid(const struct sk_buff
*skb
)
68 struct rb_node
*node
= rb_root
.rb_node
;
69 const u8
*nhcid_skb_ptr
= skb
->data
;
72 struct lowpan_nhc
*nhc
= container_of(node
, struct lowpan_nhc
,
74 u8 nhcid_skb_ptr_masked
[LOWPAN_NHC_MAX_ID_LEN
];
77 if (nhcid_skb_ptr
+ nhc
->idlen
> skb
->data
+ skb
->len
)
80 /* copy and mask afterwards the nhid value from skb */
81 memcpy(nhcid_skb_ptr_masked
, nhcid_skb_ptr
, nhc
->idlen
);
82 for (i
= 0; i
< nhc
->idlen
; i
++)
83 nhcid_skb_ptr_masked
[i
] &= nhc
->idmask
[i
];
85 result
= memcmp(nhcid_skb_ptr_masked
, nhc
->id
, nhc
->idlen
);
89 node
= node
->rb_right
;
97 int lowpan_nhc_check_compression(struct sk_buff
*skb
,
98 const struct ipv6hdr
*hdr
, u8
**hc_ptr
)
100 struct lowpan_nhc
*nhc
;
103 spin_lock_bh(&lowpan_nhc_lock
);
105 nhc
= lowpan_nexthdr_nhcs
[hdr
->nexthdr
];
106 if (!(nhc
&& nhc
->compress
))
109 spin_unlock_bh(&lowpan_nhc_lock
);
114 int lowpan_nhc_do_compression(struct sk_buff
*skb
, const struct ipv6hdr
*hdr
,
118 struct lowpan_nhc
*nhc
;
120 spin_lock_bh(&lowpan_nhc_lock
);
122 nhc
= lowpan_nexthdr_nhcs
[hdr
->nexthdr
];
123 /* check if the nhc module was removed in unlocked part.
124 * TODO: this is a workaround we should prevent unloading
125 * of nhc modules while unlocked part, this will always drop
126 * the lowpan packet but it's very unlikely.
128 * Solution isn't easy because we need to decide at
129 * lowpan_nhc_check_compression if we do a compression or not.
130 * Because the inline data which is added to skb, we can't move this
133 if (unlikely(!nhc
|| !nhc
->compress
)) {
138 /* In the case of RAW sockets the transport header is not set by
139 * the ip6 stack so we must set it ourselves
141 if (skb
->transport_header
== skb
->network_header
)
142 skb_set_transport_header(skb
, sizeof(struct ipv6hdr
));
144 ret
= nhc
->compress(skb
, hc_ptr
);
148 /* skip the transport header */
149 skb_pull(skb
, nhc
->nexthdrlen
);
152 spin_unlock_bh(&lowpan_nhc_lock
);
157 int lowpan_nhc_do_uncompression(struct sk_buff
*skb
,
158 const struct net_device
*dev
,
161 struct lowpan_nhc
*nhc
;
164 spin_lock_bh(&lowpan_nhc_lock
);
166 nhc
= lowpan_nhc_by_nhcid(skb
);
168 if (nhc
->uncompress
) {
169 ret
= nhc
->uncompress(skb
, sizeof(struct ipv6hdr
) +
172 spin_unlock_bh(&lowpan_nhc_lock
);
176 spin_unlock_bh(&lowpan_nhc_lock
);
177 netdev_warn(dev
, "received nhc id for %s which is not implemented.\n",
182 spin_unlock_bh(&lowpan_nhc_lock
);
183 netdev_warn(dev
, "received unknown nhc id which was not found.\n");
187 hdr
->nexthdr
= nhc
->nexthdr
;
188 skb_reset_transport_header(skb
);
189 raw_dump_table(__func__
, "raw transport header dump",
190 skb_transport_header(skb
), nhc
->nexthdrlen
);
192 spin_unlock_bh(&lowpan_nhc_lock
);
197 int lowpan_nhc_add(struct lowpan_nhc
*nhc
)
201 if (!nhc
->idlen
|| !nhc
->idsetup
)
204 WARN_ONCE(nhc
->idlen
> LOWPAN_NHC_MAX_ID_LEN
,
205 "LOWPAN_NHC_MAX_ID_LEN should be updated to %zd.\n",
210 spin_lock_bh(&lowpan_nhc_lock
);
212 if (lowpan_nexthdr_nhcs
[nhc
->nexthdr
]) {
217 ret
= lowpan_nhc_insert(nhc
);
221 lowpan_nexthdr_nhcs
[nhc
->nexthdr
] = nhc
;
223 spin_unlock_bh(&lowpan_nhc_lock
);
226 EXPORT_SYMBOL(lowpan_nhc_add
);
228 void lowpan_nhc_del(struct lowpan_nhc
*nhc
)
230 spin_lock_bh(&lowpan_nhc_lock
);
232 lowpan_nhc_remove(nhc
);
233 lowpan_nexthdr_nhcs
[nhc
->nexthdr
] = NULL
;
235 spin_unlock_bh(&lowpan_nhc_lock
);
239 EXPORT_SYMBOL(lowpan_nhc_del
);