18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci#include <net/6lowpan.h> 68c2ecf20Sopenharmony_ci#include <net/mac802154.h> 78c2ecf20Sopenharmony_ci#include <net/ieee802154_netdev.h> 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include "6lowpan_i.h" 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#define LOWPAN_DISPATCH_FIRST 0xc0 128c2ecf20Sopenharmony_ci#define LOWPAN_DISPATCH_FRAG_MASK 0xf8 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#define LOWPAN_DISPATCH_NALP 0x00 158c2ecf20Sopenharmony_ci#define LOWPAN_DISPATCH_ESC 0x40 168c2ecf20Sopenharmony_ci#define LOWPAN_DISPATCH_HC1 0x42 178c2ecf20Sopenharmony_ci#define LOWPAN_DISPATCH_DFF 0x43 188c2ecf20Sopenharmony_ci#define LOWPAN_DISPATCH_BC0 0x50 198c2ecf20Sopenharmony_ci#define LOWPAN_DISPATCH_MESH 0x80 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic int lowpan_give_skb_to_device(struct sk_buff *skb) 228c2ecf20Sopenharmony_ci{ 238c2ecf20Sopenharmony_ci skb->protocol = htons(ETH_P_IPV6); 248c2ecf20Sopenharmony_ci skb->dev->stats.rx_packets++; 258c2ecf20Sopenharmony_ci skb->dev->stats.rx_bytes += skb->len; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci return netif_rx(skb); 288c2ecf20Sopenharmony_ci} 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic int lowpan_rx_handlers_result(struct sk_buff *skb, lowpan_rx_result res) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci switch (res) { 338c2ecf20Sopenharmony_ci case RX_CONTINUE: 348c2ecf20Sopenharmony_ci /* nobody cared about this packet */ 358c2ecf20Sopenharmony_ci net_warn_ratelimited("%s: received unknown dispatch\n", 368c2ecf20Sopenharmony_ci __func__); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci fallthrough; 398c2ecf20Sopenharmony_ci case RX_DROP_UNUSABLE: 408c2ecf20Sopenharmony_ci kfree_skb(skb); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci fallthrough; 438c2ecf20Sopenharmony_ci case RX_DROP: 448c2ecf20Sopenharmony_ci return NET_RX_DROP; 458c2ecf20Sopenharmony_ci case RX_QUEUED: 468c2ecf20Sopenharmony_ci return lowpan_give_skb_to_device(skb); 478c2ecf20Sopenharmony_ci default: 488c2ecf20Sopenharmony_ci break; 498c2ecf20Sopenharmony_ci } 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci return NET_RX_DROP; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic inline bool lowpan_is_frag1(u8 dispatch) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci return (dispatch & LOWPAN_DISPATCH_FRAG_MASK) == LOWPAN_DISPATCH_FRAG1; 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic inline bool lowpan_is_fragn(u8 dispatch) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci return (dispatch & LOWPAN_DISPATCH_FRAG_MASK) == LOWPAN_DISPATCH_FRAGN; 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic lowpan_rx_result lowpan_rx_h_frag(struct sk_buff *skb) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci int ret; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if (!(lowpan_is_frag1(*skb_network_header(skb)) || 698c2ecf20Sopenharmony_ci lowpan_is_fragn(*skb_network_header(skb)))) 708c2ecf20Sopenharmony_ci return RX_CONTINUE; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci ret = lowpan_frag_rcv(skb, *skb_network_header(skb) & 738c2ecf20Sopenharmony_ci LOWPAN_DISPATCH_FRAG_MASK); 748c2ecf20Sopenharmony_ci if (ret == 1) 758c2ecf20Sopenharmony_ci return RX_QUEUED; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci /* Packet is freed by lowpan_frag_rcv on error or put into the frag 788c2ecf20Sopenharmony_ci * bucket. 798c2ecf20Sopenharmony_ci */ 808c2ecf20Sopenharmony_ci return RX_DROP; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ciint lowpan_iphc_decompress(struct sk_buff *skb) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct ieee802154_hdr hdr; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0) 888c2ecf20Sopenharmony_ci return -EINVAL; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci return lowpan_header_decompress(skb, skb->dev, &hdr.dest, &hdr.source); 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic lowpan_rx_result lowpan_rx_h_iphc(struct sk_buff *skb) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci int ret; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci if (!lowpan_is_iphc(*skb_network_header(skb))) 988c2ecf20Sopenharmony_ci return RX_CONTINUE; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* Setting datagram_offset to zero indicates non frag handling 1018c2ecf20Sopenharmony_ci * while doing lowpan_header_decompress. 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_ci lowpan_802154_cb(skb)->d_size = 0; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci ret = lowpan_iphc_decompress(skb); 1068c2ecf20Sopenharmony_ci if (ret < 0) 1078c2ecf20Sopenharmony_ci return RX_DROP_UNUSABLE; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci return RX_QUEUED; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cilowpan_rx_result lowpan_rx_h_ipv6(struct sk_buff *skb) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci if (!lowpan_is_ipv6(*skb_network_header(skb))) 1158c2ecf20Sopenharmony_ci return RX_CONTINUE; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci /* Pull off the 1-byte of 6lowpan header. */ 1188c2ecf20Sopenharmony_ci skb_pull(skb, 1); 1198c2ecf20Sopenharmony_ci return RX_QUEUED; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic inline bool lowpan_is_esc(u8 dispatch) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci return dispatch == LOWPAN_DISPATCH_ESC; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic lowpan_rx_result lowpan_rx_h_esc(struct sk_buff *skb) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci if (!lowpan_is_esc(*skb_network_header(skb))) 1308c2ecf20Sopenharmony_ci return RX_CONTINUE; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci net_warn_ratelimited("%s: %s\n", skb->dev->name, 1338c2ecf20Sopenharmony_ci "6LoWPAN ESC not supported\n"); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci return RX_DROP_UNUSABLE; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic inline bool lowpan_is_hc1(u8 dispatch) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci return dispatch == LOWPAN_DISPATCH_HC1; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic lowpan_rx_result lowpan_rx_h_hc1(struct sk_buff *skb) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci if (!lowpan_is_hc1(*skb_network_header(skb))) 1468c2ecf20Sopenharmony_ci return RX_CONTINUE; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci net_warn_ratelimited("%s: %s\n", skb->dev->name, 1498c2ecf20Sopenharmony_ci "6LoWPAN HC1 not supported\n"); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci return RX_DROP_UNUSABLE; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic inline bool lowpan_is_dff(u8 dispatch) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci return dispatch == LOWPAN_DISPATCH_DFF; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic lowpan_rx_result lowpan_rx_h_dff(struct sk_buff *skb) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci if (!lowpan_is_dff(*skb_network_header(skb))) 1628c2ecf20Sopenharmony_ci return RX_CONTINUE; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci net_warn_ratelimited("%s: %s\n", skb->dev->name, 1658c2ecf20Sopenharmony_ci "6LoWPAN DFF not supported\n"); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci return RX_DROP_UNUSABLE; 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic inline bool lowpan_is_bc0(u8 dispatch) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci return dispatch == LOWPAN_DISPATCH_BC0; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic lowpan_rx_result lowpan_rx_h_bc0(struct sk_buff *skb) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci if (!lowpan_is_bc0(*skb_network_header(skb))) 1788c2ecf20Sopenharmony_ci return RX_CONTINUE; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci net_warn_ratelimited("%s: %s\n", skb->dev->name, 1818c2ecf20Sopenharmony_ci "6LoWPAN BC0 not supported\n"); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci return RX_DROP_UNUSABLE; 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic inline bool lowpan_is_mesh(u8 dispatch) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci return (dispatch & LOWPAN_DISPATCH_FIRST) == LOWPAN_DISPATCH_MESH; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic lowpan_rx_result lowpan_rx_h_mesh(struct sk_buff *skb) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci if (!lowpan_is_mesh(*skb_network_header(skb))) 1948c2ecf20Sopenharmony_ci return RX_CONTINUE; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci net_warn_ratelimited("%s: %s\n", skb->dev->name, 1978c2ecf20Sopenharmony_ci "6LoWPAN MESH not supported\n"); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci return RX_DROP_UNUSABLE; 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic int lowpan_invoke_rx_handlers(struct sk_buff *skb) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci lowpan_rx_result res; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci#define CALL_RXH(rxh) \ 2078c2ecf20Sopenharmony_ci do { \ 2088c2ecf20Sopenharmony_ci res = rxh(skb); \ 2098c2ecf20Sopenharmony_ci if (res != RX_CONTINUE) \ 2108c2ecf20Sopenharmony_ci goto rxh_next; \ 2118c2ecf20Sopenharmony_ci } while (0) 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /* likely at first */ 2148c2ecf20Sopenharmony_ci CALL_RXH(lowpan_rx_h_iphc); 2158c2ecf20Sopenharmony_ci CALL_RXH(lowpan_rx_h_frag); 2168c2ecf20Sopenharmony_ci CALL_RXH(lowpan_rx_h_ipv6); 2178c2ecf20Sopenharmony_ci CALL_RXH(lowpan_rx_h_esc); 2188c2ecf20Sopenharmony_ci CALL_RXH(lowpan_rx_h_hc1); 2198c2ecf20Sopenharmony_ci CALL_RXH(lowpan_rx_h_dff); 2208c2ecf20Sopenharmony_ci CALL_RXH(lowpan_rx_h_bc0); 2218c2ecf20Sopenharmony_ci CALL_RXH(lowpan_rx_h_mesh); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cirxh_next: 2248c2ecf20Sopenharmony_ci return lowpan_rx_handlers_result(skb, res); 2258c2ecf20Sopenharmony_ci#undef CALL_RXH 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic inline bool lowpan_is_nalp(u8 dispatch) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci return (dispatch & LOWPAN_DISPATCH_FIRST) == LOWPAN_DISPATCH_NALP; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci/* Lookup for reserved dispatch values at: 2348c2ecf20Sopenharmony_ci * https://www.iana.org/assignments/_6lowpan-parameters/_6lowpan-parameters.xhtml#_6lowpan-parameters-1 2358c2ecf20Sopenharmony_ci * 2368c2ecf20Sopenharmony_ci * Last Updated: 2015-01-22 2378c2ecf20Sopenharmony_ci */ 2388c2ecf20Sopenharmony_cistatic inline bool lowpan_is_reserved(u8 dispatch) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci return ((dispatch >= 0x44 && dispatch <= 0x4F) || 2418c2ecf20Sopenharmony_ci (dispatch >= 0x51 && dispatch <= 0x5F) || 2428c2ecf20Sopenharmony_ci (dispatch >= 0xc8 && dispatch <= 0xdf) || 2438c2ecf20Sopenharmony_ci dispatch >= 0xe8); 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci/* lowpan_rx_h_check checks on generic 6LoWPAN requirements 2478c2ecf20Sopenharmony_ci * in MAC and 6LoWPAN header. 2488c2ecf20Sopenharmony_ci * 2498c2ecf20Sopenharmony_ci * Don't manipulate the skb here, it could be shared buffer. 2508c2ecf20Sopenharmony_ci */ 2518c2ecf20Sopenharmony_cistatic inline bool lowpan_rx_h_check(struct sk_buff *skb) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci __le16 fc = ieee802154_get_fc_from_skb(skb); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* check on ieee802154 conform 6LoWPAN header */ 2568c2ecf20Sopenharmony_ci if (!ieee802154_is_data(fc) || 2578c2ecf20Sopenharmony_ci !ieee802154_skb_is_intra_pan_addressing(fc, skb)) 2588c2ecf20Sopenharmony_ci return false; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* check if we can dereference the dispatch */ 2618c2ecf20Sopenharmony_ci if (unlikely(!skb->len)) 2628c2ecf20Sopenharmony_ci return false; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci if (lowpan_is_nalp(*skb_network_header(skb)) || 2658c2ecf20Sopenharmony_ci lowpan_is_reserved(*skb_network_header(skb))) 2668c2ecf20Sopenharmony_ci return false; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci return true; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic int lowpan_rcv(struct sk_buff *skb, struct net_device *wdev, 2728c2ecf20Sopenharmony_ci struct packet_type *pt, struct net_device *orig_wdev) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci struct net_device *ldev; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci if (wdev->type != ARPHRD_IEEE802154 || 2778c2ecf20Sopenharmony_ci skb->pkt_type == PACKET_OTHERHOST || 2788c2ecf20Sopenharmony_ci !lowpan_rx_h_check(skb)) 2798c2ecf20Sopenharmony_ci goto drop; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci ldev = wdev->ieee802154_ptr->lowpan_dev; 2828c2ecf20Sopenharmony_ci if (!ldev || !netif_running(ldev)) 2838c2ecf20Sopenharmony_ci goto drop; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* Replacing skb->dev and followed rx handlers will manipulate skb. */ 2868c2ecf20Sopenharmony_ci skb = skb_share_check(skb, GFP_ATOMIC); 2878c2ecf20Sopenharmony_ci if (!skb) 2888c2ecf20Sopenharmony_ci goto out; 2898c2ecf20Sopenharmony_ci skb->dev = ldev; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci /* When receive frag1 it's likely that we manipulate the buffer. 2928c2ecf20Sopenharmony_ci * When recevie iphc we manipulate the data buffer. So we need 2938c2ecf20Sopenharmony_ci * to unshare the buffer. 2948c2ecf20Sopenharmony_ci */ 2958c2ecf20Sopenharmony_ci if (lowpan_is_frag1(*skb_network_header(skb)) || 2968c2ecf20Sopenharmony_ci lowpan_is_iphc(*skb_network_header(skb))) { 2978c2ecf20Sopenharmony_ci skb = skb_unshare(skb, GFP_ATOMIC); 2988c2ecf20Sopenharmony_ci if (!skb) 2998c2ecf20Sopenharmony_ci goto out; 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci return lowpan_invoke_rx_handlers(skb); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cidrop: 3058c2ecf20Sopenharmony_ci kfree_skb(skb); 3068c2ecf20Sopenharmony_ciout: 3078c2ecf20Sopenharmony_ci return NET_RX_DROP; 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic struct packet_type lowpan_packet_type = { 3118c2ecf20Sopenharmony_ci .type = htons(ETH_P_IEEE802154), 3128c2ecf20Sopenharmony_ci .func = lowpan_rcv, 3138c2ecf20Sopenharmony_ci}; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_civoid lowpan_rx_init(void) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci dev_add_pack(&lowpan_packet_type); 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_civoid lowpan_rx_exit(void) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci dev_remove_pack(&lowpan_packet_type); 3238c2ecf20Sopenharmony_ci} 324