18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci#include <linux/errno.h>
78c2ecf20Sopenharmony_ci#include <linux/types.h>
88c2ecf20Sopenharmony_ci#include <linux/socket.h>
98c2ecf20Sopenharmony_ci#include <linux/slab.h>
108c2ecf20Sopenharmony_ci#include <linux/in.h>
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/timer.h>
138c2ecf20Sopenharmony_ci#include <linux/string.h>
148c2ecf20Sopenharmony_ci#include <linux/sockios.h>
158c2ecf20Sopenharmony_ci#include <linux/net.h>
168c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
178c2ecf20Sopenharmony_ci#include <net/ax25.h>
188c2ecf20Sopenharmony_ci#include <linux/inet.h>
198c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
208c2ecf20Sopenharmony_ci#include <linux/if_arp.h>
218c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
228c2ecf20Sopenharmony_ci#include <net/sock.h>
238c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
248c2ecf20Sopenharmony_ci#include <linux/fcntl.h>
258c2ecf20Sopenharmony_ci#include <linux/mm.h>
268c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
278c2ecf20Sopenharmony_ci#include <linux/init.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ciax25_dev *ax25_dev_list;
308c2ecf20Sopenharmony_ciDEFINE_SPINLOCK(ax25_dev_lock);
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ciax25_dev *ax25_addr_ax25dev(ax25_address *addr)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	ax25_dev *ax25_dev, *res = NULL;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	spin_lock_bh(&ax25_dev_lock);
378c2ecf20Sopenharmony_ci	for (ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next)
388c2ecf20Sopenharmony_ci		if (ax25cmp(addr, (ax25_address *)ax25_dev->dev->dev_addr) == 0) {
398c2ecf20Sopenharmony_ci			res = ax25_dev;
408c2ecf20Sopenharmony_ci			ax25_dev_hold(ax25_dev);
418c2ecf20Sopenharmony_ci		}
428c2ecf20Sopenharmony_ci	spin_unlock_bh(&ax25_dev_lock);
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	return res;
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/*
488c2ecf20Sopenharmony_ci *	This is called when an interface is brought up. These are
498c2ecf20Sopenharmony_ci *	reasonable defaults.
508c2ecf20Sopenharmony_ci */
518c2ecf20Sopenharmony_civoid ax25_dev_device_up(struct net_device *dev)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	ax25_dev *ax25_dev;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	if ((ax25_dev = kzalloc(sizeof(*ax25_dev), GFP_ATOMIC)) == NULL) {
568c2ecf20Sopenharmony_ci		printk(KERN_ERR "AX.25: ax25_dev_device_up - out of memory\n");
578c2ecf20Sopenharmony_ci		return;
588c2ecf20Sopenharmony_ci	}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	refcount_set(&ax25_dev->refcount, 1);
618c2ecf20Sopenharmony_ci	dev->ax25_ptr     = ax25_dev;
628c2ecf20Sopenharmony_ci	ax25_dev->dev     = dev;
638c2ecf20Sopenharmony_ci	dev_hold(dev);
648c2ecf20Sopenharmony_ci	ax25_dev->forward = NULL;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	ax25_dev->values[AX25_VALUES_IPDEFMODE] = AX25_DEF_IPDEFMODE;
678c2ecf20Sopenharmony_ci	ax25_dev->values[AX25_VALUES_AXDEFMODE] = AX25_DEF_AXDEFMODE;
688c2ecf20Sopenharmony_ci	ax25_dev->values[AX25_VALUES_BACKOFF]   = AX25_DEF_BACKOFF;
698c2ecf20Sopenharmony_ci	ax25_dev->values[AX25_VALUES_CONMODE]   = AX25_DEF_CONMODE;
708c2ecf20Sopenharmony_ci	ax25_dev->values[AX25_VALUES_WINDOW]    = AX25_DEF_WINDOW;
718c2ecf20Sopenharmony_ci	ax25_dev->values[AX25_VALUES_EWINDOW]   = AX25_DEF_EWINDOW;
728c2ecf20Sopenharmony_ci	ax25_dev->values[AX25_VALUES_T1]        = AX25_DEF_T1;
738c2ecf20Sopenharmony_ci	ax25_dev->values[AX25_VALUES_T2]        = AX25_DEF_T2;
748c2ecf20Sopenharmony_ci	ax25_dev->values[AX25_VALUES_T3]        = AX25_DEF_T3;
758c2ecf20Sopenharmony_ci	ax25_dev->values[AX25_VALUES_IDLE]	= AX25_DEF_IDLE;
768c2ecf20Sopenharmony_ci	ax25_dev->values[AX25_VALUES_N2]        = AX25_DEF_N2;
778c2ecf20Sopenharmony_ci	ax25_dev->values[AX25_VALUES_PACLEN]	= AX25_DEF_PACLEN;
788c2ecf20Sopenharmony_ci	ax25_dev->values[AX25_VALUES_PROTOCOL]  = AX25_DEF_PROTOCOL;
798c2ecf20Sopenharmony_ci	ax25_dev->values[AX25_VALUES_DS_TIMEOUT]= AX25_DEF_DS_TIMEOUT;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci#if defined(CONFIG_AX25_DAMA_SLAVE) || defined(CONFIG_AX25_DAMA_MASTER)
828c2ecf20Sopenharmony_ci	ax25_ds_setup_timer(ax25_dev);
838c2ecf20Sopenharmony_ci#endif
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	spin_lock_bh(&ax25_dev_lock);
868c2ecf20Sopenharmony_ci	ax25_dev->next = ax25_dev_list;
878c2ecf20Sopenharmony_ci	ax25_dev_list  = ax25_dev;
888c2ecf20Sopenharmony_ci	spin_unlock_bh(&ax25_dev_lock);
898c2ecf20Sopenharmony_ci	ax25_dev_hold(ax25_dev);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	ax25_register_dev_sysctl(ax25_dev);
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_civoid ax25_dev_device_down(struct net_device *dev)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	ax25_dev *s, *ax25_dev;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
998c2ecf20Sopenharmony_ci		return;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	ax25_unregister_dev_sysctl(ax25_dev);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	spin_lock_bh(&ax25_dev_lock);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci#ifdef CONFIG_AX25_DAMA_SLAVE
1068c2ecf20Sopenharmony_ci	ax25_ds_del_timer(ax25_dev);
1078c2ecf20Sopenharmony_ci#endif
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	/*
1108c2ecf20Sopenharmony_ci	 *	Remove any packet forwarding that points to this device.
1118c2ecf20Sopenharmony_ci	 */
1128c2ecf20Sopenharmony_ci	for (s = ax25_dev_list; s != NULL; s = s->next)
1138c2ecf20Sopenharmony_ci		if (s->forward == dev)
1148c2ecf20Sopenharmony_ci			s->forward = NULL;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	if ((s = ax25_dev_list) == ax25_dev) {
1178c2ecf20Sopenharmony_ci		ax25_dev_list = s->next;
1188c2ecf20Sopenharmony_ci		spin_unlock_bh(&ax25_dev_lock);
1198c2ecf20Sopenharmony_ci		ax25_dev_put(ax25_dev);
1208c2ecf20Sopenharmony_ci		dev->ax25_ptr = NULL;
1218c2ecf20Sopenharmony_ci		dev_put(dev);
1228c2ecf20Sopenharmony_ci		ax25_dev_put(ax25_dev);
1238c2ecf20Sopenharmony_ci		return;
1248c2ecf20Sopenharmony_ci	}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	while (s != NULL && s->next != NULL) {
1278c2ecf20Sopenharmony_ci		if (s->next == ax25_dev) {
1288c2ecf20Sopenharmony_ci			s->next = ax25_dev->next;
1298c2ecf20Sopenharmony_ci			spin_unlock_bh(&ax25_dev_lock);
1308c2ecf20Sopenharmony_ci			ax25_dev_put(ax25_dev);
1318c2ecf20Sopenharmony_ci			dev->ax25_ptr = NULL;
1328c2ecf20Sopenharmony_ci			dev_put(dev);
1338c2ecf20Sopenharmony_ci			ax25_dev_put(ax25_dev);
1348c2ecf20Sopenharmony_ci			return;
1358c2ecf20Sopenharmony_ci		}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci		s = s->next;
1388c2ecf20Sopenharmony_ci	}
1398c2ecf20Sopenharmony_ci	spin_unlock_bh(&ax25_dev_lock);
1408c2ecf20Sopenharmony_ci	dev->ax25_ptr = NULL;
1418c2ecf20Sopenharmony_ci	ax25_dev_put(ax25_dev);
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ciint ax25_fwd_ioctl(unsigned int cmd, struct ax25_fwd_struct *fwd)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	ax25_dev *ax25_dev, *fwd_dev;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	if ((ax25_dev = ax25_addr_ax25dev(&fwd->port_from)) == NULL)
1498c2ecf20Sopenharmony_ci		return -EINVAL;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	switch (cmd) {
1528c2ecf20Sopenharmony_ci	case SIOCAX25ADDFWD:
1538c2ecf20Sopenharmony_ci		fwd_dev = ax25_addr_ax25dev(&fwd->port_to);
1548c2ecf20Sopenharmony_ci		if (!fwd_dev) {
1558c2ecf20Sopenharmony_ci			ax25_dev_put(ax25_dev);
1568c2ecf20Sopenharmony_ci			return -EINVAL;
1578c2ecf20Sopenharmony_ci		}
1588c2ecf20Sopenharmony_ci		if (ax25_dev->forward) {
1598c2ecf20Sopenharmony_ci			ax25_dev_put(fwd_dev);
1608c2ecf20Sopenharmony_ci			ax25_dev_put(ax25_dev);
1618c2ecf20Sopenharmony_ci			return -EINVAL;
1628c2ecf20Sopenharmony_ci		}
1638c2ecf20Sopenharmony_ci		ax25_dev->forward = fwd_dev->dev;
1648c2ecf20Sopenharmony_ci		ax25_dev_put(fwd_dev);
1658c2ecf20Sopenharmony_ci		ax25_dev_put(ax25_dev);
1668c2ecf20Sopenharmony_ci		break;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	case SIOCAX25DELFWD:
1698c2ecf20Sopenharmony_ci		if (!ax25_dev->forward) {
1708c2ecf20Sopenharmony_ci			ax25_dev_put(ax25_dev);
1718c2ecf20Sopenharmony_ci			return -EINVAL;
1728c2ecf20Sopenharmony_ci		}
1738c2ecf20Sopenharmony_ci		ax25_dev->forward = NULL;
1748c2ecf20Sopenharmony_ci		ax25_dev_put(ax25_dev);
1758c2ecf20Sopenharmony_ci		break;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	default:
1788c2ecf20Sopenharmony_ci		ax25_dev_put(ax25_dev);
1798c2ecf20Sopenharmony_ci		return -EINVAL;
1808c2ecf20Sopenharmony_ci	}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	return 0;
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistruct net_device *ax25_fwd_dev(struct net_device *dev)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	ax25_dev *ax25_dev;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
1908c2ecf20Sopenharmony_ci		return dev;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	if (ax25_dev->forward == NULL)
1938c2ecf20Sopenharmony_ci		return dev;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	return ax25_dev->forward;
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci/*
1998c2ecf20Sopenharmony_ci *	Free all memory associated with device structures.
2008c2ecf20Sopenharmony_ci */
2018c2ecf20Sopenharmony_civoid __exit ax25_dev_free(void)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	ax25_dev *s, *ax25_dev;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	spin_lock_bh(&ax25_dev_lock);
2068c2ecf20Sopenharmony_ci	ax25_dev = ax25_dev_list;
2078c2ecf20Sopenharmony_ci	while (ax25_dev != NULL) {
2088c2ecf20Sopenharmony_ci		s        = ax25_dev;
2098c2ecf20Sopenharmony_ci		dev_put(ax25_dev->dev);
2108c2ecf20Sopenharmony_ci		ax25_dev = ax25_dev->next;
2118c2ecf20Sopenharmony_ci		kfree(s);
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci	ax25_dev_list = NULL;
2148c2ecf20Sopenharmony_ci	spin_unlock_bh(&ax25_dev_lock);
2158c2ecf20Sopenharmony_ci}
216