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
,
101 struct lowpan_nhc
*nhc
;
103 spin_lock_bh(&lowpan_nhc_lock
);
105 nhc
= lowpan_nexthdr_nhcs
[hdr
->nexthdr
];
106 if (nhc
&& nhc
->compress
)
107 *iphc0
|= LOWPAN_IPHC_NH_C
;
109 lowpan_push_hc_data(hc_ptr
, &hdr
->nexthdr
,
110 sizeof(hdr
->nexthdr
));
112 spin_unlock_bh(&lowpan_nhc_lock
);
117 int lowpan_nhc_do_compression(struct sk_buff
*skb
, const struct ipv6hdr
*hdr
,
121 struct lowpan_nhc
*nhc
;
123 spin_lock_bh(&lowpan_nhc_lock
);
125 nhc
= lowpan_nexthdr_nhcs
[hdr
->nexthdr
];
126 /* check if the nhc module was removed in unlocked part.
127 * TODO: this is a workaround we should prevent unloading
128 * of nhc modules while unlocked part, this will always drop
129 * the lowpan packet but it's very unlikely.
131 * Solution isn't easy because we need to decide at
132 * lowpan_nhc_check_compression if we do a compression or not.
133 * Because the inline data which is added to skb, we can't move this
136 if (unlikely(!nhc
|| !nhc
->compress
)) {
141 /* In the case of RAW sockets the transport header is not set by
142 * the ip6 stack so we must set it ourselves
144 if (skb
->transport_header
== skb
->network_header
)
145 skb_set_transport_header(skb
, sizeof(struct ipv6hdr
));
147 ret
= nhc
->compress(skb
, hc_ptr
);
151 /* skip the transport header */
152 skb_pull(skb
, nhc
->nexthdrlen
);
155 spin_unlock_bh(&lowpan_nhc_lock
);
160 int lowpan_nhc_do_uncompression(struct sk_buff
*skb
, struct net_device
*dev
,
163 struct lowpan_nhc
*nhc
;
166 spin_lock_bh(&lowpan_nhc_lock
);
168 nhc
= lowpan_nhc_by_nhcid(skb
);
170 if (nhc
->uncompress
) {
171 ret
= nhc
->uncompress(skb
, sizeof(struct ipv6hdr
) +
174 spin_unlock_bh(&lowpan_nhc_lock
);
178 spin_unlock_bh(&lowpan_nhc_lock
);
179 netdev_warn(dev
, "received nhc id for %s which is not implemented.\n",
184 spin_unlock_bh(&lowpan_nhc_lock
);
185 netdev_warn(dev
, "received unknown nhc id which was not found.\n");
189 hdr
->nexthdr
= nhc
->nexthdr
;
190 skb_reset_transport_header(skb
);
191 raw_dump_table(__func__
, "raw transport header dump",
192 skb_transport_header(skb
), nhc
->nexthdrlen
);
194 spin_unlock_bh(&lowpan_nhc_lock
);
199 int lowpan_nhc_add(struct lowpan_nhc
*nhc
)
203 if (!nhc
->idlen
|| !nhc
->idsetup
)
206 WARN_ONCE(nhc
->idlen
> LOWPAN_NHC_MAX_ID_LEN
,
207 "LOWPAN_NHC_MAX_ID_LEN should be updated to %zd.\n",
212 spin_lock_bh(&lowpan_nhc_lock
);
214 if (lowpan_nexthdr_nhcs
[nhc
->nexthdr
]) {
219 ret
= lowpan_nhc_insert(nhc
);
223 lowpan_nexthdr_nhcs
[nhc
->nexthdr
] = nhc
;
225 spin_unlock_bh(&lowpan_nhc_lock
);
228 EXPORT_SYMBOL(lowpan_nhc_add
);
230 void lowpan_nhc_del(struct lowpan_nhc
*nhc
)
232 spin_lock_bh(&lowpan_nhc_lock
);
234 lowpan_nhc_remove(nhc
);
235 lowpan_nexthdr_nhcs
[nhc
->nexthdr
] = NULL
;
237 spin_unlock_bh(&lowpan_nhc_lock
);
241 EXPORT_SYMBOL(lowpan_nhc_del
);