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