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