162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Copyright (C) B.A.T.M.A.N. contributors:
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Marek Lindner, Simon Wunderlich
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include "hard-interface.h"
862306a36Sopenharmony_ci#include "main.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/atomic.h>
1162306a36Sopenharmony_ci#include <linux/byteorder/generic.h>
1262306a36Sopenharmony_ci#include <linux/compiler.h>
1362306a36Sopenharmony_ci#include <linux/container_of.h>
1462306a36Sopenharmony_ci#include <linux/errno.h>
1562306a36Sopenharmony_ci#include <linux/gfp.h>
1662306a36Sopenharmony_ci#include <linux/if.h>
1762306a36Sopenharmony_ci#include <linux/if_arp.h>
1862306a36Sopenharmony_ci#include <linux/if_ether.h>
1962306a36Sopenharmony_ci#include <linux/kref.h>
2062306a36Sopenharmony_ci#include <linux/limits.h>
2162306a36Sopenharmony_ci#include <linux/list.h>
2262306a36Sopenharmony_ci#include <linux/minmax.h>
2362306a36Sopenharmony_ci#include <linux/mutex.h>
2462306a36Sopenharmony_ci#include <linux/netdevice.h>
2562306a36Sopenharmony_ci#include <linux/printk.h>
2662306a36Sopenharmony_ci#include <linux/rculist.h>
2762306a36Sopenharmony_ci#include <linux/rtnetlink.h>
2862306a36Sopenharmony_ci#include <linux/slab.h>
2962306a36Sopenharmony_ci#include <linux/spinlock.h>
3062306a36Sopenharmony_ci#include <net/net_namespace.h>
3162306a36Sopenharmony_ci#include <net/rtnetlink.h>
3262306a36Sopenharmony_ci#include <uapi/linux/batadv_packet.h>
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#include "bat_v.h"
3562306a36Sopenharmony_ci#include "bridge_loop_avoidance.h"
3662306a36Sopenharmony_ci#include "distributed-arp-table.h"
3762306a36Sopenharmony_ci#include "gateway_client.h"
3862306a36Sopenharmony_ci#include "log.h"
3962306a36Sopenharmony_ci#include "originator.h"
4062306a36Sopenharmony_ci#include "send.h"
4162306a36Sopenharmony_ci#include "soft-interface.h"
4262306a36Sopenharmony_ci#include "translation-table.h"
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci/**
4562306a36Sopenharmony_ci * batadv_hardif_release() - release hard interface from lists and queue for
4662306a36Sopenharmony_ci *  free after rcu grace period
4762306a36Sopenharmony_ci * @ref: kref pointer of the hard interface
4862306a36Sopenharmony_ci */
4962306a36Sopenharmony_civoid batadv_hardif_release(struct kref *ref)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	struct batadv_hard_iface *hard_iface;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	hard_iface = container_of(ref, struct batadv_hard_iface, refcount);
5462306a36Sopenharmony_ci	dev_put(hard_iface->net_dev);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	kfree_rcu(hard_iface, rcu);
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci/**
6062306a36Sopenharmony_ci * batadv_hardif_get_by_netdev() - Get hard interface object of a net_device
6162306a36Sopenharmony_ci * @net_dev: net_device to search for
6262306a36Sopenharmony_ci *
6362306a36Sopenharmony_ci * Return: batadv_hard_iface of net_dev (with increased refcnt), NULL on errors
6462306a36Sopenharmony_ci */
6562306a36Sopenharmony_cistruct batadv_hard_iface *
6662306a36Sopenharmony_cibatadv_hardif_get_by_netdev(const struct net_device *net_dev)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	struct batadv_hard_iface *hard_iface;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	rcu_read_lock();
7162306a36Sopenharmony_ci	list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
7262306a36Sopenharmony_ci		if (hard_iface->net_dev == net_dev &&
7362306a36Sopenharmony_ci		    kref_get_unless_zero(&hard_iface->refcount))
7462306a36Sopenharmony_ci			goto out;
7562306a36Sopenharmony_ci	}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	hard_iface = NULL;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ciout:
8062306a36Sopenharmony_ci	rcu_read_unlock();
8162306a36Sopenharmony_ci	return hard_iface;
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci/**
8562306a36Sopenharmony_ci * batadv_getlink_net() - return link net namespace (of use fallback)
8662306a36Sopenharmony_ci * @netdev: net_device to check
8762306a36Sopenharmony_ci * @fallback_net: return in case get_link_net is not available for @netdev
8862306a36Sopenharmony_ci *
8962306a36Sopenharmony_ci * Return: result of rtnl_link_ops->get_link_net or @fallback_net
9062306a36Sopenharmony_ci */
9162306a36Sopenharmony_cistatic struct net *batadv_getlink_net(const struct net_device *netdev,
9262306a36Sopenharmony_ci				      struct net *fallback_net)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	if (!netdev->rtnl_link_ops)
9562306a36Sopenharmony_ci		return fallback_net;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	if (!netdev->rtnl_link_ops->get_link_net)
9862306a36Sopenharmony_ci		return fallback_net;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	return netdev->rtnl_link_ops->get_link_net(netdev);
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci/**
10462306a36Sopenharmony_ci * batadv_mutual_parents() - check if two devices are each others parent
10562306a36Sopenharmony_ci * @dev1: 1st net dev
10662306a36Sopenharmony_ci * @net1: 1st devices netns
10762306a36Sopenharmony_ci * @dev2: 2nd net dev
10862306a36Sopenharmony_ci * @net2: 2nd devices netns
10962306a36Sopenharmony_ci *
11062306a36Sopenharmony_ci * veth devices come in pairs and each is the parent of the other!
11162306a36Sopenharmony_ci *
11262306a36Sopenharmony_ci * Return: true if the devices are each others parent, otherwise false
11362306a36Sopenharmony_ci */
11462306a36Sopenharmony_cistatic bool batadv_mutual_parents(const struct net_device *dev1,
11562306a36Sopenharmony_ci				  struct net *net1,
11662306a36Sopenharmony_ci				  const struct net_device *dev2,
11762306a36Sopenharmony_ci				  struct net *net2)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	int dev1_parent_iflink = dev_get_iflink(dev1);
12062306a36Sopenharmony_ci	int dev2_parent_iflink = dev_get_iflink(dev2);
12162306a36Sopenharmony_ci	const struct net *dev1_parent_net;
12262306a36Sopenharmony_ci	const struct net *dev2_parent_net;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	dev1_parent_net = batadv_getlink_net(dev1, net1);
12562306a36Sopenharmony_ci	dev2_parent_net = batadv_getlink_net(dev2, net2);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	if (!dev1_parent_iflink || !dev2_parent_iflink)
12862306a36Sopenharmony_ci		return false;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	return (dev1_parent_iflink == dev2->ifindex) &&
13162306a36Sopenharmony_ci	       (dev2_parent_iflink == dev1->ifindex) &&
13262306a36Sopenharmony_ci	       net_eq(dev1_parent_net, net2) &&
13362306a36Sopenharmony_ci	       net_eq(dev2_parent_net, net1);
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci/**
13762306a36Sopenharmony_ci * batadv_is_on_batman_iface() - check if a device is a batman iface descendant
13862306a36Sopenharmony_ci * @net_dev: the device to check
13962306a36Sopenharmony_ci *
14062306a36Sopenharmony_ci * If the user creates any virtual device on top of a batman-adv interface, it
14162306a36Sopenharmony_ci * is important to prevent this new interface from being used to create a new
14262306a36Sopenharmony_ci * mesh network (this behaviour would lead to a batman-over-batman
14362306a36Sopenharmony_ci * configuration). This function recursively checks all the fathers of the
14462306a36Sopenharmony_ci * device passed as argument looking for a batman-adv soft interface.
14562306a36Sopenharmony_ci *
14662306a36Sopenharmony_ci * Return: true if the device is descendant of a batman-adv mesh interface (or
14762306a36Sopenharmony_ci * if it is a batman-adv interface itself), false otherwise
14862306a36Sopenharmony_ci */
14962306a36Sopenharmony_cistatic bool batadv_is_on_batman_iface(const struct net_device *net_dev)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	struct net *net = dev_net(net_dev);
15262306a36Sopenharmony_ci	struct net_device *parent_dev;
15362306a36Sopenharmony_ci	struct net *parent_net;
15462306a36Sopenharmony_ci	int iflink;
15562306a36Sopenharmony_ci	bool ret;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	/* check if this is a batman-adv mesh interface */
15862306a36Sopenharmony_ci	if (batadv_softif_is_valid(net_dev))
15962306a36Sopenharmony_ci		return true;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	iflink = dev_get_iflink(net_dev);
16262306a36Sopenharmony_ci	if (iflink == 0)
16362306a36Sopenharmony_ci		return false;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	parent_net = batadv_getlink_net(net_dev, net);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	/* iflink to itself, most likely physical device */
16862306a36Sopenharmony_ci	if (net == parent_net && iflink == net_dev->ifindex)
16962306a36Sopenharmony_ci		return false;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	/* recurse over the parent device */
17262306a36Sopenharmony_ci	parent_dev = __dev_get_by_index((struct net *)parent_net, iflink);
17362306a36Sopenharmony_ci	if (!parent_dev) {
17462306a36Sopenharmony_ci		pr_warn("Cannot find parent device. Skipping batadv-on-batadv check for %s\n",
17562306a36Sopenharmony_ci			net_dev->name);
17662306a36Sopenharmony_ci		return false;
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	if (batadv_mutual_parents(net_dev, net, parent_dev, parent_net))
18062306a36Sopenharmony_ci		return false;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	ret = batadv_is_on_batman_iface(parent_dev);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	return ret;
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic bool batadv_is_valid_iface(const struct net_device *net_dev)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	if (net_dev->flags & IFF_LOOPBACK)
19062306a36Sopenharmony_ci		return false;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	if (net_dev->type != ARPHRD_ETHER)
19362306a36Sopenharmony_ci		return false;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	if (net_dev->addr_len != ETH_ALEN)
19662306a36Sopenharmony_ci		return false;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	/* no batman over batman */
19962306a36Sopenharmony_ci	if (batadv_is_on_batman_iface(net_dev))
20062306a36Sopenharmony_ci		return false;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	return true;
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci/**
20662306a36Sopenharmony_ci * batadv_get_real_netdevice() - check if the given netdev struct is a virtual
20762306a36Sopenharmony_ci *  interface on top of another 'real' interface
20862306a36Sopenharmony_ci * @netdev: the device to check
20962306a36Sopenharmony_ci *
21062306a36Sopenharmony_ci * Callers must hold the rtnl semaphore. You may want batadv_get_real_netdev()
21162306a36Sopenharmony_ci * instead of this.
21262306a36Sopenharmony_ci *
21362306a36Sopenharmony_ci * Return: the 'real' net device or the original net device and NULL in case
21462306a36Sopenharmony_ci *  of an error.
21562306a36Sopenharmony_ci */
21662306a36Sopenharmony_cistatic struct net_device *batadv_get_real_netdevice(struct net_device *netdev)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	struct batadv_hard_iface *hard_iface = NULL;
21962306a36Sopenharmony_ci	struct net_device *real_netdev = NULL;
22062306a36Sopenharmony_ci	struct net *real_net;
22162306a36Sopenharmony_ci	struct net *net;
22262306a36Sopenharmony_ci	int iflink;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	ASSERT_RTNL();
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	if (!netdev)
22762306a36Sopenharmony_ci		return NULL;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	iflink = dev_get_iflink(netdev);
23062306a36Sopenharmony_ci	if (iflink == 0) {
23162306a36Sopenharmony_ci		dev_hold(netdev);
23262306a36Sopenharmony_ci		return netdev;
23362306a36Sopenharmony_ci	}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	hard_iface = batadv_hardif_get_by_netdev(netdev);
23662306a36Sopenharmony_ci	if (!hard_iface || !hard_iface->soft_iface)
23762306a36Sopenharmony_ci		goto out;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	net = dev_net(hard_iface->soft_iface);
24062306a36Sopenharmony_ci	real_net = batadv_getlink_net(netdev, net);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	/* iflink to itself, most likely physical device */
24362306a36Sopenharmony_ci	if (net == real_net && netdev->ifindex == iflink) {
24462306a36Sopenharmony_ci		real_netdev = netdev;
24562306a36Sopenharmony_ci		dev_hold(real_netdev);
24662306a36Sopenharmony_ci		goto out;
24762306a36Sopenharmony_ci	}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	real_netdev = dev_get_by_index(real_net, iflink);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ciout:
25262306a36Sopenharmony_ci	batadv_hardif_put(hard_iface);
25362306a36Sopenharmony_ci	return real_netdev;
25462306a36Sopenharmony_ci}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci/**
25762306a36Sopenharmony_ci * batadv_get_real_netdev() - check if the given net_device struct is a virtual
25862306a36Sopenharmony_ci *  interface on top of another 'real' interface
25962306a36Sopenharmony_ci * @net_device: the device to check
26062306a36Sopenharmony_ci *
26162306a36Sopenharmony_ci * Return: the 'real' net device or the original net device and NULL in case
26262306a36Sopenharmony_ci *  of an error.
26362306a36Sopenharmony_ci */
26462306a36Sopenharmony_cistruct net_device *batadv_get_real_netdev(struct net_device *net_device)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	struct net_device *real_netdev;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	rtnl_lock();
26962306a36Sopenharmony_ci	real_netdev = batadv_get_real_netdevice(net_device);
27062306a36Sopenharmony_ci	rtnl_unlock();
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	return real_netdev;
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci/**
27662306a36Sopenharmony_ci * batadv_is_wext_netdev() - check if the given net_device struct is a
27762306a36Sopenharmony_ci *  wext wifi interface
27862306a36Sopenharmony_ci * @net_device: the device to check
27962306a36Sopenharmony_ci *
28062306a36Sopenharmony_ci * Return: true if the net device is a wext wireless device, false
28162306a36Sopenharmony_ci *  otherwise.
28262306a36Sopenharmony_ci */
28362306a36Sopenharmony_cistatic bool batadv_is_wext_netdev(struct net_device *net_device)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	if (!net_device)
28662306a36Sopenharmony_ci		return false;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci#ifdef CONFIG_WIRELESS_EXT
28962306a36Sopenharmony_ci	/* pre-cfg80211 drivers have to implement WEXT, so it is possible to
29062306a36Sopenharmony_ci	 * check for wireless_handlers != NULL
29162306a36Sopenharmony_ci	 */
29262306a36Sopenharmony_ci	if (net_device->wireless_handlers)
29362306a36Sopenharmony_ci		return true;
29462306a36Sopenharmony_ci#endif
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	return false;
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci/**
30062306a36Sopenharmony_ci * batadv_is_cfg80211_netdev() - check if the given net_device struct is a
30162306a36Sopenharmony_ci *  cfg80211 wifi interface
30262306a36Sopenharmony_ci * @net_device: the device to check
30362306a36Sopenharmony_ci *
30462306a36Sopenharmony_ci * Return: true if the net device is a cfg80211 wireless device, false
30562306a36Sopenharmony_ci *  otherwise.
30662306a36Sopenharmony_ci */
30762306a36Sopenharmony_cistatic bool batadv_is_cfg80211_netdev(struct net_device *net_device)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	if (!net_device)
31062306a36Sopenharmony_ci		return false;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_CFG80211)
31362306a36Sopenharmony_ci	/* cfg80211 drivers have to set ieee80211_ptr */
31462306a36Sopenharmony_ci	if (net_device->ieee80211_ptr)
31562306a36Sopenharmony_ci		return true;
31662306a36Sopenharmony_ci#endif
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	return false;
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci/**
32262306a36Sopenharmony_ci * batadv_wifi_flags_evaluate() - calculate wifi flags for net_device
32362306a36Sopenharmony_ci * @net_device: the device to check
32462306a36Sopenharmony_ci *
32562306a36Sopenharmony_ci * Return: batadv_hard_iface_wifi_flags flags of the device
32662306a36Sopenharmony_ci */
32762306a36Sopenharmony_cistatic u32 batadv_wifi_flags_evaluate(struct net_device *net_device)
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	u32 wifi_flags = 0;
33062306a36Sopenharmony_ci	struct net_device *real_netdev;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	if (batadv_is_wext_netdev(net_device))
33362306a36Sopenharmony_ci		wifi_flags |= BATADV_HARDIF_WIFI_WEXT_DIRECT;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	if (batadv_is_cfg80211_netdev(net_device))
33662306a36Sopenharmony_ci		wifi_flags |= BATADV_HARDIF_WIFI_CFG80211_DIRECT;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	real_netdev = batadv_get_real_netdevice(net_device);
33962306a36Sopenharmony_ci	if (!real_netdev)
34062306a36Sopenharmony_ci		return wifi_flags;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	if (real_netdev == net_device)
34362306a36Sopenharmony_ci		goto out;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	if (batadv_is_wext_netdev(real_netdev))
34662306a36Sopenharmony_ci		wifi_flags |= BATADV_HARDIF_WIFI_WEXT_INDIRECT;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	if (batadv_is_cfg80211_netdev(real_netdev))
34962306a36Sopenharmony_ci		wifi_flags |= BATADV_HARDIF_WIFI_CFG80211_INDIRECT;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ciout:
35262306a36Sopenharmony_ci	dev_put(real_netdev);
35362306a36Sopenharmony_ci	return wifi_flags;
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci/**
35762306a36Sopenharmony_ci * batadv_is_cfg80211_hardif() - check if the given hardif is a cfg80211 wifi
35862306a36Sopenharmony_ci *  interface
35962306a36Sopenharmony_ci * @hard_iface: the device to check
36062306a36Sopenharmony_ci *
36162306a36Sopenharmony_ci * Return: true if the net device is a cfg80211 wireless device, false
36262306a36Sopenharmony_ci *  otherwise.
36362306a36Sopenharmony_ci */
36462306a36Sopenharmony_cibool batadv_is_cfg80211_hardif(struct batadv_hard_iface *hard_iface)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	u32 allowed_flags = 0;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	allowed_flags |= BATADV_HARDIF_WIFI_CFG80211_DIRECT;
36962306a36Sopenharmony_ci	allowed_flags |= BATADV_HARDIF_WIFI_CFG80211_INDIRECT;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	return !!(hard_iface->wifi_flags & allowed_flags);
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci/**
37562306a36Sopenharmony_ci * batadv_is_wifi_hardif() - check if the given hardif is a wifi interface
37662306a36Sopenharmony_ci * @hard_iface: the device to check
37762306a36Sopenharmony_ci *
37862306a36Sopenharmony_ci * Return: true if the net device is a 802.11 wireless device, false otherwise.
37962306a36Sopenharmony_ci */
38062306a36Sopenharmony_cibool batadv_is_wifi_hardif(struct batadv_hard_iface *hard_iface)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	if (!hard_iface)
38362306a36Sopenharmony_ci		return false;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	return hard_iface->wifi_flags != 0;
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci/**
38962306a36Sopenharmony_ci * batadv_hardif_no_broadcast() - check whether (re)broadcast is necessary
39062306a36Sopenharmony_ci * @if_outgoing: the outgoing interface checked and considered for (re)broadcast
39162306a36Sopenharmony_ci * @orig_addr: the originator of this packet
39262306a36Sopenharmony_ci * @orig_neigh: originator address of the forwarder we just got the packet from
39362306a36Sopenharmony_ci *  (NULL if we originated)
39462306a36Sopenharmony_ci *
39562306a36Sopenharmony_ci * Checks whether a packet needs to be (re)broadcasted on the given interface.
39662306a36Sopenharmony_ci *
39762306a36Sopenharmony_ci * Return:
39862306a36Sopenharmony_ci *	BATADV_HARDIF_BCAST_NORECIPIENT: No neighbor on interface
39962306a36Sopenharmony_ci *	BATADV_HARDIF_BCAST_DUPFWD: Just one neighbor, but it is the forwarder
40062306a36Sopenharmony_ci *	BATADV_HARDIF_BCAST_DUPORIG: Just one neighbor, but it is the originator
40162306a36Sopenharmony_ci *	BATADV_HARDIF_BCAST_OK: Several neighbors, must broadcast
40262306a36Sopenharmony_ci */
40362306a36Sopenharmony_ciint batadv_hardif_no_broadcast(struct batadv_hard_iface *if_outgoing,
40462306a36Sopenharmony_ci			       u8 *orig_addr, u8 *orig_neigh)
40562306a36Sopenharmony_ci{
40662306a36Sopenharmony_ci	struct batadv_hardif_neigh_node *hardif_neigh;
40762306a36Sopenharmony_ci	struct hlist_node *first;
40862306a36Sopenharmony_ci	int ret = BATADV_HARDIF_BCAST_OK;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	rcu_read_lock();
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	/* 0 neighbors -> no (re)broadcast */
41362306a36Sopenharmony_ci	first = rcu_dereference(hlist_first_rcu(&if_outgoing->neigh_list));
41462306a36Sopenharmony_ci	if (!first) {
41562306a36Sopenharmony_ci		ret = BATADV_HARDIF_BCAST_NORECIPIENT;
41662306a36Sopenharmony_ci		goto out;
41762306a36Sopenharmony_ci	}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	/* >1 neighbors -> (re)broadcast */
42062306a36Sopenharmony_ci	if (rcu_dereference(hlist_next_rcu(first)))
42162306a36Sopenharmony_ci		goto out;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	hardif_neigh = hlist_entry(first, struct batadv_hardif_neigh_node,
42462306a36Sopenharmony_ci				   list);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	/* 1 neighbor, is the originator -> no rebroadcast */
42762306a36Sopenharmony_ci	if (orig_addr && batadv_compare_eth(hardif_neigh->orig, orig_addr)) {
42862306a36Sopenharmony_ci		ret = BATADV_HARDIF_BCAST_DUPORIG;
42962306a36Sopenharmony_ci	/* 1 neighbor, is the one we received from -> no rebroadcast */
43062306a36Sopenharmony_ci	} else if (orig_neigh &&
43162306a36Sopenharmony_ci		   batadv_compare_eth(hardif_neigh->orig, orig_neigh)) {
43262306a36Sopenharmony_ci		ret = BATADV_HARDIF_BCAST_DUPFWD;
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ciout:
43662306a36Sopenharmony_ci	rcu_read_unlock();
43762306a36Sopenharmony_ci	return ret;
43862306a36Sopenharmony_ci}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_cistatic struct batadv_hard_iface *
44162306a36Sopenharmony_cibatadv_hardif_get_active(const struct net_device *soft_iface)
44262306a36Sopenharmony_ci{
44362306a36Sopenharmony_ci	struct batadv_hard_iface *hard_iface;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	rcu_read_lock();
44662306a36Sopenharmony_ci	list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
44762306a36Sopenharmony_ci		if (hard_iface->soft_iface != soft_iface)
44862306a36Sopenharmony_ci			continue;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci		if (hard_iface->if_status == BATADV_IF_ACTIVE &&
45162306a36Sopenharmony_ci		    kref_get_unless_zero(&hard_iface->refcount))
45262306a36Sopenharmony_ci			goto out;
45362306a36Sopenharmony_ci	}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	hard_iface = NULL;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ciout:
45862306a36Sopenharmony_ci	rcu_read_unlock();
45962306a36Sopenharmony_ci	return hard_iface;
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_cistatic void batadv_primary_if_update_addr(struct batadv_priv *bat_priv,
46362306a36Sopenharmony_ci					  struct batadv_hard_iface *oldif)
46462306a36Sopenharmony_ci{
46562306a36Sopenharmony_ci	struct batadv_hard_iface *primary_if;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	primary_if = batadv_primary_if_get_selected(bat_priv);
46862306a36Sopenharmony_ci	if (!primary_if)
46962306a36Sopenharmony_ci		goto out;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	batadv_dat_init_own_addr(bat_priv, primary_if);
47262306a36Sopenharmony_ci	batadv_bla_update_orig_address(bat_priv, primary_if, oldif);
47362306a36Sopenharmony_ciout:
47462306a36Sopenharmony_ci	batadv_hardif_put(primary_if);
47562306a36Sopenharmony_ci}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_cistatic void batadv_primary_if_select(struct batadv_priv *bat_priv,
47862306a36Sopenharmony_ci				     struct batadv_hard_iface *new_hard_iface)
47962306a36Sopenharmony_ci{
48062306a36Sopenharmony_ci	struct batadv_hard_iface *curr_hard_iface;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	ASSERT_RTNL();
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	if (new_hard_iface)
48562306a36Sopenharmony_ci		kref_get(&new_hard_iface->refcount);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	curr_hard_iface = rcu_replace_pointer(bat_priv->primary_if,
48862306a36Sopenharmony_ci					      new_hard_iface, 1);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	if (!new_hard_iface)
49162306a36Sopenharmony_ci		goto out;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	bat_priv->algo_ops->iface.primary_set(new_hard_iface);
49462306a36Sopenharmony_ci	batadv_primary_if_update_addr(bat_priv, curr_hard_iface);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ciout:
49762306a36Sopenharmony_ci	batadv_hardif_put(curr_hard_iface);
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_cistatic bool
50162306a36Sopenharmony_cibatadv_hardif_is_iface_up(const struct batadv_hard_iface *hard_iface)
50262306a36Sopenharmony_ci{
50362306a36Sopenharmony_ci	if (hard_iface->net_dev->flags & IFF_UP)
50462306a36Sopenharmony_ci		return true;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	return false;
50762306a36Sopenharmony_ci}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_cistatic void batadv_check_known_mac_addr(const struct net_device *net_dev)
51062306a36Sopenharmony_ci{
51162306a36Sopenharmony_ci	const struct batadv_hard_iface *hard_iface;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	rcu_read_lock();
51462306a36Sopenharmony_ci	list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
51562306a36Sopenharmony_ci		if (hard_iface->if_status != BATADV_IF_ACTIVE &&
51662306a36Sopenharmony_ci		    hard_iface->if_status != BATADV_IF_TO_BE_ACTIVATED)
51762306a36Sopenharmony_ci			continue;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci		if (hard_iface->net_dev == net_dev)
52062306a36Sopenharmony_ci			continue;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci		if (!batadv_compare_eth(hard_iface->net_dev->dev_addr,
52362306a36Sopenharmony_ci					net_dev->dev_addr))
52462306a36Sopenharmony_ci			continue;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci		pr_warn("The newly added mac address (%pM) already exists on: %s\n",
52762306a36Sopenharmony_ci			net_dev->dev_addr, hard_iface->net_dev->name);
52862306a36Sopenharmony_ci		pr_warn("It is strongly recommended to keep mac addresses unique to avoid problems!\n");
52962306a36Sopenharmony_ci	}
53062306a36Sopenharmony_ci	rcu_read_unlock();
53162306a36Sopenharmony_ci}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci/**
53462306a36Sopenharmony_ci * batadv_hardif_recalc_extra_skbroom() - Recalculate skbuff extra head/tailroom
53562306a36Sopenharmony_ci * @soft_iface: netdev struct of the mesh interface
53662306a36Sopenharmony_ci */
53762306a36Sopenharmony_cistatic void batadv_hardif_recalc_extra_skbroom(struct net_device *soft_iface)
53862306a36Sopenharmony_ci{
53962306a36Sopenharmony_ci	const struct batadv_hard_iface *hard_iface;
54062306a36Sopenharmony_ci	unsigned short lower_header_len = ETH_HLEN;
54162306a36Sopenharmony_ci	unsigned short lower_headroom = 0;
54262306a36Sopenharmony_ci	unsigned short lower_tailroom = 0;
54362306a36Sopenharmony_ci	unsigned short needed_headroom;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	rcu_read_lock();
54662306a36Sopenharmony_ci	list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
54762306a36Sopenharmony_ci		if (hard_iface->if_status == BATADV_IF_NOT_IN_USE)
54862306a36Sopenharmony_ci			continue;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci		if (hard_iface->soft_iface != soft_iface)
55162306a36Sopenharmony_ci			continue;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci		lower_header_len = max_t(unsigned short, lower_header_len,
55462306a36Sopenharmony_ci					 hard_iface->net_dev->hard_header_len);
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci		lower_headroom = max_t(unsigned short, lower_headroom,
55762306a36Sopenharmony_ci				       hard_iface->net_dev->needed_headroom);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci		lower_tailroom = max_t(unsigned short, lower_tailroom,
56062306a36Sopenharmony_ci				       hard_iface->net_dev->needed_tailroom);
56162306a36Sopenharmony_ci	}
56262306a36Sopenharmony_ci	rcu_read_unlock();
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	needed_headroom = lower_headroom + (lower_header_len - ETH_HLEN);
56562306a36Sopenharmony_ci	needed_headroom += batadv_max_header_len();
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	/* fragmentation headers don't strip the unicast/... header */
56862306a36Sopenharmony_ci	needed_headroom += sizeof(struct batadv_frag_packet);
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	soft_iface->needed_headroom = needed_headroom;
57162306a36Sopenharmony_ci	soft_iface->needed_tailroom = lower_tailroom;
57262306a36Sopenharmony_ci}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci/**
57562306a36Sopenharmony_ci * batadv_hardif_min_mtu() - Calculate maximum MTU for soft interface
57662306a36Sopenharmony_ci * @soft_iface: netdev struct of the soft interface
57762306a36Sopenharmony_ci *
57862306a36Sopenharmony_ci * Return: MTU for the soft-interface (limited by the minimal MTU of all active
57962306a36Sopenharmony_ci *  slave interfaces)
58062306a36Sopenharmony_ci */
58162306a36Sopenharmony_ciint batadv_hardif_min_mtu(struct net_device *soft_iface)
58262306a36Sopenharmony_ci{
58362306a36Sopenharmony_ci	struct batadv_priv *bat_priv = netdev_priv(soft_iface);
58462306a36Sopenharmony_ci	const struct batadv_hard_iface *hard_iface;
58562306a36Sopenharmony_ci	int min_mtu = INT_MAX;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	rcu_read_lock();
58862306a36Sopenharmony_ci	list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
58962306a36Sopenharmony_ci		if (hard_iface->if_status != BATADV_IF_ACTIVE &&
59062306a36Sopenharmony_ci		    hard_iface->if_status != BATADV_IF_TO_BE_ACTIVATED)
59162306a36Sopenharmony_ci			continue;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci		if (hard_iface->soft_iface != soft_iface)
59462306a36Sopenharmony_ci			continue;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci		min_mtu = min_t(int, hard_iface->net_dev->mtu, min_mtu);
59762306a36Sopenharmony_ci	}
59862306a36Sopenharmony_ci	rcu_read_unlock();
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	if (atomic_read(&bat_priv->fragmentation) == 0)
60162306a36Sopenharmony_ci		goto out;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	/* with fragmentation enabled the maximum size of internally generated
60462306a36Sopenharmony_ci	 * packets such as translation table exchanges or tvlv containers, etc
60562306a36Sopenharmony_ci	 * has to be calculated
60662306a36Sopenharmony_ci	 */
60762306a36Sopenharmony_ci	min_mtu = min_t(int, min_mtu, BATADV_FRAG_MAX_FRAG_SIZE);
60862306a36Sopenharmony_ci	min_mtu -= sizeof(struct batadv_frag_packet);
60962306a36Sopenharmony_ci	min_mtu *= BATADV_FRAG_MAX_FRAGMENTS;
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ciout:
61262306a36Sopenharmony_ci	/* report to the other components the maximum amount of bytes that
61362306a36Sopenharmony_ci	 * batman-adv can send over the wire (without considering the payload
61462306a36Sopenharmony_ci	 * overhead). For example, this value is used by TT to compute the
61562306a36Sopenharmony_ci	 * maximum local table size
61662306a36Sopenharmony_ci	 */
61762306a36Sopenharmony_ci	atomic_set(&bat_priv->packet_size_max, min_mtu);
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	/* the real soft-interface MTU is computed by removing the payload
62062306a36Sopenharmony_ci	 * overhead from the maximum amount of bytes that was just computed.
62162306a36Sopenharmony_ci	 *
62262306a36Sopenharmony_ci	 * However batman-adv does not support MTUs bigger than ETH_DATA_LEN
62362306a36Sopenharmony_ci	 */
62462306a36Sopenharmony_ci	return min_t(int, min_mtu - batadv_max_header_len(), ETH_DATA_LEN);
62562306a36Sopenharmony_ci}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci/**
62862306a36Sopenharmony_ci * batadv_update_min_mtu() - Adjusts the MTU if a new interface with a smaller
62962306a36Sopenharmony_ci *  MTU appeared
63062306a36Sopenharmony_ci * @soft_iface: netdev struct of the soft interface
63162306a36Sopenharmony_ci */
63262306a36Sopenharmony_civoid batadv_update_min_mtu(struct net_device *soft_iface)
63362306a36Sopenharmony_ci{
63462306a36Sopenharmony_ci	struct batadv_priv *bat_priv = netdev_priv(soft_iface);
63562306a36Sopenharmony_ci	int limit_mtu;
63662306a36Sopenharmony_ci	int mtu;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	mtu = batadv_hardif_min_mtu(soft_iface);
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	if (bat_priv->mtu_set_by_user)
64162306a36Sopenharmony_ci		limit_mtu = bat_priv->mtu_set_by_user;
64262306a36Sopenharmony_ci	else
64362306a36Sopenharmony_ci		limit_mtu = ETH_DATA_LEN;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	mtu = min(mtu, limit_mtu);
64662306a36Sopenharmony_ci	dev_set_mtu(soft_iface, mtu);
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	/* Check if the local translate table should be cleaned up to match a
64962306a36Sopenharmony_ci	 * new (and smaller) MTU.
65062306a36Sopenharmony_ci	 */
65162306a36Sopenharmony_ci	batadv_tt_local_resize_to_mtu(soft_iface);
65262306a36Sopenharmony_ci}
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_cistatic void
65562306a36Sopenharmony_cibatadv_hardif_activate_interface(struct batadv_hard_iface *hard_iface)
65662306a36Sopenharmony_ci{
65762306a36Sopenharmony_ci	struct batadv_priv *bat_priv;
65862306a36Sopenharmony_ci	struct batadv_hard_iface *primary_if = NULL;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	if (hard_iface->if_status != BATADV_IF_INACTIVE)
66162306a36Sopenharmony_ci		goto out;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	bat_priv = netdev_priv(hard_iface->soft_iface);
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	bat_priv->algo_ops->iface.update_mac(hard_iface);
66662306a36Sopenharmony_ci	hard_iface->if_status = BATADV_IF_TO_BE_ACTIVATED;
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	/* the first active interface becomes our primary interface or
66962306a36Sopenharmony_ci	 * the next active interface after the old primary interface was removed
67062306a36Sopenharmony_ci	 */
67162306a36Sopenharmony_ci	primary_if = batadv_primary_if_get_selected(bat_priv);
67262306a36Sopenharmony_ci	if (!primary_if)
67362306a36Sopenharmony_ci		batadv_primary_if_select(bat_priv, hard_iface);
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	batadv_info(hard_iface->soft_iface, "Interface activated: %s\n",
67662306a36Sopenharmony_ci		    hard_iface->net_dev->name);
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	batadv_update_min_mtu(hard_iface->soft_iface);
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	if (bat_priv->algo_ops->iface.activate)
68162306a36Sopenharmony_ci		bat_priv->algo_ops->iface.activate(hard_iface);
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ciout:
68462306a36Sopenharmony_ci	batadv_hardif_put(primary_if);
68562306a36Sopenharmony_ci}
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_cistatic void
68862306a36Sopenharmony_cibatadv_hardif_deactivate_interface(struct batadv_hard_iface *hard_iface)
68962306a36Sopenharmony_ci{
69062306a36Sopenharmony_ci	if (hard_iface->if_status != BATADV_IF_ACTIVE &&
69162306a36Sopenharmony_ci	    hard_iface->if_status != BATADV_IF_TO_BE_ACTIVATED)
69262306a36Sopenharmony_ci		return;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	hard_iface->if_status = BATADV_IF_INACTIVE;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	batadv_info(hard_iface->soft_iface, "Interface deactivated: %s\n",
69762306a36Sopenharmony_ci		    hard_iface->net_dev->name);
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	batadv_update_min_mtu(hard_iface->soft_iface);
70062306a36Sopenharmony_ci}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci/**
70362306a36Sopenharmony_ci * batadv_hardif_enable_interface() - Enslave hard interface to soft interface
70462306a36Sopenharmony_ci * @hard_iface: hard interface to add to soft interface
70562306a36Sopenharmony_ci * @soft_iface: netdev struct of the mesh interface
70662306a36Sopenharmony_ci *
70762306a36Sopenharmony_ci * Return: 0 on success or negative error number in case of failure
70862306a36Sopenharmony_ci */
70962306a36Sopenharmony_ciint batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
71062306a36Sopenharmony_ci				   struct net_device *soft_iface)
71162306a36Sopenharmony_ci{
71262306a36Sopenharmony_ci	struct batadv_priv *bat_priv;
71362306a36Sopenharmony_ci	__be16 ethertype = htons(ETH_P_BATMAN);
71462306a36Sopenharmony_ci	int max_header_len = batadv_max_header_len();
71562306a36Sopenharmony_ci	unsigned int required_mtu;
71662306a36Sopenharmony_ci	unsigned int hardif_mtu;
71762306a36Sopenharmony_ci	int ret;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	hardif_mtu = READ_ONCE(hard_iface->net_dev->mtu);
72062306a36Sopenharmony_ci	required_mtu = READ_ONCE(soft_iface->mtu) + max_header_len;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	if (hardif_mtu < ETH_MIN_MTU + max_header_len)
72362306a36Sopenharmony_ci		return -EINVAL;
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
72662306a36Sopenharmony_ci		goto out;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	kref_get(&hard_iface->refcount);
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	dev_hold(soft_iface);
73162306a36Sopenharmony_ci	hard_iface->soft_iface = soft_iface;
73262306a36Sopenharmony_ci	bat_priv = netdev_priv(hard_iface->soft_iface);
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	ret = netdev_master_upper_dev_link(hard_iface->net_dev,
73562306a36Sopenharmony_ci					   soft_iface, NULL, NULL, NULL);
73662306a36Sopenharmony_ci	if (ret)
73762306a36Sopenharmony_ci		goto err_dev;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	ret = bat_priv->algo_ops->iface.enable(hard_iface);
74062306a36Sopenharmony_ci	if (ret < 0)
74162306a36Sopenharmony_ci		goto err_upper;
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	hard_iface->if_status = BATADV_IF_INACTIVE;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	kref_get(&hard_iface->refcount);
74662306a36Sopenharmony_ci	hard_iface->batman_adv_ptype.type = ethertype;
74762306a36Sopenharmony_ci	hard_iface->batman_adv_ptype.func = batadv_batman_skb_recv;
74862306a36Sopenharmony_ci	hard_iface->batman_adv_ptype.dev = hard_iface->net_dev;
74962306a36Sopenharmony_ci	dev_add_pack(&hard_iface->batman_adv_ptype);
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	batadv_info(hard_iface->soft_iface, "Adding interface: %s\n",
75262306a36Sopenharmony_ci		    hard_iface->net_dev->name);
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	if (atomic_read(&bat_priv->fragmentation) &&
75562306a36Sopenharmony_ci	    hardif_mtu < required_mtu)
75662306a36Sopenharmony_ci		batadv_info(hard_iface->soft_iface,
75762306a36Sopenharmony_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",
75862306a36Sopenharmony_ci			    hard_iface->net_dev->name, hardif_mtu,
75962306a36Sopenharmony_ci			    required_mtu);
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	if (!atomic_read(&bat_priv->fragmentation) &&
76262306a36Sopenharmony_ci	    hardif_mtu < required_mtu)
76362306a36Sopenharmony_ci		batadv_info(hard_iface->soft_iface,
76462306a36Sopenharmony_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",
76562306a36Sopenharmony_ci			    hard_iface->net_dev->name, hardif_mtu,
76662306a36Sopenharmony_ci			    required_mtu);
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	if (batadv_hardif_is_iface_up(hard_iface))
76962306a36Sopenharmony_ci		batadv_hardif_activate_interface(hard_iface);
77062306a36Sopenharmony_ci	else
77162306a36Sopenharmony_ci		batadv_err(hard_iface->soft_iface,
77262306a36Sopenharmony_ci			   "Not using interface %s (retrying later): interface not active\n",
77362306a36Sopenharmony_ci			   hard_iface->net_dev->name);
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	batadv_hardif_recalc_extra_skbroom(soft_iface);
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	if (bat_priv->algo_ops->iface.enabled)
77862306a36Sopenharmony_ci		bat_priv->algo_ops->iface.enabled(hard_iface);
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ciout:
78162306a36Sopenharmony_ci	return 0;
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_cierr_upper:
78462306a36Sopenharmony_ci	netdev_upper_dev_unlink(hard_iface->net_dev, soft_iface);
78562306a36Sopenharmony_cierr_dev:
78662306a36Sopenharmony_ci	hard_iface->soft_iface = NULL;
78762306a36Sopenharmony_ci	dev_put(soft_iface);
78862306a36Sopenharmony_ci	batadv_hardif_put(hard_iface);
78962306a36Sopenharmony_ci	return ret;
79062306a36Sopenharmony_ci}
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci/**
79362306a36Sopenharmony_ci * batadv_hardif_cnt() - get number of interfaces enslaved to soft interface
79462306a36Sopenharmony_ci * @soft_iface: soft interface to check
79562306a36Sopenharmony_ci *
79662306a36Sopenharmony_ci * This function is only using RCU for locking - the result can therefore be
79762306a36Sopenharmony_ci * off when another function is modifying the list at the same time. The
79862306a36Sopenharmony_ci * caller can use the rtnl_lock to make sure that the count is accurate.
79962306a36Sopenharmony_ci *
80062306a36Sopenharmony_ci * Return: number of connected/enslaved hard interfaces
80162306a36Sopenharmony_ci */
80262306a36Sopenharmony_cistatic size_t batadv_hardif_cnt(const struct net_device *soft_iface)
80362306a36Sopenharmony_ci{
80462306a36Sopenharmony_ci	struct batadv_hard_iface *hard_iface;
80562306a36Sopenharmony_ci	size_t count = 0;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	rcu_read_lock();
80862306a36Sopenharmony_ci	list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
80962306a36Sopenharmony_ci		if (hard_iface->soft_iface != soft_iface)
81062306a36Sopenharmony_ci			continue;
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci		count++;
81362306a36Sopenharmony_ci	}
81462306a36Sopenharmony_ci	rcu_read_unlock();
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	return count;
81762306a36Sopenharmony_ci}
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci/**
82062306a36Sopenharmony_ci * batadv_hardif_disable_interface() - Remove hard interface from soft interface
82162306a36Sopenharmony_ci * @hard_iface: hard interface to be removed
82262306a36Sopenharmony_ci */
82362306a36Sopenharmony_civoid batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
82462306a36Sopenharmony_ci{
82562306a36Sopenharmony_ci	struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
82662306a36Sopenharmony_ci	struct batadv_hard_iface *primary_if = NULL;
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	batadv_hardif_deactivate_interface(hard_iface);
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	if (hard_iface->if_status != BATADV_IF_INACTIVE)
83162306a36Sopenharmony_ci		goto out;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	batadv_info(hard_iface->soft_iface, "Removing interface: %s\n",
83462306a36Sopenharmony_ci		    hard_iface->net_dev->name);
83562306a36Sopenharmony_ci	dev_remove_pack(&hard_iface->batman_adv_ptype);
83662306a36Sopenharmony_ci	batadv_hardif_put(hard_iface);
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	primary_if = batadv_primary_if_get_selected(bat_priv);
83962306a36Sopenharmony_ci	if (hard_iface == primary_if) {
84062306a36Sopenharmony_ci		struct batadv_hard_iface *new_if;
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci		new_if = batadv_hardif_get_active(hard_iface->soft_iface);
84362306a36Sopenharmony_ci		batadv_primary_if_select(bat_priv, new_if);
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci		batadv_hardif_put(new_if);
84662306a36Sopenharmony_ci	}
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	bat_priv->algo_ops->iface.disable(hard_iface);
84962306a36Sopenharmony_ci	hard_iface->if_status = BATADV_IF_NOT_IN_USE;
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	/* delete all references to this hard_iface */
85262306a36Sopenharmony_ci	batadv_purge_orig_ref(bat_priv);
85362306a36Sopenharmony_ci	batadv_purge_outstanding_packets(bat_priv, hard_iface);
85462306a36Sopenharmony_ci	dev_put(hard_iface->soft_iface);
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	netdev_upper_dev_unlink(hard_iface->net_dev, hard_iface->soft_iface);
85762306a36Sopenharmony_ci	batadv_hardif_recalc_extra_skbroom(hard_iface->soft_iface);
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	/* nobody uses this interface anymore */
86062306a36Sopenharmony_ci	if (batadv_hardif_cnt(hard_iface->soft_iface) <= 1)
86162306a36Sopenharmony_ci		batadv_gw_check_client_stop(bat_priv);
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	hard_iface->soft_iface = NULL;
86462306a36Sopenharmony_ci	batadv_hardif_put(hard_iface);
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ciout:
86762306a36Sopenharmony_ci	batadv_hardif_put(primary_if);
86862306a36Sopenharmony_ci}
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_cistatic struct batadv_hard_iface *
87162306a36Sopenharmony_cibatadv_hardif_add_interface(struct net_device *net_dev)
87262306a36Sopenharmony_ci{
87362306a36Sopenharmony_ci	struct batadv_hard_iface *hard_iface;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	ASSERT_RTNL();
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	if (!batadv_is_valid_iface(net_dev))
87862306a36Sopenharmony_ci		goto out;
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	dev_hold(net_dev);
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	hard_iface = kzalloc(sizeof(*hard_iface), GFP_ATOMIC);
88362306a36Sopenharmony_ci	if (!hard_iface)
88462306a36Sopenharmony_ci		goto release_dev;
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	hard_iface->net_dev = net_dev;
88762306a36Sopenharmony_ci	hard_iface->soft_iface = NULL;
88862306a36Sopenharmony_ci	hard_iface->if_status = BATADV_IF_NOT_IN_USE;
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	INIT_LIST_HEAD(&hard_iface->list);
89162306a36Sopenharmony_ci	INIT_HLIST_HEAD(&hard_iface->neigh_list);
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	mutex_init(&hard_iface->bat_iv.ogm_buff_mutex);
89462306a36Sopenharmony_ci	spin_lock_init(&hard_iface->neigh_list_lock);
89562306a36Sopenharmony_ci	kref_init(&hard_iface->refcount);
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	hard_iface->num_bcasts = BATADV_NUM_BCASTS_DEFAULT;
89862306a36Sopenharmony_ci	hard_iface->wifi_flags = batadv_wifi_flags_evaluate(net_dev);
89962306a36Sopenharmony_ci	if (batadv_is_wifi_hardif(hard_iface))
90062306a36Sopenharmony_ci		hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	atomic_set(&hard_iface->hop_penalty, 0);
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	batadv_v_hardif_init(hard_iface);
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	batadv_check_known_mac_addr(hard_iface->net_dev);
90762306a36Sopenharmony_ci	kref_get(&hard_iface->refcount);
90862306a36Sopenharmony_ci	list_add_tail_rcu(&hard_iface->list, &batadv_hardif_list);
90962306a36Sopenharmony_ci	batadv_hardif_generation++;
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	return hard_iface;
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_cirelease_dev:
91462306a36Sopenharmony_ci	dev_put(net_dev);
91562306a36Sopenharmony_ciout:
91662306a36Sopenharmony_ci	return NULL;
91762306a36Sopenharmony_ci}
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_cistatic void batadv_hardif_remove_interface(struct batadv_hard_iface *hard_iface)
92062306a36Sopenharmony_ci{
92162306a36Sopenharmony_ci	ASSERT_RTNL();
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	/* first deactivate interface */
92462306a36Sopenharmony_ci	if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
92562306a36Sopenharmony_ci		batadv_hardif_disable_interface(hard_iface);
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
92862306a36Sopenharmony_ci		return;
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	hard_iface->if_status = BATADV_IF_TO_BE_REMOVED;
93162306a36Sopenharmony_ci	batadv_hardif_put(hard_iface);
93262306a36Sopenharmony_ci}
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci/**
93562306a36Sopenharmony_ci * batadv_hard_if_event_softif() - Handle events for soft interfaces
93662306a36Sopenharmony_ci * @event: NETDEV_* event to handle
93762306a36Sopenharmony_ci * @net_dev: net_device which generated an event
93862306a36Sopenharmony_ci *
93962306a36Sopenharmony_ci * Return: NOTIFY_* result
94062306a36Sopenharmony_ci */
94162306a36Sopenharmony_cistatic int batadv_hard_if_event_softif(unsigned long event,
94262306a36Sopenharmony_ci				       struct net_device *net_dev)
94362306a36Sopenharmony_ci{
94462306a36Sopenharmony_ci	struct batadv_priv *bat_priv;
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	switch (event) {
94762306a36Sopenharmony_ci	case NETDEV_REGISTER:
94862306a36Sopenharmony_ci		bat_priv = netdev_priv(net_dev);
94962306a36Sopenharmony_ci		batadv_softif_create_vlan(bat_priv, BATADV_NO_FLAGS);
95062306a36Sopenharmony_ci		break;
95162306a36Sopenharmony_ci	}
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	return NOTIFY_DONE;
95462306a36Sopenharmony_ci}
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_cistatic int batadv_hard_if_event(struct notifier_block *this,
95762306a36Sopenharmony_ci				unsigned long event, void *ptr)
95862306a36Sopenharmony_ci{
95962306a36Sopenharmony_ci	struct net_device *net_dev = netdev_notifier_info_to_dev(ptr);
96062306a36Sopenharmony_ci	struct batadv_hard_iface *hard_iface;
96162306a36Sopenharmony_ci	struct batadv_hard_iface *primary_if = NULL;
96262306a36Sopenharmony_ci	struct batadv_priv *bat_priv;
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	if (batadv_softif_is_valid(net_dev))
96562306a36Sopenharmony_ci		return batadv_hard_if_event_softif(event, net_dev);
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	hard_iface = batadv_hardif_get_by_netdev(net_dev);
96862306a36Sopenharmony_ci	if (!hard_iface && (event == NETDEV_REGISTER ||
96962306a36Sopenharmony_ci			    event == NETDEV_POST_TYPE_CHANGE))
97062306a36Sopenharmony_ci		hard_iface = batadv_hardif_add_interface(net_dev);
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	if (!hard_iface)
97362306a36Sopenharmony_ci		goto out;
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	switch (event) {
97662306a36Sopenharmony_ci	case NETDEV_UP:
97762306a36Sopenharmony_ci		batadv_hardif_activate_interface(hard_iface);
97862306a36Sopenharmony_ci		break;
97962306a36Sopenharmony_ci	case NETDEV_GOING_DOWN:
98062306a36Sopenharmony_ci	case NETDEV_DOWN:
98162306a36Sopenharmony_ci		batadv_hardif_deactivate_interface(hard_iface);
98262306a36Sopenharmony_ci		break;
98362306a36Sopenharmony_ci	case NETDEV_UNREGISTER:
98462306a36Sopenharmony_ci	case NETDEV_PRE_TYPE_CHANGE:
98562306a36Sopenharmony_ci		list_del_rcu(&hard_iface->list);
98662306a36Sopenharmony_ci		batadv_hardif_generation++;
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci		batadv_hardif_remove_interface(hard_iface);
98962306a36Sopenharmony_ci		break;
99062306a36Sopenharmony_ci	case NETDEV_CHANGEMTU:
99162306a36Sopenharmony_ci		if (hard_iface->soft_iface)
99262306a36Sopenharmony_ci			batadv_update_min_mtu(hard_iface->soft_iface);
99362306a36Sopenharmony_ci		break;
99462306a36Sopenharmony_ci	case NETDEV_CHANGEADDR:
99562306a36Sopenharmony_ci		if (hard_iface->if_status == BATADV_IF_NOT_IN_USE)
99662306a36Sopenharmony_ci			goto hardif_put;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci		batadv_check_known_mac_addr(hard_iface->net_dev);
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci		bat_priv = netdev_priv(hard_iface->soft_iface);
100162306a36Sopenharmony_ci		bat_priv->algo_ops->iface.update_mac(hard_iface);
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci		primary_if = batadv_primary_if_get_selected(bat_priv);
100462306a36Sopenharmony_ci		if (!primary_if)
100562306a36Sopenharmony_ci			goto hardif_put;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci		if (hard_iface == primary_if)
100862306a36Sopenharmony_ci			batadv_primary_if_update_addr(bat_priv, NULL);
100962306a36Sopenharmony_ci		break;
101062306a36Sopenharmony_ci	case NETDEV_CHANGEUPPER:
101162306a36Sopenharmony_ci		hard_iface->wifi_flags = batadv_wifi_flags_evaluate(net_dev);
101262306a36Sopenharmony_ci		if (batadv_is_wifi_hardif(hard_iface))
101362306a36Sopenharmony_ci			hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS;
101462306a36Sopenharmony_ci		break;
101562306a36Sopenharmony_ci	default:
101662306a36Sopenharmony_ci		break;
101762306a36Sopenharmony_ci	}
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_cihardif_put:
102062306a36Sopenharmony_ci	batadv_hardif_put(hard_iface);
102162306a36Sopenharmony_ciout:
102262306a36Sopenharmony_ci	batadv_hardif_put(primary_if);
102362306a36Sopenharmony_ci	return NOTIFY_DONE;
102462306a36Sopenharmony_ci}
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_cistruct notifier_block batadv_hard_if_notifier = {
102762306a36Sopenharmony_ci	.notifier_call = batadv_hard_if_event,
102862306a36Sopenharmony_ci};
1029