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