18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/* Copyright (C) 2007-2020  B.A.T.M.A.N. contributors:
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Marek Lindner, Simon Wunderlich
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include "hard-interface.h"
88c2ecf20Sopenharmony_ci#include "main.h"
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/atomic.h>
118c2ecf20Sopenharmony_ci#include <linux/byteorder/generic.h>
128c2ecf20Sopenharmony_ci#include <linux/errno.h>
138c2ecf20Sopenharmony_ci#include <linux/gfp.h>
148c2ecf20Sopenharmony_ci#include <linux/if.h>
158c2ecf20Sopenharmony_ci#include <linux/if_arp.h>
168c2ecf20Sopenharmony_ci#include <linux/if_ether.h>
178c2ecf20Sopenharmony_ci#include <linux/kernel.h>
188c2ecf20Sopenharmony_ci#include <linux/kref.h>
198c2ecf20Sopenharmony_ci#include <linux/limits.h>
208c2ecf20Sopenharmony_ci#include <linux/list.h>
218c2ecf20Sopenharmony_ci#include <linux/mutex.h>
228c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
238c2ecf20Sopenharmony_ci#include <linux/printk.h>
248c2ecf20Sopenharmony_ci#include <linux/rculist.h>
258c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h>
268c2ecf20Sopenharmony_ci#include <linux/slab.h>
278c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
288c2ecf20Sopenharmony_ci#include <net/net_namespace.h>
298c2ecf20Sopenharmony_ci#include <net/rtnetlink.h>
308c2ecf20Sopenharmony_ci#include <uapi/linux/batadv_packet.h>
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#include "bat_v.h"
338c2ecf20Sopenharmony_ci#include "bridge_loop_avoidance.h"
348c2ecf20Sopenharmony_ci#include "debugfs.h"
358c2ecf20Sopenharmony_ci#include "distributed-arp-table.h"
368c2ecf20Sopenharmony_ci#include "gateway_client.h"
378c2ecf20Sopenharmony_ci#include "log.h"
388c2ecf20Sopenharmony_ci#include "originator.h"
398c2ecf20Sopenharmony_ci#include "send.h"
408c2ecf20Sopenharmony_ci#include "soft-interface.h"
418c2ecf20Sopenharmony_ci#include "sysfs.h"
428c2ecf20Sopenharmony_ci#include "translation-table.h"
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci/**
458c2ecf20Sopenharmony_ci * batadv_hardif_release() - release hard interface from lists and queue for
468c2ecf20Sopenharmony_ci *  free after rcu grace period
478c2ecf20Sopenharmony_ci * @ref: kref pointer of the hard interface
488c2ecf20Sopenharmony_ci */
498c2ecf20Sopenharmony_civoid batadv_hardif_release(struct kref *ref)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	struct batadv_hard_iface *hard_iface;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	hard_iface = container_of(ref, struct batadv_hard_iface, refcount);
548c2ecf20Sopenharmony_ci	dev_put(hard_iface->net_dev);
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	kfree_rcu(hard_iface, rcu);
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci/**
608c2ecf20Sopenharmony_ci * batadv_hardif_get_by_netdev() - Get hard interface object of a net_device
618c2ecf20Sopenharmony_ci * @net_dev: net_device to search for
628c2ecf20Sopenharmony_ci *
638c2ecf20Sopenharmony_ci * Return: batadv_hard_iface of net_dev (with increased refcnt), NULL on errors
648c2ecf20Sopenharmony_ci */
658c2ecf20Sopenharmony_cistruct batadv_hard_iface *
668c2ecf20Sopenharmony_cibatadv_hardif_get_by_netdev(const struct net_device *net_dev)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	struct batadv_hard_iface *hard_iface;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	rcu_read_lock();
718c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
728c2ecf20Sopenharmony_ci		if (hard_iface->net_dev == net_dev &&
738c2ecf20Sopenharmony_ci		    kref_get_unless_zero(&hard_iface->refcount))
748c2ecf20Sopenharmony_ci			goto out;
758c2ecf20Sopenharmony_ci	}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	hard_iface = NULL;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ciout:
808c2ecf20Sopenharmony_ci	rcu_read_unlock();
818c2ecf20Sopenharmony_ci	return hard_iface;
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci/**
858c2ecf20Sopenharmony_ci * batadv_getlink_net() - return link net namespace (of use fallback)
868c2ecf20Sopenharmony_ci * @netdev: net_device to check
878c2ecf20Sopenharmony_ci * @fallback_net: return in case get_link_net is not available for @netdev
888c2ecf20Sopenharmony_ci *
898c2ecf20Sopenharmony_ci * Return: result of rtnl_link_ops->get_link_net or @fallback_net
908c2ecf20Sopenharmony_ci */
918c2ecf20Sopenharmony_cistatic struct net *batadv_getlink_net(const struct net_device *netdev,
928c2ecf20Sopenharmony_ci				      struct net *fallback_net)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	if (!netdev->rtnl_link_ops)
958c2ecf20Sopenharmony_ci		return fallback_net;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	if (!netdev->rtnl_link_ops->get_link_net)
988c2ecf20Sopenharmony_ci		return fallback_net;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	return netdev->rtnl_link_ops->get_link_net(netdev);
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci/**
1048c2ecf20Sopenharmony_ci * batadv_mutual_parents() - check if two devices are each others parent
1058c2ecf20Sopenharmony_ci * @dev1: 1st net dev
1068c2ecf20Sopenharmony_ci * @net1: 1st devices netns
1078c2ecf20Sopenharmony_ci * @dev2: 2nd net dev
1088c2ecf20Sopenharmony_ci * @net2: 2nd devices netns
1098c2ecf20Sopenharmony_ci *
1108c2ecf20Sopenharmony_ci * veth devices come in pairs and each is the parent of the other!
1118c2ecf20Sopenharmony_ci *
1128c2ecf20Sopenharmony_ci * Return: true if the devices are each others parent, otherwise false
1138c2ecf20Sopenharmony_ci */
1148c2ecf20Sopenharmony_cistatic bool batadv_mutual_parents(const struct net_device *dev1,
1158c2ecf20Sopenharmony_ci				  struct net *net1,
1168c2ecf20Sopenharmony_ci				  const struct net_device *dev2,
1178c2ecf20Sopenharmony_ci				  struct net *net2)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	int dev1_parent_iflink = dev_get_iflink(dev1);
1208c2ecf20Sopenharmony_ci	int dev2_parent_iflink = dev_get_iflink(dev2);
1218c2ecf20Sopenharmony_ci	const struct net *dev1_parent_net;
1228c2ecf20Sopenharmony_ci	const struct net *dev2_parent_net;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	dev1_parent_net = batadv_getlink_net(dev1, net1);
1258c2ecf20Sopenharmony_ci	dev2_parent_net = batadv_getlink_net(dev2, net2);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	if (!dev1_parent_iflink || !dev2_parent_iflink)
1288c2ecf20Sopenharmony_ci		return false;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	return (dev1_parent_iflink == dev2->ifindex) &&
1318c2ecf20Sopenharmony_ci	       (dev2_parent_iflink == dev1->ifindex) &&
1328c2ecf20Sopenharmony_ci	       net_eq(dev1_parent_net, net2) &&
1338c2ecf20Sopenharmony_ci	       net_eq(dev2_parent_net, net1);
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci/**
1378c2ecf20Sopenharmony_ci * batadv_is_on_batman_iface() - check if a device is a batman iface descendant
1388c2ecf20Sopenharmony_ci * @net_dev: the device to check
1398c2ecf20Sopenharmony_ci *
1408c2ecf20Sopenharmony_ci * If the user creates any virtual device on top of a batman-adv interface, it
1418c2ecf20Sopenharmony_ci * is important to prevent this new interface from being used to create a new
1428c2ecf20Sopenharmony_ci * mesh network (this behaviour would lead to a batman-over-batman
1438c2ecf20Sopenharmony_ci * configuration). This function recursively checks all the fathers of the
1448c2ecf20Sopenharmony_ci * device passed as argument looking for a batman-adv soft interface.
1458c2ecf20Sopenharmony_ci *
1468c2ecf20Sopenharmony_ci * Return: true if the device is descendant of a batman-adv mesh interface (or
1478c2ecf20Sopenharmony_ci * if it is a batman-adv interface itself), false otherwise
1488c2ecf20Sopenharmony_ci */
1498c2ecf20Sopenharmony_cistatic bool batadv_is_on_batman_iface(const struct net_device *net_dev)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	struct net *net = dev_net(net_dev);
1528c2ecf20Sopenharmony_ci	struct net_device *parent_dev;
1538c2ecf20Sopenharmony_ci	struct net *parent_net;
1548c2ecf20Sopenharmony_ci	int iflink;
1558c2ecf20Sopenharmony_ci	bool ret;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	/* check if this is a batman-adv mesh interface */
1588c2ecf20Sopenharmony_ci	if (batadv_softif_is_valid(net_dev))
1598c2ecf20Sopenharmony_ci		return true;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	iflink = dev_get_iflink(net_dev);
1628c2ecf20Sopenharmony_ci	if (iflink == 0)
1638c2ecf20Sopenharmony_ci		return false;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	parent_net = batadv_getlink_net(net_dev, net);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	/* iflink to itself, most likely physical device */
1688c2ecf20Sopenharmony_ci	if (net == parent_net && iflink == net_dev->ifindex)
1698c2ecf20Sopenharmony_ci		return false;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	/* recurse over the parent device */
1728c2ecf20Sopenharmony_ci	parent_dev = __dev_get_by_index((struct net *)parent_net, iflink);
1738c2ecf20Sopenharmony_ci	/* if we got a NULL parent_dev there is something broken.. */
1748c2ecf20Sopenharmony_ci	if (!parent_dev) {
1758c2ecf20Sopenharmony_ci		pr_err("Cannot find parent device\n");
1768c2ecf20Sopenharmony_ci		return false;
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	if (batadv_mutual_parents(net_dev, net, parent_dev, parent_net))
1808c2ecf20Sopenharmony_ci		return false;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	ret = batadv_is_on_batman_iface(parent_dev);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	return ret;
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cistatic bool batadv_is_valid_iface(const struct net_device *net_dev)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	if (net_dev->flags & IFF_LOOPBACK)
1908c2ecf20Sopenharmony_ci		return false;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	if (net_dev->type != ARPHRD_ETHER)
1938c2ecf20Sopenharmony_ci		return false;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	if (net_dev->addr_len != ETH_ALEN)
1968c2ecf20Sopenharmony_ci		return false;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	/* no batman over batman */
1998c2ecf20Sopenharmony_ci	if (batadv_is_on_batman_iface(net_dev))
2008c2ecf20Sopenharmony_ci		return false;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	return true;
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci/**
2068c2ecf20Sopenharmony_ci * batadv_get_real_netdevice() - check if the given netdev struct is a virtual
2078c2ecf20Sopenharmony_ci *  interface on top of another 'real' interface
2088c2ecf20Sopenharmony_ci * @netdev: the device to check
2098c2ecf20Sopenharmony_ci *
2108c2ecf20Sopenharmony_ci * Callers must hold the rtnl semaphore. You may want batadv_get_real_netdev()
2118c2ecf20Sopenharmony_ci * instead of this.
2128c2ecf20Sopenharmony_ci *
2138c2ecf20Sopenharmony_ci * Return: the 'real' net device or the original net device and NULL in case
2148c2ecf20Sopenharmony_ci *  of an error.
2158c2ecf20Sopenharmony_ci */
2168c2ecf20Sopenharmony_cistatic struct net_device *batadv_get_real_netdevice(struct net_device *netdev)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	struct batadv_hard_iface *hard_iface = NULL;
2198c2ecf20Sopenharmony_ci	struct net_device *real_netdev = NULL;
2208c2ecf20Sopenharmony_ci	struct net *real_net;
2218c2ecf20Sopenharmony_ci	struct net *net;
2228c2ecf20Sopenharmony_ci	int iflink;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	ASSERT_RTNL();
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	if (!netdev)
2278c2ecf20Sopenharmony_ci		return NULL;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	iflink = dev_get_iflink(netdev);
2308c2ecf20Sopenharmony_ci	if (iflink == 0) {
2318c2ecf20Sopenharmony_ci		dev_hold(netdev);
2328c2ecf20Sopenharmony_ci		return netdev;
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	hard_iface = batadv_hardif_get_by_netdev(netdev);
2368c2ecf20Sopenharmony_ci	if (!hard_iface || !hard_iface->soft_iface)
2378c2ecf20Sopenharmony_ci		goto out;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	net = dev_net(hard_iface->soft_iface);
2408c2ecf20Sopenharmony_ci	real_net = batadv_getlink_net(netdev, net);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	/* iflink to itself, most likely physical device */
2438c2ecf20Sopenharmony_ci	if (net == real_net && netdev->ifindex == iflink) {
2448c2ecf20Sopenharmony_ci		real_netdev = netdev;
2458c2ecf20Sopenharmony_ci		dev_hold(real_netdev);
2468c2ecf20Sopenharmony_ci		goto out;
2478c2ecf20Sopenharmony_ci	}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	real_netdev = dev_get_by_index(real_net, iflink);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ciout:
2528c2ecf20Sopenharmony_ci	if (hard_iface)
2538c2ecf20Sopenharmony_ci		batadv_hardif_put(hard_iface);
2548c2ecf20Sopenharmony_ci	return real_netdev;
2558c2ecf20Sopenharmony_ci}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci/**
2588c2ecf20Sopenharmony_ci * batadv_get_real_netdev() - check if the given net_device struct is a virtual
2598c2ecf20Sopenharmony_ci *  interface on top of another 'real' interface
2608c2ecf20Sopenharmony_ci * @net_device: the device to check
2618c2ecf20Sopenharmony_ci *
2628c2ecf20Sopenharmony_ci * Return: the 'real' net device or the original net device and NULL in case
2638c2ecf20Sopenharmony_ci *  of an error.
2648c2ecf20Sopenharmony_ci */
2658c2ecf20Sopenharmony_cistruct net_device *batadv_get_real_netdev(struct net_device *net_device)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	struct net_device *real_netdev;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	rtnl_lock();
2708c2ecf20Sopenharmony_ci	real_netdev = batadv_get_real_netdevice(net_device);
2718c2ecf20Sopenharmony_ci	rtnl_unlock();
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	return real_netdev;
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci/**
2778c2ecf20Sopenharmony_ci * batadv_is_wext_netdev() - check if the given net_device struct is a
2788c2ecf20Sopenharmony_ci *  wext wifi interface
2798c2ecf20Sopenharmony_ci * @net_device: the device to check
2808c2ecf20Sopenharmony_ci *
2818c2ecf20Sopenharmony_ci * Return: true if the net device is a wext wireless device, false
2828c2ecf20Sopenharmony_ci *  otherwise.
2838c2ecf20Sopenharmony_ci */
2848c2ecf20Sopenharmony_cistatic bool batadv_is_wext_netdev(struct net_device *net_device)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci	if (!net_device)
2878c2ecf20Sopenharmony_ci		return false;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci#ifdef CONFIG_WIRELESS_EXT
2908c2ecf20Sopenharmony_ci	/* pre-cfg80211 drivers have to implement WEXT, so it is possible to
2918c2ecf20Sopenharmony_ci	 * check for wireless_handlers != NULL
2928c2ecf20Sopenharmony_ci	 */
2938c2ecf20Sopenharmony_ci	if (net_device->wireless_handlers)
2948c2ecf20Sopenharmony_ci		return true;
2958c2ecf20Sopenharmony_ci#endif
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	return false;
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci/**
3018c2ecf20Sopenharmony_ci * batadv_is_cfg80211_netdev() - check if the given net_device struct is a
3028c2ecf20Sopenharmony_ci *  cfg80211 wifi interface
3038c2ecf20Sopenharmony_ci * @net_device: the device to check
3048c2ecf20Sopenharmony_ci *
3058c2ecf20Sopenharmony_ci * Return: true if the net device is a cfg80211 wireless device, false
3068c2ecf20Sopenharmony_ci *  otherwise.
3078c2ecf20Sopenharmony_ci */
3088c2ecf20Sopenharmony_cistatic bool batadv_is_cfg80211_netdev(struct net_device *net_device)
3098c2ecf20Sopenharmony_ci{
3108c2ecf20Sopenharmony_ci	if (!net_device)
3118c2ecf20Sopenharmony_ci		return false;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	/* cfg80211 drivers have to set ieee80211_ptr */
3148c2ecf20Sopenharmony_ci	if (net_device->ieee80211_ptr)
3158c2ecf20Sopenharmony_ci		return true;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	return false;
3188c2ecf20Sopenharmony_ci}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci/**
3218c2ecf20Sopenharmony_ci * batadv_wifi_flags_evaluate() - calculate wifi flags for net_device
3228c2ecf20Sopenharmony_ci * @net_device: the device to check
3238c2ecf20Sopenharmony_ci *
3248c2ecf20Sopenharmony_ci * Return: batadv_hard_iface_wifi_flags flags of the device
3258c2ecf20Sopenharmony_ci */
3268c2ecf20Sopenharmony_cistatic u32 batadv_wifi_flags_evaluate(struct net_device *net_device)
3278c2ecf20Sopenharmony_ci{
3288c2ecf20Sopenharmony_ci	u32 wifi_flags = 0;
3298c2ecf20Sopenharmony_ci	struct net_device *real_netdev;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	if (batadv_is_wext_netdev(net_device))
3328c2ecf20Sopenharmony_ci		wifi_flags |= BATADV_HARDIF_WIFI_WEXT_DIRECT;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	if (batadv_is_cfg80211_netdev(net_device))
3358c2ecf20Sopenharmony_ci		wifi_flags |= BATADV_HARDIF_WIFI_CFG80211_DIRECT;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	real_netdev = batadv_get_real_netdevice(net_device);
3388c2ecf20Sopenharmony_ci	if (!real_netdev)
3398c2ecf20Sopenharmony_ci		return wifi_flags;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	if (real_netdev == net_device)
3428c2ecf20Sopenharmony_ci		goto out;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	if (batadv_is_wext_netdev(real_netdev))
3458c2ecf20Sopenharmony_ci		wifi_flags |= BATADV_HARDIF_WIFI_WEXT_INDIRECT;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	if (batadv_is_cfg80211_netdev(real_netdev))
3488c2ecf20Sopenharmony_ci		wifi_flags |= BATADV_HARDIF_WIFI_CFG80211_INDIRECT;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ciout:
3518c2ecf20Sopenharmony_ci	dev_put(real_netdev);
3528c2ecf20Sopenharmony_ci	return wifi_flags;
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci/**
3568c2ecf20Sopenharmony_ci * batadv_is_cfg80211_hardif() - check if the given hardif is a cfg80211 wifi
3578c2ecf20Sopenharmony_ci *  interface
3588c2ecf20Sopenharmony_ci * @hard_iface: the device to check
3598c2ecf20Sopenharmony_ci *
3608c2ecf20Sopenharmony_ci * Return: true if the net device is a cfg80211 wireless device, false
3618c2ecf20Sopenharmony_ci *  otherwise.
3628c2ecf20Sopenharmony_ci */
3638c2ecf20Sopenharmony_cibool batadv_is_cfg80211_hardif(struct batadv_hard_iface *hard_iface)
3648c2ecf20Sopenharmony_ci{
3658c2ecf20Sopenharmony_ci	u32 allowed_flags = 0;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	allowed_flags |= BATADV_HARDIF_WIFI_CFG80211_DIRECT;
3688c2ecf20Sopenharmony_ci	allowed_flags |= BATADV_HARDIF_WIFI_CFG80211_INDIRECT;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	return !!(hard_iface->wifi_flags & allowed_flags);
3718c2ecf20Sopenharmony_ci}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci/**
3748c2ecf20Sopenharmony_ci * batadv_is_wifi_hardif() - check if the given hardif is a wifi interface
3758c2ecf20Sopenharmony_ci * @hard_iface: the device to check
3768c2ecf20Sopenharmony_ci *
3778c2ecf20Sopenharmony_ci * Return: true if the net device is a 802.11 wireless device, false otherwise.
3788c2ecf20Sopenharmony_ci */
3798c2ecf20Sopenharmony_cibool batadv_is_wifi_hardif(struct batadv_hard_iface *hard_iface)
3808c2ecf20Sopenharmony_ci{
3818c2ecf20Sopenharmony_ci	if (!hard_iface)
3828c2ecf20Sopenharmony_ci		return false;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	return hard_iface->wifi_flags != 0;
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci/**
3888c2ecf20Sopenharmony_ci * batadv_hardif_no_broadcast() - check whether (re)broadcast is necessary
3898c2ecf20Sopenharmony_ci * @if_outgoing: the outgoing interface checked and considered for (re)broadcast
3908c2ecf20Sopenharmony_ci * @orig_addr: the originator of this packet
3918c2ecf20Sopenharmony_ci * @orig_neigh: originator address of the forwarder we just got the packet from
3928c2ecf20Sopenharmony_ci *  (NULL if we originated)
3938c2ecf20Sopenharmony_ci *
3948c2ecf20Sopenharmony_ci * Checks whether a packet needs to be (re)broadcasted on the given interface.
3958c2ecf20Sopenharmony_ci *
3968c2ecf20Sopenharmony_ci * Return:
3978c2ecf20Sopenharmony_ci *	BATADV_HARDIF_BCAST_NORECIPIENT: No neighbor on interface
3988c2ecf20Sopenharmony_ci *	BATADV_HARDIF_BCAST_DUPFWD: Just one neighbor, but it is the forwarder
3998c2ecf20Sopenharmony_ci *	BATADV_HARDIF_BCAST_DUPORIG: Just one neighbor, but it is the originator
4008c2ecf20Sopenharmony_ci *	BATADV_HARDIF_BCAST_OK: Several neighbors, must broadcast
4018c2ecf20Sopenharmony_ci */
4028c2ecf20Sopenharmony_ciint batadv_hardif_no_broadcast(struct batadv_hard_iface *if_outgoing,
4038c2ecf20Sopenharmony_ci			       u8 *orig_addr, u8 *orig_neigh)
4048c2ecf20Sopenharmony_ci{
4058c2ecf20Sopenharmony_ci	struct batadv_hardif_neigh_node *hardif_neigh;
4068c2ecf20Sopenharmony_ci	struct hlist_node *first;
4078c2ecf20Sopenharmony_ci	int ret = BATADV_HARDIF_BCAST_OK;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	rcu_read_lock();
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	/* 0 neighbors -> no (re)broadcast */
4128c2ecf20Sopenharmony_ci	first = rcu_dereference(hlist_first_rcu(&if_outgoing->neigh_list));
4138c2ecf20Sopenharmony_ci	if (!first) {
4148c2ecf20Sopenharmony_ci		ret = BATADV_HARDIF_BCAST_NORECIPIENT;
4158c2ecf20Sopenharmony_ci		goto out;
4168c2ecf20Sopenharmony_ci	}
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	/* >1 neighbors -> (re)brodcast */
4198c2ecf20Sopenharmony_ci	if (rcu_dereference(hlist_next_rcu(first)))
4208c2ecf20Sopenharmony_ci		goto out;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	hardif_neigh = hlist_entry(first, struct batadv_hardif_neigh_node,
4238c2ecf20Sopenharmony_ci				   list);
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	/* 1 neighbor, is the originator -> no rebroadcast */
4268c2ecf20Sopenharmony_ci	if (orig_addr && batadv_compare_eth(hardif_neigh->orig, orig_addr)) {
4278c2ecf20Sopenharmony_ci		ret = BATADV_HARDIF_BCAST_DUPORIG;
4288c2ecf20Sopenharmony_ci	/* 1 neighbor, is the one we received from -> no rebroadcast */
4298c2ecf20Sopenharmony_ci	} else if (orig_neigh &&
4308c2ecf20Sopenharmony_ci		   batadv_compare_eth(hardif_neigh->orig, orig_neigh)) {
4318c2ecf20Sopenharmony_ci		ret = BATADV_HARDIF_BCAST_DUPFWD;
4328c2ecf20Sopenharmony_ci	}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ciout:
4358c2ecf20Sopenharmony_ci	rcu_read_unlock();
4368c2ecf20Sopenharmony_ci	return ret;
4378c2ecf20Sopenharmony_ci}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_cistatic struct batadv_hard_iface *
4408c2ecf20Sopenharmony_cibatadv_hardif_get_active(const struct net_device *soft_iface)
4418c2ecf20Sopenharmony_ci{
4428c2ecf20Sopenharmony_ci	struct batadv_hard_iface *hard_iface;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	rcu_read_lock();
4458c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
4468c2ecf20Sopenharmony_ci		if (hard_iface->soft_iface != soft_iface)
4478c2ecf20Sopenharmony_ci			continue;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci		if (hard_iface->if_status == BATADV_IF_ACTIVE &&
4508c2ecf20Sopenharmony_ci		    kref_get_unless_zero(&hard_iface->refcount))
4518c2ecf20Sopenharmony_ci			goto out;
4528c2ecf20Sopenharmony_ci	}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	hard_iface = NULL;
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ciout:
4578c2ecf20Sopenharmony_ci	rcu_read_unlock();
4588c2ecf20Sopenharmony_ci	return hard_iface;
4598c2ecf20Sopenharmony_ci}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_cistatic void batadv_primary_if_update_addr(struct batadv_priv *bat_priv,
4628c2ecf20Sopenharmony_ci					  struct batadv_hard_iface *oldif)
4638c2ecf20Sopenharmony_ci{
4648c2ecf20Sopenharmony_ci	struct batadv_hard_iface *primary_if;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	primary_if = batadv_primary_if_get_selected(bat_priv);
4678c2ecf20Sopenharmony_ci	if (!primary_if)
4688c2ecf20Sopenharmony_ci		goto out;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	batadv_dat_init_own_addr(bat_priv, primary_if);
4718c2ecf20Sopenharmony_ci	batadv_bla_update_orig_address(bat_priv, primary_if, oldif);
4728c2ecf20Sopenharmony_ciout:
4738c2ecf20Sopenharmony_ci	if (primary_if)
4748c2ecf20Sopenharmony_ci		batadv_hardif_put(primary_if);
4758c2ecf20Sopenharmony_ci}
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_cistatic void batadv_primary_if_select(struct batadv_priv *bat_priv,
4788c2ecf20Sopenharmony_ci				     struct batadv_hard_iface *new_hard_iface)
4798c2ecf20Sopenharmony_ci{
4808c2ecf20Sopenharmony_ci	struct batadv_hard_iface *curr_hard_iface;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	ASSERT_RTNL();
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	if (new_hard_iface)
4858c2ecf20Sopenharmony_ci		kref_get(&new_hard_iface->refcount);
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	curr_hard_iface = rcu_replace_pointer(bat_priv->primary_if,
4888c2ecf20Sopenharmony_ci					      new_hard_iface, 1);
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	if (!new_hard_iface)
4918c2ecf20Sopenharmony_ci		goto out;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	bat_priv->algo_ops->iface.primary_set(new_hard_iface);
4948c2ecf20Sopenharmony_ci	batadv_primary_if_update_addr(bat_priv, curr_hard_iface);
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ciout:
4978c2ecf20Sopenharmony_ci	if (curr_hard_iface)
4988c2ecf20Sopenharmony_ci		batadv_hardif_put(curr_hard_iface);
4998c2ecf20Sopenharmony_ci}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_cistatic bool
5028c2ecf20Sopenharmony_cibatadv_hardif_is_iface_up(const struct batadv_hard_iface *hard_iface)
5038c2ecf20Sopenharmony_ci{
5048c2ecf20Sopenharmony_ci	if (hard_iface->net_dev->flags & IFF_UP)
5058c2ecf20Sopenharmony_ci		return true;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	return false;
5088c2ecf20Sopenharmony_ci}
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_cistatic void batadv_check_known_mac_addr(const struct net_device *net_dev)
5118c2ecf20Sopenharmony_ci{
5128c2ecf20Sopenharmony_ci	const struct batadv_hard_iface *hard_iface;
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	rcu_read_lock();
5158c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
5168c2ecf20Sopenharmony_ci		if (hard_iface->if_status != BATADV_IF_ACTIVE &&
5178c2ecf20Sopenharmony_ci		    hard_iface->if_status != BATADV_IF_TO_BE_ACTIVATED)
5188c2ecf20Sopenharmony_ci			continue;
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci		if (hard_iface->net_dev == net_dev)
5218c2ecf20Sopenharmony_ci			continue;
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci		if (!batadv_compare_eth(hard_iface->net_dev->dev_addr,
5248c2ecf20Sopenharmony_ci					net_dev->dev_addr))
5258c2ecf20Sopenharmony_ci			continue;
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci		pr_warn("The newly added mac address (%pM) already exists on: %s\n",
5288c2ecf20Sopenharmony_ci			net_dev->dev_addr, hard_iface->net_dev->name);
5298c2ecf20Sopenharmony_ci		pr_warn("It is strongly recommended to keep mac addresses unique to avoid problems!\n");
5308c2ecf20Sopenharmony_ci	}
5318c2ecf20Sopenharmony_ci	rcu_read_unlock();
5328c2ecf20Sopenharmony_ci}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci/**
5358c2ecf20Sopenharmony_ci * batadv_hardif_recalc_extra_skbroom() - Recalculate skbuff extra head/tailroom
5368c2ecf20Sopenharmony_ci * @soft_iface: netdev struct of the mesh interface
5378c2ecf20Sopenharmony_ci */
5388c2ecf20Sopenharmony_cistatic void batadv_hardif_recalc_extra_skbroom(struct net_device *soft_iface)
5398c2ecf20Sopenharmony_ci{
5408c2ecf20Sopenharmony_ci	const struct batadv_hard_iface *hard_iface;
5418c2ecf20Sopenharmony_ci	unsigned short lower_header_len = ETH_HLEN;
5428c2ecf20Sopenharmony_ci	unsigned short lower_headroom = 0;
5438c2ecf20Sopenharmony_ci	unsigned short lower_tailroom = 0;
5448c2ecf20Sopenharmony_ci	unsigned short needed_headroom;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	rcu_read_lock();
5478c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
5488c2ecf20Sopenharmony_ci		if (hard_iface->if_status == BATADV_IF_NOT_IN_USE)
5498c2ecf20Sopenharmony_ci			continue;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci		if (hard_iface->soft_iface != soft_iface)
5528c2ecf20Sopenharmony_ci			continue;
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci		lower_header_len = max_t(unsigned short, lower_header_len,
5558c2ecf20Sopenharmony_ci					 hard_iface->net_dev->hard_header_len);
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci		lower_headroom = max_t(unsigned short, lower_headroom,
5588c2ecf20Sopenharmony_ci				       hard_iface->net_dev->needed_headroom);
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci		lower_tailroom = max_t(unsigned short, lower_tailroom,
5618c2ecf20Sopenharmony_ci				       hard_iface->net_dev->needed_tailroom);
5628c2ecf20Sopenharmony_ci	}
5638c2ecf20Sopenharmony_ci	rcu_read_unlock();
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	needed_headroom = lower_headroom + (lower_header_len - ETH_HLEN);
5668c2ecf20Sopenharmony_ci	needed_headroom += batadv_max_header_len();
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	/* fragmentation headers don't strip the unicast/... header */
5698c2ecf20Sopenharmony_ci	needed_headroom += sizeof(struct batadv_frag_packet);
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	soft_iface->needed_headroom = needed_headroom;
5728c2ecf20Sopenharmony_ci	soft_iface->needed_tailroom = lower_tailroom;
5738c2ecf20Sopenharmony_ci}
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci/**
5768c2ecf20Sopenharmony_ci * batadv_hardif_min_mtu() - Calculate maximum MTU for soft interface
5778c2ecf20Sopenharmony_ci * @soft_iface: netdev struct of the soft interface
5788c2ecf20Sopenharmony_ci *
5798c2ecf20Sopenharmony_ci * Return: MTU for the soft-interface (limited by the minimal MTU of all active
5808c2ecf20Sopenharmony_ci *  slave interfaces)
5818c2ecf20Sopenharmony_ci */
5828c2ecf20Sopenharmony_ciint batadv_hardif_min_mtu(struct net_device *soft_iface)
5838c2ecf20Sopenharmony_ci{
5848c2ecf20Sopenharmony_ci	struct batadv_priv *bat_priv = netdev_priv(soft_iface);
5858c2ecf20Sopenharmony_ci	const struct batadv_hard_iface *hard_iface;
5868c2ecf20Sopenharmony_ci	int min_mtu = INT_MAX;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	rcu_read_lock();
5898c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
5908c2ecf20Sopenharmony_ci		if (hard_iface->if_status != BATADV_IF_ACTIVE &&
5918c2ecf20Sopenharmony_ci		    hard_iface->if_status != BATADV_IF_TO_BE_ACTIVATED)
5928c2ecf20Sopenharmony_ci			continue;
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci		if (hard_iface->soft_iface != soft_iface)
5958c2ecf20Sopenharmony_ci			continue;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci		min_mtu = min_t(int, hard_iface->net_dev->mtu, min_mtu);
5988c2ecf20Sopenharmony_ci	}
5998c2ecf20Sopenharmony_ci	rcu_read_unlock();
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	if (atomic_read(&bat_priv->fragmentation) == 0)
6028c2ecf20Sopenharmony_ci		goto out;
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	/* with fragmentation enabled the maximum size of internally generated
6058c2ecf20Sopenharmony_ci	 * packets such as translation table exchanges or tvlv containers, etc
6068c2ecf20Sopenharmony_ci	 * has to be calculated
6078c2ecf20Sopenharmony_ci	 */
6088c2ecf20Sopenharmony_ci	min_mtu = min_t(int, min_mtu, BATADV_FRAG_MAX_FRAG_SIZE);
6098c2ecf20Sopenharmony_ci	min_mtu -= sizeof(struct batadv_frag_packet);
6108c2ecf20Sopenharmony_ci	min_mtu *= BATADV_FRAG_MAX_FRAGMENTS;
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ciout:
6138c2ecf20Sopenharmony_ci	/* report to the other components the maximum amount of bytes that
6148c2ecf20Sopenharmony_ci	 * batman-adv can send over the wire (without considering the payload
6158c2ecf20Sopenharmony_ci	 * overhead). For example, this value is used by TT to compute the
6168c2ecf20Sopenharmony_ci	 * maximum local table size
6178c2ecf20Sopenharmony_ci	 */
6188c2ecf20Sopenharmony_ci	atomic_set(&bat_priv->packet_size_max, min_mtu);
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	/* the real soft-interface MTU is computed by removing the payload
6218c2ecf20Sopenharmony_ci	 * overhead from the maximum amount of bytes that was just computed.
6228c2ecf20Sopenharmony_ci	 *
6238c2ecf20Sopenharmony_ci	 * However batman-adv does not support MTUs bigger than ETH_DATA_LEN
6248c2ecf20Sopenharmony_ci	 */
6258c2ecf20Sopenharmony_ci	return min_t(int, min_mtu - batadv_max_header_len(), ETH_DATA_LEN);
6268c2ecf20Sopenharmony_ci}
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci/**
6298c2ecf20Sopenharmony_ci * batadv_update_min_mtu() - Adjusts the MTU if a new interface with a smaller
6308c2ecf20Sopenharmony_ci *  MTU appeared
6318c2ecf20Sopenharmony_ci * @soft_iface: netdev struct of the soft interface
6328c2ecf20Sopenharmony_ci */
6338c2ecf20Sopenharmony_civoid batadv_update_min_mtu(struct net_device *soft_iface)
6348c2ecf20Sopenharmony_ci{
6358c2ecf20Sopenharmony_ci	struct batadv_priv *bat_priv = netdev_priv(soft_iface);
6368c2ecf20Sopenharmony_ci	int limit_mtu;
6378c2ecf20Sopenharmony_ci	int mtu;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	mtu = batadv_hardif_min_mtu(soft_iface);
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	if (bat_priv->mtu_set_by_user)
6428c2ecf20Sopenharmony_ci		limit_mtu = bat_priv->mtu_set_by_user;
6438c2ecf20Sopenharmony_ci	else
6448c2ecf20Sopenharmony_ci		limit_mtu = ETH_DATA_LEN;
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	mtu = min(mtu, limit_mtu);
6478c2ecf20Sopenharmony_ci	dev_set_mtu(soft_iface, mtu);
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	/* Check if the local translate table should be cleaned up to match a
6508c2ecf20Sopenharmony_ci	 * new (and smaller) MTU.
6518c2ecf20Sopenharmony_ci	 */
6528c2ecf20Sopenharmony_ci	batadv_tt_local_resize_to_mtu(soft_iface);
6538c2ecf20Sopenharmony_ci}
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_cistatic void
6568c2ecf20Sopenharmony_cibatadv_hardif_activate_interface(struct batadv_hard_iface *hard_iface)
6578c2ecf20Sopenharmony_ci{
6588c2ecf20Sopenharmony_ci	struct batadv_priv *bat_priv;
6598c2ecf20Sopenharmony_ci	struct batadv_hard_iface *primary_if = NULL;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	if (hard_iface->if_status != BATADV_IF_INACTIVE)
6628c2ecf20Sopenharmony_ci		goto out;
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	bat_priv = netdev_priv(hard_iface->soft_iface);
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	bat_priv->algo_ops->iface.update_mac(hard_iface);
6678c2ecf20Sopenharmony_ci	hard_iface->if_status = BATADV_IF_TO_BE_ACTIVATED;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	/* the first active interface becomes our primary interface or
6708c2ecf20Sopenharmony_ci	 * the next active interface after the old primary interface was removed
6718c2ecf20Sopenharmony_ci	 */
6728c2ecf20Sopenharmony_ci	primary_if = batadv_primary_if_get_selected(bat_priv);
6738c2ecf20Sopenharmony_ci	if (!primary_if)
6748c2ecf20Sopenharmony_ci		batadv_primary_if_select(bat_priv, hard_iface);
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	batadv_info(hard_iface->soft_iface, "Interface activated: %s\n",
6778c2ecf20Sopenharmony_ci		    hard_iface->net_dev->name);
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	batadv_update_min_mtu(hard_iface->soft_iface);
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	if (bat_priv->algo_ops->iface.activate)
6828c2ecf20Sopenharmony_ci		bat_priv->algo_ops->iface.activate(hard_iface);
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ciout:
6858c2ecf20Sopenharmony_ci	if (primary_if)
6868c2ecf20Sopenharmony_ci		batadv_hardif_put(primary_if);
6878c2ecf20Sopenharmony_ci}
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_cistatic void
6908c2ecf20Sopenharmony_cibatadv_hardif_deactivate_interface(struct batadv_hard_iface *hard_iface)
6918c2ecf20Sopenharmony_ci{
6928c2ecf20Sopenharmony_ci	if (hard_iface->if_status != BATADV_IF_ACTIVE &&
6938c2ecf20Sopenharmony_ci	    hard_iface->if_status != BATADV_IF_TO_BE_ACTIVATED)
6948c2ecf20Sopenharmony_ci		return;
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	hard_iface->if_status = BATADV_IF_INACTIVE;
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	batadv_info(hard_iface->soft_iface, "Interface deactivated: %s\n",
6998c2ecf20Sopenharmony_ci		    hard_iface->net_dev->name);
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	batadv_update_min_mtu(hard_iface->soft_iface);
7028c2ecf20Sopenharmony_ci}
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci/**
7058c2ecf20Sopenharmony_ci * batadv_master_del_slave() - remove hard_iface from the current master iface
7068c2ecf20Sopenharmony_ci * @slave: the interface enslaved in another master
7078c2ecf20Sopenharmony_ci * @master: the master from which slave has to be removed
7088c2ecf20Sopenharmony_ci *
7098c2ecf20Sopenharmony_ci * Invoke ndo_del_slave on master passing slave as argument. In this way the
7108c2ecf20Sopenharmony_ci * slave is free'd and the master can correctly change its internal state.
7118c2ecf20Sopenharmony_ci *
7128c2ecf20Sopenharmony_ci * Return: 0 on success, a negative value representing the error otherwise
7138c2ecf20Sopenharmony_ci */
7148c2ecf20Sopenharmony_cistatic int batadv_master_del_slave(struct batadv_hard_iface *slave,
7158c2ecf20Sopenharmony_ci				   struct net_device *master)
7168c2ecf20Sopenharmony_ci{
7178c2ecf20Sopenharmony_ci	int ret;
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	if (!master)
7208c2ecf20Sopenharmony_ci		return 0;
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	ret = -EBUSY;
7238c2ecf20Sopenharmony_ci	if (master->netdev_ops->ndo_del_slave)
7248c2ecf20Sopenharmony_ci		ret = master->netdev_ops->ndo_del_slave(master, slave->net_dev);
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	return ret;
7278c2ecf20Sopenharmony_ci}
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci/**
7308c2ecf20Sopenharmony_ci * batadv_hardif_enable_interface() - Enslave hard interface to soft interface
7318c2ecf20Sopenharmony_ci * @hard_iface: hard interface to add to soft interface
7328c2ecf20Sopenharmony_ci * @net: the applicable net namespace
7338c2ecf20Sopenharmony_ci * @iface_name: name of the soft interface
7348c2ecf20Sopenharmony_ci *
7358c2ecf20Sopenharmony_ci * Return: 0 on success or negative error number in case of failure
7368c2ecf20Sopenharmony_ci */
7378c2ecf20Sopenharmony_ciint batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
7388c2ecf20Sopenharmony_ci				   struct net *net, const char *iface_name)
7398c2ecf20Sopenharmony_ci{
7408c2ecf20Sopenharmony_ci	struct batadv_priv *bat_priv;
7418c2ecf20Sopenharmony_ci	struct net_device *soft_iface, *master;
7428c2ecf20Sopenharmony_ci	__be16 ethertype = htons(ETH_P_BATMAN);
7438c2ecf20Sopenharmony_ci	int max_header_len = batadv_max_header_len();
7448c2ecf20Sopenharmony_ci	int ret;
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
7478c2ecf20Sopenharmony_ci		goto out;
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	kref_get(&hard_iface->refcount);
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	soft_iface = dev_get_by_name(net, iface_name);
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	if (!soft_iface) {
7548c2ecf20Sopenharmony_ci		soft_iface = batadv_softif_create(net, iface_name);
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci		if (!soft_iface) {
7578c2ecf20Sopenharmony_ci			ret = -ENOMEM;
7588c2ecf20Sopenharmony_ci			goto err;
7598c2ecf20Sopenharmony_ci		}
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci		/* dev_get_by_name() increases the reference counter for us */
7628c2ecf20Sopenharmony_ci		dev_hold(soft_iface);
7638c2ecf20Sopenharmony_ci	}
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	if (!batadv_softif_is_valid(soft_iface)) {
7668c2ecf20Sopenharmony_ci		pr_err("Can't create batman mesh interface %s: already exists as regular interface\n",
7678c2ecf20Sopenharmony_ci		       soft_iface->name);
7688c2ecf20Sopenharmony_ci		ret = -EINVAL;
7698c2ecf20Sopenharmony_ci		goto err_dev;
7708c2ecf20Sopenharmony_ci	}
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	/* check if the interface is enslaved in another virtual one and
7738c2ecf20Sopenharmony_ci	 * in that case unlink it first
7748c2ecf20Sopenharmony_ci	 */
7758c2ecf20Sopenharmony_ci	master = netdev_master_upper_dev_get(hard_iface->net_dev);
7768c2ecf20Sopenharmony_ci	ret = batadv_master_del_slave(hard_iface, master);
7778c2ecf20Sopenharmony_ci	if (ret)
7788c2ecf20Sopenharmony_ci		goto err_dev;
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	hard_iface->soft_iface = soft_iface;
7818c2ecf20Sopenharmony_ci	bat_priv = netdev_priv(hard_iface->soft_iface);
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	ret = netdev_master_upper_dev_link(hard_iface->net_dev,
7848c2ecf20Sopenharmony_ci					   soft_iface, NULL, NULL, NULL);
7858c2ecf20Sopenharmony_ci	if (ret)
7868c2ecf20Sopenharmony_ci		goto err_dev;
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	ret = bat_priv->algo_ops->iface.enable(hard_iface);
7898c2ecf20Sopenharmony_ci	if (ret < 0)
7908c2ecf20Sopenharmony_ci		goto err_upper;
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci	hard_iface->if_status = BATADV_IF_INACTIVE;
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	kref_get(&hard_iface->refcount);
7958c2ecf20Sopenharmony_ci	hard_iface->batman_adv_ptype.type = ethertype;
7968c2ecf20Sopenharmony_ci	hard_iface->batman_adv_ptype.func = batadv_batman_skb_recv;
7978c2ecf20Sopenharmony_ci	hard_iface->batman_adv_ptype.dev = hard_iface->net_dev;
7988c2ecf20Sopenharmony_ci	dev_add_pack(&hard_iface->batman_adv_ptype);
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	batadv_info(hard_iface->soft_iface, "Adding interface: %s\n",
8018c2ecf20Sopenharmony_ci		    hard_iface->net_dev->name);
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	if (atomic_read(&bat_priv->fragmentation) &&
8048c2ecf20Sopenharmony_ci	    hard_iface->net_dev->mtu < ETH_DATA_LEN + max_header_len)
8058c2ecf20Sopenharmony_ci		batadv_info(hard_iface->soft_iface,
8068c2ecf20Sopenharmony_ci			    "The MTU of interface %s is too small (%i) to handle the transport of batman-adv packets. Packets going over this interface will be fragmented on layer2 which could impact the performance. Setting the MTU to %i would solve the problem.\n",
8078c2ecf20Sopenharmony_ci			    hard_iface->net_dev->name, hard_iface->net_dev->mtu,
8088c2ecf20Sopenharmony_ci			    ETH_DATA_LEN + max_header_len);
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	if (!atomic_read(&bat_priv->fragmentation) &&
8118c2ecf20Sopenharmony_ci	    hard_iface->net_dev->mtu < ETH_DATA_LEN + max_header_len)
8128c2ecf20Sopenharmony_ci		batadv_info(hard_iface->soft_iface,
8138c2ecf20Sopenharmony_ci			    "The MTU of interface %s is too small (%i) to handle the transport of batman-adv packets. If you experience problems getting traffic through try increasing the MTU to %i.\n",
8148c2ecf20Sopenharmony_ci			    hard_iface->net_dev->name, hard_iface->net_dev->mtu,
8158c2ecf20Sopenharmony_ci			    ETH_DATA_LEN + max_header_len);
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	if (batadv_hardif_is_iface_up(hard_iface))
8188c2ecf20Sopenharmony_ci		batadv_hardif_activate_interface(hard_iface);
8198c2ecf20Sopenharmony_ci	else
8208c2ecf20Sopenharmony_ci		batadv_err(hard_iface->soft_iface,
8218c2ecf20Sopenharmony_ci			   "Not using interface %s (retrying later): interface not active\n",
8228c2ecf20Sopenharmony_ci			   hard_iface->net_dev->name);
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	batadv_hardif_recalc_extra_skbroom(soft_iface);
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	if (bat_priv->algo_ops->iface.enabled)
8278c2ecf20Sopenharmony_ci		bat_priv->algo_ops->iface.enabled(hard_iface);
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ciout:
8308c2ecf20Sopenharmony_ci	return 0;
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_cierr_upper:
8338c2ecf20Sopenharmony_ci	netdev_upper_dev_unlink(hard_iface->net_dev, soft_iface);
8348c2ecf20Sopenharmony_cierr_dev:
8358c2ecf20Sopenharmony_ci	hard_iface->soft_iface = NULL;
8368c2ecf20Sopenharmony_ci	dev_put(soft_iface);
8378c2ecf20Sopenharmony_cierr:
8388c2ecf20Sopenharmony_ci	batadv_hardif_put(hard_iface);
8398c2ecf20Sopenharmony_ci	return ret;
8408c2ecf20Sopenharmony_ci}
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci/**
8438c2ecf20Sopenharmony_ci * batadv_hardif_cnt() - get number of interfaces enslaved to soft interface
8448c2ecf20Sopenharmony_ci * @soft_iface: soft interface to check
8458c2ecf20Sopenharmony_ci *
8468c2ecf20Sopenharmony_ci * This function is only using RCU for locking - the result can therefore be
8478c2ecf20Sopenharmony_ci * off when another function is modifying the list at the same time. The
8488c2ecf20Sopenharmony_ci * caller can use the rtnl_lock to make sure that the count is accurate.
8498c2ecf20Sopenharmony_ci *
8508c2ecf20Sopenharmony_ci * Return: number of connected/enslaved hard interfaces
8518c2ecf20Sopenharmony_ci */
8528c2ecf20Sopenharmony_cistatic size_t batadv_hardif_cnt(const struct net_device *soft_iface)
8538c2ecf20Sopenharmony_ci{
8548c2ecf20Sopenharmony_ci	struct batadv_hard_iface *hard_iface;
8558c2ecf20Sopenharmony_ci	size_t count = 0;
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	rcu_read_lock();
8588c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
8598c2ecf20Sopenharmony_ci		if (hard_iface->soft_iface != soft_iface)
8608c2ecf20Sopenharmony_ci			continue;
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci		count++;
8638c2ecf20Sopenharmony_ci	}
8648c2ecf20Sopenharmony_ci	rcu_read_unlock();
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	return count;
8678c2ecf20Sopenharmony_ci}
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci/**
8708c2ecf20Sopenharmony_ci * batadv_hardif_disable_interface() - Remove hard interface from soft interface
8718c2ecf20Sopenharmony_ci * @hard_iface: hard interface to be removed
8728c2ecf20Sopenharmony_ci * @autodel: whether to delete soft interface when it doesn't contain any other
8738c2ecf20Sopenharmony_ci *  slave interfaces
8748c2ecf20Sopenharmony_ci */
8758c2ecf20Sopenharmony_civoid batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface,
8768c2ecf20Sopenharmony_ci				     enum batadv_hard_if_cleanup autodel)
8778c2ecf20Sopenharmony_ci{
8788c2ecf20Sopenharmony_ci	struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
8798c2ecf20Sopenharmony_ci	struct batadv_hard_iface *primary_if = NULL;
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci	batadv_hardif_deactivate_interface(hard_iface);
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	if (hard_iface->if_status != BATADV_IF_INACTIVE)
8848c2ecf20Sopenharmony_ci		goto out;
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci	batadv_info(hard_iface->soft_iface, "Removing interface: %s\n",
8878c2ecf20Sopenharmony_ci		    hard_iface->net_dev->name);
8888c2ecf20Sopenharmony_ci	dev_remove_pack(&hard_iface->batman_adv_ptype);
8898c2ecf20Sopenharmony_ci	batadv_hardif_put(hard_iface);
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	primary_if = batadv_primary_if_get_selected(bat_priv);
8928c2ecf20Sopenharmony_ci	if (hard_iface == primary_if) {
8938c2ecf20Sopenharmony_ci		struct batadv_hard_iface *new_if;
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci		new_if = batadv_hardif_get_active(hard_iface->soft_iface);
8968c2ecf20Sopenharmony_ci		batadv_primary_if_select(bat_priv, new_if);
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci		if (new_if)
8998c2ecf20Sopenharmony_ci			batadv_hardif_put(new_if);
9008c2ecf20Sopenharmony_ci	}
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci	bat_priv->algo_ops->iface.disable(hard_iface);
9038c2ecf20Sopenharmony_ci	hard_iface->if_status = BATADV_IF_NOT_IN_USE;
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	/* delete all references to this hard_iface */
9068c2ecf20Sopenharmony_ci	batadv_purge_orig_ref(bat_priv);
9078c2ecf20Sopenharmony_ci	batadv_purge_outstanding_packets(bat_priv, hard_iface);
9088c2ecf20Sopenharmony_ci	dev_put(hard_iface->soft_iface);
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci	netdev_upper_dev_unlink(hard_iface->net_dev, hard_iface->soft_iface);
9118c2ecf20Sopenharmony_ci	batadv_hardif_recalc_extra_skbroom(hard_iface->soft_iface);
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci	/* nobody uses this interface anymore */
9148c2ecf20Sopenharmony_ci	if (batadv_hardif_cnt(hard_iface->soft_iface) <= 1) {
9158c2ecf20Sopenharmony_ci		batadv_gw_check_client_stop(bat_priv);
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci		if (autodel == BATADV_IF_CLEANUP_AUTO)
9188c2ecf20Sopenharmony_ci			batadv_softif_destroy_sysfs(hard_iface->soft_iface);
9198c2ecf20Sopenharmony_ci	}
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	hard_iface->soft_iface = NULL;
9228c2ecf20Sopenharmony_ci	batadv_hardif_put(hard_iface);
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ciout:
9258c2ecf20Sopenharmony_ci	if (primary_if)
9268c2ecf20Sopenharmony_ci		batadv_hardif_put(primary_if);
9278c2ecf20Sopenharmony_ci}
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_cistatic struct batadv_hard_iface *
9308c2ecf20Sopenharmony_cibatadv_hardif_add_interface(struct net_device *net_dev)
9318c2ecf20Sopenharmony_ci{
9328c2ecf20Sopenharmony_ci	struct batadv_hard_iface *hard_iface;
9338c2ecf20Sopenharmony_ci	int ret;
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci	ASSERT_RTNL();
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci	if (!batadv_is_valid_iface(net_dev))
9388c2ecf20Sopenharmony_ci		goto out;
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	dev_hold(net_dev);
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci	hard_iface = kzalloc(sizeof(*hard_iface), GFP_ATOMIC);
9438c2ecf20Sopenharmony_ci	if (!hard_iface)
9448c2ecf20Sopenharmony_ci		goto release_dev;
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	ret = batadv_sysfs_add_hardif(&hard_iface->hardif_obj, net_dev);
9478c2ecf20Sopenharmony_ci	if (ret)
9488c2ecf20Sopenharmony_ci		goto free_if;
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci	hard_iface->net_dev = net_dev;
9518c2ecf20Sopenharmony_ci	hard_iface->soft_iface = NULL;
9528c2ecf20Sopenharmony_ci	hard_iface->if_status = BATADV_IF_NOT_IN_USE;
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci	batadv_debugfs_add_hardif(hard_iface);
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&hard_iface->list);
9578c2ecf20Sopenharmony_ci	INIT_HLIST_HEAD(&hard_iface->neigh_list);
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci	mutex_init(&hard_iface->bat_iv.ogm_buff_mutex);
9608c2ecf20Sopenharmony_ci	spin_lock_init(&hard_iface->neigh_list_lock);
9618c2ecf20Sopenharmony_ci	kref_init(&hard_iface->refcount);
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	hard_iface->num_bcasts = BATADV_NUM_BCASTS_DEFAULT;
9648c2ecf20Sopenharmony_ci	hard_iface->wifi_flags = batadv_wifi_flags_evaluate(net_dev);
9658c2ecf20Sopenharmony_ci	if (batadv_is_wifi_hardif(hard_iface))
9668c2ecf20Sopenharmony_ci		hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS;
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	atomic_set(&hard_iface->hop_penalty, 0);
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci	batadv_v_hardif_init(hard_iface);
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci	batadv_check_known_mac_addr(hard_iface->net_dev);
9738c2ecf20Sopenharmony_ci	kref_get(&hard_iface->refcount);
9748c2ecf20Sopenharmony_ci	list_add_tail_rcu(&hard_iface->list, &batadv_hardif_list);
9758c2ecf20Sopenharmony_ci	batadv_hardif_generation++;
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci	return hard_iface;
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_cifree_if:
9808c2ecf20Sopenharmony_ci	kfree(hard_iface);
9818c2ecf20Sopenharmony_cirelease_dev:
9828c2ecf20Sopenharmony_ci	dev_put(net_dev);
9838c2ecf20Sopenharmony_ciout:
9848c2ecf20Sopenharmony_ci	return NULL;
9858c2ecf20Sopenharmony_ci}
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_cistatic void batadv_hardif_remove_interface(struct batadv_hard_iface *hard_iface)
9888c2ecf20Sopenharmony_ci{
9898c2ecf20Sopenharmony_ci	ASSERT_RTNL();
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci	/* first deactivate interface */
9928c2ecf20Sopenharmony_ci	if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
9938c2ecf20Sopenharmony_ci		batadv_hardif_disable_interface(hard_iface,
9948c2ecf20Sopenharmony_ci						BATADV_IF_CLEANUP_KEEP);
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci	if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
9978c2ecf20Sopenharmony_ci		return;
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	hard_iface->if_status = BATADV_IF_TO_BE_REMOVED;
10008c2ecf20Sopenharmony_ci	batadv_debugfs_del_hardif(hard_iface);
10018c2ecf20Sopenharmony_ci	batadv_sysfs_del_hardif(&hard_iface->hardif_obj);
10028c2ecf20Sopenharmony_ci	batadv_hardif_put(hard_iface);
10038c2ecf20Sopenharmony_ci}
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci/**
10068c2ecf20Sopenharmony_ci * batadv_hard_if_event_softif() - Handle events for soft interfaces
10078c2ecf20Sopenharmony_ci * @event: NETDEV_* event to handle
10088c2ecf20Sopenharmony_ci * @net_dev: net_device which generated an event
10098c2ecf20Sopenharmony_ci *
10108c2ecf20Sopenharmony_ci * Return: NOTIFY_* result
10118c2ecf20Sopenharmony_ci */
10128c2ecf20Sopenharmony_cistatic int batadv_hard_if_event_softif(unsigned long event,
10138c2ecf20Sopenharmony_ci				       struct net_device *net_dev)
10148c2ecf20Sopenharmony_ci{
10158c2ecf20Sopenharmony_ci	struct batadv_priv *bat_priv;
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	switch (event) {
10188c2ecf20Sopenharmony_ci	case NETDEV_REGISTER:
10198c2ecf20Sopenharmony_ci		batadv_sysfs_add_meshif(net_dev);
10208c2ecf20Sopenharmony_ci		bat_priv = netdev_priv(net_dev);
10218c2ecf20Sopenharmony_ci		batadv_softif_create_vlan(bat_priv, BATADV_NO_FLAGS);
10228c2ecf20Sopenharmony_ci		break;
10238c2ecf20Sopenharmony_ci	case NETDEV_CHANGENAME:
10248c2ecf20Sopenharmony_ci		batadv_debugfs_rename_meshif(net_dev);
10258c2ecf20Sopenharmony_ci		break;
10268c2ecf20Sopenharmony_ci	}
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
10298c2ecf20Sopenharmony_ci}
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_cistatic int batadv_hard_if_event(struct notifier_block *this,
10328c2ecf20Sopenharmony_ci				unsigned long event, void *ptr)
10338c2ecf20Sopenharmony_ci{
10348c2ecf20Sopenharmony_ci	struct net_device *net_dev = netdev_notifier_info_to_dev(ptr);
10358c2ecf20Sopenharmony_ci	struct batadv_hard_iface *hard_iface;
10368c2ecf20Sopenharmony_ci	struct batadv_hard_iface *primary_if = NULL;
10378c2ecf20Sopenharmony_ci	struct batadv_priv *bat_priv;
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci	if (batadv_softif_is_valid(net_dev))
10408c2ecf20Sopenharmony_ci		return batadv_hard_if_event_softif(event, net_dev);
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_ci	hard_iface = batadv_hardif_get_by_netdev(net_dev);
10438c2ecf20Sopenharmony_ci	if (!hard_iface && (event == NETDEV_REGISTER ||
10448c2ecf20Sopenharmony_ci			    event == NETDEV_POST_TYPE_CHANGE))
10458c2ecf20Sopenharmony_ci		hard_iface = batadv_hardif_add_interface(net_dev);
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ci	if (!hard_iface)
10488c2ecf20Sopenharmony_ci		goto out;
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci	switch (event) {
10518c2ecf20Sopenharmony_ci	case NETDEV_UP:
10528c2ecf20Sopenharmony_ci		batadv_hardif_activate_interface(hard_iface);
10538c2ecf20Sopenharmony_ci		break;
10548c2ecf20Sopenharmony_ci	case NETDEV_GOING_DOWN:
10558c2ecf20Sopenharmony_ci	case NETDEV_DOWN:
10568c2ecf20Sopenharmony_ci		batadv_hardif_deactivate_interface(hard_iface);
10578c2ecf20Sopenharmony_ci		break;
10588c2ecf20Sopenharmony_ci	case NETDEV_UNREGISTER:
10598c2ecf20Sopenharmony_ci	case NETDEV_PRE_TYPE_CHANGE:
10608c2ecf20Sopenharmony_ci		list_del_rcu(&hard_iface->list);
10618c2ecf20Sopenharmony_ci		batadv_hardif_generation++;
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_ci		batadv_hardif_remove_interface(hard_iface);
10648c2ecf20Sopenharmony_ci		break;
10658c2ecf20Sopenharmony_ci	case NETDEV_CHANGEMTU:
10668c2ecf20Sopenharmony_ci		if (hard_iface->soft_iface)
10678c2ecf20Sopenharmony_ci			batadv_update_min_mtu(hard_iface->soft_iface);
10688c2ecf20Sopenharmony_ci		break;
10698c2ecf20Sopenharmony_ci	case NETDEV_CHANGEADDR:
10708c2ecf20Sopenharmony_ci		if (hard_iface->if_status == BATADV_IF_NOT_IN_USE)
10718c2ecf20Sopenharmony_ci			goto hardif_put;
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci		batadv_check_known_mac_addr(hard_iface->net_dev);
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci		bat_priv = netdev_priv(hard_iface->soft_iface);
10768c2ecf20Sopenharmony_ci		bat_priv->algo_ops->iface.update_mac(hard_iface);
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ci		primary_if = batadv_primary_if_get_selected(bat_priv);
10798c2ecf20Sopenharmony_ci		if (!primary_if)
10808c2ecf20Sopenharmony_ci			goto hardif_put;
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci		if (hard_iface == primary_if)
10838c2ecf20Sopenharmony_ci			batadv_primary_if_update_addr(bat_priv, NULL);
10848c2ecf20Sopenharmony_ci		break;
10858c2ecf20Sopenharmony_ci	case NETDEV_CHANGEUPPER:
10868c2ecf20Sopenharmony_ci		hard_iface->wifi_flags = batadv_wifi_flags_evaluate(net_dev);
10878c2ecf20Sopenharmony_ci		if (batadv_is_wifi_hardif(hard_iface))
10888c2ecf20Sopenharmony_ci			hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS;
10898c2ecf20Sopenharmony_ci		break;
10908c2ecf20Sopenharmony_ci	case NETDEV_CHANGENAME:
10918c2ecf20Sopenharmony_ci		batadv_debugfs_rename_hardif(hard_iface);
10928c2ecf20Sopenharmony_ci		break;
10938c2ecf20Sopenharmony_ci	default:
10948c2ecf20Sopenharmony_ci		break;
10958c2ecf20Sopenharmony_ci	}
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_cihardif_put:
10988c2ecf20Sopenharmony_ci	batadv_hardif_put(hard_iface);
10998c2ecf20Sopenharmony_ciout:
11008c2ecf20Sopenharmony_ci	if (primary_if)
11018c2ecf20Sopenharmony_ci		batadv_hardif_put(primary_if);
11028c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
11038c2ecf20Sopenharmony_ci}
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_cistruct notifier_block batadv_hard_if_notifier = {
11068c2ecf20Sopenharmony_ci	.notifier_call = batadv_hard_if_event,
11078c2ecf20Sopenharmony_ci};
1108