18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2007-2012 Siemens AG
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Written by:
68c2ecf20Sopenharmony_ci * Pavel Smolenskiy <pavel.smolenskiy@gmail.com>
78c2ecf20Sopenharmony_ci * Maxim Gorbachyov <maxim.gorbachev@siemens.com>
88c2ecf20Sopenharmony_ci * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
98c2ecf20Sopenharmony_ci * Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/kernel.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
158c2ecf20Sopenharmony_ci#include <linux/crc-ccitt.h>
168c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <net/mac802154.h>
198c2ecf20Sopenharmony_ci#include <net/ieee802154_netdev.h>
208c2ecf20Sopenharmony_ci#include <net/nl802154.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include "ieee802154_i.h"
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic int ieee802154_deliver_skb(struct sk_buff *skb)
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci	skb->ip_summed = CHECKSUM_UNNECESSARY;
278c2ecf20Sopenharmony_ci	skb->protocol = htons(ETH_P_IEEE802154);
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	return netif_receive_skb(skb);
308c2ecf20Sopenharmony_ci}
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic int
338c2ecf20Sopenharmony_ciieee802154_subif_frame(struct ieee802154_sub_if_data *sdata,
348c2ecf20Sopenharmony_ci		       struct sk_buff *skb, const struct ieee802154_hdr *hdr)
358c2ecf20Sopenharmony_ci{
368c2ecf20Sopenharmony_ci	struct wpan_dev *wpan_dev = &sdata->wpan_dev;
378c2ecf20Sopenharmony_ci	__le16 span, sshort;
388c2ecf20Sopenharmony_ci	int rc;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	pr_debug("getting packet via slave interface %s\n", sdata->dev->name);
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	span = wpan_dev->pan_id;
438c2ecf20Sopenharmony_ci	sshort = wpan_dev->short_addr;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	switch (mac_cb(skb)->dest.mode) {
468c2ecf20Sopenharmony_ci	case IEEE802154_ADDR_NONE:
478c2ecf20Sopenharmony_ci		if (hdr->source.mode != IEEE802154_ADDR_NONE)
488c2ecf20Sopenharmony_ci			/* FIXME: check if we are PAN coordinator */
498c2ecf20Sopenharmony_ci			skb->pkt_type = PACKET_OTHERHOST;
508c2ecf20Sopenharmony_ci		else
518c2ecf20Sopenharmony_ci			/* ACK comes with both addresses empty */
528c2ecf20Sopenharmony_ci			skb->pkt_type = PACKET_HOST;
538c2ecf20Sopenharmony_ci		break;
548c2ecf20Sopenharmony_ci	case IEEE802154_ADDR_LONG:
558c2ecf20Sopenharmony_ci		if (mac_cb(skb)->dest.pan_id != span &&
568c2ecf20Sopenharmony_ci		    mac_cb(skb)->dest.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST))
578c2ecf20Sopenharmony_ci			skb->pkt_type = PACKET_OTHERHOST;
588c2ecf20Sopenharmony_ci		else if (mac_cb(skb)->dest.extended_addr == wpan_dev->extended_addr)
598c2ecf20Sopenharmony_ci			skb->pkt_type = PACKET_HOST;
608c2ecf20Sopenharmony_ci		else
618c2ecf20Sopenharmony_ci			skb->pkt_type = PACKET_OTHERHOST;
628c2ecf20Sopenharmony_ci		break;
638c2ecf20Sopenharmony_ci	case IEEE802154_ADDR_SHORT:
648c2ecf20Sopenharmony_ci		if (mac_cb(skb)->dest.pan_id != span &&
658c2ecf20Sopenharmony_ci		    mac_cb(skb)->dest.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST))
668c2ecf20Sopenharmony_ci			skb->pkt_type = PACKET_OTHERHOST;
678c2ecf20Sopenharmony_ci		else if (mac_cb(skb)->dest.short_addr == sshort)
688c2ecf20Sopenharmony_ci			skb->pkt_type = PACKET_HOST;
698c2ecf20Sopenharmony_ci		else if (mac_cb(skb)->dest.short_addr ==
708c2ecf20Sopenharmony_ci			  cpu_to_le16(IEEE802154_ADDR_BROADCAST))
718c2ecf20Sopenharmony_ci			skb->pkt_type = PACKET_BROADCAST;
728c2ecf20Sopenharmony_ci		else
738c2ecf20Sopenharmony_ci			skb->pkt_type = PACKET_OTHERHOST;
748c2ecf20Sopenharmony_ci		break;
758c2ecf20Sopenharmony_ci	default:
768c2ecf20Sopenharmony_ci		pr_debug("invalid dest mode\n");
778c2ecf20Sopenharmony_ci		goto fail;
788c2ecf20Sopenharmony_ci	}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	skb->dev = sdata->dev;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	/* TODO this should be moved after netif_receive_skb call, otherwise
838c2ecf20Sopenharmony_ci	 * wireshark will show a mac header with security fields and the
848c2ecf20Sopenharmony_ci	 * payload is already decrypted.
858c2ecf20Sopenharmony_ci	 */
868c2ecf20Sopenharmony_ci	rc = mac802154_llsec_decrypt(&sdata->sec, skb);
878c2ecf20Sopenharmony_ci	if (rc) {
888c2ecf20Sopenharmony_ci		pr_debug("decryption failed: %i\n", rc);
898c2ecf20Sopenharmony_ci		goto fail;
908c2ecf20Sopenharmony_ci	}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	sdata->dev->stats.rx_packets++;
938c2ecf20Sopenharmony_ci	sdata->dev->stats.rx_bytes += skb->len;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	switch (mac_cb(skb)->type) {
968c2ecf20Sopenharmony_ci	case IEEE802154_FC_TYPE_BEACON:
978c2ecf20Sopenharmony_ci	case IEEE802154_FC_TYPE_ACK:
988c2ecf20Sopenharmony_ci	case IEEE802154_FC_TYPE_MAC_CMD:
998c2ecf20Sopenharmony_ci		goto fail;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	case IEEE802154_FC_TYPE_DATA:
1028c2ecf20Sopenharmony_ci		return ieee802154_deliver_skb(skb);
1038c2ecf20Sopenharmony_ci	default:
1048c2ecf20Sopenharmony_ci		pr_warn_ratelimited("ieee802154: bad frame received "
1058c2ecf20Sopenharmony_ci				    "(type = %d)\n", mac_cb(skb)->type);
1068c2ecf20Sopenharmony_ci		goto fail;
1078c2ecf20Sopenharmony_ci	}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cifail:
1108c2ecf20Sopenharmony_ci	kfree_skb(skb);
1118c2ecf20Sopenharmony_ci	return NET_RX_DROP;
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic void
1158c2ecf20Sopenharmony_ciieee802154_print_addr(const char *name, const struct ieee802154_addr *addr)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	if (addr->mode == IEEE802154_ADDR_NONE)
1188c2ecf20Sopenharmony_ci		pr_debug("%s not present\n", name);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	pr_debug("%s PAN ID: %04x\n", name, le16_to_cpu(addr->pan_id));
1218c2ecf20Sopenharmony_ci	if (addr->mode == IEEE802154_ADDR_SHORT) {
1228c2ecf20Sopenharmony_ci		pr_debug("%s is short: %04x\n", name,
1238c2ecf20Sopenharmony_ci			 le16_to_cpu(addr->short_addr));
1248c2ecf20Sopenharmony_ci	} else {
1258c2ecf20Sopenharmony_ci		u64 hw = swab64((__force u64)addr->extended_addr);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci		pr_debug("%s is hardware: %8phC\n", name, &hw);
1288c2ecf20Sopenharmony_ci	}
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic int
1328c2ecf20Sopenharmony_ciieee802154_parse_frame_start(struct sk_buff *skb, struct ieee802154_hdr *hdr)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	int hlen;
1358c2ecf20Sopenharmony_ci	struct ieee802154_mac_cb *cb = mac_cb(skb);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	skb_reset_mac_header(skb);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	hlen = ieee802154_hdr_pull(skb, hdr);
1408c2ecf20Sopenharmony_ci	if (hlen < 0)
1418c2ecf20Sopenharmony_ci		return -EINVAL;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	skb->mac_len = hlen;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	pr_debug("fc: %04x dsn: %02x\n", le16_to_cpup((__le16 *)&hdr->fc),
1468c2ecf20Sopenharmony_ci		 hdr->seq);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	cb->type = hdr->fc.type;
1498c2ecf20Sopenharmony_ci	cb->ackreq = hdr->fc.ack_request;
1508c2ecf20Sopenharmony_ci	cb->secen = hdr->fc.security_enabled;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	ieee802154_print_addr("destination", &hdr->dest);
1538c2ecf20Sopenharmony_ci	ieee802154_print_addr("source", &hdr->source);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	cb->source = hdr->source;
1568c2ecf20Sopenharmony_ci	cb->dest = hdr->dest;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	if (hdr->fc.security_enabled) {
1598c2ecf20Sopenharmony_ci		u64 key;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci		pr_debug("seclevel %i\n", hdr->sec.level);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci		switch (hdr->sec.key_id_mode) {
1648c2ecf20Sopenharmony_ci		case IEEE802154_SCF_KEY_IMPLICIT:
1658c2ecf20Sopenharmony_ci			pr_debug("implicit key\n");
1668c2ecf20Sopenharmony_ci			break;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci		case IEEE802154_SCF_KEY_INDEX:
1698c2ecf20Sopenharmony_ci			pr_debug("key %02x\n", hdr->sec.key_id);
1708c2ecf20Sopenharmony_ci			break;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci		case IEEE802154_SCF_KEY_SHORT_INDEX:
1738c2ecf20Sopenharmony_ci			pr_debug("key %04x:%04x %02x\n",
1748c2ecf20Sopenharmony_ci				 le32_to_cpu(hdr->sec.short_src) >> 16,
1758c2ecf20Sopenharmony_ci				 le32_to_cpu(hdr->sec.short_src) & 0xffff,
1768c2ecf20Sopenharmony_ci				 hdr->sec.key_id);
1778c2ecf20Sopenharmony_ci			break;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci		case IEEE802154_SCF_KEY_HW_INDEX:
1808c2ecf20Sopenharmony_ci			key = swab64((__force u64)hdr->sec.extended_src);
1818c2ecf20Sopenharmony_ci			pr_debug("key source %8phC %02x\n", &key,
1828c2ecf20Sopenharmony_ci				 hdr->sec.key_id);
1838c2ecf20Sopenharmony_ci			break;
1848c2ecf20Sopenharmony_ci		}
1858c2ecf20Sopenharmony_ci	}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	return 0;
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_cistatic void
1918c2ecf20Sopenharmony_ci__ieee802154_rx_handle_packet(struct ieee802154_local *local,
1928c2ecf20Sopenharmony_ci			      struct sk_buff *skb)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	int ret;
1958c2ecf20Sopenharmony_ci	struct ieee802154_sub_if_data *sdata;
1968c2ecf20Sopenharmony_ci	struct ieee802154_hdr hdr;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	ret = ieee802154_parse_frame_start(skb, &hdr);
1998c2ecf20Sopenharmony_ci	if (ret) {
2008c2ecf20Sopenharmony_ci		pr_debug("got invalid frame\n");
2018c2ecf20Sopenharmony_ci		kfree_skb(skb);
2028c2ecf20Sopenharmony_ci		return;
2038c2ecf20Sopenharmony_ci	}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
2068c2ecf20Sopenharmony_ci		if (sdata->wpan_dev.iftype != NL802154_IFTYPE_NODE)
2078c2ecf20Sopenharmony_ci			continue;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci		if (!ieee802154_sdata_running(sdata))
2108c2ecf20Sopenharmony_ci			continue;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci		ieee802154_subif_frame(sdata, skb, &hdr);
2138c2ecf20Sopenharmony_ci		skb = NULL;
2148c2ecf20Sopenharmony_ci		break;
2158c2ecf20Sopenharmony_ci	}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	kfree_skb(skb);
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_cistatic void
2218c2ecf20Sopenharmony_ciieee802154_monitors_rx(struct ieee802154_local *local, struct sk_buff *skb)
2228c2ecf20Sopenharmony_ci{
2238c2ecf20Sopenharmony_ci	struct sk_buff *skb2;
2248c2ecf20Sopenharmony_ci	struct ieee802154_sub_if_data *sdata;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	skb_reset_mac_header(skb);
2278c2ecf20Sopenharmony_ci	skb->ip_summed = CHECKSUM_UNNECESSARY;
2288c2ecf20Sopenharmony_ci	skb->pkt_type = PACKET_OTHERHOST;
2298c2ecf20Sopenharmony_ci	skb->protocol = htons(ETH_P_IEEE802154);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
2328c2ecf20Sopenharmony_ci		if (sdata->wpan_dev.iftype != NL802154_IFTYPE_MONITOR)
2338c2ecf20Sopenharmony_ci			continue;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci		if (!ieee802154_sdata_running(sdata))
2368c2ecf20Sopenharmony_ci			continue;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci		skb2 = skb_clone(skb, GFP_ATOMIC);
2398c2ecf20Sopenharmony_ci		if (skb2) {
2408c2ecf20Sopenharmony_ci			skb2->dev = sdata->dev;
2418c2ecf20Sopenharmony_ci			ieee802154_deliver_skb(skb2);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci			sdata->dev->stats.rx_packets++;
2448c2ecf20Sopenharmony_ci			sdata->dev->stats.rx_bytes += skb->len;
2458c2ecf20Sopenharmony_ci		}
2468c2ecf20Sopenharmony_ci	}
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_civoid ieee802154_rx(struct ieee802154_local *local, struct sk_buff *skb)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	u16 crc;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	WARN_ON_ONCE(softirq_count() == 0);
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	if (local->suspended)
2568c2ecf20Sopenharmony_ci		goto drop;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	/* TODO: When a transceiver omits the checksum here, we
2598c2ecf20Sopenharmony_ci	 * add an own calculated one. This is currently an ugly
2608c2ecf20Sopenharmony_ci	 * solution because the monitor needs a crc here.
2618c2ecf20Sopenharmony_ci	 */
2628c2ecf20Sopenharmony_ci	if (local->hw.flags & IEEE802154_HW_RX_OMIT_CKSUM) {
2638c2ecf20Sopenharmony_ci		crc = crc_ccitt(0, skb->data, skb->len);
2648c2ecf20Sopenharmony_ci		put_unaligned_le16(crc, skb_put(skb, 2));
2658c2ecf20Sopenharmony_ci	}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	rcu_read_lock();
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	ieee802154_monitors_rx(local, skb);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	/* Check if transceiver doesn't validate the checksum.
2728c2ecf20Sopenharmony_ci	 * If not we validate the checksum here.
2738c2ecf20Sopenharmony_ci	 */
2748c2ecf20Sopenharmony_ci	if (local->hw.flags & IEEE802154_HW_RX_DROP_BAD_CKSUM) {
2758c2ecf20Sopenharmony_ci		crc = crc_ccitt(0, skb->data, skb->len);
2768c2ecf20Sopenharmony_ci		if (crc) {
2778c2ecf20Sopenharmony_ci			rcu_read_unlock();
2788c2ecf20Sopenharmony_ci			goto drop;
2798c2ecf20Sopenharmony_ci		}
2808c2ecf20Sopenharmony_ci	}
2818c2ecf20Sopenharmony_ci	/* remove crc */
2828c2ecf20Sopenharmony_ci	skb_trim(skb, skb->len - 2);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	__ieee802154_rx_handle_packet(local, skb);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	rcu_read_unlock();
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	return;
2898c2ecf20Sopenharmony_cidrop:
2908c2ecf20Sopenharmony_ci	kfree_skb(skb);
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_civoid
2948c2ecf20Sopenharmony_ciieee802154_rx_irqsafe(struct ieee802154_hw *hw, struct sk_buff *skb, u8 lqi)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	struct ieee802154_local *local = hw_to_local(hw);
2978c2ecf20Sopenharmony_ci	struct ieee802154_mac_cb *cb = mac_cb_init(skb);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	cb->lqi = lqi;
3008c2ecf20Sopenharmony_ci	skb->pkt_type = IEEE802154_RX_MSG;
3018c2ecf20Sopenharmony_ci	skb_queue_tail(&local->skb_queue, skb);
3028c2ecf20Sopenharmony_ci	tasklet_schedule(&local->tasklet);
3038c2ecf20Sopenharmony_ci}
3048c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ieee802154_rx_irqsafe);
305