2 * Copyright (C) 2014 Fraunhofer ITWM
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2
6 * as published by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
14 * Phoebe Buckheister <phoebe.buckheister@itwm.fraunhofer.de>
17 #include <linux/ieee802154.h>
19 #include <net/mac802154.h>
20 #include <net/ieee802154_netdev.h>
23 ieee802154_hdr_push_addr(u8
*buf
, const struct ieee802154_addr
*addr
,
28 if (addr
->mode
== IEEE802154_ADDR_NONE
)
32 memcpy(buf
+ pos
, &addr
->pan_id
, 2);
37 case IEEE802154_ADDR_SHORT
:
38 memcpy(buf
+ pos
, &addr
->short_addr
, 2);
42 case IEEE802154_ADDR_LONG
:
43 memcpy(buf
+ pos
, &addr
->extended_addr
, IEEE802154_ADDR_LEN
);
44 pos
+= IEEE802154_ADDR_LEN
;
55 ieee802154_hdr_push_sechdr(u8
*buf
, const struct ieee802154_sechdr
*hdr
)
60 memcpy(buf
+ 1, &hdr
->frame_counter
, 4);
62 switch (hdr
->key_id_mode
) {
63 case IEEE802154_SCF_KEY_IMPLICIT
:
66 case IEEE802154_SCF_KEY_INDEX
:
69 case IEEE802154_SCF_KEY_SHORT_INDEX
:
70 memcpy(buf
+ pos
, &hdr
->short_src
, 4);
74 case IEEE802154_SCF_KEY_HW_INDEX
:
75 memcpy(buf
+ pos
, &hdr
->extended_src
, IEEE802154_ADDR_LEN
);
76 pos
+= IEEE802154_ADDR_LEN
;
80 buf
[pos
++] = hdr
->key_id
;
86 ieee802154_hdr_push(struct sk_buff
*skb
, struct ieee802154_hdr
*hdr
)
88 u8 buf
[IEEE802154_MAX_HEADER_LEN
];
91 struct ieee802154_hdr_fc
*fc
= &hdr
->fc
;
93 buf
[pos
++] = hdr
->seq
;
95 fc
->dest_addr_mode
= hdr
->dest
.mode
;
97 rc
= ieee802154_hdr_push_addr(buf
+ pos
, &hdr
->dest
, false);
102 fc
->source_addr_mode
= hdr
->source
.mode
;
104 if (hdr
->source
.pan_id
== hdr
->dest
.pan_id
&&
105 hdr
->dest
.mode
!= IEEE802154_ADDR_NONE
)
106 fc
->intra_pan
= true;
108 rc
= ieee802154_hdr_push_addr(buf
+ pos
, &hdr
->source
, fc
->intra_pan
);
113 if (fc
->security_enabled
) {
116 rc
= ieee802154_hdr_push_sechdr(buf
+ pos
, &hdr
->sec
);
125 memcpy(skb_push(skb
, pos
), buf
, pos
);
129 EXPORT_SYMBOL_GPL(ieee802154_hdr_push
);
132 ieee802154_hdr_get_addr(const u8
*buf
, int mode
, bool omit_pan
,
133 struct ieee802154_addr
*addr
)
139 if (mode
== IEEE802154_ADDR_NONE
)
143 memcpy(&addr
->pan_id
, buf
+ pos
, 2);
147 if (mode
== IEEE802154_ADDR_SHORT
) {
148 memcpy(&addr
->short_addr
, buf
+ pos
, 2);
151 memcpy(&addr
->extended_addr
, buf
+ pos
, IEEE802154_ADDR_LEN
);
152 return pos
+ IEEE802154_ADDR_LEN
;
156 static int ieee802154_hdr_addr_len(int mode
, bool omit_pan
)
158 int pan_len
= omit_pan
? 0 : 2;
161 case IEEE802154_ADDR_NONE
: return 0;
162 case IEEE802154_ADDR_SHORT
: return 2 + pan_len
;
163 case IEEE802154_ADDR_LONG
: return IEEE802154_ADDR_LEN
+ pan_len
;
164 default: return -EINVAL
;
169 ieee802154_hdr_get_sechdr(const u8
*buf
, struct ieee802154_sechdr
*hdr
)
174 memcpy(&hdr
->frame_counter
, buf
+ 1, 4);
176 switch (hdr
->key_id_mode
) {
177 case IEEE802154_SCF_KEY_IMPLICIT
:
180 case IEEE802154_SCF_KEY_INDEX
:
183 case IEEE802154_SCF_KEY_SHORT_INDEX
:
184 memcpy(&hdr
->short_src
, buf
+ pos
, 4);
188 case IEEE802154_SCF_KEY_HW_INDEX
:
189 memcpy(&hdr
->extended_src
, buf
+ pos
, IEEE802154_ADDR_LEN
);
190 pos
+= IEEE802154_ADDR_LEN
;
194 hdr
->key_id
= buf
[pos
++];
199 static int ieee802154_sechdr_lengths
[4] = {
200 [IEEE802154_SCF_KEY_IMPLICIT
] = 5,
201 [IEEE802154_SCF_KEY_INDEX
] = 6,
202 [IEEE802154_SCF_KEY_SHORT_INDEX
] = 10,
203 [IEEE802154_SCF_KEY_HW_INDEX
] = 14,
206 static int ieee802154_hdr_sechdr_len(u8 sc
)
208 return ieee802154_sechdr_lengths
[IEEE802154_SCF_KEY_ID_MODE(sc
)];
211 static int ieee802154_hdr_minlen(const struct ieee802154_hdr
*hdr
)
215 dlen
= ieee802154_hdr_addr_len(hdr
->fc
.dest_addr_mode
, false);
216 slen
= ieee802154_hdr_addr_len(hdr
->fc
.source_addr_mode
,
219 if (slen
< 0 || dlen
< 0)
222 return 3 + dlen
+ slen
+ hdr
->fc
.security_enabled
;
226 ieee802154_hdr_get_addrs(const u8
*buf
, struct ieee802154_hdr
*hdr
)
230 pos
+= ieee802154_hdr_get_addr(buf
+ pos
, hdr
->fc
.dest_addr_mode
,
232 pos
+= ieee802154_hdr_get_addr(buf
+ pos
, hdr
->fc
.source_addr_mode
,
233 hdr
->fc
.intra_pan
, &hdr
->source
);
235 if (hdr
->fc
.intra_pan
)
236 hdr
->source
.pan_id
= hdr
->dest
.pan_id
;
242 ieee802154_hdr_pull(struct sk_buff
*skb
, struct ieee802154_hdr
*hdr
)
246 if (!pskb_may_pull(skb
, 3))
249 memcpy(hdr
, skb
->data
, 3);
251 rc
= ieee802154_hdr_minlen(hdr
);
252 if (rc
< 0 || !pskb_may_pull(skb
, rc
))
255 pos
+= ieee802154_hdr_get_addrs(skb
->data
+ pos
, hdr
);
257 if (hdr
->fc
.security_enabled
) {
258 int want
= pos
+ ieee802154_hdr_sechdr_len(skb
->data
[pos
]);
260 if (!pskb_may_pull(skb
, want
))
263 pos
+= ieee802154_hdr_get_sechdr(skb
->data
+ pos
, &hdr
->sec
);
269 EXPORT_SYMBOL_GPL(ieee802154_hdr_pull
);
272 ieee802154_hdr_peek_addrs(const struct sk_buff
*skb
, struct ieee802154_hdr
*hdr
)
274 const u8
*buf
= skb_mac_header(skb
);
277 if (buf
+ 3 > skb_tail_pointer(skb
))
282 rc
= ieee802154_hdr_minlen(hdr
);
283 if (rc
< 0 || buf
+ rc
> skb_tail_pointer(skb
))
286 pos
+= ieee802154_hdr_get_addrs(buf
+ pos
, hdr
);
289 EXPORT_SYMBOL_GPL(ieee802154_hdr_peek_addrs
);
292 ieee802154_hdr_peek(const struct sk_buff
*skb
, struct ieee802154_hdr
*hdr
)
294 const u8
*buf
= skb_mac_header(skb
);
297 pos
= ieee802154_hdr_peek_addrs(skb
, hdr
);
301 if (hdr
->fc
.security_enabled
) {
302 u8 key_id_mode
= IEEE802154_SCF_KEY_ID_MODE(*(buf
+ pos
));
303 int want
= pos
+ ieee802154_sechdr_lengths
[key_id_mode
];
305 if (buf
+ want
> skb_tail_pointer(skb
))
308 pos
+= ieee802154_hdr_get_sechdr(buf
+ pos
, &hdr
->sec
);
313 EXPORT_SYMBOL_GPL(ieee802154_hdr_peek
);
315 int ieee802154_max_payload(const struct ieee802154_hdr
*hdr
)
317 int hlen
= ieee802154_hdr_minlen(hdr
);
319 if (hdr
->fc
.security_enabled
) {
320 hlen
+= ieee802154_sechdr_lengths
[hdr
->sec
.key_id_mode
] - 1;
321 hlen
+= ieee802154_sechdr_authtag_len(&hdr
->sec
);
324 return IEEE802154_MTU
- hlen
- IEEE802154_MFR_SIZE
;
326 EXPORT_SYMBOL_GPL(ieee802154_max_payload
);