18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2014 Fraunhofer ITWM 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Written by: 68c2ecf20Sopenharmony_ci * Phoebe Buckheister <phoebe.buckheister@itwm.fraunhofer.de> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/ieee802154.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <net/mac802154.h> 128c2ecf20Sopenharmony_ci#include <net/ieee802154_netdev.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_cistatic int 158c2ecf20Sopenharmony_ciieee802154_hdr_push_addr(u8 *buf, const struct ieee802154_addr *addr, 168c2ecf20Sopenharmony_ci bool omit_pan) 178c2ecf20Sopenharmony_ci{ 188c2ecf20Sopenharmony_ci int pos = 0; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci if (addr->mode == IEEE802154_ADDR_NONE) 218c2ecf20Sopenharmony_ci return 0; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci if (!omit_pan) { 248c2ecf20Sopenharmony_ci memcpy(buf + pos, &addr->pan_id, 2); 258c2ecf20Sopenharmony_ci pos += 2; 268c2ecf20Sopenharmony_ci } 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci switch (addr->mode) { 298c2ecf20Sopenharmony_ci case IEEE802154_ADDR_SHORT: 308c2ecf20Sopenharmony_ci memcpy(buf + pos, &addr->short_addr, 2); 318c2ecf20Sopenharmony_ci pos += 2; 328c2ecf20Sopenharmony_ci break; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci case IEEE802154_ADDR_LONG: 358c2ecf20Sopenharmony_ci memcpy(buf + pos, &addr->extended_addr, IEEE802154_ADDR_LEN); 368c2ecf20Sopenharmony_ci pos += IEEE802154_ADDR_LEN; 378c2ecf20Sopenharmony_ci break; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci default: 408c2ecf20Sopenharmony_ci return -EINVAL; 418c2ecf20Sopenharmony_ci } 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci return pos; 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic int 478c2ecf20Sopenharmony_ciieee802154_hdr_push_sechdr(u8 *buf, const struct ieee802154_sechdr *hdr) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci int pos = 5; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci memcpy(buf, hdr, 1); 528c2ecf20Sopenharmony_ci memcpy(buf + 1, &hdr->frame_counter, 4); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci switch (hdr->key_id_mode) { 558c2ecf20Sopenharmony_ci case IEEE802154_SCF_KEY_IMPLICIT: 568c2ecf20Sopenharmony_ci return pos; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci case IEEE802154_SCF_KEY_INDEX: 598c2ecf20Sopenharmony_ci break; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci case IEEE802154_SCF_KEY_SHORT_INDEX: 628c2ecf20Sopenharmony_ci memcpy(buf + pos, &hdr->short_src, 4); 638c2ecf20Sopenharmony_ci pos += 4; 648c2ecf20Sopenharmony_ci break; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci case IEEE802154_SCF_KEY_HW_INDEX: 678c2ecf20Sopenharmony_ci memcpy(buf + pos, &hdr->extended_src, IEEE802154_ADDR_LEN); 688c2ecf20Sopenharmony_ci pos += IEEE802154_ADDR_LEN; 698c2ecf20Sopenharmony_ci break; 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci buf[pos++] = hdr->key_id; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci return pos; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ciint 788c2ecf20Sopenharmony_ciieee802154_hdr_push(struct sk_buff *skb, struct ieee802154_hdr *hdr) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci u8 buf[IEEE802154_MAX_HEADER_LEN]; 818c2ecf20Sopenharmony_ci int pos = 2; 828c2ecf20Sopenharmony_ci int rc; 838c2ecf20Sopenharmony_ci struct ieee802154_hdr_fc *fc = &hdr->fc; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci buf[pos++] = hdr->seq; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci fc->dest_addr_mode = hdr->dest.mode; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci rc = ieee802154_hdr_push_addr(buf + pos, &hdr->dest, false); 908c2ecf20Sopenharmony_ci if (rc < 0) 918c2ecf20Sopenharmony_ci return -EINVAL; 928c2ecf20Sopenharmony_ci pos += rc; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci fc->source_addr_mode = hdr->source.mode; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (hdr->source.pan_id == hdr->dest.pan_id && 978c2ecf20Sopenharmony_ci hdr->dest.mode != IEEE802154_ADDR_NONE) 988c2ecf20Sopenharmony_ci fc->intra_pan = true; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci rc = ieee802154_hdr_push_addr(buf + pos, &hdr->source, fc->intra_pan); 1018c2ecf20Sopenharmony_ci if (rc < 0) 1028c2ecf20Sopenharmony_ci return -EINVAL; 1038c2ecf20Sopenharmony_ci pos += rc; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (fc->security_enabled) { 1068c2ecf20Sopenharmony_ci fc->version = 1; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci rc = ieee802154_hdr_push_sechdr(buf + pos, &hdr->sec); 1098c2ecf20Sopenharmony_ci if (rc < 0) 1108c2ecf20Sopenharmony_ci return -EINVAL; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci pos += rc; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci memcpy(buf, fc, 2); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci memcpy(skb_push(skb, pos), buf, pos); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci return pos; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ieee802154_hdr_push); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic int 1248c2ecf20Sopenharmony_ciieee802154_hdr_get_addr(const u8 *buf, int mode, bool omit_pan, 1258c2ecf20Sopenharmony_ci struct ieee802154_addr *addr) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci int pos = 0; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci addr->mode = mode; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (mode == IEEE802154_ADDR_NONE) 1328c2ecf20Sopenharmony_ci return 0; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (!omit_pan) { 1358c2ecf20Sopenharmony_ci memcpy(&addr->pan_id, buf + pos, 2); 1368c2ecf20Sopenharmony_ci pos += 2; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (mode == IEEE802154_ADDR_SHORT) { 1408c2ecf20Sopenharmony_ci memcpy(&addr->short_addr, buf + pos, 2); 1418c2ecf20Sopenharmony_ci return pos + 2; 1428c2ecf20Sopenharmony_ci } else { 1438c2ecf20Sopenharmony_ci memcpy(&addr->extended_addr, buf + pos, IEEE802154_ADDR_LEN); 1448c2ecf20Sopenharmony_ci return pos + IEEE802154_ADDR_LEN; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic int ieee802154_hdr_addr_len(int mode, bool omit_pan) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci int pan_len = omit_pan ? 0 : 2; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci switch (mode) { 1538c2ecf20Sopenharmony_ci case IEEE802154_ADDR_NONE: return 0; 1548c2ecf20Sopenharmony_ci case IEEE802154_ADDR_SHORT: return 2 + pan_len; 1558c2ecf20Sopenharmony_ci case IEEE802154_ADDR_LONG: return IEEE802154_ADDR_LEN + pan_len; 1568c2ecf20Sopenharmony_ci default: return -EINVAL; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic int 1618c2ecf20Sopenharmony_ciieee802154_hdr_get_sechdr(const u8 *buf, struct ieee802154_sechdr *hdr) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci int pos = 5; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci memcpy(hdr, buf, 1); 1668c2ecf20Sopenharmony_ci memcpy(&hdr->frame_counter, buf + 1, 4); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci switch (hdr->key_id_mode) { 1698c2ecf20Sopenharmony_ci case IEEE802154_SCF_KEY_IMPLICIT: 1708c2ecf20Sopenharmony_ci return pos; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci case IEEE802154_SCF_KEY_INDEX: 1738c2ecf20Sopenharmony_ci break; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci case IEEE802154_SCF_KEY_SHORT_INDEX: 1768c2ecf20Sopenharmony_ci memcpy(&hdr->short_src, buf + pos, 4); 1778c2ecf20Sopenharmony_ci pos += 4; 1788c2ecf20Sopenharmony_ci break; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci case IEEE802154_SCF_KEY_HW_INDEX: 1818c2ecf20Sopenharmony_ci memcpy(&hdr->extended_src, buf + pos, IEEE802154_ADDR_LEN); 1828c2ecf20Sopenharmony_ci pos += IEEE802154_ADDR_LEN; 1838c2ecf20Sopenharmony_ci break; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci hdr->key_id = buf[pos++]; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci return pos; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic int ieee802154_sechdr_lengths[4] = { 1928c2ecf20Sopenharmony_ci [IEEE802154_SCF_KEY_IMPLICIT] = 5, 1938c2ecf20Sopenharmony_ci [IEEE802154_SCF_KEY_INDEX] = 6, 1948c2ecf20Sopenharmony_ci [IEEE802154_SCF_KEY_SHORT_INDEX] = 10, 1958c2ecf20Sopenharmony_ci [IEEE802154_SCF_KEY_HW_INDEX] = 14, 1968c2ecf20Sopenharmony_ci}; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic int ieee802154_hdr_sechdr_len(u8 sc) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci return ieee802154_sechdr_lengths[IEEE802154_SCF_KEY_ID_MODE(sc)]; 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic int ieee802154_hdr_minlen(const struct ieee802154_hdr *hdr) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci int dlen, slen; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci dlen = ieee802154_hdr_addr_len(hdr->fc.dest_addr_mode, false); 2088c2ecf20Sopenharmony_ci slen = ieee802154_hdr_addr_len(hdr->fc.source_addr_mode, 2098c2ecf20Sopenharmony_ci hdr->fc.intra_pan); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci if (slen < 0 || dlen < 0) 2128c2ecf20Sopenharmony_ci return -EINVAL; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci return 3 + dlen + slen + hdr->fc.security_enabled; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic int 2188c2ecf20Sopenharmony_ciieee802154_hdr_get_addrs(const u8 *buf, struct ieee802154_hdr *hdr) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci int pos = 0; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci pos += ieee802154_hdr_get_addr(buf + pos, hdr->fc.dest_addr_mode, 2238c2ecf20Sopenharmony_ci false, &hdr->dest); 2248c2ecf20Sopenharmony_ci pos += ieee802154_hdr_get_addr(buf + pos, hdr->fc.source_addr_mode, 2258c2ecf20Sopenharmony_ci hdr->fc.intra_pan, &hdr->source); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (hdr->fc.intra_pan) 2288c2ecf20Sopenharmony_ci hdr->source.pan_id = hdr->dest.pan_id; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci return pos; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ciint 2348c2ecf20Sopenharmony_ciieee802154_hdr_pull(struct sk_buff *skb, struct ieee802154_hdr *hdr) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci int pos = 3, rc; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, 3)) 2398c2ecf20Sopenharmony_ci return -EINVAL; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci memcpy(hdr, skb->data, 3); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci rc = ieee802154_hdr_minlen(hdr); 2448c2ecf20Sopenharmony_ci if (rc < 0 || !pskb_may_pull(skb, rc)) 2458c2ecf20Sopenharmony_ci return -EINVAL; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci pos += ieee802154_hdr_get_addrs(skb->data + pos, hdr); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (hdr->fc.security_enabled) { 2508c2ecf20Sopenharmony_ci int want = pos + ieee802154_hdr_sechdr_len(skb->data[pos]); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, want)) 2538c2ecf20Sopenharmony_ci return -EINVAL; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci pos += ieee802154_hdr_get_sechdr(skb->data + pos, &hdr->sec); 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci skb_pull(skb, pos); 2598c2ecf20Sopenharmony_ci return pos; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ieee802154_hdr_pull); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ciint 2648c2ecf20Sopenharmony_ciieee802154_hdr_peek_addrs(const struct sk_buff *skb, struct ieee802154_hdr *hdr) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci const u8 *buf = skb_mac_header(skb); 2678c2ecf20Sopenharmony_ci int pos = 3, rc; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (buf + 3 > skb_tail_pointer(skb)) 2708c2ecf20Sopenharmony_ci return -EINVAL; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci memcpy(hdr, buf, 3); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci rc = ieee802154_hdr_minlen(hdr); 2758c2ecf20Sopenharmony_ci if (rc < 0 || buf + rc > skb_tail_pointer(skb)) 2768c2ecf20Sopenharmony_ci return -EINVAL; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci pos += ieee802154_hdr_get_addrs(buf + pos, hdr); 2798c2ecf20Sopenharmony_ci return pos; 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ieee802154_hdr_peek_addrs); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ciint 2848c2ecf20Sopenharmony_ciieee802154_hdr_peek(const struct sk_buff *skb, struct ieee802154_hdr *hdr) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci const u8 *buf = skb_mac_header(skb); 2878c2ecf20Sopenharmony_ci int pos; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci pos = ieee802154_hdr_peek_addrs(skb, hdr); 2908c2ecf20Sopenharmony_ci if (pos < 0) 2918c2ecf20Sopenharmony_ci return -EINVAL; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (hdr->fc.security_enabled) { 2948c2ecf20Sopenharmony_ci u8 key_id_mode = IEEE802154_SCF_KEY_ID_MODE(*(buf + pos)); 2958c2ecf20Sopenharmony_ci int want = pos + ieee802154_sechdr_lengths[key_id_mode]; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (buf + want > skb_tail_pointer(skb)) 2988c2ecf20Sopenharmony_ci return -EINVAL; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci pos += ieee802154_hdr_get_sechdr(buf + pos, &hdr->sec); 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci return pos; 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ieee802154_hdr_peek); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ciint ieee802154_max_payload(const struct ieee802154_hdr *hdr) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci int hlen = ieee802154_hdr_minlen(hdr); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci if (hdr->fc.security_enabled) { 3128c2ecf20Sopenharmony_ci hlen += ieee802154_sechdr_lengths[hdr->sec.key_id_mode] - 1; 3138c2ecf20Sopenharmony_ci hlen += ieee802154_sechdr_authtag_len(&hdr->sec); 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci return IEEE802154_MTU - hlen - IEEE802154_MFR_SIZE; 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ieee802154_max_payload); 319