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