162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright (C) B.A.T.M.A.N. contributors: 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Linus Lüssing, Marek Lindner 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include "bat_v_elp.h" 862306a36Sopenharmony_ci#include "main.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/atomic.h> 1162306a36Sopenharmony_ci#include <linux/bitops.h> 1262306a36Sopenharmony_ci#include <linux/byteorder/generic.h> 1362306a36Sopenharmony_ci#include <linux/container_of.h> 1462306a36Sopenharmony_ci#include <linux/errno.h> 1562306a36Sopenharmony_ci#include <linux/etherdevice.h> 1662306a36Sopenharmony_ci#include <linux/ethtool.h> 1762306a36Sopenharmony_ci#include <linux/gfp.h> 1862306a36Sopenharmony_ci#include <linux/if_ether.h> 1962306a36Sopenharmony_ci#include <linux/jiffies.h> 2062306a36Sopenharmony_ci#include <linux/kref.h> 2162306a36Sopenharmony_ci#include <linux/minmax.h> 2262306a36Sopenharmony_ci#include <linux/netdevice.h> 2362306a36Sopenharmony_ci#include <linux/nl80211.h> 2462306a36Sopenharmony_ci#include <linux/random.h> 2562306a36Sopenharmony_ci#include <linux/rculist.h> 2662306a36Sopenharmony_ci#include <linux/rcupdate.h> 2762306a36Sopenharmony_ci#include <linux/rtnetlink.h> 2862306a36Sopenharmony_ci#include <linux/skbuff.h> 2962306a36Sopenharmony_ci#include <linux/stddef.h> 3062306a36Sopenharmony_ci#include <linux/string.h> 3162306a36Sopenharmony_ci#include <linux/types.h> 3262306a36Sopenharmony_ci#include <linux/workqueue.h> 3362306a36Sopenharmony_ci#include <net/cfg80211.h> 3462306a36Sopenharmony_ci#include <uapi/linux/batadv_packet.h> 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#include "bat_algo.h" 3762306a36Sopenharmony_ci#include "bat_v_ogm.h" 3862306a36Sopenharmony_ci#include "hard-interface.h" 3962306a36Sopenharmony_ci#include "log.h" 4062306a36Sopenharmony_ci#include "originator.h" 4162306a36Sopenharmony_ci#include "routing.h" 4262306a36Sopenharmony_ci#include "send.h" 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/** 4562306a36Sopenharmony_ci * batadv_v_elp_start_timer() - restart timer for ELP periodic work 4662306a36Sopenharmony_ci * @hard_iface: the interface for which the timer has to be reset 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_cistatic void batadv_v_elp_start_timer(struct batadv_hard_iface *hard_iface) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci unsigned int msecs; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci msecs = atomic_read(&hard_iface->bat_v.elp_interval) - BATADV_JITTER; 5362306a36Sopenharmony_ci msecs += get_random_u32_below(2 * BATADV_JITTER); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci queue_delayed_work(batadv_event_workqueue, &hard_iface->bat_v.elp_wq, 5662306a36Sopenharmony_ci msecs_to_jiffies(msecs)); 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/** 6062306a36Sopenharmony_ci * batadv_v_elp_get_throughput() - get the throughput towards a neighbour 6162306a36Sopenharmony_ci * @neigh: the neighbour for which the throughput has to be obtained 6262306a36Sopenharmony_ci * 6362306a36Sopenharmony_ci * Return: The throughput towards the given neighbour in multiples of 100kpbs 6462306a36Sopenharmony_ci * (a value of '1' equals 0.1Mbps, '10' equals 1Mbps, etc). 6562306a36Sopenharmony_ci */ 6662306a36Sopenharmony_cistatic u32 batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci struct batadv_hard_iface *hard_iface = neigh->if_incoming; 6962306a36Sopenharmony_ci struct ethtool_link_ksettings link_settings; 7062306a36Sopenharmony_ci struct net_device *real_netdev; 7162306a36Sopenharmony_ci struct station_info sinfo; 7262306a36Sopenharmony_ci u32 throughput; 7362306a36Sopenharmony_ci int ret; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci /* if the user specified a customised value for this interface, then 7662306a36Sopenharmony_ci * return it directly 7762306a36Sopenharmony_ci */ 7862306a36Sopenharmony_ci throughput = atomic_read(&hard_iface->bat_v.throughput_override); 7962306a36Sopenharmony_ci if (throughput != 0) 8062306a36Sopenharmony_ci return throughput; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci /* if this is a wireless device, then ask its throughput through 8362306a36Sopenharmony_ci * cfg80211 API 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_ci if (batadv_is_wifi_hardif(hard_iface)) { 8662306a36Sopenharmony_ci if (!batadv_is_cfg80211_hardif(hard_iface)) 8762306a36Sopenharmony_ci /* unsupported WiFi driver version */ 8862306a36Sopenharmony_ci goto default_throughput; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci real_netdev = batadv_get_real_netdev(hard_iface->net_dev); 9162306a36Sopenharmony_ci if (!real_netdev) 9262306a36Sopenharmony_ci goto default_throughput; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci ret = cfg80211_get_station(real_netdev, neigh->addr, &sinfo); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (!ret) { 9762306a36Sopenharmony_ci /* free the TID stats immediately */ 9862306a36Sopenharmony_ci cfg80211_sinfo_release_content(&sinfo); 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci dev_put(real_netdev); 10262306a36Sopenharmony_ci if (ret == -ENOENT) { 10362306a36Sopenharmony_ci /* Node is not associated anymore! It would be 10462306a36Sopenharmony_ci * possible to delete this neighbor. For now set 10562306a36Sopenharmony_ci * the throughput metric to 0. 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_ci return 0; 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci if (ret) 11062306a36Sopenharmony_ci goto default_throughput; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (sinfo.filled & BIT(NL80211_STA_INFO_EXPECTED_THROUGHPUT)) 11362306a36Sopenharmony_ci return sinfo.expected_throughput / 100; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci /* try to estimate the expected throughput based on reported tx 11662306a36Sopenharmony_ci * rates 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_ci if (sinfo.filled & BIT(NL80211_STA_INFO_TX_BITRATE)) 11962306a36Sopenharmony_ci return cfg80211_calculate_bitrate(&sinfo.txrate) / 3; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci goto default_throughput; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* if not a wifi interface, check if this device provides data via 12562306a36Sopenharmony_ci * ethtool (e.g. an Ethernet adapter) 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_ci rtnl_lock(); 12862306a36Sopenharmony_ci ret = __ethtool_get_link_ksettings(hard_iface->net_dev, &link_settings); 12962306a36Sopenharmony_ci rtnl_unlock(); 13062306a36Sopenharmony_ci if (ret == 0) { 13162306a36Sopenharmony_ci /* link characteristics might change over time */ 13262306a36Sopenharmony_ci if (link_settings.base.duplex == DUPLEX_FULL) 13362306a36Sopenharmony_ci hard_iface->bat_v.flags |= BATADV_FULL_DUPLEX; 13462306a36Sopenharmony_ci else 13562306a36Sopenharmony_ci hard_iface->bat_v.flags &= ~BATADV_FULL_DUPLEX; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci throughput = link_settings.base.speed; 13862306a36Sopenharmony_ci if (throughput && throughput != SPEED_UNKNOWN) 13962306a36Sopenharmony_ci return throughput * 10; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cidefault_throughput: 14362306a36Sopenharmony_ci if (!(hard_iface->bat_v.flags & BATADV_WARNING_DEFAULT)) { 14462306a36Sopenharmony_ci batadv_info(hard_iface->soft_iface, 14562306a36Sopenharmony_ci "WiFi driver or ethtool info does not provide information about link speeds on interface %s, therefore defaulting to hardcoded throughput values of %u.%1u Mbps. Consider overriding the throughput manually or checking your driver.\n", 14662306a36Sopenharmony_ci hard_iface->net_dev->name, 14762306a36Sopenharmony_ci BATADV_THROUGHPUT_DEFAULT_VALUE / 10, 14862306a36Sopenharmony_ci BATADV_THROUGHPUT_DEFAULT_VALUE % 10); 14962306a36Sopenharmony_ci hard_iface->bat_v.flags |= BATADV_WARNING_DEFAULT; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* if none of the above cases apply, return the base_throughput */ 15362306a36Sopenharmony_ci return BATADV_THROUGHPUT_DEFAULT_VALUE; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci/** 15762306a36Sopenharmony_ci * batadv_v_elp_throughput_metric_update() - worker updating the throughput 15862306a36Sopenharmony_ci * metric of a single hop neighbour 15962306a36Sopenharmony_ci * @work: the work queue item 16062306a36Sopenharmony_ci */ 16162306a36Sopenharmony_civoid batadv_v_elp_throughput_metric_update(struct work_struct *work) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci struct batadv_hardif_neigh_node_bat_v *neigh_bat_v; 16462306a36Sopenharmony_ci struct batadv_hardif_neigh_node *neigh; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci neigh_bat_v = container_of(work, struct batadv_hardif_neigh_node_bat_v, 16762306a36Sopenharmony_ci metric_work); 16862306a36Sopenharmony_ci neigh = container_of(neigh_bat_v, struct batadv_hardif_neigh_node, 16962306a36Sopenharmony_ci bat_v); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci ewma_throughput_add(&neigh->bat_v.throughput, 17262306a36Sopenharmony_ci batadv_v_elp_get_throughput(neigh)); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* decrement refcounter to balance increment performed before scheduling 17562306a36Sopenharmony_ci * this task 17662306a36Sopenharmony_ci */ 17762306a36Sopenharmony_ci batadv_hardif_neigh_put(neigh); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci/** 18162306a36Sopenharmony_ci * batadv_v_elp_wifi_neigh_probe() - send link probing packets to a neighbour 18262306a36Sopenharmony_ci * @neigh: the neighbour to probe 18362306a36Sopenharmony_ci * 18462306a36Sopenharmony_ci * Sends a predefined number of unicast wifi packets to a given neighbour in 18562306a36Sopenharmony_ci * order to trigger the throughput estimation on this link by the RC algorithm. 18662306a36Sopenharmony_ci * Packets are sent only if there is not enough payload unicast traffic towards 18762306a36Sopenharmony_ci * this neighbour.. 18862306a36Sopenharmony_ci * 18962306a36Sopenharmony_ci * Return: True on success and false in case of error during skb preparation. 19062306a36Sopenharmony_ci */ 19162306a36Sopenharmony_cistatic bool 19262306a36Sopenharmony_cibatadv_v_elp_wifi_neigh_probe(struct batadv_hardif_neigh_node *neigh) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci struct batadv_hard_iface *hard_iface = neigh->if_incoming; 19562306a36Sopenharmony_ci struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); 19662306a36Sopenharmony_ci unsigned long last_tx_diff; 19762306a36Sopenharmony_ci struct sk_buff *skb; 19862306a36Sopenharmony_ci int probe_len, i; 19962306a36Sopenharmony_ci int elp_skb_len; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* this probing routine is for Wifi neighbours only */ 20262306a36Sopenharmony_ci if (!batadv_is_wifi_hardif(hard_iface)) 20362306a36Sopenharmony_ci return true; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* probe the neighbor only if no unicast packets have been sent 20662306a36Sopenharmony_ci * to it in the last 100 milliseconds: this is the rate control 20762306a36Sopenharmony_ci * algorithm sampling interval (minstrel). In this way, if not 20862306a36Sopenharmony_ci * enough traffic has been sent to the neighbor, batman-adv can 20962306a36Sopenharmony_ci * generate 2 probe packets and push the RC algorithm to perform 21062306a36Sopenharmony_ci * the sampling 21162306a36Sopenharmony_ci */ 21262306a36Sopenharmony_ci last_tx_diff = jiffies_to_msecs(jiffies - neigh->bat_v.last_unicast_tx); 21362306a36Sopenharmony_ci if (last_tx_diff <= BATADV_ELP_PROBE_MAX_TX_DIFF) 21462306a36Sopenharmony_ci return true; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci probe_len = max_t(int, sizeof(struct batadv_elp_packet), 21762306a36Sopenharmony_ci BATADV_ELP_MIN_PROBE_SIZE); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci for (i = 0; i < BATADV_ELP_PROBES_PER_NODE; i++) { 22062306a36Sopenharmony_ci elp_skb_len = hard_iface->bat_v.elp_skb->len; 22162306a36Sopenharmony_ci skb = skb_copy_expand(hard_iface->bat_v.elp_skb, 0, 22262306a36Sopenharmony_ci probe_len - elp_skb_len, 22362306a36Sopenharmony_ci GFP_ATOMIC); 22462306a36Sopenharmony_ci if (!skb) 22562306a36Sopenharmony_ci return false; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci /* Tell the skb to get as big as the allocated space (we want 22862306a36Sopenharmony_ci * the packet to be exactly of that size to make the link 22962306a36Sopenharmony_ci * throughput estimation effective. 23062306a36Sopenharmony_ci */ 23162306a36Sopenharmony_ci skb_put_zero(skb, probe_len - hard_iface->bat_v.elp_skb->len); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci batadv_dbg(BATADV_DBG_BATMAN, bat_priv, 23462306a36Sopenharmony_ci "Sending unicast (probe) ELP packet on interface %s to %pM\n", 23562306a36Sopenharmony_ci hard_iface->net_dev->name, neigh->addr); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci batadv_send_skb_packet(skb, hard_iface, neigh->addr); 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci return true; 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci/** 24462306a36Sopenharmony_ci * batadv_v_elp_periodic_work() - ELP periodic task per interface 24562306a36Sopenharmony_ci * @work: work queue item 24662306a36Sopenharmony_ci * 24762306a36Sopenharmony_ci * Emits broadcast ELP messages in regular intervals. 24862306a36Sopenharmony_ci */ 24962306a36Sopenharmony_cistatic void batadv_v_elp_periodic_work(struct work_struct *work) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci struct batadv_hardif_neigh_node *hardif_neigh; 25262306a36Sopenharmony_ci struct batadv_hard_iface *hard_iface; 25362306a36Sopenharmony_ci struct batadv_hard_iface_bat_v *bat_v; 25462306a36Sopenharmony_ci struct batadv_elp_packet *elp_packet; 25562306a36Sopenharmony_ci struct batadv_priv *bat_priv; 25662306a36Sopenharmony_ci struct sk_buff *skb; 25762306a36Sopenharmony_ci u32 elp_interval; 25862306a36Sopenharmony_ci bool ret; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci bat_v = container_of(work, struct batadv_hard_iface_bat_v, elp_wq.work); 26162306a36Sopenharmony_ci hard_iface = container_of(bat_v, struct batadv_hard_iface, bat_v); 26262306a36Sopenharmony_ci bat_priv = netdev_priv(hard_iface->soft_iface); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING) 26562306a36Sopenharmony_ci goto out; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci /* we are in the process of shutting this interface down */ 26862306a36Sopenharmony_ci if (hard_iface->if_status == BATADV_IF_NOT_IN_USE || 26962306a36Sopenharmony_ci hard_iface->if_status == BATADV_IF_TO_BE_REMOVED) 27062306a36Sopenharmony_ci goto out; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci /* the interface was enabled but may not be ready yet */ 27362306a36Sopenharmony_ci if (hard_iface->if_status != BATADV_IF_ACTIVE) 27462306a36Sopenharmony_ci goto restart_timer; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci skb = skb_copy(hard_iface->bat_v.elp_skb, GFP_ATOMIC); 27762306a36Sopenharmony_ci if (!skb) 27862306a36Sopenharmony_ci goto restart_timer; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci elp_packet = (struct batadv_elp_packet *)skb->data; 28162306a36Sopenharmony_ci elp_packet->seqno = htonl(atomic_read(&hard_iface->bat_v.elp_seqno)); 28262306a36Sopenharmony_ci elp_interval = atomic_read(&hard_iface->bat_v.elp_interval); 28362306a36Sopenharmony_ci elp_packet->elp_interval = htonl(elp_interval); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci batadv_dbg(BATADV_DBG_BATMAN, bat_priv, 28662306a36Sopenharmony_ci "Sending broadcast ELP packet on interface %s, seqno %u\n", 28762306a36Sopenharmony_ci hard_iface->net_dev->name, 28862306a36Sopenharmony_ci atomic_read(&hard_iface->bat_v.elp_seqno)); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci batadv_send_broadcast_skb(skb, hard_iface); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci atomic_inc(&hard_iface->bat_v.elp_seqno); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* The throughput metric is updated on each sent packet. This way, if a 29562306a36Sopenharmony_ci * node is dead and no longer sends packets, batman-adv is still able to 29662306a36Sopenharmony_ci * react timely to its death. 29762306a36Sopenharmony_ci * 29862306a36Sopenharmony_ci * The throughput metric is updated by following these steps: 29962306a36Sopenharmony_ci * 1) if the hard_iface is wifi => send a number of unicast ELPs for 30062306a36Sopenharmony_ci * probing/sampling to each neighbor 30162306a36Sopenharmony_ci * 2) update the throughput metric value of each neighbor (note that the 30262306a36Sopenharmony_ci * value retrieved in this step might be 100ms old because the 30362306a36Sopenharmony_ci * probing packets at point 1) could still be in the HW queue) 30462306a36Sopenharmony_ci */ 30562306a36Sopenharmony_ci rcu_read_lock(); 30662306a36Sopenharmony_ci hlist_for_each_entry_rcu(hardif_neigh, &hard_iface->neigh_list, list) { 30762306a36Sopenharmony_ci if (!batadv_v_elp_wifi_neigh_probe(hardif_neigh)) 30862306a36Sopenharmony_ci /* if something goes wrong while probing, better to stop 30962306a36Sopenharmony_ci * sending packets immediately and reschedule the task 31062306a36Sopenharmony_ci */ 31162306a36Sopenharmony_ci break; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (!kref_get_unless_zero(&hardif_neigh->refcount)) 31462306a36Sopenharmony_ci continue; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci /* Reading the estimated throughput from cfg80211 is a task that 31762306a36Sopenharmony_ci * may sleep and that is not allowed in an rcu protected 31862306a36Sopenharmony_ci * context. Therefore schedule a task for that. 31962306a36Sopenharmony_ci */ 32062306a36Sopenharmony_ci ret = queue_work(batadv_event_workqueue, 32162306a36Sopenharmony_ci &hardif_neigh->bat_v.metric_work); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (!ret) 32462306a36Sopenharmony_ci batadv_hardif_neigh_put(hardif_neigh); 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci rcu_read_unlock(); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cirestart_timer: 32962306a36Sopenharmony_ci batadv_v_elp_start_timer(hard_iface); 33062306a36Sopenharmony_ciout: 33162306a36Sopenharmony_ci return; 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci/** 33562306a36Sopenharmony_ci * batadv_v_elp_iface_enable() - setup the ELP interface private resources 33662306a36Sopenharmony_ci * @hard_iface: interface for which the data has to be prepared 33762306a36Sopenharmony_ci * 33862306a36Sopenharmony_ci * Return: 0 on success or a -ENOMEM in case of failure. 33962306a36Sopenharmony_ci */ 34062306a36Sopenharmony_ciint batadv_v_elp_iface_enable(struct batadv_hard_iface *hard_iface) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci static const size_t tvlv_padding = sizeof(__be32); 34362306a36Sopenharmony_ci struct batadv_elp_packet *elp_packet; 34462306a36Sopenharmony_ci unsigned char *elp_buff; 34562306a36Sopenharmony_ci u32 random_seqno; 34662306a36Sopenharmony_ci size_t size; 34762306a36Sopenharmony_ci int res = -ENOMEM; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci size = ETH_HLEN + NET_IP_ALIGN + BATADV_ELP_HLEN + tvlv_padding; 35062306a36Sopenharmony_ci hard_iface->bat_v.elp_skb = dev_alloc_skb(size); 35162306a36Sopenharmony_ci if (!hard_iface->bat_v.elp_skb) 35262306a36Sopenharmony_ci goto out; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci skb_reserve(hard_iface->bat_v.elp_skb, ETH_HLEN + NET_IP_ALIGN); 35562306a36Sopenharmony_ci elp_buff = skb_put_zero(hard_iface->bat_v.elp_skb, 35662306a36Sopenharmony_ci BATADV_ELP_HLEN + tvlv_padding); 35762306a36Sopenharmony_ci elp_packet = (struct batadv_elp_packet *)elp_buff; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci elp_packet->packet_type = BATADV_ELP; 36062306a36Sopenharmony_ci elp_packet->version = BATADV_COMPAT_VERSION; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci /* randomize initial seqno to avoid collision */ 36362306a36Sopenharmony_ci get_random_bytes(&random_seqno, sizeof(random_seqno)); 36462306a36Sopenharmony_ci atomic_set(&hard_iface->bat_v.elp_seqno, random_seqno); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci /* assume full-duplex by default */ 36762306a36Sopenharmony_ci hard_iface->bat_v.flags |= BATADV_FULL_DUPLEX; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* warn the user (again) if there is no throughput data is available */ 37062306a36Sopenharmony_ci hard_iface->bat_v.flags &= ~BATADV_WARNING_DEFAULT; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if (batadv_is_wifi_hardif(hard_iface)) 37362306a36Sopenharmony_ci hard_iface->bat_v.flags &= ~BATADV_FULL_DUPLEX; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci INIT_DELAYED_WORK(&hard_iface->bat_v.elp_wq, 37662306a36Sopenharmony_ci batadv_v_elp_periodic_work); 37762306a36Sopenharmony_ci batadv_v_elp_start_timer(hard_iface); 37862306a36Sopenharmony_ci res = 0; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ciout: 38162306a36Sopenharmony_ci return res; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci/** 38562306a36Sopenharmony_ci * batadv_v_elp_iface_disable() - release ELP interface private resources 38662306a36Sopenharmony_ci * @hard_iface: interface for which the resources have to be released 38762306a36Sopenharmony_ci */ 38862306a36Sopenharmony_civoid batadv_v_elp_iface_disable(struct batadv_hard_iface *hard_iface) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci cancel_delayed_work_sync(&hard_iface->bat_v.elp_wq); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci dev_kfree_skb(hard_iface->bat_v.elp_skb); 39362306a36Sopenharmony_ci hard_iface->bat_v.elp_skb = NULL; 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci/** 39762306a36Sopenharmony_ci * batadv_v_elp_iface_activate() - update the ELP buffer belonging to the given 39862306a36Sopenharmony_ci * hard-interface 39962306a36Sopenharmony_ci * @primary_iface: the new primary interface 40062306a36Sopenharmony_ci * @hard_iface: interface holding the to-be-updated buffer 40162306a36Sopenharmony_ci */ 40262306a36Sopenharmony_civoid batadv_v_elp_iface_activate(struct batadv_hard_iface *primary_iface, 40362306a36Sopenharmony_ci struct batadv_hard_iface *hard_iface) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct batadv_elp_packet *elp_packet; 40662306a36Sopenharmony_ci struct sk_buff *skb; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (!hard_iface->bat_v.elp_skb) 40962306a36Sopenharmony_ci return; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci skb = hard_iface->bat_v.elp_skb; 41262306a36Sopenharmony_ci elp_packet = (struct batadv_elp_packet *)skb->data; 41362306a36Sopenharmony_ci ether_addr_copy(elp_packet->orig, 41462306a36Sopenharmony_ci primary_iface->net_dev->dev_addr); 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci/** 41862306a36Sopenharmony_ci * batadv_v_elp_primary_iface_set() - change internal data to reflect the new 41962306a36Sopenharmony_ci * primary interface 42062306a36Sopenharmony_ci * @primary_iface: the new primary interface 42162306a36Sopenharmony_ci */ 42262306a36Sopenharmony_civoid batadv_v_elp_primary_iface_set(struct batadv_hard_iface *primary_iface) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci struct batadv_hard_iface *hard_iface; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci /* update orig field of every elp iface belonging to this mesh */ 42762306a36Sopenharmony_ci rcu_read_lock(); 42862306a36Sopenharmony_ci list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { 42962306a36Sopenharmony_ci if (primary_iface->soft_iface != hard_iface->soft_iface) 43062306a36Sopenharmony_ci continue; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci batadv_v_elp_iface_activate(primary_iface, hard_iface); 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci rcu_read_unlock(); 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci/** 43862306a36Sopenharmony_ci * batadv_v_elp_neigh_update() - update an ELP neighbour node 43962306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 44062306a36Sopenharmony_ci * @neigh_addr: the neighbour interface address 44162306a36Sopenharmony_ci * @if_incoming: the interface the packet was received through 44262306a36Sopenharmony_ci * @elp_packet: the received ELP packet 44362306a36Sopenharmony_ci * 44462306a36Sopenharmony_ci * Updates the ELP neighbour node state with the data received within the new 44562306a36Sopenharmony_ci * ELP packet. 44662306a36Sopenharmony_ci */ 44762306a36Sopenharmony_cistatic void batadv_v_elp_neigh_update(struct batadv_priv *bat_priv, 44862306a36Sopenharmony_ci u8 *neigh_addr, 44962306a36Sopenharmony_ci struct batadv_hard_iface *if_incoming, 45062306a36Sopenharmony_ci struct batadv_elp_packet *elp_packet) 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci struct batadv_neigh_node *neigh; 45462306a36Sopenharmony_ci struct batadv_orig_node *orig_neigh; 45562306a36Sopenharmony_ci struct batadv_hardif_neigh_node *hardif_neigh; 45662306a36Sopenharmony_ci s32 seqno_diff; 45762306a36Sopenharmony_ci s32 elp_latest_seqno; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci orig_neigh = batadv_v_ogm_orig_get(bat_priv, elp_packet->orig); 46062306a36Sopenharmony_ci if (!orig_neigh) 46162306a36Sopenharmony_ci return; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci neigh = batadv_neigh_node_get_or_create(orig_neigh, 46462306a36Sopenharmony_ci if_incoming, neigh_addr); 46562306a36Sopenharmony_ci if (!neigh) 46662306a36Sopenharmony_ci goto orig_free; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci hardif_neigh = batadv_hardif_neigh_get(if_incoming, neigh_addr); 46962306a36Sopenharmony_ci if (!hardif_neigh) 47062306a36Sopenharmony_ci goto neigh_free; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci elp_latest_seqno = hardif_neigh->bat_v.elp_latest_seqno; 47362306a36Sopenharmony_ci seqno_diff = ntohl(elp_packet->seqno) - elp_latest_seqno; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci /* known or older sequence numbers are ignored. However always adopt 47662306a36Sopenharmony_ci * if the router seems to have been restarted. 47762306a36Sopenharmony_ci */ 47862306a36Sopenharmony_ci if (seqno_diff < 1 && seqno_diff > -BATADV_ELP_MAX_AGE) 47962306a36Sopenharmony_ci goto hardif_free; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci neigh->last_seen = jiffies; 48262306a36Sopenharmony_ci hardif_neigh->last_seen = jiffies; 48362306a36Sopenharmony_ci hardif_neigh->bat_v.elp_latest_seqno = ntohl(elp_packet->seqno); 48462306a36Sopenharmony_ci hardif_neigh->bat_v.elp_interval = ntohl(elp_packet->elp_interval); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_cihardif_free: 48762306a36Sopenharmony_ci batadv_hardif_neigh_put(hardif_neigh); 48862306a36Sopenharmony_cineigh_free: 48962306a36Sopenharmony_ci batadv_neigh_node_put(neigh); 49062306a36Sopenharmony_ciorig_free: 49162306a36Sopenharmony_ci batadv_orig_node_put(orig_neigh); 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci/** 49562306a36Sopenharmony_ci * batadv_v_elp_packet_recv() - main ELP packet handler 49662306a36Sopenharmony_ci * @skb: the received packet 49762306a36Sopenharmony_ci * @if_incoming: the interface this packet was received through 49862306a36Sopenharmony_ci * 49962306a36Sopenharmony_ci * Return: NET_RX_SUCCESS and consumes the skb if the packet was properly 50062306a36Sopenharmony_ci * processed or NET_RX_DROP in case of failure. 50162306a36Sopenharmony_ci */ 50262306a36Sopenharmony_ciint batadv_v_elp_packet_recv(struct sk_buff *skb, 50362306a36Sopenharmony_ci struct batadv_hard_iface *if_incoming) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface); 50662306a36Sopenharmony_ci struct batadv_elp_packet *elp_packet; 50762306a36Sopenharmony_ci struct batadv_hard_iface *primary_if; 50862306a36Sopenharmony_ci struct ethhdr *ethhdr; 50962306a36Sopenharmony_ci bool res; 51062306a36Sopenharmony_ci int ret = NET_RX_DROP; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci res = batadv_check_management_packet(skb, if_incoming, BATADV_ELP_HLEN); 51362306a36Sopenharmony_ci if (!res) 51462306a36Sopenharmony_ci goto free_skb; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci ethhdr = eth_hdr(skb); 51762306a36Sopenharmony_ci if (batadv_is_my_mac(bat_priv, ethhdr->h_source)) 51862306a36Sopenharmony_ci goto free_skb; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci /* did we receive a B.A.T.M.A.N. V ELP packet on an interface 52162306a36Sopenharmony_ci * that does not have B.A.T.M.A.N. V ELP enabled ? 52262306a36Sopenharmony_ci */ 52362306a36Sopenharmony_ci if (strcmp(bat_priv->algo_ops->name, "BATMAN_V") != 0) 52462306a36Sopenharmony_ci goto free_skb; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci elp_packet = (struct batadv_elp_packet *)skb->data; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci batadv_dbg(BATADV_DBG_BATMAN, bat_priv, 52962306a36Sopenharmony_ci "Received ELP packet from %pM seqno %u ORIG: %pM\n", 53062306a36Sopenharmony_ci ethhdr->h_source, ntohl(elp_packet->seqno), 53162306a36Sopenharmony_ci elp_packet->orig); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci primary_if = batadv_primary_if_get_selected(bat_priv); 53462306a36Sopenharmony_ci if (!primary_if) 53562306a36Sopenharmony_ci goto free_skb; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci batadv_v_elp_neigh_update(bat_priv, ethhdr->h_source, if_incoming, 53862306a36Sopenharmony_ci elp_packet); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci ret = NET_RX_SUCCESS; 54162306a36Sopenharmony_ci batadv_hardif_put(primary_if); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_cifree_skb: 54462306a36Sopenharmony_ci if (ret == NET_RX_SUCCESS) 54562306a36Sopenharmony_ci consume_skb(skb); 54662306a36Sopenharmony_ci else 54762306a36Sopenharmony_ci kfree_skb(skb); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci return ret; 55062306a36Sopenharmony_ci} 551