18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include <linux/kmod.h>
38c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
48c2ecf20Sopenharmony_ci#include <linux/inetdevice.h>
58c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
68c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h>
78c2ecf20Sopenharmony_ci#include <linux/net_tstamp.h>
88c2ecf20Sopenharmony_ci#include <linux/wireless.h>
98c2ecf20Sopenharmony_ci#include <net/dsa.h>
108c2ecf20Sopenharmony_ci#include <net/wext.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci/*
138c2ecf20Sopenharmony_ci *	Map an interface index to its name (SIOCGIFNAME)
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci/*
178c2ecf20Sopenharmony_ci *	We need this ioctl for efficient implementation of the
188c2ecf20Sopenharmony_ci *	if_indextoname() function required by the IPv6 API.  Without
198c2ecf20Sopenharmony_ci *	it, we would have to search all the interfaces to find a
208c2ecf20Sopenharmony_ci *	match.  --pb
218c2ecf20Sopenharmony_ci */
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistatic int dev_ifname(struct net *net, struct ifreq *ifr)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	ifr->ifr_name[IFNAMSIZ-1] = 0;
268c2ecf20Sopenharmony_ci	return netdev_get_name(net, ifr->ifr_name, ifr->ifr_ifindex);
278c2ecf20Sopenharmony_ci}
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/*
308c2ecf20Sopenharmony_ci *	Perform a SIOCGIFCONF call. This structure will change
318c2ecf20Sopenharmony_ci *	size eventually, and there is nothing I can do about it.
328c2ecf20Sopenharmony_ci *	Thus we will need a 'compatibility mode'.
338c2ecf20Sopenharmony_ci */
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ciint dev_ifconf(struct net *net, struct ifconf *ifc, int size)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	struct net_device *dev;
388c2ecf20Sopenharmony_ci	char __user *pos;
398c2ecf20Sopenharmony_ci	int len;
408c2ecf20Sopenharmony_ci	int total;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	/*
438c2ecf20Sopenharmony_ci	 *	Fetch the caller's info block.
448c2ecf20Sopenharmony_ci	 */
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	pos = ifc->ifc_buf;
478c2ecf20Sopenharmony_ci	len = ifc->ifc_len;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	/*
508c2ecf20Sopenharmony_ci	 *	Loop over the interfaces, and write an info block for each.
518c2ecf20Sopenharmony_ci	 */
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	total = 0;
548c2ecf20Sopenharmony_ci	for_each_netdev(net, dev) {
558c2ecf20Sopenharmony_ci		int done;
568c2ecf20Sopenharmony_ci		if (!pos)
578c2ecf20Sopenharmony_ci			done = inet_gifconf(dev, NULL, 0, size);
588c2ecf20Sopenharmony_ci		else
598c2ecf20Sopenharmony_ci			done = inet_gifconf(dev, pos + total,
608c2ecf20Sopenharmony_ci					    len - total, size);
618c2ecf20Sopenharmony_ci		if (done < 0)
628c2ecf20Sopenharmony_ci			return -EFAULT;
638c2ecf20Sopenharmony_ci		total += done;
648c2ecf20Sopenharmony_ci	}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	/*
678c2ecf20Sopenharmony_ci	 *	All done.  Write the updated control block back to the caller.
688c2ecf20Sopenharmony_ci	 */
698c2ecf20Sopenharmony_ci	ifc->ifc_len = total;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	/*
728c2ecf20Sopenharmony_ci	 * 	Both BSD and Solaris return 0 here, so we do too.
738c2ecf20Sopenharmony_ci	 */
748c2ecf20Sopenharmony_ci	return 0;
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci/*
788c2ecf20Sopenharmony_ci *	Perform the SIOCxIFxxx calls, inside rcu_read_lock()
798c2ecf20Sopenharmony_ci */
808c2ecf20Sopenharmony_cistatic int dev_ifsioc_locked(struct net *net, struct ifreq *ifr, unsigned int cmd)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	int err;
838c2ecf20Sopenharmony_ci	struct net_device *dev = dev_get_by_name_rcu(net, ifr->ifr_name);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	if (!dev)
868c2ecf20Sopenharmony_ci		return -ENODEV;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	switch (cmd) {
898c2ecf20Sopenharmony_ci	case SIOCGIFFLAGS:	/* Get interface flags */
908c2ecf20Sopenharmony_ci		ifr->ifr_flags = (short) dev_get_flags(dev);
918c2ecf20Sopenharmony_ci		return 0;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	case SIOCGIFMETRIC:	/* Get the metric on the interface
948c2ecf20Sopenharmony_ci				   (currently unused) */
958c2ecf20Sopenharmony_ci		ifr->ifr_metric = 0;
968c2ecf20Sopenharmony_ci		return 0;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	case SIOCGIFMTU:	/* Get the MTU of a device */
998c2ecf20Sopenharmony_ci		ifr->ifr_mtu = dev->mtu;
1008c2ecf20Sopenharmony_ci		return 0;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	case SIOCGIFSLAVE:
1038c2ecf20Sopenharmony_ci		err = -EINVAL;
1048c2ecf20Sopenharmony_ci		break;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	case SIOCGIFMAP:
1078c2ecf20Sopenharmony_ci		ifr->ifr_map.mem_start = dev->mem_start;
1088c2ecf20Sopenharmony_ci		ifr->ifr_map.mem_end   = dev->mem_end;
1098c2ecf20Sopenharmony_ci		ifr->ifr_map.base_addr = dev->base_addr;
1108c2ecf20Sopenharmony_ci		ifr->ifr_map.irq       = dev->irq;
1118c2ecf20Sopenharmony_ci		ifr->ifr_map.dma       = dev->dma;
1128c2ecf20Sopenharmony_ci		ifr->ifr_map.port      = dev->if_port;
1138c2ecf20Sopenharmony_ci		return 0;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	case SIOCGIFINDEX:
1168c2ecf20Sopenharmony_ci		ifr->ifr_ifindex = dev->ifindex;
1178c2ecf20Sopenharmony_ci		return 0;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	case SIOCGIFTXQLEN:
1208c2ecf20Sopenharmony_ci		ifr->ifr_qlen = dev->tx_queue_len;
1218c2ecf20Sopenharmony_ci		return 0;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	default:
1248c2ecf20Sopenharmony_ci		/* dev_ioctl() should ensure this case
1258c2ecf20Sopenharmony_ci		 * is never reached
1268c2ecf20Sopenharmony_ci		 */
1278c2ecf20Sopenharmony_ci		WARN_ON(1);
1288c2ecf20Sopenharmony_ci		err = -ENOTTY;
1298c2ecf20Sopenharmony_ci		break;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci	return err;
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic int net_hwtstamp_validate(struct ifreq *ifr)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	struct hwtstamp_config cfg;
1388c2ecf20Sopenharmony_ci	enum hwtstamp_tx_types tx_type;
1398c2ecf20Sopenharmony_ci	enum hwtstamp_rx_filters rx_filter;
1408c2ecf20Sopenharmony_ci	int tx_type_valid = 0;
1418c2ecf20Sopenharmony_ci	int rx_filter_valid = 0;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
1448c2ecf20Sopenharmony_ci		return -EFAULT;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	if (cfg.flags) /* reserved for future extensions */
1478c2ecf20Sopenharmony_ci		return -EINVAL;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	tx_type = cfg.tx_type;
1508c2ecf20Sopenharmony_ci	rx_filter = cfg.rx_filter;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	switch (tx_type) {
1538c2ecf20Sopenharmony_ci	case HWTSTAMP_TX_OFF:
1548c2ecf20Sopenharmony_ci	case HWTSTAMP_TX_ON:
1558c2ecf20Sopenharmony_ci	case HWTSTAMP_TX_ONESTEP_SYNC:
1568c2ecf20Sopenharmony_ci	case HWTSTAMP_TX_ONESTEP_P2P:
1578c2ecf20Sopenharmony_ci		tx_type_valid = 1;
1588c2ecf20Sopenharmony_ci		break;
1598c2ecf20Sopenharmony_ci	case __HWTSTAMP_TX_CNT:
1608c2ecf20Sopenharmony_ci		/* not a real value */
1618c2ecf20Sopenharmony_ci		break;
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	switch (rx_filter) {
1658c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_NONE:
1668c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_ALL:
1678c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_SOME:
1688c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
1698c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
1708c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
1718c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
1728c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
1738c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
1748c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
1758c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
1768c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
1778c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_EVENT:
1788c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_SYNC:
1798c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
1808c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_NTP_ALL:
1818c2ecf20Sopenharmony_ci		rx_filter_valid = 1;
1828c2ecf20Sopenharmony_ci		break;
1838c2ecf20Sopenharmony_ci	case __HWTSTAMP_FILTER_CNT:
1848c2ecf20Sopenharmony_ci		/* not a real value */
1858c2ecf20Sopenharmony_ci		break;
1868c2ecf20Sopenharmony_ci	}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	if (!tx_type_valid || !rx_filter_valid)
1898c2ecf20Sopenharmony_ci		return -ERANGE;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	return 0;
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic int dev_do_ioctl(struct net_device *dev,
1958c2ecf20Sopenharmony_ci			struct ifreq *ifr, unsigned int cmd)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	const struct net_device_ops *ops = dev->netdev_ops;
1988c2ecf20Sopenharmony_ci	int err = -EOPNOTSUPP;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	err = dsa_ndo_do_ioctl(dev, ifr, cmd);
2018c2ecf20Sopenharmony_ci	if (err == 0 || err != -EOPNOTSUPP)
2028c2ecf20Sopenharmony_ci		return err;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	if (ops->ndo_do_ioctl) {
2058c2ecf20Sopenharmony_ci		if (netif_device_present(dev))
2068c2ecf20Sopenharmony_ci			err = ops->ndo_do_ioctl(dev, ifr, cmd);
2078c2ecf20Sopenharmony_ci		else
2088c2ecf20Sopenharmony_ci			err = -ENODEV;
2098c2ecf20Sopenharmony_ci	}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	return err;
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci/*
2158c2ecf20Sopenharmony_ci *	Perform the SIOCxIFxxx calls, inside rtnl_lock()
2168c2ecf20Sopenharmony_ci */
2178c2ecf20Sopenharmony_cistatic int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	int err;
2208c2ecf20Sopenharmony_ci	struct net_device *dev = __dev_get_by_name(net, ifr->ifr_name);
2218c2ecf20Sopenharmony_ci	const struct net_device_ops *ops;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	if (!dev)
2248c2ecf20Sopenharmony_ci		return -ENODEV;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	ops = dev->netdev_ops;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	switch (cmd) {
2298c2ecf20Sopenharmony_ci	case SIOCSIFFLAGS:	/* Set interface flags */
2308c2ecf20Sopenharmony_ci		return dev_change_flags(dev, ifr->ifr_flags, NULL);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	case SIOCSIFMETRIC:	/* Set the metric on the interface
2338c2ecf20Sopenharmony_ci				   (currently unused) */
2348c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	case SIOCSIFMTU:	/* Set the MTU of a device */
2378c2ecf20Sopenharmony_ci		return dev_set_mtu(dev, ifr->ifr_mtu);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	case SIOCSIFHWADDR:
2408c2ecf20Sopenharmony_ci		if (dev->addr_len > sizeof(struct sockaddr))
2418c2ecf20Sopenharmony_ci			return -EINVAL;
2428c2ecf20Sopenharmony_ci		return dev_set_mac_address_user(dev, &ifr->ifr_hwaddr, NULL);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	case SIOCSIFHWBROADCAST:
2458c2ecf20Sopenharmony_ci		if (ifr->ifr_hwaddr.sa_family != dev->type)
2468c2ecf20Sopenharmony_ci			return -EINVAL;
2478c2ecf20Sopenharmony_ci		memcpy(dev->broadcast, ifr->ifr_hwaddr.sa_data,
2488c2ecf20Sopenharmony_ci		       min(sizeof(ifr->ifr_hwaddr.sa_data_min),
2498c2ecf20Sopenharmony_ci			   (size_t)dev->addr_len));
2508c2ecf20Sopenharmony_ci		call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
2518c2ecf20Sopenharmony_ci		return 0;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	case SIOCSIFMAP:
2548c2ecf20Sopenharmony_ci		if (ops->ndo_set_config) {
2558c2ecf20Sopenharmony_ci			if (!netif_device_present(dev))
2568c2ecf20Sopenharmony_ci				return -ENODEV;
2578c2ecf20Sopenharmony_ci			return ops->ndo_set_config(dev, &ifr->ifr_map);
2588c2ecf20Sopenharmony_ci		}
2598c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	case SIOCADDMULTI:
2628c2ecf20Sopenharmony_ci		if (!ops->ndo_set_rx_mode ||
2638c2ecf20Sopenharmony_ci		    ifr->ifr_hwaddr.sa_family != AF_UNSPEC)
2648c2ecf20Sopenharmony_ci			return -EINVAL;
2658c2ecf20Sopenharmony_ci		if (!netif_device_present(dev))
2668c2ecf20Sopenharmony_ci			return -ENODEV;
2678c2ecf20Sopenharmony_ci		return dev_mc_add_global(dev, ifr->ifr_hwaddr.sa_data);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	case SIOCDELMULTI:
2708c2ecf20Sopenharmony_ci		if (!ops->ndo_set_rx_mode ||
2718c2ecf20Sopenharmony_ci		    ifr->ifr_hwaddr.sa_family != AF_UNSPEC)
2728c2ecf20Sopenharmony_ci			return -EINVAL;
2738c2ecf20Sopenharmony_ci		if (!netif_device_present(dev))
2748c2ecf20Sopenharmony_ci			return -ENODEV;
2758c2ecf20Sopenharmony_ci		return dev_mc_del_global(dev, ifr->ifr_hwaddr.sa_data);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	case SIOCSIFTXQLEN:
2788c2ecf20Sopenharmony_ci		if (ifr->ifr_qlen < 0)
2798c2ecf20Sopenharmony_ci			return -EINVAL;
2808c2ecf20Sopenharmony_ci		return dev_change_tx_queue_len(dev, ifr->ifr_qlen);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	case SIOCSIFNAME:
2838c2ecf20Sopenharmony_ci		ifr->ifr_newname[IFNAMSIZ-1] = '\0';
2848c2ecf20Sopenharmony_ci		return dev_change_name(dev, ifr->ifr_newname);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	case SIOCSHWTSTAMP:
2878c2ecf20Sopenharmony_ci		err = net_hwtstamp_validate(ifr);
2888c2ecf20Sopenharmony_ci		if (err)
2898c2ecf20Sopenharmony_ci			return err;
2908c2ecf20Sopenharmony_ci		fallthrough;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	/*
2938c2ecf20Sopenharmony_ci	 *	Unknown or private ioctl
2948c2ecf20Sopenharmony_ci	 */
2958c2ecf20Sopenharmony_ci	default:
2968c2ecf20Sopenharmony_ci		if ((cmd >= SIOCDEVPRIVATE &&
2978c2ecf20Sopenharmony_ci		    cmd <= SIOCDEVPRIVATE + 15) ||
2988c2ecf20Sopenharmony_ci		    cmd == SIOCBONDENSLAVE ||
2998c2ecf20Sopenharmony_ci		    cmd == SIOCBONDRELEASE ||
3008c2ecf20Sopenharmony_ci		    cmd == SIOCBONDSETHWADDR ||
3018c2ecf20Sopenharmony_ci		    cmd == SIOCBONDSLAVEINFOQUERY ||
3028c2ecf20Sopenharmony_ci		    cmd == SIOCBONDINFOQUERY ||
3038c2ecf20Sopenharmony_ci		    cmd == SIOCBONDCHANGEACTIVE ||
3048c2ecf20Sopenharmony_ci		    cmd == SIOCGMIIPHY ||
3058c2ecf20Sopenharmony_ci		    cmd == SIOCGMIIREG ||
3068c2ecf20Sopenharmony_ci		    cmd == SIOCSMIIREG ||
3078c2ecf20Sopenharmony_ci		    cmd == SIOCBRADDIF ||
3088c2ecf20Sopenharmony_ci		    cmd == SIOCBRDELIF ||
3098c2ecf20Sopenharmony_ci		    cmd == SIOCSHWTSTAMP ||
3108c2ecf20Sopenharmony_ci		    cmd == SIOCGHWTSTAMP ||
3118c2ecf20Sopenharmony_ci		    cmd == SIOCWANDEV) {
3128c2ecf20Sopenharmony_ci			err = dev_do_ioctl(dev, ifr, cmd);
3138c2ecf20Sopenharmony_ci		} else
3148c2ecf20Sopenharmony_ci			err = -EINVAL;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	}
3178c2ecf20Sopenharmony_ci	return err;
3188c2ecf20Sopenharmony_ci}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci/**
3218c2ecf20Sopenharmony_ci *	dev_load 	- load a network module
3228c2ecf20Sopenharmony_ci *	@net: the applicable net namespace
3238c2ecf20Sopenharmony_ci *	@name: name of interface
3248c2ecf20Sopenharmony_ci *
3258c2ecf20Sopenharmony_ci *	If a network interface is not present and the process has suitable
3268c2ecf20Sopenharmony_ci *	privileges this function loads the module. If module loading is not
3278c2ecf20Sopenharmony_ci *	available in this kernel then it becomes a nop.
3288c2ecf20Sopenharmony_ci */
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_civoid dev_load(struct net *net, const char *name)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	struct net_device *dev;
3338c2ecf20Sopenharmony_ci	int no_module;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	rcu_read_lock();
3368c2ecf20Sopenharmony_ci	dev = dev_get_by_name_rcu(net, name);
3378c2ecf20Sopenharmony_ci	rcu_read_unlock();
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	no_module = !dev;
3408c2ecf20Sopenharmony_ci	if (no_module && capable(CAP_NET_ADMIN))
3418c2ecf20Sopenharmony_ci		no_module = request_module("netdev-%s", name);
3428c2ecf20Sopenharmony_ci	if (no_module && capable(CAP_SYS_MODULE))
3438c2ecf20Sopenharmony_ci		request_module("%s", name);
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_ciEXPORT_SYMBOL(dev_load);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci/*
3488c2ecf20Sopenharmony_ci *	This function handles all "interface"-type I/O control requests. The actual
3498c2ecf20Sopenharmony_ci *	'doing' part of this is dev_ifsioc above.
3508c2ecf20Sopenharmony_ci */
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci/**
3538c2ecf20Sopenharmony_ci *	dev_ioctl	-	network device ioctl
3548c2ecf20Sopenharmony_ci *	@net: the applicable net namespace
3558c2ecf20Sopenharmony_ci *	@cmd: command to issue
3568c2ecf20Sopenharmony_ci *	@ifr: pointer to a struct ifreq in user space
3578c2ecf20Sopenharmony_ci *	@need_copyout: whether or not copy_to_user() should be called
3588c2ecf20Sopenharmony_ci *
3598c2ecf20Sopenharmony_ci *	Issue ioctl functions to devices. This is normally called by the
3608c2ecf20Sopenharmony_ci *	user space syscall interfaces but can sometimes be useful for
3618c2ecf20Sopenharmony_ci *	other purposes. The return value is the return from the syscall if
3628c2ecf20Sopenharmony_ci *	positive or a negative errno code on error.
3638c2ecf20Sopenharmony_ci */
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ciint dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr, bool *need_copyout)
3668c2ecf20Sopenharmony_ci{
3678c2ecf20Sopenharmony_ci	int ret;
3688c2ecf20Sopenharmony_ci	char *colon;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	if (need_copyout)
3718c2ecf20Sopenharmony_ci		*need_copyout = true;
3728c2ecf20Sopenharmony_ci	if (cmd == SIOCGIFNAME)
3738c2ecf20Sopenharmony_ci		return dev_ifname(net, ifr);
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	ifr->ifr_name[IFNAMSIZ-1] = 0;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	colon = strchr(ifr->ifr_name, ':');
3788c2ecf20Sopenharmony_ci	if (colon)
3798c2ecf20Sopenharmony_ci		*colon = 0;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	/*
3828c2ecf20Sopenharmony_ci	 *	See which interface the caller is talking about.
3838c2ecf20Sopenharmony_ci	 */
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	switch (cmd) {
3868c2ecf20Sopenharmony_ci	case SIOCGIFHWADDR:
3878c2ecf20Sopenharmony_ci		dev_load(net, ifr->ifr_name);
3888c2ecf20Sopenharmony_ci		ret = dev_get_mac_address(&ifr->ifr_hwaddr, net, ifr->ifr_name);
3898c2ecf20Sopenharmony_ci		if (colon)
3908c2ecf20Sopenharmony_ci			*colon = ':';
3918c2ecf20Sopenharmony_ci		return ret;
3928c2ecf20Sopenharmony_ci	/*
3938c2ecf20Sopenharmony_ci	 *	These ioctl calls:
3948c2ecf20Sopenharmony_ci	 *	- can be done by all.
3958c2ecf20Sopenharmony_ci	 *	- atomic and do not require locking.
3968c2ecf20Sopenharmony_ci	 *	- return a value
3978c2ecf20Sopenharmony_ci	 */
3988c2ecf20Sopenharmony_ci	case SIOCGIFFLAGS:
3998c2ecf20Sopenharmony_ci	case SIOCGIFMETRIC:
4008c2ecf20Sopenharmony_ci	case SIOCGIFMTU:
4018c2ecf20Sopenharmony_ci	case SIOCGIFSLAVE:
4028c2ecf20Sopenharmony_ci	case SIOCGIFMAP:
4038c2ecf20Sopenharmony_ci	case SIOCGIFINDEX:
4048c2ecf20Sopenharmony_ci	case SIOCGIFTXQLEN:
4058c2ecf20Sopenharmony_ci		dev_load(net, ifr->ifr_name);
4068c2ecf20Sopenharmony_ci		rcu_read_lock();
4078c2ecf20Sopenharmony_ci		ret = dev_ifsioc_locked(net, ifr, cmd);
4088c2ecf20Sopenharmony_ci		rcu_read_unlock();
4098c2ecf20Sopenharmony_ci		if (colon)
4108c2ecf20Sopenharmony_ci			*colon = ':';
4118c2ecf20Sopenharmony_ci		return ret;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	case SIOCETHTOOL:
4148c2ecf20Sopenharmony_ci		dev_load(net, ifr->ifr_name);
4158c2ecf20Sopenharmony_ci		rtnl_lock();
4168c2ecf20Sopenharmony_ci		ret = dev_ethtool(net, ifr);
4178c2ecf20Sopenharmony_ci		rtnl_unlock();
4188c2ecf20Sopenharmony_ci		if (colon)
4198c2ecf20Sopenharmony_ci			*colon = ':';
4208c2ecf20Sopenharmony_ci		return ret;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	/*
4238c2ecf20Sopenharmony_ci	 *	These ioctl calls:
4248c2ecf20Sopenharmony_ci	 *	- require superuser power.
4258c2ecf20Sopenharmony_ci	 *	- require strict serialization.
4268c2ecf20Sopenharmony_ci	 *	- return a value
4278c2ecf20Sopenharmony_ci	 */
4288c2ecf20Sopenharmony_ci	case SIOCGMIIPHY:
4298c2ecf20Sopenharmony_ci	case SIOCGMIIREG:
4308c2ecf20Sopenharmony_ci	case SIOCSIFNAME:
4318c2ecf20Sopenharmony_ci		dev_load(net, ifr->ifr_name);
4328c2ecf20Sopenharmony_ci		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
4338c2ecf20Sopenharmony_ci			return -EPERM;
4348c2ecf20Sopenharmony_ci		rtnl_lock();
4358c2ecf20Sopenharmony_ci		ret = dev_ifsioc(net, ifr, cmd);
4368c2ecf20Sopenharmony_ci		rtnl_unlock();
4378c2ecf20Sopenharmony_ci		if (colon)
4388c2ecf20Sopenharmony_ci			*colon = ':';
4398c2ecf20Sopenharmony_ci		return ret;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	/*
4428c2ecf20Sopenharmony_ci	 *	These ioctl calls:
4438c2ecf20Sopenharmony_ci	 *	- require superuser power.
4448c2ecf20Sopenharmony_ci	 *	- require strict serialization.
4458c2ecf20Sopenharmony_ci	 *	- do not return a value
4468c2ecf20Sopenharmony_ci	 */
4478c2ecf20Sopenharmony_ci	case SIOCSIFMAP:
4488c2ecf20Sopenharmony_ci	case SIOCSIFTXQLEN:
4498c2ecf20Sopenharmony_ci		if (!capable(CAP_NET_ADMIN))
4508c2ecf20Sopenharmony_ci			return -EPERM;
4518c2ecf20Sopenharmony_ci		fallthrough;
4528c2ecf20Sopenharmony_ci	/*
4538c2ecf20Sopenharmony_ci	 *	These ioctl calls:
4548c2ecf20Sopenharmony_ci	 *	- require local superuser power.
4558c2ecf20Sopenharmony_ci	 *	- require strict serialization.
4568c2ecf20Sopenharmony_ci	 *	- do not return a value
4578c2ecf20Sopenharmony_ci	 */
4588c2ecf20Sopenharmony_ci	case SIOCSIFFLAGS:
4598c2ecf20Sopenharmony_ci	case SIOCSIFMETRIC:
4608c2ecf20Sopenharmony_ci	case SIOCSIFMTU:
4618c2ecf20Sopenharmony_ci	case SIOCSIFHWADDR:
4628c2ecf20Sopenharmony_ci	case SIOCSIFSLAVE:
4638c2ecf20Sopenharmony_ci	case SIOCADDMULTI:
4648c2ecf20Sopenharmony_ci	case SIOCDELMULTI:
4658c2ecf20Sopenharmony_ci	case SIOCSIFHWBROADCAST:
4668c2ecf20Sopenharmony_ci	case SIOCSMIIREG:
4678c2ecf20Sopenharmony_ci	case SIOCBONDENSLAVE:
4688c2ecf20Sopenharmony_ci	case SIOCBONDRELEASE:
4698c2ecf20Sopenharmony_ci	case SIOCBONDSETHWADDR:
4708c2ecf20Sopenharmony_ci	case SIOCBONDCHANGEACTIVE:
4718c2ecf20Sopenharmony_ci	case SIOCBRADDIF:
4728c2ecf20Sopenharmony_ci	case SIOCBRDELIF:
4738c2ecf20Sopenharmony_ci	case SIOCSHWTSTAMP:
4748c2ecf20Sopenharmony_ci		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
4758c2ecf20Sopenharmony_ci			return -EPERM;
4768c2ecf20Sopenharmony_ci		fallthrough;
4778c2ecf20Sopenharmony_ci	case SIOCBONDSLAVEINFOQUERY:
4788c2ecf20Sopenharmony_ci	case SIOCBONDINFOQUERY:
4798c2ecf20Sopenharmony_ci		dev_load(net, ifr->ifr_name);
4808c2ecf20Sopenharmony_ci		rtnl_lock();
4818c2ecf20Sopenharmony_ci		ret = dev_ifsioc(net, ifr, cmd);
4828c2ecf20Sopenharmony_ci		rtnl_unlock();
4838c2ecf20Sopenharmony_ci		if (need_copyout)
4848c2ecf20Sopenharmony_ci			*need_copyout = false;
4858c2ecf20Sopenharmony_ci		return ret;
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	case SIOCGIFMEM:
4888c2ecf20Sopenharmony_ci		/* Get the per device memory space. We can add this but
4898c2ecf20Sopenharmony_ci		 * currently do not support it */
4908c2ecf20Sopenharmony_ci	case SIOCSIFMEM:
4918c2ecf20Sopenharmony_ci		/* Set the per device memory buffer space.
4928c2ecf20Sopenharmony_ci		 * Not applicable in our case */
4938c2ecf20Sopenharmony_ci	case SIOCSIFLINK:
4948c2ecf20Sopenharmony_ci		return -ENOTTY;
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	/*
4978c2ecf20Sopenharmony_ci	 *	Unknown or private ioctl.
4988c2ecf20Sopenharmony_ci	 */
4998c2ecf20Sopenharmony_ci	default:
5008c2ecf20Sopenharmony_ci		if (cmd == SIOCWANDEV ||
5018c2ecf20Sopenharmony_ci		    cmd == SIOCGHWTSTAMP ||
5028c2ecf20Sopenharmony_ci		    (cmd >= SIOCDEVPRIVATE &&
5038c2ecf20Sopenharmony_ci		     cmd <= SIOCDEVPRIVATE + 15)) {
5048c2ecf20Sopenharmony_ci			dev_load(net, ifr->ifr_name);
5058c2ecf20Sopenharmony_ci			rtnl_lock();
5068c2ecf20Sopenharmony_ci			ret = dev_ifsioc(net, ifr, cmd);
5078c2ecf20Sopenharmony_ci			rtnl_unlock();
5088c2ecf20Sopenharmony_ci			return ret;
5098c2ecf20Sopenharmony_ci		}
5108c2ecf20Sopenharmony_ci		return -ENOTTY;
5118c2ecf20Sopenharmony_ci	}
5128c2ecf20Sopenharmony_ci}
513