xref: /openbmc/linux/net/batman-adv/hard-interface.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
17db7d9f3SSven Eckelmann // SPDX-License-Identifier: GPL-2.0
2cfa55c6dSSven Eckelmann /* Copyright (C) B.A.T.M.A.N. contributors:
3c6c8fea2SSven Eckelmann  *
4c6c8fea2SSven Eckelmann  * Marek Lindner, Simon Wunderlich
5c6c8fea2SSven Eckelmann  */
6c6c8fea2SSven Eckelmann 
7c6c8fea2SSven Eckelmann #include "hard-interface.h"
81e2c2a4fSSven Eckelmann #include "main.h"
9c6c8fea2SSven Eckelmann 
107a659d56SSven Eckelmann #include <linux/atomic.h>
111e2c2a4fSSven Eckelmann #include <linux/byteorder/generic.h>
12*112cbcb4SSven Eckelmann #include <linux/compiler.h>
13eb7da4f1SSven Eckelmann #include <linux/container_of.h>
14b1cb8a71SShigeru Yoshida #include <linux/errno.h>
15b92b94acSSven Eckelmann #include <linux/gfp.h>
16fcafa5e7SSven Eckelmann #include <linux/if.h>
17c6c8fea2SSven Eckelmann #include <linux/if_arp.h>
18af5d4f77SAntonio Quartulli #include <linux/if_ether.h>
197a659d56SSven Eckelmann #include <linux/kref.h>
20e1928752SSven Eckelmann #include <linux/limits.h>
211e2c2a4fSSven Eckelmann #include <linux/list.h>
22fcd193e1SSven Eckelmann #include <linux/minmax.h>
2340e220b4SSven Eckelmann #include <linux/mutex.h>
241e2c2a4fSSven Eckelmann #include <linux/netdevice.h>
251e2c2a4fSSven Eckelmann #include <linux/printk.h>
261e2c2a4fSSven Eckelmann #include <linux/rculist.h>
271e2c2a4fSSven Eckelmann #include <linux/rtnetlink.h>
281e2c2a4fSSven Eckelmann #include <linux/slab.h>
29cef63419SMarek Lindner #include <linux/spinlock.h>
30275019d2SAndrew Lunn #include <net/net_namespace.h>
31275019d2SAndrew Lunn #include <net/rtnetlink.h>
32fec149f5SSven Eckelmann #include <uapi/linux/batadv_packet.h>
331e2c2a4fSSven Eckelmann 
34a2d08166SSven Eckelmann #include "bat_v.h"
351e2c2a4fSSven Eckelmann #include "bridge_loop_avoidance.h"
361e2c2a4fSSven Eckelmann #include "distributed-arp-table.h"
371e2c2a4fSSven Eckelmann #include "gateway_client.h"
38ba412080SSven Eckelmann #include "log.h"
391e2c2a4fSSven Eckelmann #include "originator.h"
401e2c2a4fSSven Eckelmann #include "send.h"
411e2c2a4fSSven Eckelmann #include "soft-interface.h"
421e2c2a4fSSven Eckelmann #include "translation-table.h"
43c6c8fea2SSven Eckelmann 
44140ed8e8SSven Eckelmann /**
457e9a8c2cSSven Eckelmann  * batadv_hardif_release() - release hard interface from lists and queue for
46140ed8e8SSven Eckelmann  *  free after rcu grace period
477a659d56SSven Eckelmann  * @ref: kref pointer of the hard interface
48140ed8e8SSven Eckelmann  */
batadv_hardif_release(struct kref * ref)497a659d56SSven Eckelmann void batadv_hardif_release(struct kref *ref)
50c6c8fea2SSven Eckelmann {
517a659d56SSven Eckelmann 	struct batadv_hard_iface *hard_iface;
527a659d56SSven Eckelmann 
537a659d56SSven Eckelmann 	hard_iface = container_of(ref, struct batadv_hard_iface, refcount);
54e6c10f43SMarek Lindner 	dev_put(hard_iface->net_dev);
55140ed8e8SSven Eckelmann 
56140ed8e8SSven Eckelmann 	kfree_rcu(hard_iface, rcu);
57c6c8fea2SSven Eckelmann }
58c6c8fea2SSven Eckelmann 
59ff15c27cSSven Eckelmann /**
60ff15c27cSSven Eckelmann  * batadv_hardif_get_by_netdev() - Get hard interface object of a net_device
61ff15c27cSSven Eckelmann  * @net_dev: net_device to search for
62ff15c27cSSven Eckelmann  *
63ff15c27cSSven Eckelmann  * Return: batadv_hard_iface of net_dev (with increased refcnt), NULL on errors
64ff15c27cSSven Eckelmann  */
6556303d34SSven Eckelmann struct batadv_hard_iface *
batadv_hardif_get_by_netdev(const struct net_device * net_dev)6656303d34SSven Eckelmann batadv_hardif_get_by_netdev(const struct net_device *net_dev)
67c6c8fea2SSven Eckelmann {
6856303d34SSven Eckelmann 	struct batadv_hard_iface *hard_iface;
69c6c8fea2SSven Eckelmann 
70c6c8fea2SSven Eckelmann 	rcu_read_lock();
713193e8fdSSven Eckelmann 	list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
72e6c10f43SMarek Lindner 		if (hard_iface->net_dev == net_dev &&
737a659d56SSven Eckelmann 		    kref_get_unless_zero(&hard_iface->refcount))
74c6c8fea2SSven Eckelmann 			goto out;
75c6c8fea2SSven Eckelmann 	}
76c6c8fea2SSven Eckelmann 
77e6c10f43SMarek Lindner 	hard_iface = NULL;
78c6c8fea2SSven Eckelmann 
79c6c8fea2SSven Eckelmann out:
80c6c8fea2SSven Eckelmann 	rcu_read_unlock();
81e6c10f43SMarek Lindner 	return hard_iface;
82c6c8fea2SSven Eckelmann }
83c6c8fea2SSven Eckelmann 
84b7eddd0bSAntonio Quartulli /**
857e9a8c2cSSven Eckelmann  * batadv_getlink_net() - return link net namespace (of use fallback)
86275019d2SAndrew Lunn  * @netdev: net_device to check
87275019d2SAndrew Lunn  * @fallback_net: return in case get_link_net is not available for @netdev
88275019d2SAndrew Lunn  *
89275019d2SAndrew Lunn  * Return: result of rtnl_link_ops->get_link_net or @fallback_net
90275019d2SAndrew Lunn  */
batadv_getlink_net(const struct net_device * netdev,struct net * fallback_net)9188ffc7d0SSven Eckelmann static struct net *batadv_getlink_net(const struct net_device *netdev,
9288ffc7d0SSven Eckelmann 				      struct net *fallback_net)
93275019d2SAndrew Lunn {
94275019d2SAndrew Lunn 	if (!netdev->rtnl_link_ops)
95275019d2SAndrew Lunn 		return fallback_net;
96275019d2SAndrew Lunn 
97275019d2SAndrew Lunn 	if (!netdev->rtnl_link_ops->get_link_net)
98275019d2SAndrew Lunn 		return fallback_net;
99275019d2SAndrew Lunn 
100275019d2SAndrew Lunn 	return netdev->rtnl_link_ops->get_link_net(netdev);
101275019d2SAndrew Lunn }
102275019d2SAndrew Lunn 
103275019d2SAndrew Lunn /**
1047e9a8c2cSSven Eckelmann  * batadv_mutual_parents() - check if two devices are each others parent
105275019d2SAndrew Lunn  * @dev1: 1st net dev
106275019d2SAndrew Lunn  * @net1: 1st devices netns
107275019d2SAndrew Lunn  * @dev2: 2nd net dev
108275019d2SAndrew Lunn  * @net2: 2nd devices netns
1091bc4e2b0SAndrew Lunn  *
1101bc4e2b0SAndrew Lunn  * veth devices come in pairs and each is the parent of the other!
1111bc4e2b0SAndrew Lunn  *
1121bc4e2b0SAndrew Lunn  * Return: true if the devices are each others parent, otherwise false
1131bc4e2b0SAndrew Lunn  */
batadv_mutual_parents(const struct net_device * dev1,struct net * net1,const struct net_device * dev2,struct net * net2)1141bc4e2b0SAndrew Lunn static bool batadv_mutual_parents(const struct net_device *dev1,
11588ffc7d0SSven Eckelmann 				  struct net *net1,
116275019d2SAndrew Lunn 				  const struct net_device *dev2,
11788ffc7d0SSven Eckelmann 				  struct net *net2)
1181bc4e2b0SAndrew Lunn {
1191bc4e2b0SAndrew Lunn 	int dev1_parent_iflink = dev_get_iflink(dev1);
1201bc4e2b0SAndrew Lunn 	int dev2_parent_iflink = dev_get_iflink(dev2);
121275019d2SAndrew Lunn 	const struct net *dev1_parent_net;
122275019d2SAndrew Lunn 	const struct net *dev2_parent_net;
123275019d2SAndrew Lunn 
124275019d2SAndrew Lunn 	dev1_parent_net = batadv_getlink_net(dev1, net1);
125275019d2SAndrew Lunn 	dev2_parent_net = batadv_getlink_net(dev2, net2);
1261bc4e2b0SAndrew Lunn 
1271bc4e2b0SAndrew Lunn 	if (!dev1_parent_iflink || !dev2_parent_iflink)
1281bc4e2b0SAndrew Lunn 		return false;
1291bc4e2b0SAndrew Lunn 
1301bc4e2b0SAndrew Lunn 	return (dev1_parent_iflink == dev2->ifindex) &&
131275019d2SAndrew Lunn 	       (dev2_parent_iflink == dev1->ifindex) &&
132275019d2SAndrew Lunn 	       net_eq(dev1_parent_net, net2) &&
133275019d2SAndrew Lunn 	       net_eq(dev2_parent_net, net1);
1341bc4e2b0SAndrew Lunn }
1351bc4e2b0SAndrew Lunn 
1361bc4e2b0SAndrew Lunn /**
1377e9a8c2cSSven Eckelmann  * batadv_is_on_batman_iface() - check if a device is a batman iface descendant
138b7eddd0bSAntonio Quartulli  * @net_dev: the device to check
139b7eddd0bSAntonio Quartulli  *
140b7eddd0bSAntonio Quartulli  * If the user creates any virtual device on top of a batman-adv interface, it
141bccb48c8SSven Eckelmann  * is important to prevent this new interface from being used to create a new
142bccb48c8SSven Eckelmann  * mesh network (this behaviour would lead to a batman-over-batman
143bccb48c8SSven Eckelmann  * configuration). This function recursively checks all the fathers of the
144bccb48c8SSven Eckelmann  * device passed as argument looking for a batman-adv soft interface.
145b7eddd0bSAntonio Quartulli  *
14662fe710fSSven Eckelmann  * Return: true if the device is descendant of a batman-adv mesh interface (or
147b7eddd0bSAntonio Quartulli  * if it is a batman-adv interface itself), false otherwise
148b7eddd0bSAntonio Quartulli  */
batadv_is_on_batman_iface(const struct net_device * net_dev)149b7eddd0bSAntonio Quartulli static bool batadv_is_on_batman_iface(const struct net_device *net_dev)
150b7eddd0bSAntonio Quartulli {
1512cd45a06SAndrew Lunn 	struct net *net = dev_net(net_dev);
152275019d2SAndrew Lunn 	struct net_device *parent_dev;
15388ffc7d0SSven Eckelmann 	struct net *parent_net;
154690bb6fbSSven Eckelmann 	int iflink;
155b7eddd0bSAntonio Quartulli 	bool ret;
156b7eddd0bSAntonio Quartulli 
157b7eddd0bSAntonio Quartulli 	/* check if this is a batman-adv mesh interface */
158b7eddd0bSAntonio Quartulli 	if (batadv_softif_is_valid(net_dev))
159b7eddd0bSAntonio Quartulli 		return true;
160b7eddd0bSAntonio Quartulli 
161690bb6fbSSven Eckelmann 	iflink = dev_get_iflink(net_dev);
1626c1f41afSSven Eckelmann 	if (iflink == 0)
163b7eddd0bSAntonio Quartulli 		return false;
164b7eddd0bSAntonio Quartulli 
165275019d2SAndrew Lunn 	parent_net = batadv_getlink_net(net_dev, net);
166275019d2SAndrew Lunn 
1676c1f41afSSven Eckelmann 	/* iflink to itself, most likely physical device */
1686c1f41afSSven Eckelmann 	if (net == parent_net && iflink == net_dev->ifindex)
1696c1f41afSSven Eckelmann 		return false;
1706c1f41afSSven Eckelmann 
171b7eddd0bSAntonio Quartulli 	/* recurse over the parent device */
172690bb6fbSSven Eckelmann 	parent_dev = __dev_get_by_index((struct net *)parent_net, iflink);
173955d3411SSven Eckelmann 	if (!parent_dev) {
1746ee3c393SSven Eckelmann 		pr_warn("Cannot find parent device. Skipping batadv-on-batadv check for %s\n",
1756ee3c393SSven Eckelmann 			net_dev->name);
176b7eddd0bSAntonio Quartulli 		return false;
177955d3411SSven Eckelmann 	}
178b7eddd0bSAntonio Quartulli 
179275019d2SAndrew Lunn 	if (batadv_mutual_parents(net_dev, net, parent_dev, parent_net))
1801bc4e2b0SAndrew Lunn 		return false;
1811bc4e2b0SAndrew Lunn 
182b7eddd0bSAntonio Quartulli 	ret = batadv_is_on_batman_iface(parent_dev);
183b7eddd0bSAntonio Quartulli 
184b7eddd0bSAntonio Quartulli 	return ret;
185b7eddd0bSAntonio Quartulli }
186b7eddd0bSAntonio Quartulli 
batadv_is_valid_iface(const struct net_device * net_dev)1874b426b10SSven Eckelmann static bool batadv_is_valid_iface(const struct net_device *net_dev)
188c6c8fea2SSven Eckelmann {
189c6c8fea2SSven Eckelmann 	if (net_dev->flags & IFF_LOOPBACK)
1904b426b10SSven Eckelmann 		return false;
191c6c8fea2SSven Eckelmann 
192c6c8fea2SSven Eckelmann 	if (net_dev->type != ARPHRD_ETHER)
1934b426b10SSven Eckelmann 		return false;
194c6c8fea2SSven Eckelmann 
195c6c8fea2SSven Eckelmann 	if (net_dev->addr_len != ETH_ALEN)
1964b426b10SSven Eckelmann 		return false;
197c6c8fea2SSven Eckelmann 
198c6c8fea2SSven Eckelmann 	/* no batman over batman */
199b7eddd0bSAntonio Quartulli 	if (batadv_is_on_batman_iface(net_dev))
2004b426b10SSven Eckelmann 		return false;
201c6c8fea2SSven Eckelmann 
2024b426b10SSven Eckelmann 	return true;
203c6c8fea2SSven Eckelmann }
204c6c8fea2SSven Eckelmann 
205de68d100SMatthias Schiffer /**
2067e9a8c2cSSven Eckelmann  * batadv_get_real_netdevice() - check if the given netdev struct is a virtual
2075ed4a460SMarek Lindner  *  interface on top of another 'real' interface
2085ed4a460SMarek Lindner  * @netdev: the device to check
2095ed4a460SMarek Lindner  *
2101942de1bSMarek Lindner  * Callers must hold the rtnl semaphore. You may want batadv_get_real_netdev()
2111942de1bSMarek Lindner  * instead of this.
2121942de1bSMarek Lindner  *
2135ed4a460SMarek Lindner  * Return: the 'real' net device or the original net device and NULL in case
2145ed4a460SMarek Lindner  *  of an error.
2155ed4a460SMarek Lindner  */
batadv_get_real_netdevice(struct net_device * netdev)2165ed4a460SMarek Lindner static struct net_device *batadv_get_real_netdevice(struct net_device *netdev)
2175ed4a460SMarek Lindner {
2185ed4a460SMarek Lindner 	struct batadv_hard_iface *hard_iface = NULL;
2195ed4a460SMarek Lindner 	struct net_device *real_netdev = NULL;
2205ed4a460SMarek Lindner 	struct net *real_net;
2215ed4a460SMarek Lindner 	struct net *net;
2226116ba09SSven Eckelmann 	int iflink;
2235ed4a460SMarek Lindner 
2245ed4a460SMarek Lindner 	ASSERT_RTNL();
2255ed4a460SMarek Lindner 
2265ed4a460SMarek Lindner 	if (!netdev)
2275ed4a460SMarek Lindner 		return NULL;
2285ed4a460SMarek Lindner 
2296116ba09SSven Eckelmann 	iflink = dev_get_iflink(netdev);
2306c1f41afSSven Eckelmann 	if (iflink == 0) {
2315ed4a460SMarek Lindner 		dev_hold(netdev);
2325ed4a460SMarek Lindner 		return netdev;
2335ed4a460SMarek Lindner 	}
2345ed4a460SMarek Lindner 
2355ed4a460SMarek Lindner 	hard_iface = batadv_hardif_get_by_netdev(netdev);
2365ed4a460SMarek Lindner 	if (!hard_iface || !hard_iface->soft_iface)
2375ed4a460SMarek Lindner 		goto out;
2385ed4a460SMarek Lindner 
2395ed4a460SMarek Lindner 	net = dev_net(hard_iface->soft_iface);
2405ed4a460SMarek Lindner 	real_net = batadv_getlink_net(netdev, net);
2416c1f41afSSven Eckelmann 
2426c1f41afSSven Eckelmann 	/* iflink to itself, most likely physical device */
2436c1f41afSSven Eckelmann 	if (net == real_net && netdev->ifindex == iflink) {
2446c1f41afSSven Eckelmann 		real_netdev = netdev;
2456c1f41afSSven Eckelmann 		dev_hold(real_netdev);
2466c1f41afSSven Eckelmann 		goto out;
2476c1f41afSSven Eckelmann 	}
2486c1f41afSSven Eckelmann 
2496116ba09SSven Eckelmann 	real_netdev = dev_get_by_index(real_net, iflink);
2505ed4a460SMarek Lindner 
2515ed4a460SMarek Lindner out:
2525ed4a460SMarek Lindner 	batadv_hardif_put(hard_iface);
2535ed4a460SMarek Lindner 	return real_netdev;
2545ed4a460SMarek Lindner }
2555ed4a460SMarek Lindner 
2565ed4a460SMarek Lindner /**
2577e9a8c2cSSven Eckelmann  * batadv_get_real_netdev() - check if the given net_device struct is a virtual
2581942de1bSMarek Lindner  *  interface on top of another 'real' interface
259de68d100SMatthias Schiffer  * @net_device: the device to check
260de68d100SMatthias Schiffer  *
2611942de1bSMarek Lindner  * Return: the 'real' net device or the original net device and NULL in case
2621942de1bSMarek Lindner  *  of an error.
263de68d100SMatthias Schiffer  */
batadv_get_real_netdev(struct net_device * net_device)2641942de1bSMarek Lindner struct net_device *batadv_get_real_netdev(struct net_device *net_device)
2651942de1bSMarek Lindner {
2661942de1bSMarek Lindner 	struct net_device *real_netdev;
2671942de1bSMarek Lindner 
2681942de1bSMarek Lindner 	rtnl_lock();
2691942de1bSMarek Lindner 	real_netdev = batadv_get_real_netdevice(net_device);
2701942de1bSMarek Lindner 	rtnl_unlock();
2711942de1bSMarek Lindner 
2721942de1bSMarek Lindner 	return real_netdev;
2731942de1bSMarek Lindner }
2741942de1bSMarek Lindner 
2751942de1bSMarek Lindner /**
2767e9a8c2cSSven Eckelmann  * batadv_is_wext_netdev() - check if the given net_device struct is a
27710b1bbb4SSven Eckelmann  *  wext wifi interface
278f44a3ae9SMarek Lindner  * @net_device: the device to check
279f44a3ae9SMarek Lindner  *
28010b1bbb4SSven Eckelmann  * Return: true if the net device is a wext wireless device, false
281f44a3ae9SMarek Lindner  *  otherwise.
282f44a3ae9SMarek Lindner  */
batadv_is_wext_netdev(struct net_device * net_device)28310b1bbb4SSven Eckelmann static bool batadv_is_wext_netdev(struct net_device *net_device)
284de68d100SMatthias Schiffer {
2850c69aeccSAntonio Quartulli 	if (!net_device)
2860c69aeccSAntonio Quartulli 		return false;
2870c69aeccSAntonio Quartulli 
288de68d100SMatthias Schiffer #ifdef CONFIG_WIRELESS_EXT
289de68d100SMatthias Schiffer 	/* pre-cfg80211 drivers have to implement WEXT, so it is possible to
290de68d100SMatthias Schiffer 	 * check for wireless_handlers != NULL
291de68d100SMatthias Schiffer 	 */
292de68d100SMatthias Schiffer 	if (net_device->wireless_handlers)
293de68d100SMatthias Schiffer 		return true;
294de68d100SMatthias Schiffer #endif
295de68d100SMatthias Schiffer 
29610b1bbb4SSven Eckelmann 	return false;
29710b1bbb4SSven Eckelmann }
29810b1bbb4SSven Eckelmann 
29910b1bbb4SSven Eckelmann /**
3007e9a8c2cSSven Eckelmann  * batadv_is_cfg80211_netdev() - check if the given net_device struct is a
30110b1bbb4SSven Eckelmann  *  cfg80211 wifi interface
30210b1bbb4SSven Eckelmann  * @net_device: the device to check
30310b1bbb4SSven Eckelmann  *
30410b1bbb4SSven Eckelmann  * Return: true if the net device is a cfg80211 wireless device, false
30510b1bbb4SSven Eckelmann  *  otherwise.
30610b1bbb4SSven Eckelmann  */
batadv_is_cfg80211_netdev(struct net_device * net_device)30710b1bbb4SSven Eckelmann static bool batadv_is_cfg80211_netdev(struct net_device *net_device)
30810b1bbb4SSven Eckelmann {
30910b1bbb4SSven Eckelmann 	if (!net_device)
31010b1bbb4SSven Eckelmann 		return false;
31110b1bbb4SSven Eckelmann 
312c304eddcSJakub Kicinski #if IS_ENABLED(CONFIG_CFG80211)
313de68d100SMatthias Schiffer 	/* cfg80211 drivers have to set ieee80211_ptr */
314de68d100SMatthias Schiffer 	if (net_device->ieee80211_ptr)
315de68d100SMatthias Schiffer 		return true;
316c304eddcSJakub Kicinski #endif
317de68d100SMatthias Schiffer 
318de68d100SMatthias Schiffer 	return false;
319de68d100SMatthias Schiffer }
320de68d100SMatthias Schiffer 
3213111beedSLinus Lüssing /**
3227e9a8c2cSSven Eckelmann  * batadv_wifi_flags_evaluate() - calculate wifi flags for net_device
32310b1bbb4SSven Eckelmann  * @net_device: the device to check
32410b1bbb4SSven Eckelmann  *
32510b1bbb4SSven Eckelmann  * Return: batadv_hard_iface_wifi_flags flags of the device
32610b1bbb4SSven Eckelmann  */
batadv_wifi_flags_evaluate(struct net_device * net_device)32710b1bbb4SSven Eckelmann static u32 batadv_wifi_flags_evaluate(struct net_device *net_device)
32810b1bbb4SSven Eckelmann {
32910b1bbb4SSven Eckelmann 	u32 wifi_flags = 0;
3305ed4a460SMarek Lindner 	struct net_device *real_netdev;
33110b1bbb4SSven Eckelmann 
33210b1bbb4SSven Eckelmann 	if (batadv_is_wext_netdev(net_device))
33310b1bbb4SSven Eckelmann 		wifi_flags |= BATADV_HARDIF_WIFI_WEXT_DIRECT;
33410b1bbb4SSven Eckelmann 
33510b1bbb4SSven Eckelmann 	if (batadv_is_cfg80211_netdev(net_device))
33610b1bbb4SSven Eckelmann 		wifi_flags |= BATADV_HARDIF_WIFI_CFG80211_DIRECT;
33710b1bbb4SSven Eckelmann 
3385ed4a460SMarek Lindner 	real_netdev = batadv_get_real_netdevice(net_device);
3395ed4a460SMarek Lindner 	if (!real_netdev)
3405ed4a460SMarek Lindner 		return wifi_flags;
3415ed4a460SMarek Lindner 
3425ed4a460SMarek Lindner 	if (real_netdev == net_device)
3435ed4a460SMarek Lindner 		goto out;
3445ed4a460SMarek Lindner 
3455ed4a460SMarek Lindner 	if (batadv_is_wext_netdev(real_netdev))
3465ed4a460SMarek Lindner 		wifi_flags |= BATADV_HARDIF_WIFI_WEXT_INDIRECT;
3475ed4a460SMarek Lindner 
3485ed4a460SMarek Lindner 	if (batadv_is_cfg80211_netdev(real_netdev))
3495ed4a460SMarek Lindner 		wifi_flags |= BATADV_HARDIF_WIFI_CFG80211_INDIRECT;
3505ed4a460SMarek Lindner 
3515ed4a460SMarek Lindner out:
3525ed4a460SMarek Lindner 	dev_put(real_netdev);
35310b1bbb4SSven Eckelmann 	return wifi_flags;
35410b1bbb4SSven Eckelmann }
35510b1bbb4SSven Eckelmann 
35610b1bbb4SSven Eckelmann /**
3577e9a8c2cSSven Eckelmann  * batadv_is_cfg80211_hardif() - check if the given hardif is a cfg80211 wifi
35810b1bbb4SSven Eckelmann  *  interface
35910b1bbb4SSven Eckelmann  * @hard_iface: the device to check
36010b1bbb4SSven Eckelmann  *
36110b1bbb4SSven Eckelmann  * Return: true if the net device is a cfg80211 wireless device, false
36210b1bbb4SSven Eckelmann  *  otherwise.
36310b1bbb4SSven Eckelmann  */
batadv_is_cfg80211_hardif(struct batadv_hard_iface * hard_iface)36410b1bbb4SSven Eckelmann bool batadv_is_cfg80211_hardif(struct batadv_hard_iface *hard_iface)
36510b1bbb4SSven Eckelmann {
36610b1bbb4SSven Eckelmann 	u32 allowed_flags = 0;
36710b1bbb4SSven Eckelmann 
36810b1bbb4SSven Eckelmann 	allowed_flags |= BATADV_HARDIF_WIFI_CFG80211_DIRECT;
3695ed4a460SMarek Lindner 	allowed_flags |= BATADV_HARDIF_WIFI_CFG80211_INDIRECT;
37010b1bbb4SSven Eckelmann 
37110b1bbb4SSven Eckelmann 	return !!(hard_iface->wifi_flags & allowed_flags);
37210b1bbb4SSven Eckelmann }
37310b1bbb4SSven Eckelmann 
37410b1bbb4SSven Eckelmann /**
3757e9a8c2cSSven Eckelmann  * batadv_is_wifi_hardif() - check if the given hardif is a wifi interface
37610b1bbb4SSven Eckelmann  * @hard_iface: the device to check
37710b1bbb4SSven Eckelmann  *
37810b1bbb4SSven Eckelmann  * Return: true if the net device is a 802.11 wireless device, false otherwise.
37910b1bbb4SSven Eckelmann  */
batadv_is_wifi_hardif(struct batadv_hard_iface * hard_iface)38010b1bbb4SSven Eckelmann bool batadv_is_wifi_hardif(struct batadv_hard_iface *hard_iface)
38110b1bbb4SSven Eckelmann {
38210b1bbb4SSven Eckelmann 	if (!hard_iface)
38310b1bbb4SSven Eckelmann 		return false;
38410b1bbb4SSven Eckelmann 
38510b1bbb4SSven Eckelmann 	return hard_iface->wifi_flags != 0;
386de68d100SMatthias Schiffer }
387de68d100SMatthias Schiffer 
3883111beedSLinus Lüssing /**
3897e9a8c2cSSven Eckelmann  * batadv_hardif_no_broadcast() - check whether (re)broadcast is necessary
3903111beedSLinus Lüssing  * @if_outgoing: the outgoing interface checked and considered for (re)broadcast
3913111beedSLinus Lüssing  * @orig_addr: the originator of this packet
3923111beedSLinus Lüssing  * @orig_neigh: originator address of the forwarder we just got the packet from
3933111beedSLinus Lüssing  *  (NULL if we originated)
3943111beedSLinus Lüssing  *
3953111beedSLinus Lüssing  * Checks whether a packet needs to be (re)broadcasted on the given interface.
3963111beedSLinus Lüssing  *
3973111beedSLinus Lüssing  * Return:
3983111beedSLinus Lüssing  *	BATADV_HARDIF_BCAST_NORECIPIENT: No neighbor on interface
3993111beedSLinus Lüssing  *	BATADV_HARDIF_BCAST_DUPFWD: Just one neighbor, but it is the forwarder
4003111beedSLinus Lüssing  *	BATADV_HARDIF_BCAST_DUPORIG: Just one neighbor, but it is the originator
4013111beedSLinus Lüssing  *	BATADV_HARDIF_BCAST_OK: Several neighbors, must broadcast
4023111beedSLinus Lüssing  */
batadv_hardif_no_broadcast(struct batadv_hard_iface * if_outgoing,u8 * orig_addr,u8 * orig_neigh)4033111beedSLinus Lüssing int batadv_hardif_no_broadcast(struct batadv_hard_iface *if_outgoing,
4043111beedSLinus Lüssing 			       u8 *orig_addr, u8 *orig_neigh)
4053111beedSLinus Lüssing {
4063111beedSLinus Lüssing 	struct batadv_hardif_neigh_node *hardif_neigh;
4073111beedSLinus Lüssing 	struct hlist_node *first;
4083111beedSLinus Lüssing 	int ret = BATADV_HARDIF_BCAST_OK;
4093111beedSLinus Lüssing 
4103111beedSLinus Lüssing 	rcu_read_lock();
4113111beedSLinus Lüssing 
4123111beedSLinus Lüssing 	/* 0 neighbors -> no (re)broadcast */
4133111beedSLinus Lüssing 	first = rcu_dereference(hlist_first_rcu(&if_outgoing->neigh_list));
4143111beedSLinus Lüssing 	if (!first) {
4153111beedSLinus Lüssing 		ret = BATADV_HARDIF_BCAST_NORECIPIENT;
4163111beedSLinus Lüssing 		goto out;
4173111beedSLinus Lüssing 	}
4183111beedSLinus Lüssing 
419791ad7f5SZheng Yongjun 	/* >1 neighbors -> (re)broadcast */
4203111beedSLinus Lüssing 	if (rcu_dereference(hlist_next_rcu(first)))
4213111beedSLinus Lüssing 		goto out;
4223111beedSLinus Lüssing 
4233111beedSLinus Lüssing 	hardif_neigh = hlist_entry(first, struct batadv_hardif_neigh_node,
4243111beedSLinus Lüssing 				   list);
4253111beedSLinus Lüssing 
4263111beedSLinus Lüssing 	/* 1 neighbor, is the originator -> no rebroadcast */
4273111beedSLinus Lüssing 	if (orig_addr && batadv_compare_eth(hardif_neigh->orig, orig_addr)) {
4283111beedSLinus Lüssing 		ret = BATADV_HARDIF_BCAST_DUPORIG;
4293111beedSLinus Lüssing 	/* 1 neighbor, is the one we received from -> no rebroadcast */
4303111beedSLinus Lüssing 	} else if (orig_neigh &&
4313111beedSLinus Lüssing 		   batadv_compare_eth(hardif_neigh->orig, orig_neigh)) {
4323111beedSLinus Lüssing 		ret = BATADV_HARDIF_BCAST_DUPFWD;
4333111beedSLinus Lüssing 	}
4343111beedSLinus Lüssing 
4353111beedSLinus Lüssing out:
4363111beedSLinus Lüssing 	rcu_read_unlock();
4373111beedSLinus Lüssing 	return ret;
4383111beedSLinus Lüssing }
4393111beedSLinus Lüssing 
44056303d34SSven Eckelmann static struct batadv_hard_iface *
batadv_hardif_get_active(const struct net_device * soft_iface)44118a1cb6eSSven Eckelmann batadv_hardif_get_active(const struct net_device *soft_iface)
442c6c8fea2SSven Eckelmann {
44356303d34SSven Eckelmann 	struct batadv_hard_iface *hard_iface;
444c6c8fea2SSven Eckelmann 
445c6c8fea2SSven Eckelmann 	rcu_read_lock();
4463193e8fdSSven Eckelmann 	list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
447e6c10f43SMarek Lindner 		if (hard_iface->soft_iface != soft_iface)
448c6c8fea2SSven Eckelmann 			continue;
449c6c8fea2SSven Eckelmann 
450e9a4f295SSven Eckelmann 		if (hard_iface->if_status == BATADV_IF_ACTIVE &&
4517a659d56SSven Eckelmann 		    kref_get_unless_zero(&hard_iface->refcount))
452c6c8fea2SSven Eckelmann 			goto out;
453c6c8fea2SSven Eckelmann 	}
454c6c8fea2SSven Eckelmann 
455e6c10f43SMarek Lindner 	hard_iface = NULL;
456c6c8fea2SSven Eckelmann 
457c6c8fea2SSven Eckelmann out:
458c6c8fea2SSven Eckelmann 	rcu_read_unlock();
459e6c10f43SMarek Lindner 	return hard_iface;
460c6c8fea2SSven Eckelmann }
461c6c8fea2SSven Eckelmann 
batadv_primary_if_update_addr(struct batadv_priv * bat_priv,struct batadv_hard_iface * oldif)46256303d34SSven Eckelmann static void batadv_primary_if_update_addr(struct batadv_priv *bat_priv,
46356303d34SSven Eckelmann 					  struct batadv_hard_iface *oldif)
464c6c8fea2SSven Eckelmann {
46556303d34SSven Eckelmann 	struct batadv_hard_iface *primary_if;
46632ae9b22SMarek Lindner 
467e5d89254SSven Eckelmann 	primary_if = batadv_primary_if_get_selected(bat_priv);
46832ae9b22SMarek Lindner 	if (!primary_if)
46932ae9b22SMarek Lindner 		goto out;
470c6c8fea2SSven Eckelmann 
471785ea114SAntonio Quartulli 	batadv_dat_init_own_addr(bat_priv, primary_if);
47208adf151SSven Eckelmann 	batadv_bla_update_orig_address(bat_priv, primary_if, oldif);
47332ae9b22SMarek Lindner out:
47482047ad7SSven Eckelmann 	batadv_hardif_put(primary_if);
475c6c8fea2SSven Eckelmann }
476c6c8fea2SSven Eckelmann 
batadv_primary_if_select(struct batadv_priv * bat_priv,struct batadv_hard_iface * new_hard_iface)47756303d34SSven Eckelmann static void batadv_primary_if_select(struct batadv_priv *bat_priv,
47856303d34SSven Eckelmann 				     struct batadv_hard_iface *new_hard_iface)
479c6c8fea2SSven Eckelmann {
48056303d34SSven Eckelmann 	struct batadv_hard_iface *curr_hard_iface;
481c6c8fea2SSven Eckelmann 
482c3caf519SSven Eckelmann 	ASSERT_RTNL();
483c6c8fea2SSven Eckelmann 
48417a86915SSven Eckelmann 	if (new_hard_iface)
48517a86915SSven Eckelmann 		kref_get(&new_hard_iface->refcount);
486c6c8fea2SSven Eckelmann 
487cf78bb0bSAntonio Quartulli 	curr_hard_iface = rcu_replace_pointer(bat_priv->primary_if,
488cf78bb0bSAntonio Quartulli 					      new_hard_iface, 1);
489c6c8fea2SSven Eckelmann 
49032ae9b22SMarek Lindner 	if (!new_hard_iface)
49123721387SSimon Wunderlich 		goto out;
49232ae9b22SMarek Lindner 
49329824a55SAntonio Quartulli 	bat_priv->algo_ops->iface.primary_set(new_hard_iface);
49418a1cb6eSSven Eckelmann 	batadv_primary_if_update_addr(bat_priv, curr_hard_iface);
49523721387SSimon Wunderlich 
49623721387SSimon Wunderlich out:
49782047ad7SSven Eckelmann 	batadv_hardif_put(curr_hard_iface);
498c6c8fea2SSven Eckelmann }
499c6c8fea2SSven Eckelmann 
50056303d34SSven Eckelmann static bool
batadv_hardif_is_iface_up(const struct batadv_hard_iface * hard_iface)50156303d34SSven Eckelmann batadv_hardif_is_iface_up(const struct batadv_hard_iface *hard_iface)
502c6c8fea2SSven Eckelmann {
503e6c10f43SMarek Lindner 	if (hard_iface->net_dev->flags & IFF_UP)
504c6c8fea2SSven Eckelmann 		return true;
505c6c8fea2SSven Eckelmann 
506c6c8fea2SSven Eckelmann 	return false;
507c6c8fea2SSven Eckelmann }
508c6c8fea2SSven Eckelmann 
batadv_check_known_mac_addr(const struct net_device * net_dev)50918a1cb6eSSven Eckelmann static void batadv_check_known_mac_addr(const struct net_device *net_dev)
510c6c8fea2SSven Eckelmann {
51156303d34SSven Eckelmann 	const struct batadv_hard_iface *hard_iface;
512c6c8fea2SSven Eckelmann 
513c6c8fea2SSven Eckelmann 	rcu_read_lock();
5143193e8fdSSven Eckelmann 	list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
515825ffe1fSSven Eckelmann 		if (hard_iface->if_status != BATADV_IF_ACTIVE &&
516825ffe1fSSven Eckelmann 		    hard_iface->if_status != BATADV_IF_TO_BE_ACTIVATED)
517c6c8fea2SSven Eckelmann 			continue;
518c6c8fea2SSven Eckelmann 
519e6c10f43SMarek Lindner 		if (hard_iface->net_dev == net_dev)
520c6c8fea2SSven Eckelmann 			continue;
521c6c8fea2SSven Eckelmann 
5221eda58bfSSven Eckelmann 		if (!batadv_compare_eth(hard_iface->net_dev->dev_addr,
523c6c8fea2SSven Eckelmann 					net_dev->dev_addr))
524c6c8fea2SSven Eckelmann 			continue;
525c6c8fea2SSven Eckelmann 
52667969581SSven Eckelmann 		pr_warn("The newly added mac address (%pM) already exists on: %s\n",
52786ceb360SSven Eckelmann 			net_dev->dev_addr, hard_iface->net_dev->name);
52867969581SSven Eckelmann 		pr_warn("It is strongly recommended to keep mac addresses unique to avoid problems!\n");
529c6c8fea2SSven Eckelmann 	}
530c6c8fea2SSven Eckelmann 	rcu_read_unlock();
531c6c8fea2SSven Eckelmann }
532c6c8fea2SSven Eckelmann 
5337bca68c7SSven Eckelmann /**
5347bca68c7SSven Eckelmann  * batadv_hardif_recalc_extra_skbroom() - Recalculate skbuff extra head/tailroom
5357bca68c7SSven Eckelmann  * @soft_iface: netdev struct of the mesh interface
5367bca68c7SSven Eckelmann  */
batadv_hardif_recalc_extra_skbroom(struct net_device * soft_iface)5377bca68c7SSven Eckelmann static void batadv_hardif_recalc_extra_skbroom(struct net_device *soft_iface)
5387bca68c7SSven Eckelmann {
5397bca68c7SSven Eckelmann 	const struct batadv_hard_iface *hard_iface;
5407bca68c7SSven Eckelmann 	unsigned short lower_header_len = ETH_HLEN;
5417bca68c7SSven Eckelmann 	unsigned short lower_headroom = 0;
5427bca68c7SSven Eckelmann 	unsigned short lower_tailroom = 0;
5437bca68c7SSven Eckelmann 	unsigned short needed_headroom;
5447bca68c7SSven Eckelmann 
5457bca68c7SSven Eckelmann 	rcu_read_lock();
5467bca68c7SSven Eckelmann 	list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
5477bca68c7SSven Eckelmann 		if (hard_iface->if_status == BATADV_IF_NOT_IN_USE)
5487bca68c7SSven Eckelmann 			continue;
5497bca68c7SSven Eckelmann 
5507bca68c7SSven Eckelmann 		if (hard_iface->soft_iface != soft_iface)
5517bca68c7SSven Eckelmann 			continue;
5527bca68c7SSven Eckelmann 
5537bca68c7SSven Eckelmann 		lower_header_len = max_t(unsigned short, lower_header_len,
5547bca68c7SSven Eckelmann 					 hard_iface->net_dev->hard_header_len);
5557bca68c7SSven Eckelmann 
5567bca68c7SSven Eckelmann 		lower_headroom = max_t(unsigned short, lower_headroom,
5577bca68c7SSven Eckelmann 				       hard_iface->net_dev->needed_headroom);
5587bca68c7SSven Eckelmann 
5597bca68c7SSven Eckelmann 		lower_tailroom = max_t(unsigned short, lower_tailroom,
5607bca68c7SSven Eckelmann 				       hard_iface->net_dev->needed_tailroom);
5617bca68c7SSven Eckelmann 	}
5627bca68c7SSven Eckelmann 	rcu_read_unlock();
5637bca68c7SSven Eckelmann 
5647bca68c7SSven Eckelmann 	needed_headroom = lower_headroom + (lower_header_len - ETH_HLEN);
5657bca68c7SSven Eckelmann 	needed_headroom += batadv_max_header_len();
5667bca68c7SSven Eckelmann 
5674ca23e2cSSven Eckelmann 	/* fragmentation headers don't strip the unicast/... header */
5684ca23e2cSSven Eckelmann 	needed_headroom += sizeof(struct batadv_frag_packet);
5694ca23e2cSSven Eckelmann 
5707bca68c7SSven Eckelmann 	soft_iface->needed_headroom = needed_headroom;
5717bca68c7SSven Eckelmann 	soft_iface->needed_tailroom = lower_tailroom;
5727bca68c7SSven Eckelmann }
5737bca68c7SSven Eckelmann 
574ff15c27cSSven Eckelmann /**
575ff15c27cSSven Eckelmann  * batadv_hardif_min_mtu() - Calculate maximum MTU for soft interface
576ff15c27cSSven Eckelmann  * @soft_iface: netdev struct of the soft interface
577ff15c27cSSven Eckelmann  *
578ff15c27cSSven Eckelmann  * Return: MTU for the soft-interface (limited by the minimal MTU of all active
579ff15c27cSSven Eckelmann  *  slave interfaces)
580ff15c27cSSven Eckelmann  */
batadv_hardif_min_mtu(struct net_device * soft_iface)5819563877eSSven Eckelmann int batadv_hardif_min_mtu(struct net_device *soft_iface)
582c6c8fea2SSven Eckelmann {
583a19d3d85SMarek Lindner 	struct batadv_priv *bat_priv = netdev_priv(soft_iface);
58456303d34SSven Eckelmann 	const struct batadv_hard_iface *hard_iface;
585930cd6e4SAntonio Quartulli 	int min_mtu = INT_MAX;
586c6c8fea2SSven Eckelmann 
587c6c8fea2SSven Eckelmann 	rcu_read_lock();
5883193e8fdSSven Eckelmann 	list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
589825ffe1fSSven Eckelmann 		if (hard_iface->if_status != BATADV_IF_ACTIVE &&
590825ffe1fSSven Eckelmann 		    hard_iface->if_status != BATADV_IF_TO_BE_ACTIVATED)
591c6c8fea2SSven Eckelmann 			continue;
592c6c8fea2SSven Eckelmann 
593e6c10f43SMarek Lindner 		if (hard_iface->soft_iface != soft_iface)
594c6c8fea2SSven Eckelmann 			continue;
595c6c8fea2SSven Eckelmann 
596a19d3d85SMarek Lindner 		min_mtu = min_t(int, hard_iface->net_dev->mtu, min_mtu);
597c6c8fea2SSven Eckelmann 	}
598c6c8fea2SSven Eckelmann 	rcu_read_unlock();
599a19d3d85SMarek Lindner 
600a19d3d85SMarek Lindner 	if (atomic_read(&bat_priv->fragmentation) == 0)
601a19d3d85SMarek Lindner 		goto out;
602a19d3d85SMarek Lindner 
603a19d3d85SMarek Lindner 	/* with fragmentation enabled the maximum size of internally generated
604a19d3d85SMarek Lindner 	 * packets such as translation table exchanges or tvlv containers, etc
605a19d3d85SMarek Lindner 	 * has to be calculated
606a19d3d85SMarek Lindner 	 */
607a19d3d85SMarek Lindner 	min_mtu = min_t(int, min_mtu, BATADV_FRAG_MAX_FRAG_SIZE);
608a19d3d85SMarek Lindner 	min_mtu -= sizeof(struct batadv_frag_packet);
609a19d3d85SMarek Lindner 	min_mtu *= BATADV_FRAG_MAX_FRAGMENTS;
610a19d3d85SMarek Lindner 
611c6c8fea2SSven Eckelmann out:
612930cd6e4SAntonio Quartulli 	/* report to the other components the maximum amount of bytes that
613930cd6e4SAntonio Quartulli 	 * batman-adv can send over the wire (without considering the payload
614930cd6e4SAntonio Quartulli 	 * overhead). For example, this value is used by TT to compute the
61521ba5ab2SSven Eckelmann 	 * maximum local table size
616930cd6e4SAntonio Quartulli 	 */
617930cd6e4SAntonio Quartulli 	atomic_set(&bat_priv->packet_size_max, min_mtu);
618930cd6e4SAntonio Quartulli 
619930cd6e4SAntonio Quartulli 	/* the real soft-interface MTU is computed by removing the payload
620930cd6e4SAntonio Quartulli 	 * overhead from the maximum amount of bytes that was just computed.
621930cd6e4SAntonio Quartulli 	 *
622930cd6e4SAntonio Quartulli 	 * However batman-adv does not support MTUs bigger than ETH_DATA_LEN
623930cd6e4SAntonio Quartulli 	 */
624930cd6e4SAntonio Quartulli 	return min_t(int, min_mtu - batadv_max_header_len(), ETH_DATA_LEN);
625c6c8fea2SSven Eckelmann }
626c6c8fea2SSven Eckelmann 
627ff15c27cSSven Eckelmann /**
628ff15c27cSSven Eckelmann  * batadv_update_min_mtu() - Adjusts the MTU if a new interface with a smaller
629ff15c27cSSven Eckelmann  *  MTU appeared
630ff15c27cSSven Eckelmann  * @soft_iface: netdev struct of the soft interface
631ff15c27cSSven Eckelmann  */
batadv_update_min_mtu(struct net_device * soft_iface)6329563877eSSven Eckelmann void batadv_update_min_mtu(struct net_device *soft_iface)
633c6c8fea2SSven Eckelmann {
634d8e42a2bSSven Eckelmann 	struct batadv_priv *bat_priv = netdev_priv(soft_iface);
635d8e42a2bSSven Eckelmann 	int limit_mtu;
636d8e42a2bSSven Eckelmann 	int mtu;
637d8e42a2bSSven Eckelmann 
638d8e42a2bSSven Eckelmann 	mtu = batadv_hardif_min_mtu(soft_iface);
639d8e42a2bSSven Eckelmann 
640d8e42a2bSSven Eckelmann 	if (bat_priv->mtu_set_by_user)
641d8e42a2bSSven Eckelmann 		limit_mtu = bat_priv->mtu_set_by_user;
642d8e42a2bSSven Eckelmann 	else
643d8e42a2bSSven Eckelmann 		limit_mtu = ETH_DATA_LEN;
644d8e42a2bSSven Eckelmann 
645d8e42a2bSSven Eckelmann 	mtu = min(mtu, limit_mtu);
646d8e42a2bSSven Eckelmann 	dev_set_mtu(soft_iface, mtu);
647c6c8fea2SSven Eckelmann 
648a19d3d85SMarek Lindner 	/* Check if the local translate table should be cleaned up to match a
649a19d3d85SMarek Lindner 	 * new (and smaller) MTU.
650a19d3d85SMarek Lindner 	 */
651a19d3d85SMarek Lindner 	batadv_tt_local_resize_to_mtu(soft_iface);
652c6c8fea2SSven Eckelmann }
653c6c8fea2SSven Eckelmann 
65456303d34SSven Eckelmann static void
batadv_hardif_activate_interface(struct batadv_hard_iface * hard_iface)65556303d34SSven Eckelmann batadv_hardif_activate_interface(struct batadv_hard_iface *hard_iface)
656c6c8fea2SSven Eckelmann {
65756303d34SSven Eckelmann 	struct batadv_priv *bat_priv;
65856303d34SSven Eckelmann 	struct batadv_hard_iface *primary_if = NULL;
659c6c8fea2SSven Eckelmann 
660e9a4f295SSven Eckelmann 	if (hard_iface->if_status != BATADV_IF_INACTIVE)
66132ae9b22SMarek Lindner 		goto out;
662c6c8fea2SSven Eckelmann 
663e6c10f43SMarek Lindner 	bat_priv = netdev_priv(hard_iface->soft_iface);
664c6c8fea2SSven Eckelmann 
66529824a55SAntonio Quartulli 	bat_priv->algo_ops->iface.update_mac(hard_iface);
666e9a4f295SSven Eckelmann 	hard_iface->if_status = BATADV_IF_TO_BE_ACTIVATED;
667c6c8fea2SSven Eckelmann 
6689cfc7bd6SSven Eckelmann 	/* the first active interface becomes our primary interface or
669015758d0SAntonio Quartulli 	 * the next active interface after the old primary interface was removed
670c6c8fea2SSven Eckelmann 	 */
671e5d89254SSven Eckelmann 	primary_if = batadv_primary_if_get_selected(bat_priv);
67232ae9b22SMarek Lindner 	if (!primary_if)
67318a1cb6eSSven Eckelmann 		batadv_primary_if_select(bat_priv, hard_iface);
674c6c8fea2SSven Eckelmann 
6753e34819eSSven Eckelmann 	batadv_info(hard_iface->soft_iface, "Interface activated: %s\n",
676e6c10f43SMarek Lindner 		    hard_iface->net_dev->name);
677c6c8fea2SSven Eckelmann 
6789563877eSSven Eckelmann 	batadv_update_min_mtu(hard_iface->soft_iface);
67932ae9b22SMarek Lindner 
68029824a55SAntonio Quartulli 	if (bat_priv->algo_ops->iface.activate)
68129824a55SAntonio Quartulli 		bat_priv->algo_ops->iface.activate(hard_iface);
682b6cf5d49SAntonio Quartulli 
68332ae9b22SMarek Lindner out:
68482047ad7SSven Eckelmann 	batadv_hardif_put(primary_if);
685c6c8fea2SSven Eckelmann }
686c6c8fea2SSven Eckelmann 
68756303d34SSven Eckelmann static void
batadv_hardif_deactivate_interface(struct batadv_hard_iface * hard_iface)68856303d34SSven Eckelmann batadv_hardif_deactivate_interface(struct batadv_hard_iface *hard_iface)
689c6c8fea2SSven Eckelmann {
690825ffe1fSSven Eckelmann 	if (hard_iface->if_status != BATADV_IF_ACTIVE &&
691825ffe1fSSven Eckelmann 	    hard_iface->if_status != BATADV_IF_TO_BE_ACTIVATED)
692c6c8fea2SSven Eckelmann 		return;
693c6c8fea2SSven Eckelmann 
694e9a4f295SSven Eckelmann 	hard_iface->if_status = BATADV_IF_INACTIVE;
695c6c8fea2SSven Eckelmann 
6963e34819eSSven Eckelmann 	batadv_info(hard_iface->soft_iface, "Interface deactivated: %s\n",
697e6c10f43SMarek Lindner 		    hard_iface->net_dev->name);
698c6c8fea2SSven Eckelmann 
6999563877eSSven Eckelmann 	batadv_update_min_mtu(hard_iface->soft_iface);
700c6c8fea2SSven Eckelmann }
701c6c8fea2SSven Eckelmann 
702cb4b0d48SAntonio Quartulli /**
703ff15c27cSSven Eckelmann  * batadv_hardif_enable_interface() - Enslave hard interface to soft interface
704ff15c27cSSven Eckelmann  * @hard_iface: hard interface to add to soft interface
705fa205602SSven Eckelmann  * @soft_iface: netdev struct of the mesh interface
706ff15c27cSSven Eckelmann  *
707ff15c27cSSven Eckelmann  * Return: 0 on success or negative error number in case of failure
708ff15c27cSSven Eckelmann  */
batadv_hardif_enable_interface(struct batadv_hard_iface * hard_iface,struct net_device * soft_iface)70956303d34SSven Eckelmann int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
710fa205602SSven Eckelmann 				   struct net_device *soft_iface)
711c6c8fea2SSven Eckelmann {
71256303d34SSven Eckelmann 	struct batadv_priv *bat_priv;
713293e9338SAntonio Quartulli 	__be16 ethertype = htons(ETH_P_BATMAN);
714411d6ed9SMarek Lindner 	int max_header_len = batadv_max_header_len();
715*112cbcb4SSven Eckelmann 	unsigned int required_mtu;
716*112cbcb4SSven Eckelmann 	unsigned int hardif_mtu;
717e44d8fe2SSven Eckelmann 	int ret;
718c6c8fea2SSven Eckelmann 
719*112cbcb4SSven Eckelmann 	hardif_mtu = READ_ONCE(hard_iface->net_dev->mtu);
720*112cbcb4SSven Eckelmann 	required_mtu = READ_ONCE(soft_iface->mtu) + max_header_len;
721*112cbcb4SSven Eckelmann 
722*112cbcb4SSven Eckelmann 	if (hardif_mtu < ETH_MIN_MTU + max_header_len)
723b1cb8a71SShigeru Yoshida 		return -EINVAL;
724b1cb8a71SShigeru Yoshida 
725e9a4f295SSven Eckelmann 	if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
726c6c8fea2SSven Eckelmann 		goto out;
727c6c8fea2SSven Eckelmann 
72817a86915SSven Eckelmann 	kref_get(&hard_iface->refcount);
729ed75ccbeSMarek Lindner 
730fa205602SSven Eckelmann 	dev_hold(soft_iface);
731e44d8fe2SSven Eckelmann 	hard_iface->soft_iface = soft_iface;
732e6c10f43SMarek Lindner 	bat_priv = netdev_priv(hard_iface->soft_iface);
733d0b9fd89SMarek Lindner 
7346dffb044SJiri Pirko 	ret = netdev_master_upper_dev_link(hard_iface->net_dev,
73542ab19eeSDavid Ahern 					   soft_iface, NULL, NULL, NULL);
7363dbd550bSSven Eckelmann 	if (ret)
7373dbd550bSSven Eckelmann 		goto err_dev;
7383dbd550bSSven Eckelmann 
73929824a55SAntonio Quartulli 	ret = bat_priv->algo_ops->iface.enable(hard_iface);
7405346c35eSSven Eckelmann 	if (ret < 0)
7413dbd550bSSven Eckelmann 		goto err_upper;
742c6c8fea2SSven Eckelmann 
743e9a4f295SSven Eckelmann 	hard_iface->if_status = BATADV_IF_INACTIVE;
744c6c8fea2SSven Eckelmann 
745d7d6de95SSven Eckelmann 	kref_get(&hard_iface->refcount);
7467e071c79SSven Eckelmann 	hard_iface->batman_adv_ptype.type = ethertype;
7473193e8fdSSven Eckelmann 	hard_iface->batman_adv_ptype.func = batadv_batman_skb_recv;
748e6c10f43SMarek Lindner 	hard_iface->batman_adv_ptype.dev = hard_iface->net_dev;
749e6c10f43SMarek Lindner 	dev_add_pack(&hard_iface->batman_adv_ptype);
750c6c8fea2SSven Eckelmann 
7513e34819eSSven Eckelmann 	batadv_info(hard_iface->soft_iface, "Adding interface: %s\n",
752e6c10f43SMarek Lindner 		    hard_iface->net_dev->name);
753c6c8fea2SSven Eckelmann 
7540aca2369SSven Eckelmann 	if (atomic_read(&bat_priv->fragmentation) &&
755*112cbcb4SSven Eckelmann 	    hardif_mtu < required_mtu)
7563e34819eSSven Eckelmann 		batadv_info(hard_iface->soft_iface,
757411d6ed9SMarek Lindner 			    "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",
758*112cbcb4SSven Eckelmann 			    hard_iface->net_dev->name, hardif_mtu,
759*112cbcb4SSven Eckelmann 			    required_mtu);
760c6c8fea2SSven Eckelmann 
7610aca2369SSven Eckelmann 	if (!atomic_read(&bat_priv->fragmentation) &&
762*112cbcb4SSven Eckelmann 	    hardif_mtu < required_mtu)
7633e34819eSSven Eckelmann 		batadv_info(hard_iface->soft_iface,
764411d6ed9SMarek Lindner 			    "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",
765*112cbcb4SSven Eckelmann 			    hard_iface->net_dev->name, hardif_mtu,
766*112cbcb4SSven Eckelmann 			    required_mtu);
767c6c8fea2SSven Eckelmann 
76818a1cb6eSSven Eckelmann 	if (batadv_hardif_is_iface_up(hard_iface))
76918a1cb6eSSven Eckelmann 		batadv_hardif_activate_interface(hard_iface);
770c6c8fea2SSven Eckelmann 	else
7713e34819eSSven Eckelmann 		batadv_err(hard_iface->soft_iface,
77286ceb360SSven Eckelmann 			   "Not using interface %s (retrying later): interface not active\n",
773e6c10f43SMarek Lindner 			   hard_iface->net_dev->name);
774c6c8fea2SSven Eckelmann 
7757bca68c7SSven Eckelmann 	batadv_hardif_recalc_extra_skbroom(soft_iface);
7767bca68c7SSven Eckelmann 
7779e6b5648SSven Eckelmann 	if (bat_priv->algo_ops->iface.enabled)
7789e6b5648SSven Eckelmann 		bat_priv->algo_ops->iface.enabled(hard_iface);
7799e6b5648SSven Eckelmann 
780c6c8fea2SSven Eckelmann out:
781c6c8fea2SSven Eckelmann 	return 0;
782c6c8fea2SSven Eckelmann 
7833dbd550bSSven Eckelmann err_upper:
7843dbd550bSSven Eckelmann 	netdev_upper_dev_unlink(hard_iface->net_dev, soft_iface);
78577af7575SMarek Lindner err_dev:
7863dbd550bSSven Eckelmann 	hard_iface->soft_iface = NULL;
78777af7575SMarek Lindner 	dev_put(soft_iface);
78882047ad7SSven Eckelmann 	batadv_hardif_put(hard_iface);
789e44d8fe2SSven Eckelmann 	return ret;
790c6c8fea2SSven Eckelmann }
791c6c8fea2SSven Eckelmann 
792ff15c27cSSven Eckelmann /**
793dee222c7SSven Eckelmann  * batadv_hardif_cnt() - get number of interfaces enslaved to soft interface
794dee222c7SSven Eckelmann  * @soft_iface: soft interface to check
795dee222c7SSven Eckelmann  *
796dee222c7SSven Eckelmann  * This function is only using RCU for locking - the result can therefore be
797bccb48c8SSven Eckelmann  * off when another function is modifying the list at the same time. The
798dee222c7SSven Eckelmann  * caller can use the rtnl_lock to make sure that the count is accurate.
799dee222c7SSven Eckelmann  *
800dee222c7SSven Eckelmann  * Return: number of connected/enslaved hard interfaces
801dee222c7SSven Eckelmann  */
batadv_hardif_cnt(const struct net_device * soft_iface)802dee222c7SSven Eckelmann static size_t batadv_hardif_cnt(const struct net_device *soft_iface)
803dee222c7SSven Eckelmann {
804dee222c7SSven Eckelmann 	struct batadv_hard_iface *hard_iface;
805dee222c7SSven Eckelmann 	size_t count = 0;
806dee222c7SSven Eckelmann 
807dee222c7SSven Eckelmann 	rcu_read_lock();
808dee222c7SSven Eckelmann 	list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
809dee222c7SSven Eckelmann 		if (hard_iface->soft_iface != soft_iface)
810dee222c7SSven Eckelmann 			continue;
811dee222c7SSven Eckelmann 
812dee222c7SSven Eckelmann 		count++;
813dee222c7SSven Eckelmann 	}
814dee222c7SSven Eckelmann 	rcu_read_unlock();
815dee222c7SSven Eckelmann 
816dee222c7SSven Eckelmann 	return count;
817dee222c7SSven Eckelmann }
818dee222c7SSven Eckelmann 
819dee222c7SSven Eckelmann /**
820ff15c27cSSven Eckelmann  * batadv_hardif_disable_interface() - Remove hard interface from soft interface
821ff15c27cSSven Eckelmann  * @hard_iface: hard interface to be removed
822ff15c27cSSven Eckelmann  */
batadv_hardif_disable_interface(struct batadv_hard_iface * hard_iface)823a962cb29SSven Eckelmann void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
824c6c8fea2SSven Eckelmann {
82556303d34SSven Eckelmann 	struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
82656303d34SSven Eckelmann 	struct batadv_hard_iface *primary_if = NULL;
827c6c8fea2SSven Eckelmann 
82818a1cb6eSSven Eckelmann 	batadv_hardif_deactivate_interface(hard_iface);
829c6c8fea2SSven Eckelmann 
830e9a4f295SSven Eckelmann 	if (hard_iface->if_status != BATADV_IF_INACTIVE)
83132ae9b22SMarek Lindner 		goto out;
832c6c8fea2SSven Eckelmann 
8333e34819eSSven Eckelmann 	batadv_info(hard_iface->soft_iface, "Removing interface: %s\n",
834e6c10f43SMarek Lindner 		    hard_iface->net_dev->name);
835e6c10f43SMarek Lindner 	dev_remove_pack(&hard_iface->batman_adv_ptype);
836d7d6de95SSven Eckelmann 	batadv_hardif_put(hard_iface);
837c6c8fea2SSven Eckelmann 
838e5d89254SSven Eckelmann 	primary_if = batadv_primary_if_get_selected(bat_priv);
83932ae9b22SMarek Lindner 	if (hard_iface == primary_if) {
84056303d34SSven Eckelmann 		struct batadv_hard_iface *new_if;
841c6c8fea2SSven Eckelmann 
84218a1cb6eSSven Eckelmann 		new_if = batadv_hardif_get_active(hard_iface->soft_iface);
84318a1cb6eSSven Eckelmann 		batadv_primary_if_select(bat_priv, new_if);
844c6c8fea2SSven Eckelmann 
84582047ad7SSven Eckelmann 		batadv_hardif_put(new_if);
846c6c8fea2SSven Eckelmann 	}
847c6c8fea2SSven Eckelmann 
84829824a55SAntonio Quartulli 	bat_priv->algo_ops->iface.disable(hard_iface);
849e9a4f295SSven Eckelmann 	hard_iface->if_status = BATADV_IF_NOT_IN_USE;
850c6c8fea2SSven Eckelmann 
851e6c10f43SMarek Lindner 	/* delete all references to this hard_iface */
8527d211efcSSven Eckelmann 	batadv_purge_orig_ref(bat_priv);
8539455e34cSSven Eckelmann 	batadv_purge_outstanding_packets(bat_priv, hard_iface);
854e6c10f43SMarek Lindner 	dev_put(hard_iface->soft_iface);
855c6c8fea2SSven Eckelmann 
856a5256f7eSAntonio Quartulli 	netdev_upper_dev_unlink(hard_iface->net_dev, hard_iface->soft_iface);
8577bca68c7SSven Eckelmann 	batadv_hardif_recalc_extra_skbroom(hard_iface->soft_iface);
858a5256f7eSAntonio Quartulli 
859c6c8fea2SSven Eckelmann 	/* nobody uses this interface anymore */
86076e9f276SSven Eckelmann 	if (batadv_hardif_cnt(hard_iface->soft_iface) <= 1)
8618257f55aSAntonio Quartulli 		batadv_gw_check_client_stop(bat_priv);
8628257f55aSAntonio Quartulli 
86327915aa6SSven Eckelmann 	hard_iface->soft_iface = NULL;
86482047ad7SSven Eckelmann 	batadv_hardif_put(hard_iface);
86532ae9b22SMarek Lindner 
86632ae9b22SMarek Lindner out:
86782047ad7SSven Eckelmann 	batadv_hardif_put(primary_if);
868c6c8fea2SSven Eckelmann }
869c6c8fea2SSven Eckelmann 
87056303d34SSven Eckelmann static struct batadv_hard_iface *
batadv_hardif_add_interface(struct net_device * net_dev)87118a1cb6eSSven Eckelmann batadv_hardif_add_interface(struct net_device *net_dev)
872c6c8fea2SSven Eckelmann {
87356303d34SSven Eckelmann 	struct batadv_hard_iface *hard_iface;
874c6c8fea2SSven Eckelmann 
875c3caf519SSven Eckelmann 	ASSERT_RTNL();
876c3caf519SSven Eckelmann 
8774b426b10SSven Eckelmann 	if (!batadv_is_valid_iface(net_dev))
878c6c8fea2SSven Eckelmann 		goto out;
879c6c8fea2SSven Eckelmann 
880c6c8fea2SSven Eckelmann 	dev_hold(net_dev);
881c6c8fea2SSven Eckelmann 
8827db3fc29SAntonio Quartulli 	hard_iface = kzalloc(sizeof(*hard_iface), GFP_ATOMIC);
883320f422fSJoe Perches 	if (!hard_iface)
884c6c8fea2SSven Eckelmann 		goto release_dev;
885c6c8fea2SSven Eckelmann 
886e6c10f43SMarek Lindner 	hard_iface->net_dev = net_dev;
887e6c10f43SMarek Lindner 	hard_iface->soft_iface = NULL;
888e9a4f295SSven Eckelmann 	hard_iface->if_status = BATADV_IF_NOT_IN_USE;
8895bc7c1ebSSimon Wunderlich 
890e6c10f43SMarek Lindner 	INIT_LIST_HEAD(&hard_iface->list);
891cef63419SMarek Lindner 	INIT_HLIST_HEAD(&hard_iface->neigh_list);
8925bc44dc8SSimon Wunderlich 
89340e220b4SSven Eckelmann 	mutex_init(&hard_iface->bat_iv.ogm_buff_mutex);
894cef63419SMarek Lindner 	spin_lock_init(&hard_iface->neigh_list_lock);
895b2367e46SSven Eckelmann 	kref_init(&hard_iface->refcount);
896cef63419SMarek Lindner 
897caf65bfcSMatthias Schiffer 	hard_iface->num_bcasts = BATADV_NUM_BCASTS_DEFAULT;
89810b1bbb4SSven Eckelmann 	hard_iface->wifi_flags = batadv_wifi_flags_evaluate(net_dev);
89910b1bbb4SSven Eckelmann 	if (batadv_is_wifi_hardif(hard_iface))
900caf65bfcSMatthias Schiffer 		hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS;
901caf65bfcSMatthias Schiffer 
9023bda14d0SLinus Lüssing 	atomic_set(&hard_iface->hop_penalty, 0);
9033bda14d0SLinus Lüssing 
9047db682d1SMarek Lindner 	batadv_v_hardif_init(hard_iface);
9057db682d1SMarek Lindner 
90618a1cb6eSSven Eckelmann 	batadv_check_known_mac_addr(hard_iface->net_dev);
907b2367e46SSven Eckelmann 	kref_get(&hard_iface->refcount);
9083193e8fdSSven Eckelmann 	list_add_tail_rcu(&hard_iface->list, &batadv_hardif_list);
909fb69be69SSven Eckelmann 	batadv_hardif_generation++;
910c6c8fea2SSven Eckelmann 
911e6c10f43SMarek Lindner 	return hard_iface;
912c6c8fea2SSven Eckelmann 
913c6c8fea2SSven Eckelmann release_dev:
914c6c8fea2SSven Eckelmann 	dev_put(net_dev);
915c6c8fea2SSven Eckelmann out:
916c6c8fea2SSven Eckelmann 	return NULL;
917c6c8fea2SSven Eckelmann }
918c6c8fea2SSven Eckelmann 
batadv_hardif_remove_interface(struct batadv_hard_iface * hard_iface)91956303d34SSven Eckelmann static void batadv_hardif_remove_interface(struct batadv_hard_iface *hard_iface)
920c6c8fea2SSven Eckelmann {
921c3caf519SSven Eckelmann 	ASSERT_RTNL();
922c3caf519SSven Eckelmann 
923c6c8fea2SSven Eckelmann 	/* first deactivate interface */
924e9a4f295SSven Eckelmann 	if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
925a962cb29SSven Eckelmann 		batadv_hardif_disable_interface(hard_iface);
926c6c8fea2SSven Eckelmann 
927e9a4f295SSven Eckelmann 	if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
928c6c8fea2SSven Eckelmann 		return;
929c6c8fea2SSven Eckelmann 
930e9a4f295SSven Eckelmann 	hard_iface->if_status = BATADV_IF_TO_BE_REMOVED;
931569c9850SSven Eckelmann 	batadv_hardif_put(hard_iface);
932c6c8fea2SSven Eckelmann }
933c6c8fea2SSven Eckelmann 
934ff15c27cSSven Eckelmann /**
9356da7be7dSSven Eckelmann  * batadv_hard_if_event_softif() - Handle events for soft interfaces
9366da7be7dSSven Eckelmann  * @event: NETDEV_* event to handle
9376da7be7dSSven Eckelmann  * @net_dev: net_device which generated an event
9386da7be7dSSven Eckelmann  *
9396da7be7dSSven Eckelmann  * Return: NOTIFY_* result
9406da7be7dSSven Eckelmann  */
batadv_hard_if_event_softif(unsigned long event,struct net_device * net_dev)9416da7be7dSSven Eckelmann static int batadv_hard_if_event_softif(unsigned long event,
9426da7be7dSSven Eckelmann 				       struct net_device *net_dev)
9436da7be7dSSven Eckelmann {
9446da7be7dSSven Eckelmann 	struct batadv_priv *bat_priv;
9456da7be7dSSven Eckelmann 
9466da7be7dSSven Eckelmann 	switch (event) {
9476da7be7dSSven Eckelmann 	case NETDEV_REGISTER:
9486da7be7dSSven Eckelmann 		bat_priv = netdev_priv(net_dev);
9496da7be7dSSven Eckelmann 		batadv_softif_create_vlan(bat_priv, BATADV_NO_FLAGS);
9506da7be7dSSven Eckelmann 		break;
9516da7be7dSSven Eckelmann 	}
9526da7be7dSSven Eckelmann 
9536da7be7dSSven Eckelmann 	return NOTIFY_DONE;
9546da7be7dSSven Eckelmann }
9556da7be7dSSven Eckelmann 
batadv_hard_if_event(struct notifier_block * this,unsigned long event,void * ptr)95618a1cb6eSSven Eckelmann static int batadv_hard_if_event(struct notifier_block *this,
957c6c8fea2SSven Eckelmann 				unsigned long event, void *ptr)
958c6c8fea2SSven Eckelmann {
959351638e7SJiri Pirko 	struct net_device *net_dev = netdev_notifier_info_to_dev(ptr);
96056303d34SSven Eckelmann 	struct batadv_hard_iface *hard_iface;
96156303d34SSven Eckelmann 	struct batadv_hard_iface *primary_if = NULL;
96256303d34SSven Eckelmann 	struct batadv_priv *bat_priv;
963c6c8fea2SSven Eckelmann 
9646da7be7dSSven Eckelmann 	if (batadv_softif_is_valid(net_dev))
9656da7be7dSSven Eckelmann 		return batadv_hard_if_event_softif(event, net_dev);
96637130293SSven Eckelmann 
96756303d34SSven Eckelmann 	hard_iface = batadv_hardif_get_by_netdev(net_dev);
968a1a66b11SAndrew Lunn 	if (!hard_iface && (event == NETDEV_REGISTER ||
969a1a66b11SAndrew Lunn 			    event == NETDEV_POST_TYPE_CHANGE))
97018a1cb6eSSven Eckelmann 		hard_iface = batadv_hardif_add_interface(net_dev);
971c6c8fea2SSven Eckelmann 
972e6c10f43SMarek Lindner 	if (!hard_iface)
973c6c8fea2SSven Eckelmann 		goto out;
974c6c8fea2SSven Eckelmann 
975c6c8fea2SSven Eckelmann 	switch (event) {
976c6c8fea2SSven Eckelmann 	case NETDEV_UP:
97718a1cb6eSSven Eckelmann 		batadv_hardif_activate_interface(hard_iface);
978c6c8fea2SSven Eckelmann 		break;
979c6c8fea2SSven Eckelmann 	case NETDEV_GOING_DOWN:
980c6c8fea2SSven Eckelmann 	case NETDEV_DOWN:
98118a1cb6eSSven Eckelmann 		batadv_hardif_deactivate_interface(hard_iface);
982c6c8fea2SSven Eckelmann 		break;
983c6c8fea2SSven Eckelmann 	case NETDEV_UNREGISTER:
984a1a66b11SAndrew Lunn 	case NETDEV_PRE_TYPE_CHANGE:
985e6c10f43SMarek Lindner 		list_del_rcu(&hard_iface->list);
986fb69be69SSven Eckelmann 		batadv_hardif_generation++;
987c6c8fea2SSven Eckelmann 
98818a1cb6eSSven Eckelmann 		batadv_hardif_remove_interface(hard_iface);
989c6c8fea2SSven Eckelmann 		break;
990c6c8fea2SSven Eckelmann 	case NETDEV_CHANGEMTU:
991e6c10f43SMarek Lindner 		if (hard_iface->soft_iface)
9929563877eSSven Eckelmann 			batadv_update_min_mtu(hard_iface->soft_iface);
993c6c8fea2SSven Eckelmann 		break;
994c6c8fea2SSven Eckelmann 	case NETDEV_CHANGEADDR:
995e9a4f295SSven Eckelmann 		if (hard_iface->if_status == BATADV_IF_NOT_IN_USE)
996c6c8fea2SSven Eckelmann 			goto hardif_put;
997c6c8fea2SSven Eckelmann 
99818a1cb6eSSven Eckelmann 		batadv_check_known_mac_addr(hard_iface->net_dev);
999c6c8fea2SSven Eckelmann 
1000e6c10f43SMarek Lindner 		bat_priv = netdev_priv(hard_iface->soft_iface);
100129824a55SAntonio Quartulli 		bat_priv->algo_ops->iface.update_mac(hard_iface);
100201c4224bSMarek Lindner 
1003e5d89254SSven Eckelmann 		primary_if = batadv_primary_if_get_selected(bat_priv);
100432ae9b22SMarek Lindner 		if (!primary_if)
100532ae9b22SMarek Lindner 			goto hardif_put;
100632ae9b22SMarek Lindner 
100732ae9b22SMarek Lindner 		if (hard_iface == primary_if)
100818a1cb6eSSven Eckelmann 			batadv_primary_if_update_addr(bat_priv, NULL);
1009c6c8fea2SSven Eckelmann 		break;
1010ee3b5e9fSSven Eckelmann 	case NETDEV_CHANGEUPPER:
1011ee3b5e9fSSven Eckelmann 		hard_iface->wifi_flags = batadv_wifi_flags_evaluate(net_dev);
1012ee3b5e9fSSven Eckelmann 		if (batadv_is_wifi_hardif(hard_iface))
1013ee3b5e9fSSven Eckelmann 			hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS;
1014ee3b5e9fSSven Eckelmann 		break;
1015c6c8fea2SSven Eckelmann 	default:
1016c6c8fea2SSven Eckelmann 		break;
1017f81c6224SJoe Perches 	}
1018c6c8fea2SSven Eckelmann 
1019c6c8fea2SSven Eckelmann hardif_put:
102082047ad7SSven Eckelmann 	batadv_hardif_put(hard_iface);
1021c6c8fea2SSven Eckelmann out:
102282047ad7SSven Eckelmann 	batadv_hardif_put(primary_if);
1023c6c8fea2SSven Eckelmann 	return NOTIFY_DONE;
1024c6c8fea2SSven Eckelmann }
1025c6c8fea2SSven Eckelmann 
10269563877eSSven Eckelmann struct notifier_block batadv_hard_if_notifier = {
102718a1cb6eSSven Eckelmann 	.notifier_call = batadv_hard_if_event,
1028c6c8fea2SSven Eckelmann };
1029