162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <linux/if_arp.h>
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include <net/6lowpan.h>
662306a36Sopenharmony_ci#include <net/mac802154.h>
762306a36Sopenharmony_ci#include <net/ieee802154_netdev.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "6lowpan_i.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#define LOWPAN_DISPATCH_FIRST		0xc0
1262306a36Sopenharmony_ci#define LOWPAN_DISPATCH_FRAG_MASK	0xf8
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define LOWPAN_DISPATCH_NALP		0x00
1562306a36Sopenharmony_ci#define LOWPAN_DISPATCH_ESC		0x40
1662306a36Sopenharmony_ci#define LOWPAN_DISPATCH_HC1		0x42
1762306a36Sopenharmony_ci#define LOWPAN_DISPATCH_DFF		0x43
1862306a36Sopenharmony_ci#define LOWPAN_DISPATCH_BC0		0x50
1962306a36Sopenharmony_ci#define LOWPAN_DISPATCH_MESH		0x80
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic int lowpan_give_skb_to_device(struct sk_buff *skb)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	skb->protocol = htons(ETH_P_IPV6);
2462306a36Sopenharmony_ci	skb->dev->stats.rx_packets++;
2562306a36Sopenharmony_ci	skb->dev->stats.rx_bytes += skb->len;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	return netif_rx(skb);
2862306a36Sopenharmony_ci}
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic int lowpan_rx_handlers_result(struct sk_buff *skb, lowpan_rx_result res)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	switch (res) {
3362306a36Sopenharmony_ci	case RX_CONTINUE:
3462306a36Sopenharmony_ci		/* nobody cared about this packet */
3562306a36Sopenharmony_ci		net_warn_ratelimited("%s: received unknown dispatch\n",
3662306a36Sopenharmony_ci				     __func__);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci		fallthrough;
3962306a36Sopenharmony_ci	case RX_DROP_UNUSABLE:
4062306a36Sopenharmony_ci		kfree_skb(skb);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci		fallthrough;
4362306a36Sopenharmony_ci	case RX_DROP:
4462306a36Sopenharmony_ci		return NET_RX_DROP;
4562306a36Sopenharmony_ci	case RX_QUEUED:
4662306a36Sopenharmony_ci		return lowpan_give_skb_to_device(skb);
4762306a36Sopenharmony_ci	default:
4862306a36Sopenharmony_ci		break;
4962306a36Sopenharmony_ci	}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	return NET_RX_DROP;
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic inline bool lowpan_is_frag1(u8 dispatch)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	return (dispatch & LOWPAN_DISPATCH_FRAG_MASK) == LOWPAN_DISPATCH_FRAG1;
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic inline bool lowpan_is_fragn(u8 dispatch)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	return (dispatch & LOWPAN_DISPATCH_FRAG_MASK) == LOWPAN_DISPATCH_FRAGN;
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic lowpan_rx_result lowpan_rx_h_frag(struct sk_buff *skb)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	int ret;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	if (!(lowpan_is_frag1(*skb_network_header(skb)) ||
6962306a36Sopenharmony_ci	      lowpan_is_fragn(*skb_network_header(skb))))
7062306a36Sopenharmony_ci		return RX_CONTINUE;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	ret = lowpan_frag_rcv(skb, *skb_network_header(skb) &
7362306a36Sopenharmony_ci			      LOWPAN_DISPATCH_FRAG_MASK);
7462306a36Sopenharmony_ci	if (ret == 1)
7562306a36Sopenharmony_ci		return RX_QUEUED;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	/* Packet is freed by lowpan_frag_rcv on error or put into the frag
7862306a36Sopenharmony_ci	 * bucket.
7962306a36Sopenharmony_ci	 */
8062306a36Sopenharmony_ci	return RX_DROP;
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ciint lowpan_iphc_decompress(struct sk_buff *skb)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	struct ieee802154_hdr hdr;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0)
8862306a36Sopenharmony_ci		return -EINVAL;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	return lowpan_header_decompress(skb, skb->dev, &hdr.dest, &hdr.source);
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic lowpan_rx_result lowpan_rx_h_iphc(struct sk_buff *skb)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	int ret;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	if (!lowpan_is_iphc(*skb_network_header(skb)))
9862306a36Sopenharmony_ci		return RX_CONTINUE;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	/* Setting datagram_offset to zero indicates non frag handling
10162306a36Sopenharmony_ci	 * while doing lowpan_header_decompress.
10262306a36Sopenharmony_ci	 */
10362306a36Sopenharmony_ci	lowpan_802154_cb(skb)->d_size = 0;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	ret = lowpan_iphc_decompress(skb);
10662306a36Sopenharmony_ci	if (ret < 0)
10762306a36Sopenharmony_ci		return RX_DROP_UNUSABLE;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	return RX_QUEUED;
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cilowpan_rx_result lowpan_rx_h_ipv6(struct sk_buff *skb)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	if (!lowpan_is_ipv6(*skb_network_header(skb)))
11562306a36Sopenharmony_ci		return RX_CONTINUE;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	/* Pull off the 1-byte of 6lowpan header. */
11862306a36Sopenharmony_ci	skb_pull(skb, 1);
11962306a36Sopenharmony_ci	return RX_QUEUED;
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic inline bool lowpan_is_esc(u8 dispatch)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	return dispatch == LOWPAN_DISPATCH_ESC;
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic lowpan_rx_result lowpan_rx_h_esc(struct sk_buff *skb)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	if (!lowpan_is_esc(*skb_network_header(skb)))
13062306a36Sopenharmony_ci		return RX_CONTINUE;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	net_warn_ratelimited("%s: %s\n", skb->dev->name,
13362306a36Sopenharmony_ci			     "6LoWPAN ESC not supported\n");
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	return RX_DROP_UNUSABLE;
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic inline bool lowpan_is_hc1(u8 dispatch)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	return dispatch == LOWPAN_DISPATCH_HC1;
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic lowpan_rx_result lowpan_rx_h_hc1(struct sk_buff *skb)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	if (!lowpan_is_hc1(*skb_network_header(skb)))
14662306a36Sopenharmony_ci		return RX_CONTINUE;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	net_warn_ratelimited("%s: %s\n", skb->dev->name,
14962306a36Sopenharmony_ci			     "6LoWPAN HC1 not supported\n");
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	return RX_DROP_UNUSABLE;
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic inline bool lowpan_is_dff(u8 dispatch)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	return dispatch == LOWPAN_DISPATCH_DFF;
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic lowpan_rx_result lowpan_rx_h_dff(struct sk_buff *skb)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	if (!lowpan_is_dff(*skb_network_header(skb)))
16262306a36Sopenharmony_ci		return RX_CONTINUE;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	net_warn_ratelimited("%s: %s\n", skb->dev->name,
16562306a36Sopenharmony_ci			     "6LoWPAN DFF not supported\n");
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	return RX_DROP_UNUSABLE;
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic inline bool lowpan_is_bc0(u8 dispatch)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	return dispatch == LOWPAN_DISPATCH_BC0;
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic lowpan_rx_result lowpan_rx_h_bc0(struct sk_buff *skb)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	if (!lowpan_is_bc0(*skb_network_header(skb)))
17862306a36Sopenharmony_ci		return RX_CONTINUE;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	net_warn_ratelimited("%s: %s\n", skb->dev->name,
18162306a36Sopenharmony_ci			     "6LoWPAN BC0 not supported\n");
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	return RX_DROP_UNUSABLE;
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic inline bool lowpan_is_mesh(u8 dispatch)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	return (dispatch & LOWPAN_DISPATCH_FIRST) == LOWPAN_DISPATCH_MESH;
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic lowpan_rx_result lowpan_rx_h_mesh(struct sk_buff *skb)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	if (!lowpan_is_mesh(*skb_network_header(skb)))
19462306a36Sopenharmony_ci		return RX_CONTINUE;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	net_warn_ratelimited("%s: %s\n", skb->dev->name,
19762306a36Sopenharmony_ci			     "6LoWPAN MESH not supported\n");
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	return RX_DROP_UNUSABLE;
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic int lowpan_invoke_rx_handlers(struct sk_buff *skb)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	lowpan_rx_result res;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci#define CALL_RXH(rxh)			\
20762306a36Sopenharmony_ci	do {				\
20862306a36Sopenharmony_ci		res = rxh(skb);	\
20962306a36Sopenharmony_ci		if (res != RX_CONTINUE)	\
21062306a36Sopenharmony_ci			goto rxh_next;	\
21162306a36Sopenharmony_ci	} while (0)
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	/* likely at first */
21462306a36Sopenharmony_ci	CALL_RXH(lowpan_rx_h_iphc);
21562306a36Sopenharmony_ci	CALL_RXH(lowpan_rx_h_frag);
21662306a36Sopenharmony_ci	CALL_RXH(lowpan_rx_h_ipv6);
21762306a36Sopenharmony_ci	CALL_RXH(lowpan_rx_h_esc);
21862306a36Sopenharmony_ci	CALL_RXH(lowpan_rx_h_hc1);
21962306a36Sopenharmony_ci	CALL_RXH(lowpan_rx_h_dff);
22062306a36Sopenharmony_ci	CALL_RXH(lowpan_rx_h_bc0);
22162306a36Sopenharmony_ci	CALL_RXH(lowpan_rx_h_mesh);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cirxh_next:
22462306a36Sopenharmony_ci	return lowpan_rx_handlers_result(skb, res);
22562306a36Sopenharmony_ci#undef CALL_RXH
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic inline bool lowpan_is_nalp(u8 dispatch)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	return (dispatch & LOWPAN_DISPATCH_FIRST) == LOWPAN_DISPATCH_NALP;
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci/* Lookup for reserved dispatch values at:
23462306a36Sopenharmony_ci * https://www.iana.org/assignments/_6lowpan-parameters/_6lowpan-parameters.xhtml#_6lowpan-parameters-1
23562306a36Sopenharmony_ci *
23662306a36Sopenharmony_ci * Last Updated: 2015-01-22
23762306a36Sopenharmony_ci */
23862306a36Sopenharmony_cistatic inline bool lowpan_is_reserved(u8 dispatch)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	return ((dispatch >= 0x44 && dispatch <= 0x4F) ||
24162306a36Sopenharmony_ci		(dispatch >= 0x51 && dispatch <= 0x5F) ||
24262306a36Sopenharmony_ci		(dispatch >= 0xc8 && dispatch <= 0xdf) ||
24362306a36Sopenharmony_ci		dispatch >= 0xe8);
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci/* lowpan_rx_h_check checks on generic 6LoWPAN requirements
24762306a36Sopenharmony_ci * in MAC and 6LoWPAN header.
24862306a36Sopenharmony_ci *
24962306a36Sopenharmony_ci * Don't manipulate the skb here, it could be shared buffer.
25062306a36Sopenharmony_ci */
25162306a36Sopenharmony_cistatic inline bool lowpan_rx_h_check(struct sk_buff *skb)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	__le16 fc = ieee802154_get_fc_from_skb(skb);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	/* check on ieee802154 conform 6LoWPAN header */
25662306a36Sopenharmony_ci	if (!ieee802154_is_data(fc) ||
25762306a36Sopenharmony_ci	    !ieee802154_skb_is_intra_pan_addressing(fc, skb))
25862306a36Sopenharmony_ci		return false;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	/* check if we can dereference the dispatch */
26162306a36Sopenharmony_ci	if (unlikely(!skb->len))
26262306a36Sopenharmony_ci		return false;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	if (lowpan_is_nalp(*skb_network_header(skb)) ||
26562306a36Sopenharmony_ci	    lowpan_is_reserved(*skb_network_header(skb)))
26662306a36Sopenharmony_ci		return false;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	return true;
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic int lowpan_rcv(struct sk_buff *skb, struct net_device *wdev,
27262306a36Sopenharmony_ci		      struct packet_type *pt, struct net_device *orig_wdev)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	struct net_device *ldev;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	if (wdev->type != ARPHRD_IEEE802154 ||
27762306a36Sopenharmony_ci	    skb->pkt_type == PACKET_OTHERHOST ||
27862306a36Sopenharmony_ci	    !lowpan_rx_h_check(skb))
27962306a36Sopenharmony_ci		goto drop;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	ldev = wdev->ieee802154_ptr->lowpan_dev;
28262306a36Sopenharmony_ci	if (!ldev || !netif_running(ldev))
28362306a36Sopenharmony_ci		goto drop;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	/* Replacing skb->dev and followed rx handlers will manipulate skb. */
28662306a36Sopenharmony_ci	skb = skb_share_check(skb, GFP_ATOMIC);
28762306a36Sopenharmony_ci	if (!skb)
28862306a36Sopenharmony_ci		goto out;
28962306a36Sopenharmony_ci	skb->dev = ldev;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	/* When receive frag1 it's likely that we manipulate the buffer.
29262306a36Sopenharmony_ci	 * When recevie iphc we manipulate the data buffer. So we need
29362306a36Sopenharmony_ci	 * to unshare the buffer.
29462306a36Sopenharmony_ci	 */
29562306a36Sopenharmony_ci	if (lowpan_is_frag1(*skb_network_header(skb)) ||
29662306a36Sopenharmony_ci	    lowpan_is_iphc(*skb_network_header(skb))) {
29762306a36Sopenharmony_ci		skb = skb_unshare(skb, GFP_ATOMIC);
29862306a36Sopenharmony_ci		if (!skb)
29962306a36Sopenharmony_ci			goto out;
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	return lowpan_invoke_rx_handlers(skb);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cidrop:
30562306a36Sopenharmony_ci	kfree_skb(skb);
30662306a36Sopenharmony_ciout:
30762306a36Sopenharmony_ci	return NET_RX_DROP;
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_cistatic struct packet_type lowpan_packet_type = {
31162306a36Sopenharmony_ci	.type = htons(ETH_P_IEEE802154),
31262306a36Sopenharmony_ci	.func = lowpan_rcv,
31362306a36Sopenharmony_ci};
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_civoid lowpan_rx_init(void)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	dev_add_pack(&lowpan_packet_type);
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_civoid lowpan_rx_exit(void)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	dev_remove_pack(&lowpan_packet_type);
32362306a36Sopenharmony_ci}
324