162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#include <linux/module.h>
762306a36Sopenharmony_ci#include <linux/proc_fs.h>
862306a36Sopenharmony_ci#include <linux/kernel.h>
962306a36Sopenharmony_ci#include <linux/interrupt.h>
1062306a36Sopenharmony_ci#include <linux/fs.h>
1162306a36Sopenharmony_ci#include <linux/types.h>
1262306a36Sopenharmony_ci#include <linux/sysctl.h>
1362306a36Sopenharmony_ci#include <linux/string.h>
1462306a36Sopenharmony_ci#include <linux/socket.h>
1562306a36Sopenharmony_ci#include <linux/errno.h>
1662306a36Sopenharmony_ci#include <linux/fcntl.h>
1762306a36Sopenharmony_ci#include <linux/in.h>
1862306a36Sopenharmony_ci#include <linux/if_ether.h>	/* For the statistics structure. */
1962306a36Sopenharmony_ci#include <linux/slab.h>
2062306a36Sopenharmony_ci#include <linux/uaccess.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include <asm/io.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include <linux/inet.h>
2562306a36Sopenharmony_ci#include <linux/netdevice.h>
2662306a36Sopenharmony_ci#include <linux/etherdevice.h>
2762306a36Sopenharmony_ci#include <linux/if_arp.h>
2862306a36Sopenharmony_ci#include <linux/skbuff.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#include <net/ip.h>
3162306a36Sopenharmony_ci#include <net/arp.h>
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#include <net/ax25.h>
3462306a36Sopenharmony_ci#include <net/netrom.h>
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci/*
3762306a36Sopenharmony_ci *	Only allow IP over NET/ROM frames through if the netrom device is up.
3862306a36Sopenharmony_ci */
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ciint nr_rx_ip(struct sk_buff *skb, struct net_device *dev)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	struct net_device_stats *stats = &dev->stats;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	if (!netif_running(dev)) {
4562306a36Sopenharmony_ci		stats->rx_dropped++;
4662306a36Sopenharmony_ci		return 0;
4762306a36Sopenharmony_ci	}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	stats->rx_packets++;
5062306a36Sopenharmony_ci	stats->rx_bytes += skb->len;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	skb->protocol = htons(ETH_P_IP);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	/* Spoof incoming device */
5562306a36Sopenharmony_ci	skb->dev      = dev;
5662306a36Sopenharmony_ci	skb->mac_header = skb->network_header;
5762306a36Sopenharmony_ci	skb_reset_network_header(skb);
5862306a36Sopenharmony_ci	skb->pkt_type = PACKET_HOST;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	netif_rx(skb);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	return 1;
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic int nr_header(struct sk_buff *skb, struct net_device *dev,
6662306a36Sopenharmony_ci		     unsigned short type,
6762306a36Sopenharmony_ci		     const void *daddr, const void *saddr, unsigned int len)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	unsigned char *buff = skb_push(skb, NR_NETWORK_LEN + NR_TRANSPORT_LEN);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	memcpy(buff, (saddr != NULL) ? saddr : dev->dev_addr, dev->addr_len);
7262306a36Sopenharmony_ci	buff[6] &= ~AX25_CBIT;
7362306a36Sopenharmony_ci	buff[6] &= ~AX25_EBIT;
7462306a36Sopenharmony_ci	buff[6] |= AX25_SSSID_SPARE;
7562306a36Sopenharmony_ci	buff    += AX25_ADDR_LEN;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	if (daddr != NULL)
7862306a36Sopenharmony_ci		memcpy(buff, daddr, dev->addr_len);
7962306a36Sopenharmony_ci	buff[6] &= ~AX25_CBIT;
8062306a36Sopenharmony_ci	buff[6] |= AX25_EBIT;
8162306a36Sopenharmony_ci	buff[6] |= AX25_SSSID_SPARE;
8262306a36Sopenharmony_ci	buff    += AX25_ADDR_LEN;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	*buff++ = READ_ONCE(sysctl_netrom_network_ttl_initialiser);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	*buff++ = NR_PROTO_IP;
8762306a36Sopenharmony_ci	*buff++ = NR_PROTO_IP;
8862306a36Sopenharmony_ci	*buff++ = 0;
8962306a36Sopenharmony_ci	*buff++ = 0;
9062306a36Sopenharmony_ci	*buff++ = NR_PROTOEXT;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (daddr != NULL)
9362306a36Sopenharmony_ci		return 37;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	return -37;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic int __must_check nr_set_mac_address(struct net_device *dev, void *addr)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	struct sockaddr *sa = addr;
10162306a36Sopenharmony_ci	int err;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	if (!memcmp(dev->dev_addr, sa->sa_data, dev->addr_len))
10462306a36Sopenharmony_ci		return 0;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	if (dev->flags & IFF_UP) {
10762306a36Sopenharmony_ci		err = ax25_listen_register((ax25_address *)sa->sa_data, NULL);
10862306a36Sopenharmony_ci		if (err)
10962306a36Sopenharmony_ci			return err;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci		ax25_listen_release((const ax25_address *)dev->dev_addr, NULL);
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	dev_addr_set(dev, sa->sa_data);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	return 0;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic int nr_open(struct net_device *dev)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	int err;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	err = ax25_listen_register((const ax25_address *)dev->dev_addr, NULL);
12462306a36Sopenharmony_ci	if (err)
12562306a36Sopenharmony_ci		return err;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	netif_start_queue(dev);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	return 0;
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic int nr_close(struct net_device *dev)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	ax25_listen_release((const ax25_address *)dev->dev_addr, NULL);
13562306a36Sopenharmony_ci	netif_stop_queue(dev);
13662306a36Sopenharmony_ci	return 0;
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic netdev_tx_t nr_xmit(struct sk_buff *skb, struct net_device *dev)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	struct net_device_stats *stats = &dev->stats;
14262306a36Sopenharmony_ci	unsigned int len = skb->len;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	if (!nr_route_frame(skb, NULL)) {
14562306a36Sopenharmony_ci		kfree_skb(skb);
14662306a36Sopenharmony_ci		stats->tx_errors++;
14762306a36Sopenharmony_ci		return NETDEV_TX_OK;
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	stats->tx_packets++;
15162306a36Sopenharmony_ci	stats->tx_bytes += len;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	return NETDEV_TX_OK;
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic const struct header_ops nr_header_ops = {
15762306a36Sopenharmony_ci	.create	= nr_header,
15862306a36Sopenharmony_ci};
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic const struct net_device_ops nr_netdev_ops = {
16162306a36Sopenharmony_ci	.ndo_open		= nr_open,
16262306a36Sopenharmony_ci	.ndo_stop		= nr_close,
16362306a36Sopenharmony_ci	.ndo_start_xmit		= nr_xmit,
16462306a36Sopenharmony_ci	.ndo_set_mac_address    = nr_set_mac_address,
16562306a36Sopenharmony_ci};
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_civoid nr_setup(struct net_device *dev)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	dev->mtu		= NR_MAX_PACKET_SIZE;
17062306a36Sopenharmony_ci	dev->netdev_ops		= &nr_netdev_ops;
17162306a36Sopenharmony_ci	dev->header_ops		= &nr_header_ops;
17262306a36Sopenharmony_ci	dev->hard_header_len	= NR_NETWORK_LEN + NR_TRANSPORT_LEN;
17362306a36Sopenharmony_ci	dev->addr_len		= AX25_ADDR_LEN;
17462306a36Sopenharmony_ci	dev->type		= ARPHRD_NETROM;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	/* New-style flags. */
17762306a36Sopenharmony_ci	dev->flags		= IFF_NOARP;
17862306a36Sopenharmony_ci}
179