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 "originator.h" 862306a36Sopenharmony_ci#include "main.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/atomic.h> 1162306a36Sopenharmony_ci#include <linux/container_of.h> 1262306a36Sopenharmony_ci#include <linux/errno.h> 1362306a36Sopenharmony_ci#include <linux/etherdevice.h> 1462306a36Sopenharmony_ci#include <linux/gfp.h> 1562306a36Sopenharmony_ci#include <linux/jiffies.h> 1662306a36Sopenharmony_ci#include <linux/kref.h> 1762306a36Sopenharmony_ci#include <linux/list.h> 1862306a36Sopenharmony_ci#include <linux/lockdep.h> 1962306a36Sopenharmony_ci#include <linux/netdevice.h> 2062306a36Sopenharmony_ci#include <linux/netlink.h> 2162306a36Sopenharmony_ci#include <linux/rculist.h> 2262306a36Sopenharmony_ci#include <linux/rcupdate.h> 2362306a36Sopenharmony_ci#include <linux/skbuff.h> 2462306a36Sopenharmony_ci#include <linux/slab.h> 2562306a36Sopenharmony_ci#include <linux/spinlock.h> 2662306a36Sopenharmony_ci#include <linux/stddef.h> 2762306a36Sopenharmony_ci#include <linux/workqueue.h> 2862306a36Sopenharmony_ci#include <net/sock.h> 2962306a36Sopenharmony_ci#include <uapi/linux/batadv_packet.h> 3062306a36Sopenharmony_ci#include <uapi/linux/batman_adv.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include "bat_algo.h" 3362306a36Sopenharmony_ci#include "distributed-arp-table.h" 3462306a36Sopenharmony_ci#include "fragmentation.h" 3562306a36Sopenharmony_ci#include "gateway_client.h" 3662306a36Sopenharmony_ci#include "hard-interface.h" 3762306a36Sopenharmony_ci#include "hash.h" 3862306a36Sopenharmony_ci#include "log.h" 3962306a36Sopenharmony_ci#include "multicast.h" 4062306a36Sopenharmony_ci#include "netlink.h" 4162306a36Sopenharmony_ci#include "network-coding.h" 4262306a36Sopenharmony_ci#include "routing.h" 4362306a36Sopenharmony_ci#include "soft-interface.h" 4462306a36Sopenharmony_ci#include "translation-table.h" 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* hash class keys */ 4762306a36Sopenharmony_cistatic struct lock_class_key batadv_orig_hash_lock_class_key; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/** 5062306a36Sopenharmony_ci * batadv_orig_hash_find() - Find and return originator from orig_hash 5162306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 5262306a36Sopenharmony_ci * @data: mac address of the originator 5362306a36Sopenharmony_ci * 5462306a36Sopenharmony_ci * Return: orig_node (with increased refcnt), NULL on errors 5562306a36Sopenharmony_ci */ 5662306a36Sopenharmony_cistruct batadv_orig_node * 5762306a36Sopenharmony_cibatadv_orig_hash_find(struct batadv_priv *bat_priv, const void *data) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci struct batadv_hashtable *hash = bat_priv->orig_hash; 6062306a36Sopenharmony_ci struct hlist_head *head; 6162306a36Sopenharmony_ci struct batadv_orig_node *orig_node, *orig_node_tmp = NULL; 6262306a36Sopenharmony_ci int index; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if (!hash) 6562306a36Sopenharmony_ci return NULL; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci index = batadv_choose_orig(data, hash->size); 6862306a36Sopenharmony_ci head = &hash->table[index]; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci rcu_read_lock(); 7162306a36Sopenharmony_ci hlist_for_each_entry_rcu(orig_node, head, hash_entry) { 7262306a36Sopenharmony_ci if (!batadv_compare_eth(orig_node, data)) 7362306a36Sopenharmony_ci continue; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (!kref_get_unless_zero(&orig_node->refcount)) 7662306a36Sopenharmony_ci continue; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci orig_node_tmp = orig_node; 7962306a36Sopenharmony_ci break; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci rcu_read_unlock(); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci return orig_node_tmp; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic void batadv_purge_orig(struct work_struct *work); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/** 8962306a36Sopenharmony_ci * batadv_compare_orig() - comparing function used in the originator hash table 9062306a36Sopenharmony_ci * @node: node in the local table 9162306a36Sopenharmony_ci * @data2: second object to compare the node to 9262306a36Sopenharmony_ci * 9362306a36Sopenharmony_ci * Return: true if they are the same originator 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_cibool batadv_compare_orig(const struct hlist_node *node, const void *data2) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci const void *data1 = container_of(node, struct batadv_orig_node, 9862306a36Sopenharmony_ci hash_entry); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci return batadv_compare_eth(data1, data2); 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci/** 10462306a36Sopenharmony_ci * batadv_orig_node_vlan_get() - get an orig_node_vlan object 10562306a36Sopenharmony_ci * @orig_node: the originator serving the VLAN 10662306a36Sopenharmony_ci * @vid: the VLAN identifier 10762306a36Sopenharmony_ci * 10862306a36Sopenharmony_ci * Return: the vlan object identified by vid and belonging to orig_node or NULL 10962306a36Sopenharmony_ci * if it does not exist. 11062306a36Sopenharmony_ci */ 11162306a36Sopenharmony_cistruct batadv_orig_node_vlan * 11262306a36Sopenharmony_cibatadv_orig_node_vlan_get(struct batadv_orig_node *orig_node, 11362306a36Sopenharmony_ci unsigned short vid) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci struct batadv_orig_node_vlan *vlan = NULL, *tmp; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci rcu_read_lock(); 11862306a36Sopenharmony_ci hlist_for_each_entry_rcu(tmp, &orig_node->vlan_list, list) { 11962306a36Sopenharmony_ci if (tmp->vid != vid) 12062306a36Sopenharmony_ci continue; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (!kref_get_unless_zero(&tmp->refcount)) 12362306a36Sopenharmony_ci continue; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci vlan = tmp; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci break; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci rcu_read_unlock(); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci return vlan; 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci/** 13562306a36Sopenharmony_ci * batadv_orig_node_vlan_new() - search and possibly create an orig_node_vlan 13662306a36Sopenharmony_ci * object 13762306a36Sopenharmony_ci * @orig_node: the originator serving the VLAN 13862306a36Sopenharmony_ci * @vid: the VLAN identifier 13962306a36Sopenharmony_ci * 14062306a36Sopenharmony_ci * Return: NULL in case of failure or the vlan object identified by vid and 14162306a36Sopenharmony_ci * belonging to orig_node otherwise. The object is created and added to the list 14262306a36Sopenharmony_ci * if it does not exist. 14362306a36Sopenharmony_ci * 14462306a36Sopenharmony_ci * The object is returned with refcounter increased by 1. 14562306a36Sopenharmony_ci */ 14662306a36Sopenharmony_cistruct batadv_orig_node_vlan * 14762306a36Sopenharmony_cibatadv_orig_node_vlan_new(struct batadv_orig_node *orig_node, 14862306a36Sopenharmony_ci unsigned short vid) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci struct batadv_orig_node_vlan *vlan; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci spin_lock_bh(&orig_node->vlan_list_lock); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci /* first look if an object for this vid already exists */ 15562306a36Sopenharmony_ci vlan = batadv_orig_node_vlan_get(orig_node, vid); 15662306a36Sopenharmony_ci if (vlan) 15762306a36Sopenharmony_ci goto out; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci vlan = kzalloc(sizeof(*vlan), GFP_ATOMIC); 16062306a36Sopenharmony_ci if (!vlan) 16162306a36Sopenharmony_ci goto out; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci kref_init(&vlan->refcount); 16462306a36Sopenharmony_ci vlan->vid = vid; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci kref_get(&vlan->refcount); 16762306a36Sopenharmony_ci hlist_add_head_rcu(&vlan->list, &orig_node->vlan_list); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ciout: 17062306a36Sopenharmony_ci spin_unlock_bh(&orig_node->vlan_list_lock); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci return vlan; 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci/** 17662306a36Sopenharmony_ci * batadv_orig_node_vlan_release() - release originator-vlan object from lists 17762306a36Sopenharmony_ci * and queue for free after rcu grace period 17862306a36Sopenharmony_ci * @ref: kref pointer of the originator-vlan object 17962306a36Sopenharmony_ci */ 18062306a36Sopenharmony_civoid batadv_orig_node_vlan_release(struct kref *ref) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci struct batadv_orig_node_vlan *orig_vlan; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci orig_vlan = container_of(ref, struct batadv_orig_node_vlan, refcount); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci kfree_rcu(orig_vlan, rcu); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci/** 19062306a36Sopenharmony_ci * batadv_originator_init() - Initialize all originator structures 19162306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 19262306a36Sopenharmony_ci * 19362306a36Sopenharmony_ci * Return: 0 on success or negative error number in case of failure 19462306a36Sopenharmony_ci */ 19562306a36Sopenharmony_ciint batadv_originator_init(struct batadv_priv *bat_priv) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci if (bat_priv->orig_hash) 19862306a36Sopenharmony_ci return 0; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci bat_priv->orig_hash = batadv_hash_new(1024); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (!bat_priv->orig_hash) 20362306a36Sopenharmony_ci goto err; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci batadv_hash_set_lock_class(bat_priv->orig_hash, 20662306a36Sopenharmony_ci &batadv_orig_hash_lock_class_key); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci INIT_DELAYED_WORK(&bat_priv->orig_work, batadv_purge_orig); 20962306a36Sopenharmony_ci queue_delayed_work(batadv_event_workqueue, 21062306a36Sopenharmony_ci &bat_priv->orig_work, 21162306a36Sopenharmony_ci msecs_to_jiffies(BATADV_ORIG_WORK_PERIOD)); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci return 0; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cierr: 21662306a36Sopenharmony_ci return -ENOMEM; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci/** 22062306a36Sopenharmony_ci * batadv_neigh_ifinfo_release() - release neigh_ifinfo from lists and queue for 22162306a36Sopenharmony_ci * free after rcu grace period 22262306a36Sopenharmony_ci * @ref: kref pointer of the neigh_ifinfo 22362306a36Sopenharmony_ci */ 22462306a36Sopenharmony_civoid batadv_neigh_ifinfo_release(struct kref *ref) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci struct batadv_neigh_ifinfo *neigh_ifinfo; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci neigh_ifinfo = container_of(ref, struct batadv_neigh_ifinfo, refcount); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (neigh_ifinfo->if_outgoing != BATADV_IF_DEFAULT) 23162306a36Sopenharmony_ci batadv_hardif_put(neigh_ifinfo->if_outgoing); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci kfree_rcu(neigh_ifinfo, rcu); 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci/** 23762306a36Sopenharmony_ci * batadv_hardif_neigh_release() - release hardif neigh node from lists and 23862306a36Sopenharmony_ci * queue for free after rcu grace period 23962306a36Sopenharmony_ci * @ref: kref pointer of the neigh_node 24062306a36Sopenharmony_ci */ 24162306a36Sopenharmony_civoid batadv_hardif_neigh_release(struct kref *ref) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct batadv_hardif_neigh_node *hardif_neigh; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci hardif_neigh = container_of(ref, struct batadv_hardif_neigh_node, 24662306a36Sopenharmony_ci refcount); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci spin_lock_bh(&hardif_neigh->if_incoming->neigh_list_lock); 24962306a36Sopenharmony_ci hlist_del_init_rcu(&hardif_neigh->list); 25062306a36Sopenharmony_ci spin_unlock_bh(&hardif_neigh->if_incoming->neigh_list_lock); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci batadv_hardif_put(hardif_neigh->if_incoming); 25362306a36Sopenharmony_ci kfree_rcu(hardif_neigh, rcu); 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci/** 25762306a36Sopenharmony_ci * batadv_neigh_node_release() - release neigh_node from lists and queue for 25862306a36Sopenharmony_ci * free after rcu grace period 25962306a36Sopenharmony_ci * @ref: kref pointer of the neigh_node 26062306a36Sopenharmony_ci */ 26162306a36Sopenharmony_civoid batadv_neigh_node_release(struct kref *ref) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci struct hlist_node *node_tmp; 26462306a36Sopenharmony_ci struct batadv_neigh_node *neigh_node; 26562306a36Sopenharmony_ci struct batadv_neigh_ifinfo *neigh_ifinfo; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci neigh_node = container_of(ref, struct batadv_neigh_node, refcount); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci hlist_for_each_entry_safe(neigh_ifinfo, node_tmp, 27062306a36Sopenharmony_ci &neigh_node->ifinfo_list, list) { 27162306a36Sopenharmony_ci batadv_neigh_ifinfo_put(neigh_ifinfo); 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci batadv_hardif_neigh_put(neigh_node->hardif_neigh); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci batadv_hardif_put(neigh_node->if_incoming); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci kfree_rcu(neigh_node, rcu); 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci/** 28262306a36Sopenharmony_ci * batadv_orig_router_get() - router to the originator depending on iface 28362306a36Sopenharmony_ci * @orig_node: the orig node for the router 28462306a36Sopenharmony_ci * @if_outgoing: the interface where the payload packet has been received or 28562306a36Sopenharmony_ci * the OGM should be sent to 28662306a36Sopenharmony_ci * 28762306a36Sopenharmony_ci * Return: the neighbor which should be the router for this orig_node/iface. 28862306a36Sopenharmony_ci * 28962306a36Sopenharmony_ci * The object is returned with refcounter increased by 1. 29062306a36Sopenharmony_ci */ 29162306a36Sopenharmony_cistruct batadv_neigh_node * 29262306a36Sopenharmony_cibatadv_orig_router_get(struct batadv_orig_node *orig_node, 29362306a36Sopenharmony_ci const struct batadv_hard_iface *if_outgoing) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci struct batadv_orig_ifinfo *orig_ifinfo; 29662306a36Sopenharmony_ci struct batadv_neigh_node *router = NULL; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci rcu_read_lock(); 29962306a36Sopenharmony_ci hlist_for_each_entry_rcu(orig_ifinfo, &orig_node->ifinfo_list, list) { 30062306a36Sopenharmony_ci if (orig_ifinfo->if_outgoing != if_outgoing) 30162306a36Sopenharmony_ci continue; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci router = rcu_dereference(orig_ifinfo->router); 30462306a36Sopenharmony_ci break; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (router && !kref_get_unless_zero(&router->refcount)) 30862306a36Sopenharmony_ci router = NULL; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci rcu_read_unlock(); 31162306a36Sopenharmony_ci return router; 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci/** 31562306a36Sopenharmony_ci * batadv_orig_ifinfo_get() - find the ifinfo from an orig_node 31662306a36Sopenharmony_ci * @orig_node: the orig node to be queried 31762306a36Sopenharmony_ci * @if_outgoing: the interface for which the ifinfo should be acquired 31862306a36Sopenharmony_ci * 31962306a36Sopenharmony_ci * Return: the requested orig_ifinfo or NULL if not found. 32062306a36Sopenharmony_ci * 32162306a36Sopenharmony_ci * The object is returned with refcounter increased by 1. 32262306a36Sopenharmony_ci */ 32362306a36Sopenharmony_cistruct batadv_orig_ifinfo * 32462306a36Sopenharmony_cibatadv_orig_ifinfo_get(struct batadv_orig_node *orig_node, 32562306a36Sopenharmony_ci struct batadv_hard_iface *if_outgoing) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci struct batadv_orig_ifinfo *tmp, *orig_ifinfo = NULL; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci rcu_read_lock(); 33062306a36Sopenharmony_ci hlist_for_each_entry_rcu(tmp, &orig_node->ifinfo_list, 33162306a36Sopenharmony_ci list) { 33262306a36Sopenharmony_ci if (tmp->if_outgoing != if_outgoing) 33362306a36Sopenharmony_ci continue; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci if (!kref_get_unless_zero(&tmp->refcount)) 33662306a36Sopenharmony_ci continue; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci orig_ifinfo = tmp; 33962306a36Sopenharmony_ci break; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci rcu_read_unlock(); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci return orig_ifinfo; 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci/** 34762306a36Sopenharmony_ci * batadv_orig_ifinfo_new() - search and possibly create an orig_ifinfo object 34862306a36Sopenharmony_ci * @orig_node: the orig node to be queried 34962306a36Sopenharmony_ci * @if_outgoing: the interface for which the ifinfo should be acquired 35062306a36Sopenharmony_ci * 35162306a36Sopenharmony_ci * Return: NULL in case of failure or the orig_ifinfo object for the if_outgoing 35262306a36Sopenharmony_ci * interface otherwise. The object is created and added to the list 35362306a36Sopenharmony_ci * if it does not exist. 35462306a36Sopenharmony_ci * 35562306a36Sopenharmony_ci * The object is returned with refcounter increased by 1. 35662306a36Sopenharmony_ci */ 35762306a36Sopenharmony_cistruct batadv_orig_ifinfo * 35862306a36Sopenharmony_cibatadv_orig_ifinfo_new(struct batadv_orig_node *orig_node, 35962306a36Sopenharmony_ci struct batadv_hard_iface *if_outgoing) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci struct batadv_orig_ifinfo *orig_ifinfo; 36262306a36Sopenharmony_ci unsigned long reset_time; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci spin_lock_bh(&orig_node->neigh_list_lock); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci orig_ifinfo = batadv_orig_ifinfo_get(orig_node, if_outgoing); 36762306a36Sopenharmony_ci if (orig_ifinfo) 36862306a36Sopenharmony_ci goto out; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci orig_ifinfo = kzalloc(sizeof(*orig_ifinfo), GFP_ATOMIC); 37162306a36Sopenharmony_ci if (!orig_ifinfo) 37262306a36Sopenharmony_ci goto out; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (if_outgoing != BATADV_IF_DEFAULT) 37562306a36Sopenharmony_ci kref_get(&if_outgoing->refcount); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci reset_time = jiffies - 1; 37862306a36Sopenharmony_ci reset_time -= msecs_to_jiffies(BATADV_RESET_PROTECTION_MS); 37962306a36Sopenharmony_ci orig_ifinfo->batman_seqno_reset = reset_time; 38062306a36Sopenharmony_ci orig_ifinfo->if_outgoing = if_outgoing; 38162306a36Sopenharmony_ci INIT_HLIST_NODE(&orig_ifinfo->list); 38262306a36Sopenharmony_ci kref_init(&orig_ifinfo->refcount); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci kref_get(&orig_ifinfo->refcount); 38562306a36Sopenharmony_ci hlist_add_head_rcu(&orig_ifinfo->list, 38662306a36Sopenharmony_ci &orig_node->ifinfo_list); 38762306a36Sopenharmony_ciout: 38862306a36Sopenharmony_ci spin_unlock_bh(&orig_node->neigh_list_lock); 38962306a36Sopenharmony_ci return orig_ifinfo; 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci/** 39362306a36Sopenharmony_ci * batadv_neigh_ifinfo_get() - find the ifinfo from an neigh_node 39462306a36Sopenharmony_ci * @neigh: the neigh node to be queried 39562306a36Sopenharmony_ci * @if_outgoing: the interface for which the ifinfo should be acquired 39662306a36Sopenharmony_ci * 39762306a36Sopenharmony_ci * The object is returned with refcounter increased by 1. 39862306a36Sopenharmony_ci * 39962306a36Sopenharmony_ci * Return: the requested neigh_ifinfo or NULL if not found 40062306a36Sopenharmony_ci */ 40162306a36Sopenharmony_cistruct batadv_neigh_ifinfo * 40262306a36Sopenharmony_cibatadv_neigh_ifinfo_get(struct batadv_neigh_node *neigh, 40362306a36Sopenharmony_ci struct batadv_hard_iface *if_outgoing) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct batadv_neigh_ifinfo *neigh_ifinfo = NULL, 40662306a36Sopenharmony_ci *tmp_neigh_ifinfo; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci rcu_read_lock(); 40962306a36Sopenharmony_ci hlist_for_each_entry_rcu(tmp_neigh_ifinfo, &neigh->ifinfo_list, 41062306a36Sopenharmony_ci list) { 41162306a36Sopenharmony_ci if (tmp_neigh_ifinfo->if_outgoing != if_outgoing) 41262306a36Sopenharmony_ci continue; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci if (!kref_get_unless_zero(&tmp_neigh_ifinfo->refcount)) 41562306a36Sopenharmony_ci continue; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci neigh_ifinfo = tmp_neigh_ifinfo; 41862306a36Sopenharmony_ci break; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci rcu_read_unlock(); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci return neigh_ifinfo; 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci/** 42662306a36Sopenharmony_ci * batadv_neigh_ifinfo_new() - search and possibly create an neigh_ifinfo object 42762306a36Sopenharmony_ci * @neigh: the neigh node to be queried 42862306a36Sopenharmony_ci * @if_outgoing: the interface for which the ifinfo should be acquired 42962306a36Sopenharmony_ci * 43062306a36Sopenharmony_ci * Return: NULL in case of failure or the neigh_ifinfo object for the 43162306a36Sopenharmony_ci * if_outgoing interface otherwise. The object is created and added to the list 43262306a36Sopenharmony_ci * if it does not exist. 43362306a36Sopenharmony_ci * 43462306a36Sopenharmony_ci * The object is returned with refcounter increased by 1. 43562306a36Sopenharmony_ci */ 43662306a36Sopenharmony_cistruct batadv_neigh_ifinfo * 43762306a36Sopenharmony_cibatadv_neigh_ifinfo_new(struct batadv_neigh_node *neigh, 43862306a36Sopenharmony_ci struct batadv_hard_iface *if_outgoing) 43962306a36Sopenharmony_ci{ 44062306a36Sopenharmony_ci struct batadv_neigh_ifinfo *neigh_ifinfo; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci spin_lock_bh(&neigh->ifinfo_lock); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci neigh_ifinfo = batadv_neigh_ifinfo_get(neigh, if_outgoing); 44562306a36Sopenharmony_ci if (neigh_ifinfo) 44662306a36Sopenharmony_ci goto out; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci neigh_ifinfo = kzalloc(sizeof(*neigh_ifinfo), GFP_ATOMIC); 44962306a36Sopenharmony_ci if (!neigh_ifinfo) 45062306a36Sopenharmony_ci goto out; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci if (if_outgoing) 45362306a36Sopenharmony_ci kref_get(&if_outgoing->refcount); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci INIT_HLIST_NODE(&neigh_ifinfo->list); 45662306a36Sopenharmony_ci kref_init(&neigh_ifinfo->refcount); 45762306a36Sopenharmony_ci neigh_ifinfo->if_outgoing = if_outgoing; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci kref_get(&neigh_ifinfo->refcount); 46062306a36Sopenharmony_ci hlist_add_head_rcu(&neigh_ifinfo->list, &neigh->ifinfo_list); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ciout: 46362306a36Sopenharmony_ci spin_unlock_bh(&neigh->ifinfo_lock); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci return neigh_ifinfo; 46662306a36Sopenharmony_ci} 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci/** 46962306a36Sopenharmony_ci * batadv_neigh_node_get() - retrieve a neighbour from the list 47062306a36Sopenharmony_ci * @orig_node: originator which the neighbour belongs to 47162306a36Sopenharmony_ci * @hard_iface: the interface where this neighbour is connected to 47262306a36Sopenharmony_ci * @addr: the address of the neighbour 47362306a36Sopenharmony_ci * 47462306a36Sopenharmony_ci * Looks for and possibly returns a neighbour belonging to this originator list 47562306a36Sopenharmony_ci * which is connected through the provided hard interface. 47662306a36Sopenharmony_ci * 47762306a36Sopenharmony_ci * Return: neighbor when found. Otherwise NULL 47862306a36Sopenharmony_ci */ 47962306a36Sopenharmony_cistatic struct batadv_neigh_node * 48062306a36Sopenharmony_cibatadv_neigh_node_get(const struct batadv_orig_node *orig_node, 48162306a36Sopenharmony_ci const struct batadv_hard_iface *hard_iface, 48262306a36Sopenharmony_ci const u8 *addr) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci struct batadv_neigh_node *tmp_neigh_node, *res = NULL; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci rcu_read_lock(); 48762306a36Sopenharmony_ci hlist_for_each_entry_rcu(tmp_neigh_node, &orig_node->neigh_list, list) { 48862306a36Sopenharmony_ci if (!batadv_compare_eth(tmp_neigh_node->addr, addr)) 48962306a36Sopenharmony_ci continue; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci if (tmp_neigh_node->if_incoming != hard_iface) 49262306a36Sopenharmony_ci continue; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (!kref_get_unless_zero(&tmp_neigh_node->refcount)) 49562306a36Sopenharmony_ci continue; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci res = tmp_neigh_node; 49862306a36Sopenharmony_ci break; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci rcu_read_unlock(); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci return res; 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci/** 50662306a36Sopenharmony_ci * batadv_hardif_neigh_create() - create a hardif neighbour node 50762306a36Sopenharmony_ci * @hard_iface: the interface this neighbour is connected to 50862306a36Sopenharmony_ci * @neigh_addr: the interface address of the neighbour to retrieve 50962306a36Sopenharmony_ci * @orig_node: originator object representing the neighbour 51062306a36Sopenharmony_ci * 51162306a36Sopenharmony_ci * Return: the hardif neighbour node if found or created or NULL otherwise. 51262306a36Sopenharmony_ci */ 51362306a36Sopenharmony_cistatic struct batadv_hardif_neigh_node * 51462306a36Sopenharmony_cibatadv_hardif_neigh_create(struct batadv_hard_iface *hard_iface, 51562306a36Sopenharmony_ci const u8 *neigh_addr, 51662306a36Sopenharmony_ci struct batadv_orig_node *orig_node) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); 51962306a36Sopenharmony_ci struct batadv_hardif_neigh_node *hardif_neigh; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci spin_lock_bh(&hard_iface->neigh_list_lock); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* check if neighbor hasn't been added in the meantime */ 52462306a36Sopenharmony_ci hardif_neigh = batadv_hardif_neigh_get(hard_iface, neigh_addr); 52562306a36Sopenharmony_ci if (hardif_neigh) 52662306a36Sopenharmony_ci goto out; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci hardif_neigh = kzalloc(sizeof(*hardif_neigh), GFP_ATOMIC); 52962306a36Sopenharmony_ci if (!hardif_neigh) 53062306a36Sopenharmony_ci goto out; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci kref_get(&hard_iface->refcount); 53362306a36Sopenharmony_ci INIT_HLIST_NODE(&hardif_neigh->list); 53462306a36Sopenharmony_ci ether_addr_copy(hardif_neigh->addr, neigh_addr); 53562306a36Sopenharmony_ci ether_addr_copy(hardif_neigh->orig, orig_node->orig); 53662306a36Sopenharmony_ci hardif_neigh->if_incoming = hard_iface; 53762306a36Sopenharmony_ci hardif_neigh->last_seen = jiffies; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci kref_init(&hardif_neigh->refcount); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci if (bat_priv->algo_ops->neigh.hardif_init) 54262306a36Sopenharmony_ci bat_priv->algo_ops->neigh.hardif_init(hardif_neigh); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci hlist_add_head_rcu(&hardif_neigh->list, &hard_iface->neigh_list); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ciout: 54762306a36Sopenharmony_ci spin_unlock_bh(&hard_iface->neigh_list_lock); 54862306a36Sopenharmony_ci return hardif_neigh; 54962306a36Sopenharmony_ci} 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci/** 55262306a36Sopenharmony_ci * batadv_hardif_neigh_get_or_create() - retrieve or create a hardif neighbour 55362306a36Sopenharmony_ci * node 55462306a36Sopenharmony_ci * @hard_iface: the interface this neighbour is connected to 55562306a36Sopenharmony_ci * @neigh_addr: the interface address of the neighbour to retrieve 55662306a36Sopenharmony_ci * @orig_node: originator object representing the neighbour 55762306a36Sopenharmony_ci * 55862306a36Sopenharmony_ci * Return: the hardif neighbour node if found or created or NULL otherwise. 55962306a36Sopenharmony_ci */ 56062306a36Sopenharmony_cistatic struct batadv_hardif_neigh_node * 56162306a36Sopenharmony_cibatadv_hardif_neigh_get_or_create(struct batadv_hard_iface *hard_iface, 56262306a36Sopenharmony_ci const u8 *neigh_addr, 56362306a36Sopenharmony_ci struct batadv_orig_node *orig_node) 56462306a36Sopenharmony_ci{ 56562306a36Sopenharmony_ci struct batadv_hardif_neigh_node *hardif_neigh; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci /* first check without locking to avoid the overhead */ 56862306a36Sopenharmony_ci hardif_neigh = batadv_hardif_neigh_get(hard_iface, neigh_addr); 56962306a36Sopenharmony_ci if (hardif_neigh) 57062306a36Sopenharmony_ci return hardif_neigh; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci return batadv_hardif_neigh_create(hard_iface, neigh_addr, orig_node); 57362306a36Sopenharmony_ci} 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci/** 57662306a36Sopenharmony_ci * batadv_hardif_neigh_get() - retrieve a hardif neighbour from the list 57762306a36Sopenharmony_ci * @hard_iface: the interface where this neighbour is connected to 57862306a36Sopenharmony_ci * @neigh_addr: the address of the neighbour 57962306a36Sopenharmony_ci * 58062306a36Sopenharmony_ci * Looks for and possibly returns a neighbour belonging to this hard interface. 58162306a36Sopenharmony_ci * 58262306a36Sopenharmony_ci * Return: neighbor when found. Otherwise NULL 58362306a36Sopenharmony_ci */ 58462306a36Sopenharmony_cistruct batadv_hardif_neigh_node * 58562306a36Sopenharmony_cibatadv_hardif_neigh_get(const struct batadv_hard_iface *hard_iface, 58662306a36Sopenharmony_ci const u8 *neigh_addr) 58762306a36Sopenharmony_ci{ 58862306a36Sopenharmony_ci struct batadv_hardif_neigh_node *tmp_hardif_neigh, *hardif_neigh = NULL; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci rcu_read_lock(); 59162306a36Sopenharmony_ci hlist_for_each_entry_rcu(tmp_hardif_neigh, 59262306a36Sopenharmony_ci &hard_iface->neigh_list, list) { 59362306a36Sopenharmony_ci if (!batadv_compare_eth(tmp_hardif_neigh->addr, neigh_addr)) 59462306a36Sopenharmony_ci continue; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci if (!kref_get_unless_zero(&tmp_hardif_neigh->refcount)) 59762306a36Sopenharmony_ci continue; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci hardif_neigh = tmp_hardif_neigh; 60062306a36Sopenharmony_ci break; 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci rcu_read_unlock(); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci return hardif_neigh; 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci/** 60862306a36Sopenharmony_ci * batadv_neigh_node_create() - create a neigh node object 60962306a36Sopenharmony_ci * @orig_node: originator object representing the neighbour 61062306a36Sopenharmony_ci * @hard_iface: the interface where the neighbour is connected to 61162306a36Sopenharmony_ci * @neigh_addr: the mac address of the neighbour interface 61262306a36Sopenharmony_ci * 61362306a36Sopenharmony_ci * Allocates a new neigh_node object and initialises all the generic fields. 61462306a36Sopenharmony_ci * 61562306a36Sopenharmony_ci * Return: the neighbour node if found or created or NULL otherwise. 61662306a36Sopenharmony_ci */ 61762306a36Sopenharmony_cistatic struct batadv_neigh_node * 61862306a36Sopenharmony_cibatadv_neigh_node_create(struct batadv_orig_node *orig_node, 61962306a36Sopenharmony_ci struct batadv_hard_iface *hard_iface, 62062306a36Sopenharmony_ci const u8 *neigh_addr) 62162306a36Sopenharmony_ci{ 62262306a36Sopenharmony_ci struct batadv_neigh_node *neigh_node; 62362306a36Sopenharmony_ci struct batadv_hardif_neigh_node *hardif_neigh = NULL; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci spin_lock_bh(&orig_node->neigh_list_lock); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci neigh_node = batadv_neigh_node_get(orig_node, hard_iface, neigh_addr); 62862306a36Sopenharmony_ci if (neigh_node) 62962306a36Sopenharmony_ci goto out; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci hardif_neigh = batadv_hardif_neigh_get_or_create(hard_iface, 63262306a36Sopenharmony_ci neigh_addr, orig_node); 63362306a36Sopenharmony_ci if (!hardif_neigh) 63462306a36Sopenharmony_ci goto out; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci neigh_node = kzalloc(sizeof(*neigh_node), GFP_ATOMIC); 63762306a36Sopenharmony_ci if (!neigh_node) 63862306a36Sopenharmony_ci goto out; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci INIT_HLIST_NODE(&neigh_node->list); 64162306a36Sopenharmony_ci INIT_HLIST_HEAD(&neigh_node->ifinfo_list); 64262306a36Sopenharmony_ci spin_lock_init(&neigh_node->ifinfo_lock); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci kref_get(&hard_iface->refcount); 64562306a36Sopenharmony_ci ether_addr_copy(neigh_node->addr, neigh_addr); 64662306a36Sopenharmony_ci neigh_node->if_incoming = hard_iface; 64762306a36Sopenharmony_ci neigh_node->orig_node = orig_node; 64862306a36Sopenharmony_ci neigh_node->last_seen = jiffies; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci /* increment unique neighbor refcount */ 65162306a36Sopenharmony_ci kref_get(&hardif_neigh->refcount); 65262306a36Sopenharmony_ci neigh_node->hardif_neigh = hardif_neigh; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci /* extra reference for return */ 65562306a36Sopenharmony_ci kref_init(&neigh_node->refcount); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci kref_get(&neigh_node->refcount); 65862306a36Sopenharmony_ci hlist_add_head_rcu(&neigh_node->list, &orig_node->neigh_list); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci batadv_dbg(BATADV_DBG_BATMAN, orig_node->bat_priv, 66162306a36Sopenharmony_ci "Creating new neighbor %pM for orig_node %pM on interface %s\n", 66262306a36Sopenharmony_ci neigh_addr, orig_node->orig, hard_iface->net_dev->name); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ciout: 66562306a36Sopenharmony_ci spin_unlock_bh(&orig_node->neigh_list_lock); 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci batadv_hardif_neigh_put(hardif_neigh); 66862306a36Sopenharmony_ci return neigh_node; 66962306a36Sopenharmony_ci} 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci/** 67262306a36Sopenharmony_ci * batadv_neigh_node_get_or_create() - retrieve or create a neigh node object 67362306a36Sopenharmony_ci * @orig_node: originator object representing the neighbour 67462306a36Sopenharmony_ci * @hard_iface: the interface where the neighbour is connected to 67562306a36Sopenharmony_ci * @neigh_addr: the mac address of the neighbour interface 67662306a36Sopenharmony_ci * 67762306a36Sopenharmony_ci * Return: the neighbour node if found or created or NULL otherwise. 67862306a36Sopenharmony_ci */ 67962306a36Sopenharmony_cistruct batadv_neigh_node * 68062306a36Sopenharmony_cibatadv_neigh_node_get_or_create(struct batadv_orig_node *orig_node, 68162306a36Sopenharmony_ci struct batadv_hard_iface *hard_iface, 68262306a36Sopenharmony_ci const u8 *neigh_addr) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci struct batadv_neigh_node *neigh_node; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci /* first check without locking to avoid the overhead */ 68762306a36Sopenharmony_ci neigh_node = batadv_neigh_node_get(orig_node, hard_iface, neigh_addr); 68862306a36Sopenharmony_ci if (neigh_node) 68962306a36Sopenharmony_ci return neigh_node; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci return batadv_neigh_node_create(orig_node, hard_iface, neigh_addr); 69262306a36Sopenharmony_ci} 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci/** 69562306a36Sopenharmony_ci * batadv_hardif_neigh_dump() - Dump to netlink the neighbor infos for a 69662306a36Sopenharmony_ci * specific outgoing interface 69762306a36Sopenharmony_ci * @msg: message to dump into 69862306a36Sopenharmony_ci * @cb: parameters for the dump 69962306a36Sopenharmony_ci * 70062306a36Sopenharmony_ci * Return: 0 or error value 70162306a36Sopenharmony_ci */ 70262306a36Sopenharmony_ciint batadv_hardif_neigh_dump(struct sk_buff *msg, struct netlink_callback *cb) 70362306a36Sopenharmony_ci{ 70462306a36Sopenharmony_ci struct net *net = sock_net(cb->skb->sk); 70562306a36Sopenharmony_ci struct net_device *soft_iface; 70662306a36Sopenharmony_ci struct net_device *hard_iface = NULL; 70762306a36Sopenharmony_ci struct batadv_hard_iface *hardif = BATADV_IF_DEFAULT; 70862306a36Sopenharmony_ci struct batadv_priv *bat_priv; 70962306a36Sopenharmony_ci struct batadv_hard_iface *primary_if = NULL; 71062306a36Sopenharmony_ci int ret; 71162306a36Sopenharmony_ci int ifindex, hard_ifindex; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci ifindex = batadv_netlink_get_ifindex(cb->nlh, BATADV_ATTR_MESH_IFINDEX); 71462306a36Sopenharmony_ci if (!ifindex) 71562306a36Sopenharmony_ci return -EINVAL; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci soft_iface = dev_get_by_index(net, ifindex); 71862306a36Sopenharmony_ci if (!soft_iface || !batadv_softif_is_valid(soft_iface)) { 71962306a36Sopenharmony_ci ret = -ENODEV; 72062306a36Sopenharmony_ci goto out; 72162306a36Sopenharmony_ci } 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci bat_priv = netdev_priv(soft_iface); 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci primary_if = batadv_primary_if_get_selected(bat_priv); 72662306a36Sopenharmony_ci if (!primary_if || primary_if->if_status != BATADV_IF_ACTIVE) { 72762306a36Sopenharmony_ci ret = -ENOENT; 72862306a36Sopenharmony_ci goto out; 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci hard_ifindex = batadv_netlink_get_ifindex(cb->nlh, 73262306a36Sopenharmony_ci BATADV_ATTR_HARD_IFINDEX); 73362306a36Sopenharmony_ci if (hard_ifindex) { 73462306a36Sopenharmony_ci hard_iface = dev_get_by_index(net, hard_ifindex); 73562306a36Sopenharmony_ci if (hard_iface) 73662306a36Sopenharmony_ci hardif = batadv_hardif_get_by_netdev(hard_iface); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci if (!hardif) { 73962306a36Sopenharmony_ci ret = -ENODEV; 74062306a36Sopenharmony_ci goto out; 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if (hardif->soft_iface != soft_iface) { 74462306a36Sopenharmony_ci ret = -ENOENT; 74562306a36Sopenharmony_ci goto out; 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci } 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci if (!bat_priv->algo_ops->neigh.dump) { 75062306a36Sopenharmony_ci ret = -EOPNOTSUPP; 75162306a36Sopenharmony_ci goto out; 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci bat_priv->algo_ops->neigh.dump(msg, cb, bat_priv, hardif); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci ret = msg->len; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci out: 75962306a36Sopenharmony_ci batadv_hardif_put(hardif); 76062306a36Sopenharmony_ci dev_put(hard_iface); 76162306a36Sopenharmony_ci batadv_hardif_put(primary_if); 76262306a36Sopenharmony_ci dev_put(soft_iface); 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci return ret; 76562306a36Sopenharmony_ci} 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci/** 76862306a36Sopenharmony_ci * batadv_orig_ifinfo_release() - release orig_ifinfo from lists and queue for 76962306a36Sopenharmony_ci * free after rcu grace period 77062306a36Sopenharmony_ci * @ref: kref pointer of the orig_ifinfo 77162306a36Sopenharmony_ci */ 77262306a36Sopenharmony_civoid batadv_orig_ifinfo_release(struct kref *ref) 77362306a36Sopenharmony_ci{ 77462306a36Sopenharmony_ci struct batadv_orig_ifinfo *orig_ifinfo; 77562306a36Sopenharmony_ci struct batadv_neigh_node *router; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci orig_ifinfo = container_of(ref, struct batadv_orig_ifinfo, refcount); 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci if (orig_ifinfo->if_outgoing != BATADV_IF_DEFAULT) 78062306a36Sopenharmony_ci batadv_hardif_put(orig_ifinfo->if_outgoing); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci /* this is the last reference to this object */ 78362306a36Sopenharmony_ci router = rcu_dereference_protected(orig_ifinfo->router, true); 78462306a36Sopenharmony_ci batadv_neigh_node_put(router); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci kfree_rcu(orig_ifinfo, rcu); 78762306a36Sopenharmony_ci} 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci/** 79062306a36Sopenharmony_ci * batadv_orig_node_free_rcu() - free the orig_node 79162306a36Sopenharmony_ci * @rcu: rcu pointer of the orig_node 79262306a36Sopenharmony_ci */ 79362306a36Sopenharmony_cistatic void batadv_orig_node_free_rcu(struct rcu_head *rcu) 79462306a36Sopenharmony_ci{ 79562306a36Sopenharmony_ci struct batadv_orig_node *orig_node; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci orig_node = container_of(rcu, struct batadv_orig_node, rcu); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci batadv_mcast_purge_orig(orig_node); 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci batadv_frag_purge_orig(orig_node, NULL); 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci kfree(orig_node->tt_buff); 80462306a36Sopenharmony_ci kfree(orig_node); 80562306a36Sopenharmony_ci} 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci/** 80862306a36Sopenharmony_ci * batadv_orig_node_release() - release orig_node from lists and queue for 80962306a36Sopenharmony_ci * free after rcu grace period 81062306a36Sopenharmony_ci * @ref: kref pointer of the orig_node 81162306a36Sopenharmony_ci */ 81262306a36Sopenharmony_civoid batadv_orig_node_release(struct kref *ref) 81362306a36Sopenharmony_ci{ 81462306a36Sopenharmony_ci struct hlist_node *node_tmp; 81562306a36Sopenharmony_ci struct batadv_neigh_node *neigh_node; 81662306a36Sopenharmony_ci struct batadv_orig_node *orig_node; 81762306a36Sopenharmony_ci struct batadv_orig_ifinfo *orig_ifinfo; 81862306a36Sopenharmony_ci struct batadv_orig_node_vlan *vlan; 81962306a36Sopenharmony_ci struct batadv_orig_ifinfo *last_candidate; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci orig_node = container_of(ref, struct batadv_orig_node, refcount); 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci spin_lock_bh(&orig_node->neigh_list_lock); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci /* for all neighbors towards this originator ... */ 82662306a36Sopenharmony_ci hlist_for_each_entry_safe(neigh_node, node_tmp, 82762306a36Sopenharmony_ci &orig_node->neigh_list, list) { 82862306a36Sopenharmony_ci hlist_del_rcu(&neigh_node->list); 82962306a36Sopenharmony_ci batadv_neigh_node_put(neigh_node); 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci hlist_for_each_entry_safe(orig_ifinfo, node_tmp, 83362306a36Sopenharmony_ci &orig_node->ifinfo_list, list) { 83462306a36Sopenharmony_ci hlist_del_rcu(&orig_ifinfo->list); 83562306a36Sopenharmony_ci batadv_orig_ifinfo_put(orig_ifinfo); 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci last_candidate = orig_node->last_bonding_candidate; 83962306a36Sopenharmony_ci orig_node->last_bonding_candidate = NULL; 84062306a36Sopenharmony_ci spin_unlock_bh(&orig_node->neigh_list_lock); 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci batadv_orig_ifinfo_put(last_candidate); 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci spin_lock_bh(&orig_node->vlan_list_lock); 84562306a36Sopenharmony_ci hlist_for_each_entry_safe(vlan, node_tmp, &orig_node->vlan_list, list) { 84662306a36Sopenharmony_ci hlist_del_rcu(&vlan->list); 84762306a36Sopenharmony_ci batadv_orig_node_vlan_put(vlan); 84862306a36Sopenharmony_ci } 84962306a36Sopenharmony_ci spin_unlock_bh(&orig_node->vlan_list_lock); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci /* Free nc_nodes */ 85262306a36Sopenharmony_ci batadv_nc_purge_orig(orig_node->bat_priv, orig_node, NULL); 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci call_rcu(&orig_node->rcu, batadv_orig_node_free_rcu); 85562306a36Sopenharmony_ci} 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci/** 85862306a36Sopenharmony_ci * batadv_originator_free() - Free all originator structures 85962306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 86062306a36Sopenharmony_ci */ 86162306a36Sopenharmony_civoid batadv_originator_free(struct batadv_priv *bat_priv) 86262306a36Sopenharmony_ci{ 86362306a36Sopenharmony_ci struct batadv_hashtable *hash = bat_priv->orig_hash; 86462306a36Sopenharmony_ci struct hlist_node *node_tmp; 86562306a36Sopenharmony_ci struct hlist_head *head; 86662306a36Sopenharmony_ci spinlock_t *list_lock; /* spinlock to protect write access */ 86762306a36Sopenharmony_ci struct batadv_orig_node *orig_node; 86862306a36Sopenharmony_ci u32 i; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci if (!hash) 87162306a36Sopenharmony_ci return; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci cancel_delayed_work_sync(&bat_priv->orig_work); 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci bat_priv->orig_hash = NULL; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci for (i = 0; i < hash->size; i++) { 87862306a36Sopenharmony_ci head = &hash->table[i]; 87962306a36Sopenharmony_ci list_lock = &hash->list_locks[i]; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci spin_lock_bh(list_lock); 88262306a36Sopenharmony_ci hlist_for_each_entry_safe(orig_node, node_tmp, 88362306a36Sopenharmony_ci head, hash_entry) { 88462306a36Sopenharmony_ci hlist_del_rcu(&orig_node->hash_entry); 88562306a36Sopenharmony_ci batadv_orig_node_put(orig_node); 88662306a36Sopenharmony_ci } 88762306a36Sopenharmony_ci spin_unlock_bh(list_lock); 88862306a36Sopenharmony_ci } 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci batadv_hash_destroy(hash); 89162306a36Sopenharmony_ci} 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci/** 89462306a36Sopenharmony_ci * batadv_orig_node_new() - creates a new orig_node 89562306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 89662306a36Sopenharmony_ci * @addr: the mac address of the originator 89762306a36Sopenharmony_ci * 89862306a36Sopenharmony_ci * Creates a new originator object and initialises all the generic fields. 89962306a36Sopenharmony_ci * The new object is not added to the originator list. 90062306a36Sopenharmony_ci * 90162306a36Sopenharmony_ci * Return: the newly created object or NULL on failure. 90262306a36Sopenharmony_ci */ 90362306a36Sopenharmony_cistruct batadv_orig_node *batadv_orig_node_new(struct batadv_priv *bat_priv, 90462306a36Sopenharmony_ci const u8 *addr) 90562306a36Sopenharmony_ci{ 90662306a36Sopenharmony_ci struct batadv_orig_node *orig_node; 90762306a36Sopenharmony_ci struct batadv_orig_node_vlan *vlan; 90862306a36Sopenharmony_ci unsigned long reset_time; 90962306a36Sopenharmony_ci int i; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci batadv_dbg(BATADV_DBG_BATMAN, bat_priv, 91262306a36Sopenharmony_ci "Creating new originator: %pM\n", addr); 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci orig_node = kzalloc(sizeof(*orig_node), GFP_ATOMIC); 91562306a36Sopenharmony_ci if (!orig_node) 91662306a36Sopenharmony_ci return NULL; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci INIT_HLIST_HEAD(&orig_node->neigh_list); 91962306a36Sopenharmony_ci INIT_HLIST_HEAD(&orig_node->vlan_list); 92062306a36Sopenharmony_ci INIT_HLIST_HEAD(&orig_node->ifinfo_list); 92162306a36Sopenharmony_ci spin_lock_init(&orig_node->bcast_seqno_lock); 92262306a36Sopenharmony_ci spin_lock_init(&orig_node->neigh_list_lock); 92362306a36Sopenharmony_ci spin_lock_init(&orig_node->tt_buff_lock); 92462306a36Sopenharmony_ci spin_lock_init(&orig_node->tt_lock); 92562306a36Sopenharmony_ci spin_lock_init(&orig_node->vlan_list_lock); 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci batadv_nc_init_orig(orig_node); 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci /* extra reference for return */ 93062306a36Sopenharmony_ci kref_init(&orig_node->refcount); 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci orig_node->bat_priv = bat_priv; 93362306a36Sopenharmony_ci ether_addr_copy(orig_node->orig, addr); 93462306a36Sopenharmony_ci batadv_dat_init_orig_node_addr(orig_node); 93562306a36Sopenharmony_ci atomic_set(&orig_node->last_ttvn, 0); 93662306a36Sopenharmony_ci orig_node->tt_buff = NULL; 93762306a36Sopenharmony_ci orig_node->tt_buff_len = 0; 93862306a36Sopenharmony_ci orig_node->last_seen = jiffies; 93962306a36Sopenharmony_ci reset_time = jiffies - 1 - msecs_to_jiffies(BATADV_RESET_PROTECTION_MS); 94062306a36Sopenharmony_ci orig_node->bcast_seqno_reset = reset_time; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci#ifdef CONFIG_BATMAN_ADV_MCAST 94362306a36Sopenharmony_ci orig_node->mcast_flags = BATADV_MCAST_WANT_NO_RTR4; 94462306a36Sopenharmony_ci orig_node->mcast_flags |= BATADV_MCAST_WANT_NO_RTR6; 94562306a36Sopenharmony_ci INIT_HLIST_NODE(&orig_node->mcast_want_all_unsnoopables_node); 94662306a36Sopenharmony_ci INIT_HLIST_NODE(&orig_node->mcast_want_all_ipv4_node); 94762306a36Sopenharmony_ci INIT_HLIST_NODE(&orig_node->mcast_want_all_ipv6_node); 94862306a36Sopenharmony_ci spin_lock_init(&orig_node->mcast_handler_lock); 94962306a36Sopenharmony_ci#endif 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci /* create a vlan object for the "untagged" LAN */ 95262306a36Sopenharmony_ci vlan = batadv_orig_node_vlan_new(orig_node, BATADV_NO_FLAGS); 95362306a36Sopenharmony_ci if (!vlan) 95462306a36Sopenharmony_ci goto free_orig_node; 95562306a36Sopenharmony_ci /* batadv_orig_node_vlan_new() increases the refcounter. 95662306a36Sopenharmony_ci * Immediately release vlan since it is not needed anymore in this 95762306a36Sopenharmony_ci * context 95862306a36Sopenharmony_ci */ 95962306a36Sopenharmony_ci batadv_orig_node_vlan_put(vlan); 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci for (i = 0; i < BATADV_FRAG_BUFFER_COUNT; i++) { 96262306a36Sopenharmony_ci INIT_HLIST_HEAD(&orig_node->fragments[i].fragment_list); 96362306a36Sopenharmony_ci spin_lock_init(&orig_node->fragments[i].lock); 96462306a36Sopenharmony_ci orig_node->fragments[i].size = 0; 96562306a36Sopenharmony_ci } 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci return orig_node; 96862306a36Sopenharmony_cifree_orig_node: 96962306a36Sopenharmony_ci kfree(orig_node); 97062306a36Sopenharmony_ci return NULL; 97162306a36Sopenharmony_ci} 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci/** 97462306a36Sopenharmony_ci * batadv_purge_neigh_ifinfo() - purge obsolete ifinfo entries from neighbor 97562306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 97662306a36Sopenharmony_ci * @neigh: orig node which is to be checked 97762306a36Sopenharmony_ci */ 97862306a36Sopenharmony_cistatic void 97962306a36Sopenharmony_cibatadv_purge_neigh_ifinfo(struct batadv_priv *bat_priv, 98062306a36Sopenharmony_ci struct batadv_neigh_node *neigh) 98162306a36Sopenharmony_ci{ 98262306a36Sopenharmony_ci struct batadv_neigh_ifinfo *neigh_ifinfo; 98362306a36Sopenharmony_ci struct batadv_hard_iface *if_outgoing; 98462306a36Sopenharmony_ci struct hlist_node *node_tmp; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci spin_lock_bh(&neigh->ifinfo_lock); 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci /* for all ifinfo objects for this neighinator */ 98962306a36Sopenharmony_ci hlist_for_each_entry_safe(neigh_ifinfo, node_tmp, 99062306a36Sopenharmony_ci &neigh->ifinfo_list, list) { 99162306a36Sopenharmony_ci if_outgoing = neigh_ifinfo->if_outgoing; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci /* always keep the default interface */ 99462306a36Sopenharmony_ci if (if_outgoing == BATADV_IF_DEFAULT) 99562306a36Sopenharmony_ci continue; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci /* don't purge if the interface is not (going) down */ 99862306a36Sopenharmony_ci if (if_outgoing->if_status != BATADV_IF_INACTIVE && 99962306a36Sopenharmony_ci if_outgoing->if_status != BATADV_IF_NOT_IN_USE && 100062306a36Sopenharmony_ci if_outgoing->if_status != BATADV_IF_TO_BE_REMOVED) 100162306a36Sopenharmony_ci continue; 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci batadv_dbg(BATADV_DBG_BATMAN, bat_priv, 100462306a36Sopenharmony_ci "neighbor/ifinfo purge: neighbor %pM, iface: %s\n", 100562306a36Sopenharmony_ci neigh->addr, if_outgoing->net_dev->name); 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci hlist_del_rcu(&neigh_ifinfo->list); 100862306a36Sopenharmony_ci batadv_neigh_ifinfo_put(neigh_ifinfo); 100962306a36Sopenharmony_ci } 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci spin_unlock_bh(&neigh->ifinfo_lock); 101262306a36Sopenharmony_ci} 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci/** 101562306a36Sopenharmony_ci * batadv_purge_orig_ifinfo() - purge obsolete ifinfo entries from originator 101662306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 101762306a36Sopenharmony_ci * @orig_node: orig node which is to be checked 101862306a36Sopenharmony_ci * 101962306a36Sopenharmony_ci * Return: true if any ifinfo entry was purged, false otherwise. 102062306a36Sopenharmony_ci */ 102162306a36Sopenharmony_cistatic bool 102262306a36Sopenharmony_cibatadv_purge_orig_ifinfo(struct batadv_priv *bat_priv, 102362306a36Sopenharmony_ci struct batadv_orig_node *orig_node) 102462306a36Sopenharmony_ci{ 102562306a36Sopenharmony_ci struct batadv_orig_ifinfo *orig_ifinfo; 102662306a36Sopenharmony_ci struct batadv_hard_iface *if_outgoing; 102762306a36Sopenharmony_ci struct hlist_node *node_tmp; 102862306a36Sopenharmony_ci bool ifinfo_purged = false; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci spin_lock_bh(&orig_node->neigh_list_lock); 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci /* for all ifinfo objects for this originator */ 103362306a36Sopenharmony_ci hlist_for_each_entry_safe(orig_ifinfo, node_tmp, 103462306a36Sopenharmony_ci &orig_node->ifinfo_list, list) { 103562306a36Sopenharmony_ci if_outgoing = orig_ifinfo->if_outgoing; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci /* always keep the default interface */ 103862306a36Sopenharmony_ci if (if_outgoing == BATADV_IF_DEFAULT) 103962306a36Sopenharmony_ci continue; 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci /* don't purge if the interface is not (going) down */ 104262306a36Sopenharmony_ci if (if_outgoing->if_status != BATADV_IF_INACTIVE && 104362306a36Sopenharmony_ci if_outgoing->if_status != BATADV_IF_NOT_IN_USE && 104462306a36Sopenharmony_ci if_outgoing->if_status != BATADV_IF_TO_BE_REMOVED) 104562306a36Sopenharmony_ci continue; 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci batadv_dbg(BATADV_DBG_BATMAN, bat_priv, 104862306a36Sopenharmony_ci "router/ifinfo purge: originator %pM, iface: %s\n", 104962306a36Sopenharmony_ci orig_node->orig, if_outgoing->net_dev->name); 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci ifinfo_purged = true; 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci hlist_del_rcu(&orig_ifinfo->list); 105462306a36Sopenharmony_ci batadv_orig_ifinfo_put(orig_ifinfo); 105562306a36Sopenharmony_ci if (orig_node->last_bonding_candidate == orig_ifinfo) { 105662306a36Sopenharmony_ci orig_node->last_bonding_candidate = NULL; 105762306a36Sopenharmony_ci batadv_orig_ifinfo_put(orig_ifinfo); 105862306a36Sopenharmony_ci } 105962306a36Sopenharmony_ci } 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci spin_unlock_bh(&orig_node->neigh_list_lock); 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci return ifinfo_purged; 106462306a36Sopenharmony_ci} 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci/** 106762306a36Sopenharmony_ci * batadv_purge_orig_neighbors() - purges neighbors from originator 106862306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 106962306a36Sopenharmony_ci * @orig_node: orig node which is to be checked 107062306a36Sopenharmony_ci * 107162306a36Sopenharmony_ci * Return: true if any neighbor was purged, false otherwise 107262306a36Sopenharmony_ci */ 107362306a36Sopenharmony_cistatic bool 107462306a36Sopenharmony_cibatadv_purge_orig_neighbors(struct batadv_priv *bat_priv, 107562306a36Sopenharmony_ci struct batadv_orig_node *orig_node) 107662306a36Sopenharmony_ci{ 107762306a36Sopenharmony_ci struct hlist_node *node_tmp; 107862306a36Sopenharmony_ci struct batadv_neigh_node *neigh_node; 107962306a36Sopenharmony_ci bool neigh_purged = false; 108062306a36Sopenharmony_ci unsigned long last_seen; 108162306a36Sopenharmony_ci struct batadv_hard_iface *if_incoming; 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci spin_lock_bh(&orig_node->neigh_list_lock); 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci /* for all neighbors towards this originator ... */ 108662306a36Sopenharmony_ci hlist_for_each_entry_safe(neigh_node, node_tmp, 108762306a36Sopenharmony_ci &orig_node->neigh_list, list) { 108862306a36Sopenharmony_ci last_seen = neigh_node->last_seen; 108962306a36Sopenharmony_ci if_incoming = neigh_node->if_incoming; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci if (batadv_has_timed_out(last_seen, BATADV_PURGE_TIMEOUT) || 109262306a36Sopenharmony_ci if_incoming->if_status == BATADV_IF_INACTIVE || 109362306a36Sopenharmony_ci if_incoming->if_status == BATADV_IF_NOT_IN_USE || 109462306a36Sopenharmony_ci if_incoming->if_status == BATADV_IF_TO_BE_REMOVED) { 109562306a36Sopenharmony_ci if (if_incoming->if_status == BATADV_IF_INACTIVE || 109662306a36Sopenharmony_ci if_incoming->if_status == BATADV_IF_NOT_IN_USE || 109762306a36Sopenharmony_ci if_incoming->if_status == BATADV_IF_TO_BE_REMOVED) 109862306a36Sopenharmony_ci batadv_dbg(BATADV_DBG_BATMAN, bat_priv, 109962306a36Sopenharmony_ci "neighbor purge: originator %pM, neighbor: %pM, iface: %s\n", 110062306a36Sopenharmony_ci orig_node->orig, neigh_node->addr, 110162306a36Sopenharmony_ci if_incoming->net_dev->name); 110262306a36Sopenharmony_ci else 110362306a36Sopenharmony_ci batadv_dbg(BATADV_DBG_BATMAN, bat_priv, 110462306a36Sopenharmony_ci "neighbor timeout: originator %pM, neighbor: %pM, last_seen: %u\n", 110562306a36Sopenharmony_ci orig_node->orig, neigh_node->addr, 110662306a36Sopenharmony_ci jiffies_to_msecs(last_seen)); 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci neigh_purged = true; 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci hlist_del_rcu(&neigh_node->list); 111162306a36Sopenharmony_ci batadv_neigh_node_put(neigh_node); 111262306a36Sopenharmony_ci } else { 111362306a36Sopenharmony_ci /* only necessary if not the whole neighbor is to be 111462306a36Sopenharmony_ci * deleted, but some interface has been removed. 111562306a36Sopenharmony_ci */ 111662306a36Sopenharmony_ci batadv_purge_neigh_ifinfo(bat_priv, neigh_node); 111762306a36Sopenharmony_ci } 111862306a36Sopenharmony_ci } 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci spin_unlock_bh(&orig_node->neigh_list_lock); 112162306a36Sopenharmony_ci return neigh_purged; 112262306a36Sopenharmony_ci} 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci/** 112562306a36Sopenharmony_ci * batadv_find_best_neighbor() - finds the best neighbor after purging 112662306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 112762306a36Sopenharmony_ci * @orig_node: orig node which is to be checked 112862306a36Sopenharmony_ci * @if_outgoing: the interface for which the metric should be compared 112962306a36Sopenharmony_ci * 113062306a36Sopenharmony_ci * Return: the current best neighbor, with refcount increased. 113162306a36Sopenharmony_ci */ 113262306a36Sopenharmony_cistatic struct batadv_neigh_node * 113362306a36Sopenharmony_cibatadv_find_best_neighbor(struct batadv_priv *bat_priv, 113462306a36Sopenharmony_ci struct batadv_orig_node *orig_node, 113562306a36Sopenharmony_ci struct batadv_hard_iface *if_outgoing) 113662306a36Sopenharmony_ci{ 113762306a36Sopenharmony_ci struct batadv_neigh_node *best = NULL, *neigh; 113862306a36Sopenharmony_ci struct batadv_algo_ops *bao = bat_priv->algo_ops; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci rcu_read_lock(); 114162306a36Sopenharmony_ci hlist_for_each_entry_rcu(neigh, &orig_node->neigh_list, list) { 114262306a36Sopenharmony_ci if (best && (bao->neigh.cmp(neigh, if_outgoing, best, 114362306a36Sopenharmony_ci if_outgoing) <= 0)) 114462306a36Sopenharmony_ci continue; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci if (!kref_get_unless_zero(&neigh->refcount)) 114762306a36Sopenharmony_ci continue; 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci batadv_neigh_node_put(best); 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci best = neigh; 115262306a36Sopenharmony_ci } 115362306a36Sopenharmony_ci rcu_read_unlock(); 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci return best; 115662306a36Sopenharmony_ci} 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci/** 115962306a36Sopenharmony_ci * batadv_purge_orig_node() - purges obsolete information from an orig_node 116062306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 116162306a36Sopenharmony_ci * @orig_node: orig node which is to be checked 116262306a36Sopenharmony_ci * 116362306a36Sopenharmony_ci * This function checks if the orig_node or substructures of it have become 116462306a36Sopenharmony_ci * obsolete, and purges this information if that's the case. 116562306a36Sopenharmony_ci * 116662306a36Sopenharmony_ci * Return: true if the orig_node is to be removed, false otherwise. 116762306a36Sopenharmony_ci */ 116862306a36Sopenharmony_cistatic bool batadv_purge_orig_node(struct batadv_priv *bat_priv, 116962306a36Sopenharmony_ci struct batadv_orig_node *orig_node) 117062306a36Sopenharmony_ci{ 117162306a36Sopenharmony_ci struct batadv_neigh_node *best_neigh_node; 117262306a36Sopenharmony_ci struct batadv_hard_iface *hard_iface; 117362306a36Sopenharmony_ci bool changed_ifinfo, changed_neigh; 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci if (batadv_has_timed_out(orig_node->last_seen, 117662306a36Sopenharmony_ci 2 * BATADV_PURGE_TIMEOUT)) { 117762306a36Sopenharmony_ci batadv_dbg(BATADV_DBG_BATMAN, bat_priv, 117862306a36Sopenharmony_ci "Originator timeout: originator %pM, last_seen %u\n", 117962306a36Sopenharmony_ci orig_node->orig, 118062306a36Sopenharmony_ci jiffies_to_msecs(orig_node->last_seen)); 118162306a36Sopenharmony_ci return true; 118262306a36Sopenharmony_ci } 118362306a36Sopenharmony_ci changed_ifinfo = batadv_purge_orig_ifinfo(bat_priv, orig_node); 118462306a36Sopenharmony_ci changed_neigh = batadv_purge_orig_neighbors(bat_priv, orig_node); 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci if (!changed_ifinfo && !changed_neigh) 118762306a36Sopenharmony_ci return false; 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci /* first for NULL ... */ 119062306a36Sopenharmony_ci best_neigh_node = batadv_find_best_neighbor(bat_priv, orig_node, 119162306a36Sopenharmony_ci BATADV_IF_DEFAULT); 119262306a36Sopenharmony_ci batadv_update_route(bat_priv, orig_node, BATADV_IF_DEFAULT, 119362306a36Sopenharmony_ci best_neigh_node); 119462306a36Sopenharmony_ci batadv_neigh_node_put(best_neigh_node); 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci /* ... then for all other interfaces. */ 119762306a36Sopenharmony_ci rcu_read_lock(); 119862306a36Sopenharmony_ci list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { 119962306a36Sopenharmony_ci if (hard_iface->if_status != BATADV_IF_ACTIVE) 120062306a36Sopenharmony_ci continue; 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci if (hard_iface->soft_iface != bat_priv->soft_iface) 120362306a36Sopenharmony_ci continue; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci if (!kref_get_unless_zero(&hard_iface->refcount)) 120662306a36Sopenharmony_ci continue; 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci best_neigh_node = batadv_find_best_neighbor(bat_priv, 120962306a36Sopenharmony_ci orig_node, 121062306a36Sopenharmony_ci hard_iface); 121162306a36Sopenharmony_ci batadv_update_route(bat_priv, orig_node, hard_iface, 121262306a36Sopenharmony_ci best_neigh_node); 121362306a36Sopenharmony_ci batadv_neigh_node_put(best_neigh_node); 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci batadv_hardif_put(hard_iface); 121662306a36Sopenharmony_ci } 121762306a36Sopenharmony_ci rcu_read_unlock(); 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci return false; 122062306a36Sopenharmony_ci} 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci/** 122362306a36Sopenharmony_ci * batadv_purge_orig_ref() - Purge all outdated originators 122462306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 122562306a36Sopenharmony_ci */ 122662306a36Sopenharmony_civoid batadv_purge_orig_ref(struct batadv_priv *bat_priv) 122762306a36Sopenharmony_ci{ 122862306a36Sopenharmony_ci struct batadv_hashtable *hash = bat_priv->orig_hash; 122962306a36Sopenharmony_ci struct hlist_node *node_tmp; 123062306a36Sopenharmony_ci struct hlist_head *head; 123162306a36Sopenharmony_ci spinlock_t *list_lock; /* spinlock to protect write access */ 123262306a36Sopenharmony_ci struct batadv_orig_node *orig_node; 123362306a36Sopenharmony_ci u32 i; 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci if (!hash) 123662306a36Sopenharmony_ci return; 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci /* for all origins... */ 123962306a36Sopenharmony_ci for (i = 0; i < hash->size; i++) { 124062306a36Sopenharmony_ci head = &hash->table[i]; 124162306a36Sopenharmony_ci list_lock = &hash->list_locks[i]; 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci spin_lock_bh(list_lock); 124462306a36Sopenharmony_ci hlist_for_each_entry_safe(orig_node, node_tmp, 124562306a36Sopenharmony_ci head, hash_entry) { 124662306a36Sopenharmony_ci if (batadv_purge_orig_node(bat_priv, orig_node)) { 124762306a36Sopenharmony_ci batadv_gw_node_delete(bat_priv, orig_node); 124862306a36Sopenharmony_ci hlist_del_rcu(&orig_node->hash_entry); 124962306a36Sopenharmony_ci batadv_tt_global_del_orig(orig_node->bat_priv, 125062306a36Sopenharmony_ci orig_node, -1, 125162306a36Sopenharmony_ci "originator timed out"); 125262306a36Sopenharmony_ci batadv_orig_node_put(orig_node); 125362306a36Sopenharmony_ci continue; 125462306a36Sopenharmony_ci } 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci batadv_frag_purge_orig(orig_node, 125762306a36Sopenharmony_ci batadv_frag_check_entry); 125862306a36Sopenharmony_ci } 125962306a36Sopenharmony_ci spin_unlock_bh(list_lock); 126062306a36Sopenharmony_ci } 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci batadv_gw_election(bat_priv); 126362306a36Sopenharmony_ci} 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_cistatic void batadv_purge_orig(struct work_struct *work) 126662306a36Sopenharmony_ci{ 126762306a36Sopenharmony_ci struct delayed_work *delayed_work; 126862306a36Sopenharmony_ci struct batadv_priv *bat_priv; 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci delayed_work = to_delayed_work(work); 127162306a36Sopenharmony_ci bat_priv = container_of(delayed_work, struct batadv_priv, orig_work); 127262306a36Sopenharmony_ci batadv_purge_orig_ref(bat_priv); 127362306a36Sopenharmony_ci queue_delayed_work(batadv_event_workqueue, 127462306a36Sopenharmony_ci &bat_priv->orig_work, 127562306a36Sopenharmony_ci msecs_to_jiffies(BATADV_ORIG_WORK_PERIOD)); 127662306a36Sopenharmony_ci} 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci/** 127962306a36Sopenharmony_ci * batadv_orig_dump() - Dump to netlink the originator infos for a specific 128062306a36Sopenharmony_ci * outgoing interface 128162306a36Sopenharmony_ci * @msg: message to dump into 128262306a36Sopenharmony_ci * @cb: parameters for the dump 128362306a36Sopenharmony_ci * 128462306a36Sopenharmony_ci * Return: 0 or error value 128562306a36Sopenharmony_ci */ 128662306a36Sopenharmony_ciint batadv_orig_dump(struct sk_buff *msg, struct netlink_callback *cb) 128762306a36Sopenharmony_ci{ 128862306a36Sopenharmony_ci struct net *net = sock_net(cb->skb->sk); 128962306a36Sopenharmony_ci struct net_device *soft_iface; 129062306a36Sopenharmony_ci struct net_device *hard_iface = NULL; 129162306a36Sopenharmony_ci struct batadv_hard_iface *hardif = BATADV_IF_DEFAULT; 129262306a36Sopenharmony_ci struct batadv_priv *bat_priv; 129362306a36Sopenharmony_ci struct batadv_hard_iface *primary_if = NULL; 129462306a36Sopenharmony_ci int ret; 129562306a36Sopenharmony_ci int ifindex, hard_ifindex; 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci ifindex = batadv_netlink_get_ifindex(cb->nlh, BATADV_ATTR_MESH_IFINDEX); 129862306a36Sopenharmony_ci if (!ifindex) 129962306a36Sopenharmony_ci return -EINVAL; 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci soft_iface = dev_get_by_index(net, ifindex); 130262306a36Sopenharmony_ci if (!soft_iface || !batadv_softif_is_valid(soft_iface)) { 130362306a36Sopenharmony_ci ret = -ENODEV; 130462306a36Sopenharmony_ci goto out; 130562306a36Sopenharmony_ci } 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci bat_priv = netdev_priv(soft_iface); 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci primary_if = batadv_primary_if_get_selected(bat_priv); 131062306a36Sopenharmony_ci if (!primary_if || primary_if->if_status != BATADV_IF_ACTIVE) { 131162306a36Sopenharmony_ci ret = -ENOENT; 131262306a36Sopenharmony_ci goto out; 131362306a36Sopenharmony_ci } 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci hard_ifindex = batadv_netlink_get_ifindex(cb->nlh, 131662306a36Sopenharmony_ci BATADV_ATTR_HARD_IFINDEX); 131762306a36Sopenharmony_ci if (hard_ifindex) { 131862306a36Sopenharmony_ci hard_iface = dev_get_by_index(net, hard_ifindex); 131962306a36Sopenharmony_ci if (hard_iface) 132062306a36Sopenharmony_ci hardif = batadv_hardif_get_by_netdev(hard_iface); 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci if (!hardif) { 132362306a36Sopenharmony_ci ret = -ENODEV; 132462306a36Sopenharmony_ci goto out; 132562306a36Sopenharmony_ci } 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci if (hardif->soft_iface != soft_iface) { 132862306a36Sopenharmony_ci ret = -ENOENT; 132962306a36Sopenharmony_ci goto out; 133062306a36Sopenharmony_ci } 133162306a36Sopenharmony_ci } 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci if (!bat_priv->algo_ops->orig.dump) { 133462306a36Sopenharmony_ci ret = -EOPNOTSUPP; 133562306a36Sopenharmony_ci goto out; 133662306a36Sopenharmony_ci } 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci bat_priv->algo_ops->orig.dump(msg, cb, bat_priv, hardif); 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci ret = msg->len; 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci out: 134362306a36Sopenharmony_ci batadv_hardif_put(hardif); 134462306a36Sopenharmony_ci dev_put(hard_iface); 134562306a36Sopenharmony_ci batadv_hardif_put(primary_if); 134662306a36Sopenharmony_ci dev_put(soft_iface); 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci return ret; 134962306a36Sopenharmony_ci} 1350