18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/* Copyright (C) 2007-2020  B.A.T.M.A.N. contributors:
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Marek Lindner, Simon Wunderlich
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include "bat_iv_ogm.h"
88c2ecf20Sopenharmony_ci#include "main.h"
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/atomic.h>
118c2ecf20Sopenharmony_ci#include <linux/bitmap.h>
128c2ecf20Sopenharmony_ci#include <linux/bitops.h>
138c2ecf20Sopenharmony_ci#include <linux/bug.h>
148c2ecf20Sopenharmony_ci#include <linux/byteorder/generic.h>
158c2ecf20Sopenharmony_ci#include <linux/cache.h>
168c2ecf20Sopenharmony_ci#include <linux/errno.h>
178c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
188c2ecf20Sopenharmony_ci#include <linux/gfp.h>
198c2ecf20Sopenharmony_ci#include <linux/if_ether.h>
208c2ecf20Sopenharmony_ci#include <linux/init.h>
218c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
228c2ecf20Sopenharmony_ci#include <linux/kernel.h>
238c2ecf20Sopenharmony_ci#include <linux/kref.h>
248c2ecf20Sopenharmony_ci#include <linux/list.h>
258c2ecf20Sopenharmony_ci#include <linux/lockdep.h>
268c2ecf20Sopenharmony_ci#include <linux/mutex.h>
278c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
288c2ecf20Sopenharmony_ci#include <linux/netlink.h>
298c2ecf20Sopenharmony_ci#include <linux/pkt_sched.h>
308c2ecf20Sopenharmony_ci#include <linux/prandom.h>
318c2ecf20Sopenharmony_ci#include <linux/printk.h>
328c2ecf20Sopenharmony_ci#include <linux/random.h>
338c2ecf20Sopenharmony_ci#include <linux/rculist.h>
348c2ecf20Sopenharmony_ci#include <linux/rcupdate.h>
358c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
368c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
378c2ecf20Sopenharmony_ci#include <linux/slab.h>
388c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
398c2ecf20Sopenharmony_ci#include <linux/stddef.h>
408c2ecf20Sopenharmony_ci#include <linux/string.h>
418c2ecf20Sopenharmony_ci#include <linux/types.h>
428c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
438c2ecf20Sopenharmony_ci#include <net/genetlink.h>
448c2ecf20Sopenharmony_ci#include <net/netlink.h>
458c2ecf20Sopenharmony_ci#include <uapi/linux/batadv_packet.h>
468c2ecf20Sopenharmony_ci#include <uapi/linux/batman_adv.h>
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#include "bat_algo.h"
498c2ecf20Sopenharmony_ci#include "bitarray.h"
508c2ecf20Sopenharmony_ci#include "gateway_client.h"
518c2ecf20Sopenharmony_ci#include "hard-interface.h"
528c2ecf20Sopenharmony_ci#include "hash.h"
538c2ecf20Sopenharmony_ci#include "log.h"
548c2ecf20Sopenharmony_ci#include "netlink.h"
558c2ecf20Sopenharmony_ci#include "network-coding.h"
568c2ecf20Sopenharmony_ci#include "originator.h"
578c2ecf20Sopenharmony_ci#include "routing.h"
588c2ecf20Sopenharmony_ci#include "send.h"
598c2ecf20Sopenharmony_ci#include "translation-table.h"
608c2ecf20Sopenharmony_ci#include "tvlv.h"
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic void batadv_iv_send_outstanding_bat_ogm_packet(struct work_struct *work);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci/**
658c2ecf20Sopenharmony_ci * enum batadv_dup_status - duplicate status
668c2ecf20Sopenharmony_ci */
678c2ecf20Sopenharmony_cienum batadv_dup_status {
688c2ecf20Sopenharmony_ci	/** @BATADV_NO_DUP: the packet is no duplicate */
698c2ecf20Sopenharmony_ci	BATADV_NO_DUP = 0,
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	/**
728c2ecf20Sopenharmony_ci	 * @BATADV_ORIG_DUP: OGM is a duplicate in the originator (but not for
738c2ecf20Sopenharmony_ci	 *  the neighbor)
748c2ecf20Sopenharmony_ci	 */
758c2ecf20Sopenharmony_ci	BATADV_ORIG_DUP,
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	/** @BATADV_NEIGH_DUP: OGM is a duplicate for the neighbor */
788c2ecf20Sopenharmony_ci	BATADV_NEIGH_DUP,
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	/**
818c2ecf20Sopenharmony_ci	 * @BATADV_PROTECTED: originator is currently protected (after reboot)
828c2ecf20Sopenharmony_ci	 */
838c2ecf20Sopenharmony_ci	BATADV_PROTECTED,
848c2ecf20Sopenharmony_ci};
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci/**
878c2ecf20Sopenharmony_ci * batadv_ring_buffer_set() - update the ring buffer with the given value
888c2ecf20Sopenharmony_ci * @lq_recv: pointer to the ring buffer
898c2ecf20Sopenharmony_ci * @lq_index: index to store the value at
908c2ecf20Sopenharmony_ci * @value: value to store in the ring buffer
918c2ecf20Sopenharmony_ci */
928c2ecf20Sopenharmony_cistatic void batadv_ring_buffer_set(u8 lq_recv[], u8 *lq_index, u8 value)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	lq_recv[*lq_index] = value;
958c2ecf20Sopenharmony_ci	*lq_index = (*lq_index + 1) % BATADV_TQ_GLOBAL_WINDOW_SIZE;
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci/**
998c2ecf20Sopenharmony_ci * batadv_ring_buffer_avg() - compute the average of all non-zero values stored
1008c2ecf20Sopenharmony_ci * in the given ring buffer
1018c2ecf20Sopenharmony_ci * @lq_recv: pointer to the ring buffer
1028c2ecf20Sopenharmony_ci *
1038c2ecf20Sopenharmony_ci * Return: computed average value.
1048c2ecf20Sopenharmony_ci */
1058c2ecf20Sopenharmony_cistatic u8 batadv_ring_buffer_avg(const u8 lq_recv[])
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	const u8 *ptr;
1088c2ecf20Sopenharmony_ci	u16 count = 0;
1098c2ecf20Sopenharmony_ci	u16 i = 0;
1108c2ecf20Sopenharmony_ci	u16 sum = 0;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	ptr = lq_recv;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	while (i < BATADV_TQ_GLOBAL_WINDOW_SIZE) {
1158c2ecf20Sopenharmony_ci		if (*ptr != 0) {
1168c2ecf20Sopenharmony_ci			count++;
1178c2ecf20Sopenharmony_ci			sum += *ptr;
1188c2ecf20Sopenharmony_ci		}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci		i++;
1218c2ecf20Sopenharmony_ci		ptr++;
1228c2ecf20Sopenharmony_ci	}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	if (count == 0)
1258c2ecf20Sopenharmony_ci		return 0;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	return (u8)(sum / count);
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci/**
1318c2ecf20Sopenharmony_ci * batadv_iv_ogm_orig_get() - retrieve or create (if does not exist) an
1328c2ecf20Sopenharmony_ci *  originator
1338c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
1348c2ecf20Sopenharmony_ci * @addr: mac address of the originator
1358c2ecf20Sopenharmony_ci *
1368c2ecf20Sopenharmony_ci * Return: the originator object corresponding to the passed mac address or NULL
1378c2ecf20Sopenharmony_ci * on failure.
1388c2ecf20Sopenharmony_ci * If the object does not exist, it is created and initialised.
1398c2ecf20Sopenharmony_ci */
1408c2ecf20Sopenharmony_cistatic struct batadv_orig_node *
1418c2ecf20Sopenharmony_cibatadv_iv_ogm_orig_get(struct batadv_priv *bat_priv, const u8 *addr)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	struct batadv_orig_node *orig_node;
1448c2ecf20Sopenharmony_ci	int hash_added;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	orig_node = batadv_orig_hash_find(bat_priv, addr);
1478c2ecf20Sopenharmony_ci	if (orig_node)
1488c2ecf20Sopenharmony_ci		return orig_node;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	orig_node = batadv_orig_node_new(bat_priv, addr);
1518c2ecf20Sopenharmony_ci	if (!orig_node)
1528c2ecf20Sopenharmony_ci		return NULL;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	spin_lock_init(&orig_node->bat_iv.ogm_cnt_lock);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	kref_get(&orig_node->refcount);
1578c2ecf20Sopenharmony_ci	hash_added = batadv_hash_add(bat_priv->orig_hash, batadv_compare_orig,
1588c2ecf20Sopenharmony_ci				     batadv_choose_orig, orig_node,
1598c2ecf20Sopenharmony_ci				     &orig_node->hash_entry);
1608c2ecf20Sopenharmony_ci	if (hash_added != 0)
1618c2ecf20Sopenharmony_ci		goto free_orig_node_hash;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	return orig_node;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cifree_orig_node_hash:
1668c2ecf20Sopenharmony_ci	/* reference for batadv_hash_add */
1678c2ecf20Sopenharmony_ci	batadv_orig_node_put(orig_node);
1688c2ecf20Sopenharmony_ci	/* reference from batadv_orig_node_new */
1698c2ecf20Sopenharmony_ci	batadv_orig_node_put(orig_node);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	return NULL;
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cistatic struct batadv_neigh_node *
1758c2ecf20Sopenharmony_cibatadv_iv_ogm_neigh_new(struct batadv_hard_iface *hard_iface,
1768c2ecf20Sopenharmony_ci			const u8 *neigh_addr,
1778c2ecf20Sopenharmony_ci			struct batadv_orig_node *orig_node,
1788c2ecf20Sopenharmony_ci			struct batadv_orig_node *orig_neigh)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	struct batadv_neigh_node *neigh_node;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	neigh_node = batadv_neigh_node_get_or_create(orig_node,
1838c2ecf20Sopenharmony_ci						     hard_iface, neigh_addr);
1848c2ecf20Sopenharmony_ci	if (!neigh_node)
1858c2ecf20Sopenharmony_ci		goto out;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	neigh_node->orig_node = orig_neigh;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ciout:
1908c2ecf20Sopenharmony_ci	return neigh_node;
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistatic int batadv_iv_ogm_iface_enable(struct batadv_hard_iface *hard_iface)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	struct batadv_ogm_packet *batadv_ogm_packet;
1968c2ecf20Sopenharmony_ci	unsigned char *ogm_buff;
1978c2ecf20Sopenharmony_ci	u32 random_seqno;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	mutex_lock(&hard_iface->bat_iv.ogm_buff_mutex);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	/* randomize initial seqno to avoid collision */
2028c2ecf20Sopenharmony_ci	get_random_bytes(&random_seqno, sizeof(random_seqno));
2038c2ecf20Sopenharmony_ci	atomic_set(&hard_iface->bat_iv.ogm_seqno, random_seqno);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	hard_iface->bat_iv.ogm_buff_len = BATADV_OGM_HLEN;
2068c2ecf20Sopenharmony_ci	ogm_buff = kmalloc(hard_iface->bat_iv.ogm_buff_len, GFP_ATOMIC);
2078c2ecf20Sopenharmony_ci	if (!ogm_buff) {
2088c2ecf20Sopenharmony_ci		mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex);
2098c2ecf20Sopenharmony_ci		return -ENOMEM;
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	hard_iface->bat_iv.ogm_buff = ogm_buff;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	batadv_ogm_packet = (struct batadv_ogm_packet *)ogm_buff;
2158c2ecf20Sopenharmony_ci	batadv_ogm_packet->packet_type = BATADV_IV_OGM;
2168c2ecf20Sopenharmony_ci	batadv_ogm_packet->version = BATADV_COMPAT_VERSION;
2178c2ecf20Sopenharmony_ci	batadv_ogm_packet->ttl = 2;
2188c2ecf20Sopenharmony_ci	batadv_ogm_packet->flags = BATADV_NO_FLAGS;
2198c2ecf20Sopenharmony_ci	batadv_ogm_packet->reserved = 0;
2208c2ecf20Sopenharmony_ci	batadv_ogm_packet->tq = BATADV_TQ_MAX_VALUE;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	return 0;
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_cistatic void batadv_iv_ogm_iface_disable(struct batadv_hard_iface *hard_iface)
2288c2ecf20Sopenharmony_ci{
2298c2ecf20Sopenharmony_ci	mutex_lock(&hard_iface->bat_iv.ogm_buff_mutex);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	kfree(hard_iface->bat_iv.ogm_buff);
2328c2ecf20Sopenharmony_ci	hard_iface->bat_iv.ogm_buff = NULL;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex);
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cistatic void batadv_iv_ogm_iface_update_mac(struct batadv_hard_iface *hard_iface)
2388c2ecf20Sopenharmony_ci{
2398c2ecf20Sopenharmony_ci	struct batadv_ogm_packet *batadv_ogm_packet;
2408c2ecf20Sopenharmony_ci	void *ogm_buff;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	mutex_lock(&hard_iface->bat_iv.ogm_buff_mutex);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	ogm_buff = hard_iface->bat_iv.ogm_buff;
2458c2ecf20Sopenharmony_ci	if (!ogm_buff)
2468c2ecf20Sopenharmony_ci		goto unlock;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	batadv_ogm_packet = ogm_buff;
2498c2ecf20Sopenharmony_ci	ether_addr_copy(batadv_ogm_packet->orig,
2508c2ecf20Sopenharmony_ci			hard_iface->net_dev->dev_addr);
2518c2ecf20Sopenharmony_ci	ether_addr_copy(batadv_ogm_packet->prev_sender,
2528c2ecf20Sopenharmony_ci			hard_iface->net_dev->dev_addr);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ciunlock:
2558c2ecf20Sopenharmony_ci	mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex);
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic void
2598c2ecf20Sopenharmony_cibatadv_iv_ogm_primary_iface_set(struct batadv_hard_iface *hard_iface)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	struct batadv_ogm_packet *batadv_ogm_packet;
2628c2ecf20Sopenharmony_ci	void *ogm_buff;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	mutex_lock(&hard_iface->bat_iv.ogm_buff_mutex);
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	ogm_buff = hard_iface->bat_iv.ogm_buff;
2678c2ecf20Sopenharmony_ci	if (!ogm_buff)
2688c2ecf20Sopenharmony_ci		goto unlock;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	batadv_ogm_packet = ogm_buff;
2718c2ecf20Sopenharmony_ci	batadv_ogm_packet->ttl = BATADV_TTL;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ciunlock:
2748c2ecf20Sopenharmony_ci	mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex);
2758c2ecf20Sopenharmony_ci}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci/* when do we schedule our own ogm to be sent */
2788c2ecf20Sopenharmony_cistatic unsigned long
2798c2ecf20Sopenharmony_cibatadv_iv_ogm_emit_send_time(const struct batadv_priv *bat_priv)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	unsigned int msecs;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	msecs = atomic_read(&bat_priv->orig_interval) - BATADV_JITTER;
2848c2ecf20Sopenharmony_ci	msecs += prandom_u32_max(2 * BATADV_JITTER);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	return jiffies + msecs_to_jiffies(msecs);
2878c2ecf20Sopenharmony_ci}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci/* when do we schedule a ogm packet to be sent */
2908c2ecf20Sopenharmony_cistatic unsigned long batadv_iv_ogm_fwd_send_time(void)
2918c2ecf20Sopenharmony_ci{
2928c2ecf20Sopenharmony_ci	return jiffies + msecs_to_jiffies(prandom_u32_max(BATADV_JITTER / 2));
2938c2ecf20Sopenharmony_ci}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci/* apply hop penalty for a normal link */
2968c2ecf20Sopenharmony_cistatic u8 batadv_hop_penalty(u8 tq, const struct batadv_priv *bat_priv)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	int hop_penalty = atomic_read(&bat_priv->hop_penalty);
2998c2ecf20Sopenharmony_ci	int new_tq;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	new_tq = tq * (BATADV_TQ_MAX_VALUE - hop_penalty);
3028c2ecf20Sopenharmony_ci	new_tq /= BATADV_TQ_MAX_VALUE;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	return new_tq;
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci/**
3088c2ecf20Sopenharmony_ci * batadv_iv_ogm_aggr_packet() - checks if there is another OGM attached
3098c2ecf20Sopenharmony_ci * @buff_pos: current position in the skb
3108c2ecf20Sopenharmony_ci * @packet_len: total length of the skb
3118c2ecf20Sopenharmony_ci * @ogm_packet: potential OGM in buffer
3128c2ecf20Sopenharmony_ci *
3138c2ecf20Sopenharmony_ci * Return: true if there is enough space for another OGM, false otherwise.
3148c2ecf20Sopenharmony_ci */
3158c2ecf20Sopenharmony_cistatic bool
3168c2ecf20Sopenharmony_cibatadv_iv_ogm_aggr_packet(int buff_pos, int packet_len,
3178c2ecf20Sopenharmony_ci			  const struct batadv_ogm_packet *ogm_packet)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	int next_buff_pos = 0;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	/* check if there is enough space for the header */
3228c2ecf20Sopenharmony_ci	next_buff_pos += buff_pos + sizeof(*ogm_packet);
3238c2ecf20Sopenharmony_ci	if (next_buff_pos > packet_len)
3248c2ecf20Sopenharmony_ci		return false;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	/* check if there is enough space for the optional TVLV */
3278c2ecf20Sopenharmony_ci	next_buff_pos += ntohs(ogm_packet->tvlv_len);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	return (next_buff_pos <= packet_len) &&
3308c2ecf20Sopenharmony_ci	       (next_buff_pos <= BATADV_MAX_AGGREGATION_BYTES);
3318c2ecf20Sopenharmony_ci}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci/* send a batman ogm to a given interface */
3348c2ecf20Sopenharmony_cistatic void batadv_iv_ogm_send_to_if(struct batadv_forw_packet *forw_packet,
3358c2ecf20Sopenharmony_ci				     struct batadv_hard_iface *hard_iface)
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci	struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
3388c2ecf20Sopenharmony_ci	const char *fwd_str;
3398c2ecf20Sopenharmony_ci	u8 packet_num;
3408c2ecf20Sopenharmony_ci	s16 buff_pos;
3418c2ecf20Sopenharmony_ci	struct batadv_ogm_packet *batadv_ogm_packet;
3428c2ecf20Sopenharmony_ci	struct sk_buff *skb;
3438c2ecf20Sopenharmony_ci	u8 *packet_pos;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	if (hard_iface->if_status != BATADV_IF_ACTIVE)
3468c2ecf20Sopenharmony_ci		return;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	packet_num = 0;
3498c2ecf20Sopenharmony_ci	buff_pos = 0;
3508c2ecf20Sopenharmony_ci	packet_pos = forw_packet->skb->data;
3518c2ecf20Sopenharmony_ci	batadv_ogm_packet = (struct batadv_ogm_packet *)packet_pos;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	/* adjust all flags and log packets */
3548c2ecf20Sopenharmony_ci	while (batadv_iv_ogm_aggr_packet(buff_pos, forw_packet->packet_len,
3558c2ecf20Sopenharmony_ci					 batadv_ogm_packet)) {
3568c2ecf20Sopenharmony_ci		/* we might have aggregated direct link packets with an
3578c2ecf20Sopenharmony_ci		 * ordinary base packet
3588c2ecf20Sopenharmony_ci		 */
3598c2ecf20Sopenharmony_ci		if (forw_packet->direct_link_flags & BIT(packet_num) &&
3608c2ecf20Sopenharmony_ci		    forw_packet->if_incoming == hard_iface)
3618c2ecf20Sopenharmony_ci			batadv_ogm_packet->flags |= BATADV_DIRECTLINK;
3628c2ecf20Sopenharmony_ci		else
3638c2ecf20Sopenharmony_ci			batadv_ogm_packet->flags &= ~BATADV_DIRECTLINK;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci		if (packet_num > 0 || !forw_packet->own)
3668c2ecf20Sopenharmony_ci			fwd_str = "Forwarding";
3678c2ecf20Sopenharmony_ci		else
3688c2ecf20Sopenharmony_ci			fwd_str = "Sending own";
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci		batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
3718c2ecf20Sopenharmony_ci			   "%s %spacket (originator %pM, seqno %u, TQ %d, TTL %d, IDF %s) on interface %s [%pM]\n",
3728c2ecf20Sopenharmony_ci			   fwd_str, (packet_num > 0 ? "aggregated " : ""),
3738c2ecf20Sopenharmony_ci			   batadv_ogm_packet->orig,
3748c2ecf20Sopenharmony_ci			   ntohl(batadv_ogm_packet->seqno),
3758c2ecf20Sopenharmony_ci			   batadv_ogm_packet->tq, batadv_ogm_packet->ttl,
3768c2ecf20Sopenharmony_ci			   ((batadv_ogm_packet->flags & BATADV_DIRECTLINK) ?
3778c2ecf20Sopenharmony_ci			    "on" : "off"),
3788c2ecf20Sopenharmony_ci			   hard_iface->net_dev->name,
3798c2ecf20Sopenharmony_ci			   hard_iface->net_dev->dev_addr);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci		buff_pos += BATADV_OGM_HLEN;
3828c2ecf20Sopenharmony_ci		buff_pos += ntohs(batadv_ogm_packet->tvlv_len);
3838c2ecf20Sopenharmony_ci		packet_num++;
3848c2ecf20Sopenharmony_ci		packet_pos = forw_packet->skb->data + buff_pos;
3858c2ecf20Sopenharmony_ci		batadv_ogm_packet = (struct batadv_ogm_packet *)packet_pos;
3868c2ecf20Sopenharmony_ci	}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	/* create clone because function is called more than once */
3898c2ecf20Sopenharmony_ci	skb = skb_clone(forw_packet->skb, GFP_ATOMIC);
3908c2ecf20Sopenharmony_ci	if (skb) {
3918c2ecf20Sopenharmony_ci		batadv_inc_counter(bat_priv, BATADV_CNT_MGMT_TX);
3928c2ecf20Sopenharmony_ci		batadv_add_counter(bat_priv, BATADV_CNT_MGMT_TX_BYTES,
3938c2ecf20Sopenharmony_ci				   skb->len + ETH_HLEN);
3948c2ecf20Sopenharmony_ci		batadv_send_broadcast_skb(skb, hard_iface);
3958c2ecf20Sopenharmony_ci	}
3968c2ecf20Sopenharmony_ci}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci/* send a batman ogm packet */
3998c2ecf20Sopenharmony_cistatic void batadv_iv_ogm_emit(struct batadv_forw_packet *forw_packet)
4008c2ecf20Sopenharmony_ci{
4018c2ecf20Sopenharmony_ci	struct net_device *soft_iface;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	if (!forw_packet->if_incoming) {
4048c2ecf20Sopenharmony_ci		pr_err("Error - can't forward packet: incoming iface not specified\n");
4058c2ecf20Sopenharmony_ci		return;
4068c2ecf20Sopenharmony_ci	}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	soft_iface = forw_packet->if_incoming->soft_iface;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	if (WARN_ON(!forw_packet->if_outgoing))
4118c2ecf20Sopenharmony_ci		return;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	if (forw_packet->if_outgoing->soft_iface != soft_iface) {
4148c2ecf20Sopenharmony_ci		pr_warn("%s: soft interface switch for queued OGM\n", __func__);
4158c2ecf20Sopenharmony_ci		return;
4168c2ecf20Sopenharmony_ci	}
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	if (forw_packet->if_incoming->if_status != BATADV_IF_ACTIVE)
4198c2ecf20Sopenharmony_ci		return;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	/* only for one specific outgoing interface */
4228c2ecf20Sopenharmony_ci	batadv_iv_ogm_send_to_if(forw_packet, forw_packet->if_outgoing);
4238c2ecf20Sopenharmony_ci}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci/**
4268c2ecf20Sopenharmony_ci * batadv_iv_ogm_can_aggregate() - find out if an OGM can be aggregated on an
4278c2ecf20Sopenharmony_ci *  existing forward packet
4288c2ecf20Sopenharmony_ci * @new_bat_ogm_packet: OGM packet to be aggregated
4298c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
4308c2ecf20Sopenharmony_ci * @packet_len: (total) length of the OGM
4318c2ecf20Sopenharmony_ci * @send_time: timestamp (jiffies) when the packet is to be sent
4328c2ecf20Sopenharmony_ci * @directlink: true if this is a direct link packet
4338c2ecf20Sopenharmony_ci * @if_incoming: interface where the packet was received
4348c2ecf20Sopenharmony_ci * @if_outgoing: interface for which the retransmission should be considered
4358c2ecf20Sopenharmony_ci * @forw_packet: the forwarded packet which should be checked
4368c2ecf20Sopenharmony_ci *
4378c2ecf20Sopenharmony_ci * Return: true if new_packet can be aggregated with forw_packet
4388c2ecf20Sopenharmony_ci */
4398c2ecf20Sopenharmony_cistatic bool
4408c2ecf20Sopenharmony_cibatadv_iv_ogm_can_aggregate(const struct batadv_ogm_packet *new_bat_ogm_packet,
4418c2ecf20Sopenharmony_ci			    struct batadv_priv *bat_priv,
4428c2ecf20Sopenharmony_ci			    int packet_len, unsigned long send_time,
4438c2ecf20Sopenharmony_ci			    bool directlink,
4448c2ecf20Sopenharmony_ci			    const struct batadv_hard_iface *if_incoming,
4458c2ecf20Sopenharmony_ci			    const struct batadv_hard_iface *if_outgoing,
4468c2ecf20Sopenharmony_ci			    const struct batadv_forw_packet *forw_packet)
4478c2ecf20Sopenharmony_ci{
4488c2ecf20Sopenharmony_ci	struct batadv_ogm_packet *batadv_ogm_packet;
4498c2ecf20Sopenharmony_ci	int aggregated_bytes = forw_packet->packet_len + packet_len;
4508c2ecf20Sopenharmony_ci	struct batadv_hard_iface *primary_if = NULL;
4518c2ecf20Sopenharmony_ci	bool res = false;
4528c2ecf20Sopenharmony_ci	unsigned long aggregation_end_time;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	batadv_ogm_packet = (struct batadv_ogm_packet *)forw_packet->skb->data;
4558c2ecf20Sopenharmony_ci	aggregation_end_time = send_time;
4568c2ecf20Sopenharmony_ci	aggregation_end_time += msecs_to_jiffies(BATADV_MAX_AGGREGATION_MS);
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	/* we can aggregate the current packet to this aggregated packet
4598c2ecf20Sopenharmony_ci	 * if:
4608c2ecf20Sopenharmony_ci	 *
4618c2ecf20Sopenharmony_ci	 * - the send time is within our MAX_AGGREGATION_MS time
4628c2ecf20Sopenharmony_ci	 * - the resulting packet wont be bigger than
4638c2ecf20Sopenharmony_ci	 *   MAX_AGGREGATION_BYTES
4648c2ecf20Sopenharmony_ci	 * otherwise aggregation is not possible
4658c2ecf20Sopenharmony_ci	 */
4668c2ecf20Sopenharmony_ci	if (!time_before(send_time, forw_packet->send_time) ||
4678c2ecf20Sopenharmony_ci	    !time_after_eq(aggregation_end_time, forw_packet->send_time))
4688c2ecf20Sopenharmony_ci		return false;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	if (aggregated_bytes > BATADV_MAX_AGGREGATION_BYTES)
4718c2ecf20Sopenharmony_ci		return false;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	/* packet is not leaving on the same interface. */
4748c2ecf20Sopenharmony_ci	if (forw_packet->if_outgoing != if_outgoing)
4758c2ecf20Sopenharmony_ci		return false;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	/* check aggregation compatibility
4788c2ecf20Sopenharmony_ci	 * -> direct link packets are broadcasted on
4798c2ecf20Sopenharmony_ci	 *    their interface only
4808c2ecf20Sopenharmony_ci	 * -> aggregate packet if the current packet is
4818c2ecf20Sopenharmony_ci	 *    a "global" packet as well as the base
4828c2ecf20Sopenharmony_ci	 *    packet
4838c2ecf20Sopenharmony_ci	 */
4848c2ecf20Sopenharmony_ci	primary_if = batadv_primary_if_get_selected(bat_priv);
4858c2ecf20Sopenharmony_ci	if (!primary_if)
4868c2ecf20Sopenharmony_ci		return false;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	/* packets without direct link flag and high TTL
4898c2ecf20Sopenharmony_ci	 * are flooded through the net
4908c2ecf20Sopenharmony_ci	 */
4918c2ecf20Sopenharmony_ci	if (!directlink &&
4928c2ecf20Sopenharmony_ci	    !(batadv_ogm_packet->flags & BATADV_DIRECTLINK) &&
4938c2ecf20Sopenharmony_ci	    batadv_ogm_packet->ttl != 1 &&
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	    /* own packets originating non-primary
4968c2ecf20Sopenharmony_ci	     * interfaces leave only that interface
4978c2ecf20Sopenharmony_ci	     */
4988c2ecf20Sopenharmony_ci	    (!forw_packet->own ||
4998c2ecf20Sopenharmony_ci	     forw_packet->if_incoming == primary_if)) {
5008c2ecf20Sopenharmony_ci		res = true;
5018c2ecf20Sopenharmony_ci		goto out;
5028c2ecf20Sopenharmony_ci	}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	/* if the incoming packet is sent via this one
5058c2ecf20Sopenharmony_ci	 * interface only - we still can aggregate
5068c2ecf20Sopenharmony_ci	 */
5078c2ecf20Sopenharmony_ci	if (directlink &&
5088c2ecf20Sopenharmony_ci	    new_bat_ogm_packet->ttl == 1 &&
5098c2ecf20Sopenharmony_ci	    forw_packet->if_incoming == if_incoming &&
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	    /* packets from direct neighbors or
5128c2ecf20Sopenharmony_ci	     * own secondary interface packets
5138c2ecf20Sopenharmony_ci	     * (= secondary interface packets in general)
5148c2ecf20Sopenharmony_ci	     */
5158c2ecf20Sopenharmony_ci	    (batadv_ogm_packet->flags & BATADV_DIRECTLINK ||
5168c2ecf20Sopenharmony_ci	     (forw_packet->own &&
5178c2ecf20Sopenharmony_ci	      forw_packet->if_incoming != primary_if))) {
5188c2ecf20Sopenharmony_ci		res = true;
5198c2ecf20Sopenharmony_ci		goto out;
5208c2ecf20Sopenharmony_ci	}
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ciout:
5238c2ecf20Sopenharmony_ci	if (primary_if)
5248c2ecf20Sopenharmony_ci		batadv_hardif_put(primary_if);
5258c2ecf20Sopenharmony_ci	return res;
5268c2ecf20Sopenharmony_ci}
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci/**
5298c2ecf20Sopenharmony_ci * batadv_iv_ogm_aggregate_new() - create a new aggregated packet and add this
5308c2ecf20Sopenharmony_ci *  packet to it.
5318c2ecf20Sopenharmony_ci * @packet_buff: pointer to the OGM
5328c2ecf20Sopenharmony_ci * @packet_len: (total) length of the OGM
5338c2ecf20Sopenharmony_ci * @send_time: timestamp (jiffies) when the packet is to be sent
5348c2ecf20Sopenharmony_ci * @direct_link: whether this OGM has direct link status
5358c2ecf20Sopenharmony_ci * @if_incoming: interface where the packet was received
5368c2ecf20Sopenharmony_ci * @if_outgoing: interface for which the retransmission should be considered
5378c2ecf20Sopenharmony_ci * @own_packet: true if it is a self-generated ogm
5388c2ecf20Sopenharmony_ci */
5398c2ecf20Sopenharmony_cistatic void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
5408c2ecf20Sopenharmony_ci					int packet_len, unsigned long send_time,
5418c2ecf20Sopenharmony_ci					bool direct_link,
5428c2ecf20Sopenharmony_ci					struct batadv_hard_iface *if_incoming,
5438c2ecf20Sopenharmony_ci					struct batadv_hard_iface *if_outgoing,
5448c2ecf20Sopenharmony_ci					int own_packet)
5458c2ecf20Sopenharmony_ci{
5468c2ecf20Sopenharmony_ci	struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
5478c2ecf20Sopenharmony_ci	struct batadv_forw_packet *forw_packet_aggr;
5488c2ecf20Sopenharmony_ci	struct sk_buff *skb;
5498c2ecf20Sopenharmony_ci	unsigned char *skb_buff;
5508c2ecf20Sopenharmony_ci	unsigned int skb_size;
5518c2ecf20Sopenharmony_ci	atomic_t *queue_left = own_packet ? NULL : &bat_priv->batman_queue_left;
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	if (atomic_read(&bat_priv->aggregated_ogms) &&
5548c2ecf20Sopenharmony_ci	    packet_len < BATADV_MAX_AGGREGATION_BYTES)
5558c2ecf20Sopenharmony_ci		skb_size = BATADV_MAX_AGGREGATION_BYTES;
5568c2ecf20Sopenharmony_ci	else
5578c2ecf20Sopenharmony_ci		skb_size = packet_len;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	skb_size += ETH_HLEN;
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	skb = netdev_alloc_skb_ip_align(NULL, skb_size);
5628c2ecf20Sopenharmony_ci	if (!skb)
5638c2ecf20Sopenharmony_ci		return;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	forw_packet_aggr = batadv_forw_packet_alloc(if_incoming, if_outgoing,
5668c2ecf20Sopenharmony_ci						    queue_left, bat_priv, skb);
5678c2ecf20Sopenharmony_ci	if (!forw_packet_aggr) {
5688c2ecf20Sopenharmony_ci		kfree_skb(skb);
5698c2ecf20Sopenharmony_ci		return;
5708c2ecf20Sopenharmony_ci	}
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	forw_packet_aggr->skb->priority = TC_PRIO_CONTROL;
5738c2ecf20Sopenharmony_ci	skb_reserve(forw_packet_aggr->skb, ETH_HLEN);
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	skb_buff = skb_put(forw_packet_aggr->skb, packet_len);
5768c2ecf20Sopenharmony_ci	forw_packet_aggr->packet_len = packet_len;
5778c2ecf20Sopenharmony_ci	memcpy(skb_buff, packet_buff, packet_len);
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	forw_packet_aggr->own = own_packet;
5808c2ecf20Sopenharmony_ci	forw_packet_aggr->direct_link_flags = BATADV_NO_FLAGS;
5818c2ecf20Sopenharmony_ci	forw_packet_aggr->send_time = send_time;
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	/* save packet direct link flag status */
5848c2ecf20Sopenharmony_ci	if (direct_link)
5858c2ecf20Sopenharmony_ci		forw_packet_aggr->direct_link_flags |= 1;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&forw_packet_aggr->delayed_work,
5888c2ecf20Sopenharmony_ci			  batadv_iv_send_outstanding_bat_ogm_packet);
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	batadv_forw_packet_ogmv1_queue(bat_priv, forw_packet_aggr, send_time);
5918c2ecf20Sopenharmony_ci}
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci/* aggregate a new packet into the existing ogm packet */
5948c2ecf20Sopenharmony_cistatic void batadv_iv_ogm_aggregate(struct batadv_forw_packet *forw_packet_aggr,
5958c2ecf20Sopenharmony_ci				    const unsigned char *packet_buff,
5968c2ecf20Sopenharmony_ci				    int packet_len, bool direct_link)
5978c2ecf20Sopenharmony_ci{
5988c2ecf20Sopenharmony_ci	unsigned long new_direct_link_flag;
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	skb_put_data(forw_packet_aggr->skb, packet_buff, packet_len);
6018c2ecf20Sopenharmony_ci	forw_packet_aggr->packet_len += packet_len;
6028c2ecf20Sopenharmony_ci	forw_packet_aggr->num_packets++;
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	/* save packet direct link flag status */
6058c2ecf20Sopenharmony_ci	if (direct_link) {
6068c2ecf20Sopenharmony_ci		new_direct_link_flag = BIT(forw_packet_aggr->num_packets);
6078c2ecf20Sopenharmony_ci		forw_packet_aggr->direct_link_flags |= new_direct_link_flag;
6088c2ecf20Sopenharmony_ci	}
6098c2ecf20Sopenharmony_ci}
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci/**
6128c2ecf20Sopenharmony_ci * batadv_iv_ogm_queue_add() - queue up an OGM for transmission
6138c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
6148c2ecf20Sopenharmony_ci * @packet_buff: pointer to the OGM
6158c2ecf20Sopenharmony_ci * @packet_len: (total) length of the OGM
6168c2ecf20Sopenharmony_ci * @if_incoming: interface where the packet was received
6178c2ecf20Sopenharmony_ci * @if_outgoing: interface for which the retransmission should be considered
6188c2ecf20Sopenharmony_ci * @own_packet: true if it is a self-generated ogm
6198c2ecf20Sopenharmony_ci * @send_time: timestamp (jiffies) when the packet is to be sent
6208c2ecf20Sopenharmony_ci */
6218c2ecf20Sopenharmony_cistatic void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv,
6228c2ecf20Sopenharmony_ci				    unsigned char *packet_buff,
6238c2ecf20Sopenharmony_ci				    int packet_len,
6248c2ecf20Sopenharmony_ci				    struct batadv_hard_iface *if_incoming,
6258c2ecf20Sopenharmony_ci				    struct batadv_hard_iface *if_outgoing,
6268c2ecf20Sopenharmony_ci				    int own_packet, unsigned long send_time)
6278c2ecf20Sopenharmony_ci{
6288c2ecf20Sopenharmony_ci	/* _aggr -> pointer to the packet we want to aggregate with
6298c2ecf20Sopenharmony_ci	 * _pos -> pointer to the position in the queue
6308c2ecf20Sopenharmony_ci	 */
6318c2ecf20Sopenharmony_ci	struct batadv_forw_packet *forw_packet_aggr = NULL;
6328c2ecf20Sopenharmony_ci	struct batadv_forw_packet *forw_packet_pos = NULL;
6338c2ecf20Sopenharmony_ci	struct batadv_ogm_packet *batadv_ogm_packet;
6348c2ecf20Sopenharmony_ci	bool direct_link;
6358c2ecf20Sopenharmony_ci	unsigned long max_aggregation_jiffies;
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	batadv_ogm_packet = (struct batadv_ogm_packet *)packet_buff;
6388c2ecf20Sopenharmony_ci	direct_link = !!(batadv_ogm_packet->flags & BATADV_DIRECTLINK);
6398c2ecf20Sopenharmony_ci	max_aggregation_jiffies = msecs_to_jiffies(BATADV_MAX_AGGREGATION_MS);
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	/* find position for the packet in the forward queue */
6428c2ecf20Sopenharmony_ci	spin_lock_bh(&bat_priv->forw_bat_list_lock);
6438c2ecf20Sopenharmony_ci	/* own packets are not to be aggregated */
6448c2ecf20Sopenharmony_ci	if (atomic_read(&bat_priv->aggregated_ogms) && !own_packet) {
6458c2ecf20Sopenharmony_ci		hlist_for_each_entry(forw_packet_pos,
6468c2ecf20Sopenharmony_ci				     &bat_priv->forw_bat_list, list) {
6478c2ecf20Sopenharmony_ci			if (batadv_iv_ogm_can_aggregate(batadv_ogm_packet,
6488c2ecf20Sopenharmony_ci							bat_priv, packet_len,
6498c2ecf20Sopenharmony_ci							send_time, direct_link,
6508c2ecf20Sopenharmony_ci							if_incoming,
6518c2ecf20Sopenharmony_ci							if_outgoing,
6528c2ecf20Sopenharmony_ci							forw_packet_pos)) {
6538c2ecf20Sopenharmony_ci				forw_packet_aggr = forw_packet_pos;
6548c2ecf20Sopenharmony_ci				break;
6558c2ecf20Sopenharmony_ci			}
6568c2ecf20Sopenharmony_ci		}
6578c2ecf20Sopenharmony_ci	}
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	/* nothing to aggregate with - either aggregation disabled or no
6608c2ecf20Sopenharmony_ci	 * suitable aggregation packet found
6618c2ecf20Sopenharmony_ci	 */
6628c2ecf20Sopenharmony_ci	if (!forw_packet_aggr) {
6638c2ecf20Sopenharmony_ci		/* the following section can run without the lock */
6648c2ecf20Sopenharmony_ci		spin_unlock_bh(&bat_priv->forw_bat_list_lock);
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci		/* if we could not aggregate this packet with one of the others
6678c2ecf20Sopenharmony_ci		 * we hold it back for a while, so that it might be aggregated
6688c2ecf20Sopenharmony_ci		 * later on
6698c2ecf20Sopenharmony_ci		 */
6708c2ecf20Sopenharmony_ci		if (!own_packet && atomic_read(&bat_priv->aggregated_ogms))
6718c2ecf20Sopenharmony_ci			send_time += max_aggregation_jiffies;
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci		batadv_iv_ogm_aggregate_new(packet_buff, packet_len,
6748c2ecf20Sopenharmony_ci					    send_time, direct_link,
6758c2ecf20Sopenharmony_ci					    if_incoming, if_outgoing,
6768c2ecf20Sopenharmony_ci					    own_packet);
6778c2ecf20Sopenharmony_ci	} else {
6788c2ecf20Sopenharmony_ci		batadv_iv_ogm_aggregate(forw_packet_aggr, packet_buff,
6798c2ecf20Sopenharmony_ci					packet_len, direct_link);
6808c2ecf20Sopenharmony_ci		spin_unlock_bh(&bat_priv->forw_bat_list_lock);
6818c2ecf20Sopenharmony_ci	}
6828c2ecf20Sopenharmony_ci}
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_cistatic void batadv_iv_ogm_forward(struct batadv_orig_node *orig_node,
6858c2ecf20Sopenharmony_ci				  const struct ethhdr *ethhdr,
6868c2ecf20Sopenharmony_ci				  struct batadv_ogm_packet *batadv_ogm_packet,
6878c2ecf20Sopenharmony_ci				  bool is_single_hop_neigh,
6888c2ecf20Sopenharmony_ci				  bool is_from_best_next_hop,
6898c2ecf20Sopenharmony_ci				  struct batadv_hard_iface *if_incoming,
6908c2ecf20Sopenharmony_ci				  struct batadv_hard_iface *if_outgoing)
6918c2ecf20Sopenharmony_ci{
6928c2ecf20Sopenharmony_ci	struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
6938c2ecf20Sopenharmony_ci	u16 tvlv_len;
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	if (batadv_ogm_packet->ttl <= 1) {
6968c2ecf20Sopenharmony_ci		batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "ttl exceeded\n");
6978c2ecf20Sopenharmony_ci		return;
6988c2ecf20Sopenharmony_ci	}
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	if (!is_from_best_next_hop) {
7018c2ecf20Sopenharmony_ci		/* Mark the forwarded packet when it is not coming from our
7028c2ecf20Sopenharmony_ci		 * best next hop. We still need to forward the packet for our
7038c2ecf20Sopenharmony_ci		 * neighbor link quality detection to work in case the packet
7048c2ecf20Sopenharmony_ci		 * originated from a single hop neighbor. Otherwise we can
7058c2ecf20Sopenharmony_ci		 * simply drop the ogm.
7068c2ecf20Sopenharmony_ci		 */
7078c2ecf20Sopenharmony_ci		if (is_single_hop_neigh)
7088c2ecf20Sopenharmony_ci			batadv_ogm_packet->flags |= BATADV_NOT_BEST_NEXT_HOP;
7098c2ecf20Sopenharmony_ci		else
7108c2ecf20Sopenharmony_ci			return;
7118c2ecf20Sopenharmony_ci	}
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	tvlv_len = ntohs(batadv_ogm_packet->tvlv_len);
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	batadv_ogm_packet->ttl--;
7168c2ecf20Sopenharmony_ci	ether_addr_copy(batadv_ogm_packet->prev_sender, ethhdr->h_source);
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	/* apply hop penalty */
7198c2ecf20Sopenharmony_ci	batadv_ogm_packet->tq = batadv_hop_penalty(batadv_ogm_packet->tq,
7208c2ecf20Sopenharmony_ci						   bat_priv);
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
7238c2ecf20Sopenharmony_ci		   "Forwarding packet: tq: %i, ttl: %i\n",
7248c2ecf20Sopenharmony_ci		   batadv_ogm_packet->tq, batadv_ogm_packet->ttl);
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	if (is_single_hop_neigh)
7278c2ecf20Sopenharmony_ci		batadv_ogm_packet->flags |= BATADV_DIRECTLINK;
7288c2ecf20Sopenharmony_ci	else
7298c2ecf20Sopenharmony_ci		batadv_ogm_packet->flags &= ~BATADV_DIRECTLINK;
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	batadv_iv_ogm_queue_add(bat_priv, (unsigned char *)batadv_ogm_packet,
7328c2ecf20Sopenharmony_ci				BATADV_OGM_HLEN + tvlv_len,
7338c2ecf20Sopenharmony_ci				if_incoming, if_outgoing, 0,
7348c2ecf20Sopenharmony_ci				batadv_iv_ogm_fwd_send_time());
7358c2ecf20Sopenharmony_ci}
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci/**
7388c2ecf20Sopenharmony_ci * batadv_iv_ogm_slide_own_bcast_window() - bitshift own OGM broadcast windows
7398c2ecf20Sopenharmony_ci *  for the given interface
7408c2ecf20Sopenharmony_ci * @hard_iface: the interface for which the windows have to be shifted
7418c2ecf20Sopenharmony_ci */
7428c2ecf20Sopenharmony_cistatic void
7438c2ecf20Sopenharmony_cibatadv_iv_ogm_slide_own_bcast_window(struct batadv_hard_iface *hard_iface)
7448c2ecf20Sopenharmony_ci{
7458c2ecf20Sopenharmony_ci	struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
7468c2ecf20Sopenharmony_ci	struct batadv_hashtable *hash = bat_priv->orig_hash;
7478c2ecf20Sopenharmony_ci	struct hlist_head *head;
7488c2ecf20Sopenharmony_ci	struct batadv_orig_node *orig_node;
7498c2ecf20Sopenharmony_ci	struct batadv_orig_ifinfo *orig_ifinfo;
7508c2ecf20Sopenharmony_ci	unsigned long *word;
7518c2ecf20Sopenharmony_ci	u32 i;
7528c2ecf20Sopenharmony_ci	u8 *w;
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	for (i = 0; i < hash->size; i++) {
7558c2ecf20Sopenharmony_ci		head = &hash->table[i];
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci		rcu_read_lock();
7588c2ecf20Sopenharmony_ci		hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
7598c2ecf20Sopenharmony_ci			hlist_for_each_entry_rcu(orig_ifinfo,
7608c2ecf20Sopenharmony_ci						 &orig_node->ifinfo_list,
7618c2ecf20Sopenharmony_ci						 list) {
7628c2ecf20Sopenharmony_ci				if (orig_ifinfo->if_outgoing != hard_iface)
7638c2ecf20Sopenharmony_ci					continue;
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci				spin_lock_bh(&orig_node->bat_iv.ogm_cnt_lock);
7668c2ecf20Sopenharmony_ci				word = orig_ifinfo->bat_iv.bcast_own;
7678c2ecf20Sopenharmony_ci				batadv_bit_get_packet(bat_priv, word, 1, 0);
7688c2ecf20Sopenharmony_ci				w = &orig_ifinfo->bat_iv.bcast_own_sum;
7698c2ecf20Sopenharmony_ci				*w = bitmap_weight(word,
7708c2ecf20Sopenharmony_ci						   BATADV_TQ_LOCAL_WINDOW_SIZE);
7718c2ecf20Sopenharmony_ci				spin_unlock_bh(&orig_node->bat_iv.ogm_cnt_lock);
7728c2ecf20Sopenharmony_ci			}
7738c2ecf20Sopenharmony_ci		}
7748c2ecf20Sopenharmony_ci		rcu_read_unlock();
7758c2ecf20Sopenharmony_ci	}
7768c2ecf20Sopenharmony_ci}
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci/**
7798c2ecf20Sopenharmony_ci * batadv_iv_ogm_schedule_buff() - schedule submission of hardif ogm buffer
7808c2ecf20Sopenharmony_ci * @hard_iface: interface whose ogm buffer should be transmitted
7818c2ecf20Sopenharmony_ci */
7828c2ecf20Sopenharmony_cistatic void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
7838c2ecf20Sopenharmony_ci{
7848c2ecf20Sopenharmony_ci	struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
7858c2ecf20Sopenharmony_ci	unsigned char **ogm_buff = &hard_iface->bat_iv.ogm_buff;
7868c2ecf20Sopenharmony_ci	struct batadv_ogm_packet *batadv_ogm_packet;
7878c2ecf20Sopenharmony_ci	struct batadv_hard_iface *primary_if, *tmp_hard_iface;
7888c2ecf20Sopenharmony_ci	int *ogm_buff_len = &hard_iface->bat_iv.ogm_buff_len;
7898c2ecf20Sopenharmony_ci	u32 seqno;
7908c2ecf20Sopenharmony_ci	u16 tvlv_len = 0;
7918c2ecf20Sopenharmony_ci	unsigned long send_time;
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	lockdep_assert_held(&hard_iface->bat_iv.ogm_buff_mutex);
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	/* interface already disabled by batadv_iv_ogm_iface_disable */
7968c2ecf20Sopenharmony_ci	if (!*ogm_buff)
7978c2ecf20Sopenharmony_ci		return;
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	/* the interface gets activated here to avoid race conditions between
8008c2ecf20Sopenharmony_ci	 * the moment of activating the interface in
8018c2ecf20Sopenharmony_ci	 * hardif_activate_interface() where the originator mac is set and
8028c2ecf20Sopenharmony_ci	 * outdated packets (especially uninitialized mac addresses) in the
8038c2ecf20Sopenharmony_ci	 * packet queue
8048c2ecf20Sopenharmony_ci	 */
8058c2ecf20Sopenharmony_ci	if (hard_iface->if_status == BATADV_IF_TO_BE_ACTIVATED)
8068c2ecf20Sopenharmony_ci		hard_iface->if_status = BATADV_IF_ACTIVE;
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	primary_if = batadv_primary_if_get_selected(bat_priv);
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	if (hard_iface == primary_if) {
8118c2ecf20Sopenharmony_ci		/* tt changes have to be committed before the tvlv data is
8128c2ecf20Sopenharmony_ci		 * appended as it may alter the tt tvlv container
8138c2ecf20Sopenharmony_ci		 */
8148c2ecf20Sopenharmony_ci		batadv_tt_local_commit_changes(bat_priv);
8158c2ecf20Sopenharmony_ci		tvlv_len = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff,
8168c2ecf20Sopenharmony_ci							    ogm_buff_len,
8178c2ecf20Sopenharmony_ci							    BATADV_OGM_HLEN);
8188c2ecf20Sopenharmony_ci	}
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	batadv_ogm_packet = (struct batadv_ogm_packet *)(*ogm_buff);
8218c2ecf20Sopenharmony_ci	batadv_ogm_packet->tvlv_len = htons(tvlv_len);
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	/* change sequence number to network order */
8248c2ecf20Sopenharmony_ci	seqno = (u32)atomic_read(&hard_iface->bat_iv.ogm_seqno);
8258c2ecf20Sopenharmony_ci	batadv_ogm_packet->seqno = htonl(seqno);
8268c2ecf20Sopenharmony_ci	atomic_inc(&hard_iface->bat_iv.ogm_seqno);
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	batadv_iv_ogm_slide_own_bcast_window(hard_iface);
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	send_time = batadv_iv_ogm_emit_send_time(bat_priv);
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	if (hard_iface != primary_if) {
8338c2ecf20Sopenharmony_ci		/* OGMs from secondary interfaces are only scheduled on their
8348c2ecf20Sopenharmony_ci		 * respective interfaces.
8358c2ecf20Sopenharmony_ci		 */
8368c2ecf20Sopenharmony_ci		batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, *ogm_buff_len,
8378c2ecf20Sopenharmony_ci					hard_iface, hard_iface, 1, send_time);
8388c2ecf20Sopenharmony_ci		goto out;
8398c2ecf20Sopenharmony_ci	}
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	/* OGMs from primary interfaces are scheduled on all
8428c2ecf20Sopenharmony_ci	 * interfaces.
8438c2ecf20Sopenharmony_ci	 */
8448c2ecf20Sopenharmony_ci	rcu_read_lock();
8458c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(tmp_hard_iface, &batadv_hardif_list, list) {
8468c2ecf20Sopenharmony_ci		if (tmp_hard_iface->soft_iface != hard_iface->soft_iface)
8478c2ecf20Sopenharmony_ci			continue;
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci		if (!kref_get_unless_zero(&tmp_hard_iface->refcount))
8508c2ecf20Sopenharmony_ci			continue;
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci		batadv_iv_ogm_queue_add(bat_priv, *ogm_buff,
8538c2ecf20Sopenharmony_ci					*ogm_buff_len, hard_iface,
8548c2ecf20Sopenharmony_ci					tmp_hard_iface, 1, send_time);
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci		batadv_hardif_put(tmp_hard_iface);
8578c2ecf20Sopenharmony_ci	}
8588c2ecf20Sopenharmony_ci	rcu_read_unlock();
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ciout:
8618c2ecf20Sopenharmony_ci	if (primary_if)
8628c2ecf20Sopenharmony_ci		batadv_hardif_put(primary_if);
8638c2ecf20Sopenharmony_ci}
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_cistatic void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface)
8668c2ecf20Sopenharmony_ci{
8678c2ecf20Sopenharmony_ci	if (hard_iface->if_status == BATADV_IF_NOT_IN_USE ||
8688c2ecf20Sopenharmony_ci	    hard_iface->if_status == BATADV_IF_TO_BE_REMOVED)
8698c2ecf20Sopenharmony_ci		return;
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	mutex_lock(&hard_iface->bat_iv.ogm_buff_mutex);
8728c2ecf20Sopenharmony_ci	batadv_iv_ogm_schedule_buff(hard_iface);
8738c2ecf20Sopenharmony_ci	mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex);
8748c2ecf20Sopenharmony_ci}
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci/**
8778c2ecf20Sopenharmony_ci * batadv_iv_orig_ifinfo_sum() - Get bcast_own sum for originator over interface
8788c2ecf20Sopenharmony_ci * @orig_node: originator which reproadcasted the OGMs directly
8798c2ecf20Sopenharmony_ci * @if_outgoing: interface which transmitted the original OGM and received the
8808c2ecf20Sopenharmony_ci *  direct rebroadcast
8818c2ecf20Sopenharmony_ci *
8828c2ecf20Sopenharmony_ci * Return: Number of replied (rebroadcasted) OGMs which were transmitted by
8838c2ecf20Sopenharmony_ci *  an originator and directly (without intermediate hop) received by a specific
8848c2ecf20Sopenharmony_ci *  interface
8858c2ecf20Sopenharmony_ci */
8868c2ecf20Sopenharmony_cistatic u8 batadv_iv_orig_ifinfo_sum(struct batadv_orig_node *orig_node,
8878c2ecf20Sopenharmony_ci				    struct batadv_hard_iface *if_outgoing)
8888c2ecf20Sopenharmony_ci{
8898c2ecf20Sopenharmony_ci	struct batadv_orig_ifinfo *orig_ifinfo;
8908c2ecf20Sopenharmony_ci	u8 sum;
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	orig_ifinfo = batadv_orig_ifinfo_get(orig_node, if_outgoing);
8938c2ecf20Sopenharmony_ci	if (!orig_ifinfo)
8948c2ecf20Sopenharmony_ci		return 0;
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	spin_lock_bh(&orig_node->bat_iv.ogm_cnt_lock);
8978c2ecf20Sopenharmony_ci	sum = orig_ifinfo->bat_iv.bcast_own_sum;
8988c2ecf20Sopenharmony_ci	spin_unlock_bh(&orig_node->bat_iv.ogm_cnt_lock);
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci	batadv_orig_ifinfo_put(orig_ifinfo);
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci	return sum;
9038c2ecf20Sopenharmony_ci}
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci/**
9068c2ecf20Sopenharmony_ci * batadv_iv_ogm_orig_update() - use OGM to update corresponding data in an
9078c2ecf20Sopenharmony_ci *  originator
9088c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
9098c2ecf20Sopenharmony_ci * @orig_node: the orig node who originally emitted the ogm packet
9108c2ecf20Sopenharmony_ci * @orig_ifinfo: ifinfo for the outgoing interface of the orig_node
9118c2ecf20Sopenharmony_ci * @ethhdr: Ethernet header of the OGM
9128c2ecf20Sopenharmony_ci * @batadv_ogm_packet: the ogm packet
9138c2ecf20Sopenharmony_ci * @if_incoming: interface where the packet was received
9148c2ecf20Sopenharmony_ci * @if_outgoing: interface for which the retransmission should be considered
9158c2ecf20Sopenharmony_ci * @dup_status: the duplicate status of this ogm packet.
9168c2ecf20Sopenharmony_ci */
9178c2ecf20Sopenharmony_cistatic void
9188c2ecf20Sopenharmony_cibatadv_iv_ogm_orig_update(struct batadv_priv *bat_priv,
9198c2ecf20Sopenharmony_ci			  struct batadv_orig_node *orig_node,
9208c2ecf20Sopenharmony_ci			  struct batadv_orig_ifinfo *orig_ifinfo,
9218c2ecf20Sopenharmony_ci			  const struct ethhdr *ethhdr,
9228c2ecf20Sopenharmony_ci			  const struct batadv_ogm_packet *batadv_ogm_packet,
9238c2ecf20Sopenharmony_ci			  struct batadv_hard_iface *if_incoming,
9248c2ecf20Sopenharmony_ci			  struct batadv_hard_iface *if_outgoing,
9258c2ecf20Sopenharmony_ci			  enum batadv_dup_status dup_status)
9268c2ecf20Sopenharmony_ci{
9278c2ecf20Sopenharmony_ci	struct batadv_neigh_ifinfo *neigh_ifinfo = NULL;
9288c2ecf20Sopenharmony_ci	struct batadv_neigh_ifinfo *router_ifinfo = NULL;
9298c2ecf20Sopenharmony_ci	struct batadv_neigh_node *neigh_node = NULL;
9308c2ecf20Sopenharmony_ci	struct batadv_neigh_node *tmp_neigh_node = NULL;
9318c2ecf20Sopenharmony_ci	struct batadv_neigh_node *router = NULL;
9328c2ecf20Sopenharmony_ci	u8 sum_orig, sum_neigh;
9338c2ecf20Sopenharmony_ci	u8 *neigh_addr;
9348c2ecf20Sopenharmony_ci	u8 tq_avg;
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci	batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
9378c2ecf20Sopenharmony_ci		   "%s(): Searching and updating originator entry of received packet\n",
9388c2ecf20Sopenharmony_ci		   __func__);
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	rcu_read_lock();
9418c2ecf20Sopenharmony_ci	hlist_for_each_entry_rcu(tmp_neigh_node,
9428c2ecf20Sopenharmony_ci				 &orig_node->neigh_list, list) {
9438c2ecf20Sopenharmony_ci		neigh_addr = tmp_neigh_node->addr;
9448c2ecf20Sopenharmony_ci		if (batadv_compare_eth(neigh_addr, ethhdr->h_source) &&
9458c2ecf20Sopenharmony_ci		    tmp_neigh_node->if_incoming == if_incoming &&
9468c2ecf20Sopenharmony_ci		    kref_get_unless_zero(&tmp_neigh_node->refcount)) {
9478c2ecf20Sopenharmony_ci			if (WARN(neigh_node, "too many matching neigh_nodes"))
9488c2ecf20Sopenharmony_ci				batadv_neigh_node_put(neigh_node);
9498c2ecf20Sopenharmony_ci			neigh_node = tmp_neigh_node;
9508c2ecf20Sopenharmony_ci			continue;
9518c2ecf20Sopenharmony_ci		}
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci		if (dup_status != BATADV_NO_DUP)
9548c2ecf20Sopenharmony_ci			continue;
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci		/* only update the entry for this outgoing interface */
9578c2ecf20Sopenharmony_ci		neigh_ifinfo = batadv_neigh_ifinfo_get(tmp_neigh_node,
9588c2ecf20Sopenharmony_ci						       if_outgoing);
9598c2ecf20Sopenharmony_ci		if (!neigh_ifinfo)
9608c2ecf20Sopenharmony_ci			continue;
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci		spin_lock_bh(&tmp_neigh_node->ifinfo_lock);
9638c2ecf20Sopenharmony_ci		batadv_ring_buffer_set(neigh_ifinfo->bat_iv.tq_recv,
9648c2ecf20Sopenharmony_ci				       &neigh_ifinfo->bat_iv.tq_index, 0);
9658c2ecf20Sopenharmony_ci		tq_avg = batadv_ring_buffer_avg(neigh_ifinfo->bat_iv.tq_recv);
9668c2ecf20Sopenharmony_ci		neigh_ifinfo->bat_iv.tq_avg = tq_avg;
9678c2ecf20Sopenharmony_ci		spin_unlock_bh(&tmp_neigh_node->ifinfo_lock);
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci		batadv_neigh_ifinfo_put(neigh_ifinfo);
9708c2ecf20Sopenharmony_ci		neigh_ifinfo = NULL;
9718c2ecf20Sopenharmony_ci	}
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	if (!neigh_node) {
9748c2ecf20Sopenharmony_ci		struct batadv_orig_node *orig_tmp;
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci		orig_tmp = batadv_iv_ogm_orig_get(bat_priv, ethhdr->h_source);
9778c2ecf20Sopenharmony_ci		if (!orig_tmp)
9788c2ecf20Sopenharmony_ci			goto unlock;
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci		neigh_node = batadv_iv_ogm_neigh_new(if_incoming,
9818c2ecf20Sopenharmony_ci						     ethhdr->h_source,
9828c2ecf20Sopenharmony_ci						     orig_node, orig_tmp);
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_ci		batadv_orig_node_put(orig_tmp);
9858c2ecf20Sopenharmony_ci		if (!neigh_node)
9868c2ecf20Sopenharmony_ci			goto unlock;
9878c2ecf20Sopenharmony_ci	} else {
9888c2ecf20Sopenharmony_ci		batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
9898c2ecf20Sopenharmony_ci			   "Updating existing last-hop neighbor of originator\n");
9908c2ecf20Sopenharmony_ci	}
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_ci	rcu_read_unlock();
9938c2ecf20Sopenharmony_ci	neigh_ifinfo = batadv_neigh_ifinfo_new(neigh_node, if_outgoing);
9948c2ecf20Sopenharmony_ci	if (!neigh_ifinfo)
9958c2ecf20Sopenharmony_ci		goto out;
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	neigh_node->last_seen = jiffies;
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	spin_lock_bh(&neigh_node->ifinfo_lock);
10008c2ecf20Sopenharmony_ci	batadv_ring_buffer_set(neigh_ifinfo->bat_iv.tq_recv,
10018c2ecf20Sopenharmony_ci			       &neigh_ifinfo->bat_iv.tq_index,
10028c2ecf20Sopenharmony_ci			       batadv_ogm_packet->tq);
10038c2ecf20Sopenharmony_ci	tq_avg = batadv_ring_buffer_avg(neigh_ifinfo->bat_iv.tq_recv);
10048c2ecf20Sopenharmony_ci	neigh_ifinfo->bat_iv.tq_avg = tq_avg;
10058c2ecf20Sopenharmony_ci	spin_unlock_bh(&neigh_node->ifinfo_lock);
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	if (dup_status == BATADV_NO_DUP) {
10088c2ecf20Sopenharmony_ci		orig_ifinfo->last_ttl = batadv_ogm_packet->ttl;
10098c2ecf20Sopenharmony_ci		neigh_ifinfo->last_ttl = batadv_ogm_packet->ttl;
10108c2ecf20Sopenharmony_ci	}
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci	/* if this neighbor already is our next hop there is nothing
10138c2ecf20Sopenharmony_ci	 * to change
10148c2ecf20Sopenharmony_ci	 */
10158c2ecf20Sopenharmony_ci	router = batadv_orig_router_get(orig_node, if_outgoing);
10168c2ecf20Sopenharmony_ci	if (router == neigh_node)
10178c2ecf20Sopenharmony_ci		goto out;
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci	if (router) {
10208c2ecf20Sopenharmony_ci		router_ifinfo = batadv_neigh_ifinfo_get(router, if_outgoing);
10218c2ecf20Sopenharmony_ci		if (!router_ifinfo)
10228c2ecf20Sopenharmony_ci			goto out;
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci		/* if this neighbor does not offer a better TQ we won't
10258c2ecf20Sopenharmony_ci		 * consider it
10268c2ecf20Sopenharmony_ci		 */
10278c2ecf20Sopenharmony_ci		if (router_ifinfo->bat_iv.tq_avg > neigh_ifinfo->bat_iv.tq_avg)
10288c2ecf20Sopenharmony_ci			goto out;
10298c2ecf20Sopenharmony_ci	}
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	/* if the TQ is the same and the link not more symmetric we
10328c2ecf20Sopenharmony_ci	 * won't consider it either
10338c2ecf20Sopenharmony_ci	 */
10348c2ecf20Sopenharmony_ci	if (router_ifinfo &&
10358c2ecf20Sopenharmony_ci	    neigh_ifinfo->bat_iv.tq_avg == router_ifinfo->bat_iv.tq_avg) {
10368c2ecf20Sopenharmony_ci		sum_orig = batadv_iv_orig_ifinfo_sum(router->orig_node,
10378c2ecf20Sopenharmony_ci						     router->if_incoming);
10388c2ecf20Sopenharmony_ci		sum_neigh = batadv_iv_orig_ifinfo_sum(neigh_node->orig_node,
10398c2ecf20Sopenharmony_ci						      neigh_node->if_incoming);
10408c2ecf20Sopenharmony_ci		if (sum_orig >= sum_neigh)
10418c2ecf20Sopenharmony_ci			goto out;
10428c2ecf20Sopenharmony_ci	}
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_ci	batadv_update_route(bat_priv, orig_node, if_outgoing, neigh_node);
10458c2ecf20Sopenharmony_ci	goto out;
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ciunlock:
10488c2ecf20Sopenharmony_ci	rcu_read_unlock();
10498c2ecf20Sopenharmony_ciout:
10508c2ecf20Sopenharmony_ci	if (neigh_node)
10518c2ecf20Sopenharmony_ci		batadv_neigh_node_put(neigh_node);
10528c2ecf20Sopenharmony_ci	if (router)
10538c2ecf20Sopenharmony_ci		batadv_neigh_node_put(router);
10548c2ecf20Sopenharmony_ci	if (neigh_ifinfo)
10558c2ecf20Sopenharmony_ci		batadv_neigh_ifinfo_put(neigh_ifinfo);
10568c2ecf20Sopenharmony_ci	if (router_ifinfo)
10578c2ecf20Sopenharmony_ci		batadv_neigh_ifinfo_put(router_ifinfo);
10588c2ecf20Sopenharmony_ci}
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_ci/**
10618c2ecf20Sopenharmony_ci * batadv_iv_ogm_calc_tq() - calculate tq for current received ogm packet
10628c2ecf20Sopenharmony_ci * @orig_node: the orig node who originally emitted the ogm packet
10638c2ecf20Sopenharmony_ci * @orig_neigh_node: the orig node struct of the neighbor who sent the packet
10648c2ecf20Sopenharmony_ci * @batadv_ogm_packet: the ogm packet
10658c2ecf20Sopenharmony_ci * @if_incoming: interface where the packet was received
10668c2ecf20Sopenharmony_ci * @if_outgoing: interface for which the retransmission should be considered
10678c2ecf20Sopenharmony_ci *
10688c2ecf20Sopenharmony_ci * Return: true if the link can be considered bidirectional, false otherwise
10698c2ecf20Sopenharmony_ci */
10708c2ecf20Sopenharmony_cistatic bool batadv_iv_ogm_calc_tq(struct batadv_orig_node *orig_node,
10718c2ecf20Sopenharmony_ci				  struct batadv_orig_node *orig_neigh_node,
10728c2ecf20Sopenharmony_ci				  struct batadv_ogm_packet *batadv_ogm_packet,
10738c2ecf20Sopenharmony_ci				  struct batadv_hard_iface *if_incoming,
10748c2ecf20Sopenharmony_ci				  struct batadv_hard_iface *if_outgoing)
10758c2ecf20Sopenharmony_ci{
10768c2ecf20Sopenharmony_ci	struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
10778c2ecf20Sopenharmony_ci	struct batadv_neigh_node *neigh_node = NULL, *tmp_neigh_node;
10788c2ecf20Sopenharmony_ci	struct batadv_neigh_ifinfo *neigh_ifinfo;
10798c2ecf20Sopenharmony_ci	u8 total_count;
10808c2ecf20Sopenharmony_ci	u8 orig_eq_count, neigh_rq_count, neigh_rq_inv, tq_own;
10818c2ecf20Sopenharmony_ci	unsigned int tq_iface_hop_penalty = BATADV_TQ_MAX_VALUE;
10828c2ecf20Sopenharmony_ci	unsigned int neigh_rq_inv_cube, neigh_rq_max_cube;
10838c2ecf20Sopenharmony_ci	unsigned int tq_asym_penalty, inv_asym_penalty;
10848c2ecf20Sopenharmony_ci	unsigned int combined_tq;
10858c2ecf20Sopenharmony_ci	bool ret = false;
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci	/* find corresponding one hop neighbor */
10888c2ecf20Sopenharmony_ci	rcu_read_lock();
10898c2ecf20Sopenharmony_ci	hlist_for_each_entry_rcu(tmp_neigh_node,
10908c2ecf20Sopenharmony_ci				 &orig_neigh_node->neigh_list, list) {
10918c2ecf20Sopenharmony_ci		if (!batadv_compare_eth(tmp_neigh_node->addr,
10928c2ecf20Sopenharmony_ci					orig_neigh_node->orig))
10938c2ecf20Sopenharmony_ci			continue;
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci		if (tmp_neigh_node->if_incoming != if_incoming)
10968c2ecf20Sopenharmony_ci			continue;
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_ci		if (!kref_get_unless_zero(&tmp_neigh_node->refcount))
10998c2ecf20Sopenharmony_ci			continue;
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci		neigh_node = tmp_neigh_node;
11028c2ecf20Sopenharmony_ci		break;
11038c2ecf20Sopenharmony_ci	}
11048c2ecf20Sopenharmony_ci	rcu_read_unlock();
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci	if (!neigh_node)
11078c2ecf20Sopenharmony_ci		neigh_node = batadv_iv_ogm_neigh_new(if_incoming,
11088c2ecf20Sopenharmony_ci						     orig_neigh_node->orig,
11098c2ecf20Sopenharmony_ci						     orig_neigh_node,
11108c2ecf20Sopenharmony_ci						     orig_neigh_node);
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_ci	if (!neigh_node)
11138c2ecf20Sopenharmony_ci		goto out;
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ci	/* if orig_node is direct neighbor update neigh_node last_seen */
11168c2ecf20Sopenharmony_ci	if (orig_node == orig_neigh_node)
11178c2ecf20Sopenharmony_ci		neigh_node->last_seen = jiffies;
11188c2ecf20Sopenharmony_ci
11198c2ecf20Sopenharmony_ci	orig_node->last_seen = jiffies;
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci	/* find packet count of corresponding one hop neighbor */
11228c2ecf20Sopenharmony_ci	orig_eq_count = batadv_iv_orig_ifinfo_sum(orig_neigh_node, if_incoming);
11238c2ecf20Sopenharmony_ci	neigh_ifinfo = batadv_neigh_ifinfo_new(neigh_node, if_outgoing);
11248c2ecf20Sopenharmony_ci	if (neigh_ifinfo) {
11258c2ecf20Sopenharmony_ci		neigh_rq_count = neigh_ifinfo->bat_iv.real_packet_count;
11268c2ecf20Sopenharmony_ci		batadv_neigh_ifinfo_put(neigh_ifinfo);
11278c2ecf20Sopenharmony_ci	} else {
11288c2ecf20Sopenharmony_ci		neigh_rq_count = 0;
11298c2ecf20Sopenharmony_ci	}
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_ci	/* pay attention to not get a value bigger than 100 % */
11328c2ecf20Sopenharmony_ci	if (orig_eq_count > neigh_rq_count)
11338c2ecf20Sopenharmony_ci		total_count = neigh_rq_count;
11348c2ecf20Sopenharmony_ci	else
11358c2ecf20Sopenharmony_ci		total_count = orig_eq_count;
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci	/* if we have too few packets (too less data) we set tq_own to zero
11388c2ecf20Sopenharmony_ci	 * if we receive too few packets it is not considered bidirectional
11398c2ecf20Sopenharmony_ci	 */
11408c2ecf20Sopenharmony_ci	if (total_count < BATADV_TQ_LOCAL_BIDRECT_SEND_MINIMUM ||
11418c2ecf20Sopenharmony_ci	    neigh_rq_count < BATADV_TQ_LOCAL_BIDRECT_RECV_MINIMUM)
11428c2ecf20Sopenharmony_ci		tq_own = 0;
11438c2ecf20Sopenharmony_ci	else
11448c2ecf20Sopenharmony_ci		/* neigh_node->real_packet_count is never zero as we
11458c2ecf20Sopenharmony_ci		 * only purge old information when getting new
11468c2ecf20Sopenharmony_ci		 * information
11478c2ecf20Sopenharmony_ci		 */
11488c2ecf20Sopenharmony_ci		tq_own = (BATADV_TQ_MAX_VALUE * total_count) /	neigh_rq_count;
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci	/* 1 - ((1-x) ** 3), normalized to TQ_MAX_VALUE this does
11518c2ecf20Sopenharmony_ci	 * affect the nearly-symmetric links only a little, but
11528c2ecf20Sopenharmony_ci	 * punishes asymmetric links more.  This will give a value
11538c2ecf20Sopenharmony_ci	 * between 0 and TQ_MAX_VALUE
11548c2ecf20Sopenharmony_ci	 */
11558c2ecf20Sopenharmony_ci	neigh_rq_inv = BATADV_TQ_LOCAL_WINDOW_SIZE - neigh_rq_count;
11568c2ecf20Sopenharmony_ci	neigh_rq_inv_cube = neigh_rq_inv * neigh_rq_inv * neigh_rq_inv;
11578c2ecf20Sopenharmony_ci	neigh_rq_max_cube = BATADV_TQ_LOCAL_WINDOW_SIZE *
11588c2ecf20Sopenharmony_ci			    BATADV_TQ_LOCAL_WINDOW_SIZE *
11598c2ecf20Sopenharmony_ci			    BATADV_TQ_LOCAL_WINDOW_SIZE;
11608c2ecf20Sopenharmony_ci	inv_asym_penalty = BATADV_TQ_MAX_VALUE * neigh_rq_inv_cube;
11618c2ecf20Sopenharmony_ci	inv_asym_penalty /= neigh_rq_max_cube;
11628c2ecf20Sopenharmony_ci	tq_asym_penalty = BATADV_TQ_MAX_VALUE - inv_asym_penalty;
11638c2ecf20Sopenharmony_ci	tq_iface_hop_penalty -= atomic_read(&if_incoming->hop_penalty);
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_ci	/* penalize if the OGM is forwarded on the same interface. WiFi
11668c2ecf20Sopenharmony_ci	 * interfaces and other half duplex devices suffer from throughput
11678c2ecf20Sopenharmony_ci	 * drops as they can't send and receive at the same time.
11688c2ecf20Sopenharmony_ci	 */
11698c2ecf20Sopenharmony_ci	if (if_outgoing && if_incoming == if_outgoing &&
11708c2ecf20Sopenharmony_ci	    batadv_is_wifi_hardif(if_outgoing))
11718c2ecf20Sopenharmony_ci		tq_iface_hop_penalty = batadv_hop_penalty(tq_iface_hop_penalty,
11728c2ecf20Sopenharmony_ci							  bat_priv);
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci	combined_tq = batadv_ogm_packet->tq *
11758c2ecf20Sopenharmony_ci		      tq_own *
11768c2ecf20Sopenharmony_ci		      tq_asym_penalty *
11778c2ecf20Sopenharmony_ci		      tq_iface_hop_penalty;
11788c2ecf20Sopenharmony_ci	combined_tq /= BATADV_TQ_MAX_VALUE *
11798c2ecf20Sopenharmony_ci		       BATADV_TQ_MAX_VALUE *
11808c2ecf20Sopenharmony_ci		       BATADV_TQ_MAX_VALUE;
11818c2ecf20Sopenharmony_ci	batadv_ogm_packet->tq = combined_tq;
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci	batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
11848c2ecf20Sopenharmony_ci		   "bidirectional: orig = %pM neigh = %pM => own_bcast = %2i, real recv = %2i, local tq: %3i, asym_penalty: %3i, iface_hop_penalty: %3i, total tq: %3i, if_incoming = %s, if_outgoing = %s\n",
11858c2ecf20Sopenharmony_ci		   orig_node->orig, orig_neigh_node->orig, total_count,
11868c2ecf20Sopenharmony_ci		   neigh_rq_count, tq_own, tq_asym_penalty,
11878c2ecf20Sopenharmony_ci		   tq_iface_hop_penalty, batadv_ogm_packet->tq,
11888c2ecf20Sopenharmony_ci		   if_incoming->net_dev->name,
11898c2ecf20Sopenharmony_ci		   if_outgoing ? if_outgoing->net_dev->name : "DEFAULT");
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_ci	/* if link has the minimum required transmission quality
11928c2ecf20Sopenharmony_ci	 * consider it bidirectional
11938c2ecf20Sopenharmony_ci	 */
11948c2ecf20Sopenharmony_ci	if (batadv_ogm_packet->tq >= BATADV_TQ_TOTAL_BIDRECT_LIMIT)
11958c2ecf20Sopenharmony_ci		ret = true;
11968c2ecf20Sopenharmony_ci
11978c2ecf20Sopenharmony_ciout:
11988c2ecf20Sopenharmony_ci	if (neigh_node)
11998c2ecf20Sopenharmony_ci		batadv_neigh_node_put(neigh_node);
12008c2ecf20Sopenharmony_ci	return ret;
12018c2ecf20Sopenharmony_ci}
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci/**
12048c2ecf20Sopenharmony_ci * batadv_iv_ogm_update_seqnos() -  process a batman packet for all interfaces,
12058c2ecf20Sopenharmony_ci *  adjust the sequence number and find out whether it is a duplicate
12068c2ecf20Sopenharmony_ci * @ethhdr: ethernet header of the packet
12078c2ecf20Sopenharmony_ci * @batadv_ogm_packet: OGM packet to be considered
12088c2ecf20Sopenharmony_ci * @if_incoming: interface on which the OGM packet was received
12098c2ecf20Sopenharmony_ci * @if_outgoing: interface for which the retransmission should be considered
12108c2ecf20Sopenharmony_ci *
12118c2ecf20Sopenharmony_ci * Return: duplicate status as enum batadv_dup_status
12128c2ecf20Sopenharmony_ci */
12138c2ecf20Sopenharmony_cistatic enum batadv_dup_status
12148c2ecf20Sopenharmony_cibatadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr,
12158c2ecf20Sopenharmony_ci			    const struct batadv_ogm_packet *batadv_ogm_packet,
12168c2ecf20Sopenharmony_ci			    const struct batadv_hard_iface *if_incoming,
12178c2ecf20Sopenharmony_ci			    struct batadv_hard_iface *if_outgoing)
12188c2ecf20Sopenharmony_ci{
12198c2ecf20Sopenharmony_ci	struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
12208c2ecf20Sopenharmony_ci	struct batadv_orig_node *orig_node;
12218c2ecf20Sopenharmony_ci	struct batadv_orig_ifinfo *orig_ifinfo = NULL;
12228c2ecf20Sopenharmony_ci	struct batadv_neigh_node *neigh_node;
12238c2ecf20Sopenharmony_ci	struct batadv_neigh_ifinfo *neigh_ifinfo;
12248c2ecf20Sopenharmony_ci	bool is_dup;
12258c2ecf20Sopenharmony_ci	s32 seq_diff;
12268c2ecf20Sopenharmony_ci	bool need_update = false;
12278c2ecf20Sopenharmony_ci	int set_mark;
12288c2ecf20Sopenharmony_ci	enum batadv_dup_status ret = BATADV_NO_DUP;
12298c2ecf20Sopenharmony_ci	u32 seqno = ntohl(batadv_ogm_packet->seqno);
12308c2ecf20Sopenharmony_ci	u8 *neigh_addr;
12318c2ecf20Sopenharmony_ci	u8 packet_count;
12328c2ecf20Sopenharmony_ci	unsigned long *bitmap;
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_ci	orig_node = batadv_iv_ogm_orig_get(bat_priv, batadv_ogm_packet->orig);
12358c2ecf20Sopenharmony_ci	if (!orig_node)
12368c2ecf20Sopenharmony_ci		return BATADV_NO_DUP;
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci	orig_ifinfo = batadv_orig_ifinfo_new(orig_node, if_outgoing);
12398c2ecf20Sopenharmony_ci	if (WARN_ON(!orig_ifinfo)) {
12408c2ecf20Sopenharmony_ci		batadv_orig_node_put(orig_node);
12418c2ecf20Sopenharmony_ci		return 0;
12428c2ecf20Sopenharmony_ci	}
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_ci	spin_lock_bh(&orig_node->bat_iv.ogm_cnt_lock);
12458c2ecf20Sopenharmony_ci	seq_diff = seqno - orig_ifinfo->last_real_seqno;
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci	/* signalize caller that the packet is to be dropped. */
12488c2ecf20Sopenharmony_ci	if (!hlist_empty(&orig_node->neigh_list) &&
12498c2ecf20Sopenharmony_ci	    batadv_window_protected(bat_priv, seq_diff,
12508c2ecf20Sopenharmony_ci				    BATADV_TQ_LOCAL_WINDOW_SIZE,
12518c2ecf20Sopenharmony_ci				    &orig_ifinfo->batman_seqno_reset, NULL)) {
12528c2ecf20Sopenharmony_ci		ret = BATADV_PROTECTED;
12538c2ecf20Sopenharmony_ci		goto out;
12548c2ecf20Sopenharmony_ci	}
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci	rcu_read_lock();
12578c2ecf20Sopenharmony_ci	hlist_for_each_entry_rcu(neigh_node, &orig_node->neigh_list, list) {
12588c2ecf20Sopenharmony_ci		neigh_ifinfo = batadv_neigh_ifinfo_new(neigh_node,
12598c2ecf20Sopenharmony_ci						       if_outgoing);
12608c2ecf20Sopenharmony_ci		if (!neigh_ifinfo)
12618c2ecf20Sopenharmony_ci			continue;
12628c2ecf20Sopenharmony_ci
12638c2ecf20Sopenharmony_ci		neigh_addr = neigh_node->addr;
12648c2ecf20Sopenharmony_ci		is_dup = batadv_test_bit(neigh_ifinfo->bat_iv.real_bits,
12658c2ecf20Sopenharmony_ci					 orig_ifinfo->last_real_seqno,
12668c2ecf20Sopenharmony_ci					 seqno);
12678c2ecf20Sopenharmony_ci
12688c2ecf20Sopenharmony_ci		if (batadv_compare_eth(neigh_addr, ethhdr->h_source) &&
12698c2ecf20Sopenharmony_ci		    neigh_node->if_incoming == if_incoming) {
12708c2ecf20Sopenharmony_ci			set_mark = 1;
12718c2ecf20Sopenharmony_ci			if (is_dup)
12728c2ecf20Sopenharmony_ci				ret = BATADV_NEIGH_DUP;
12738c2ecf20Sopenharmony_ci		} else {
12748c2ecf20Sopenharmony_ci			set_mark = 0;
12758c2ecf20Sopenharmony_ci			if (is_dup && ret != BATADV_NEIGH_DUP)
12768c2ecf20Sopenharmony_ci				ret = BATADV_ORIG_DUP;
12778c2ecf20Sopenharmony_ci		}
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_ci		/* if the window moved, set the update flag. */
12808c2ecf20Sopenharmony_ci		bitmap = neigh_ifinfo->bat_iv.real_bits;
12818c2ecf20Sopenharmony_ci		need_update |= batadv_bit_get_packet(bat_priv, bitmap,
12828c2ecf20Sopenharmony_ci						     seq_diff, set_mark);
12838c2ecf20Sopenharmony_ci
12848c2ecf20Sopenharmony_ci		packet_count = bitmap_weight(bitmap,
12858c2ecf20Sopenharmony_ci					     BATADV_TQ_LOCAL_WINDOW_SIZE);
12868c2ecf20Sopenharmony_ci		neigh_ifinfo->bat_iv.real_packet_count = packet_count;
12878c2ecf20Sopenharmony_ci		batadv_neigh_ifinfo_put(neigh_ifinfo);
12888c2ecf20Sopenharmony_ci	}
12898c2ecf20Sopenharmony_ci	rcu_read_unlock();
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci	if (need_update) {
12928c2ecf20Sopenharmony_ci		batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
12938c2ecf20Sopenharmony_ci			   "%s updating last_seqno: old %u, new %u\n",
12948c2ecf20Sopenharmony_ci			   if_outgoing ? if_outgoing->net_dev->name : "DEFAULT",
12958c2ecf20Sopenharmony_ci			   orig_ifinfo->last_real_seqno, seqno);
12968c2ecf20Sopenharmony_ci		orig_ifinfo->last_real_seqno = seqno;
12978c2ecf20Sopenharmony_ci	}
12988c2ecf20Sopenharmony_ci
12998c2ecf20Sopenharmony_ciout:
13008c2ecf20Sopenharmony_ci	spin_unlock_bh(&orig_node->bat_iv.ogm_cnt_lock);
13018c2ecf20Sopenharmony_ci	batadv_orig_node_put(orig_node);
13028c2ecf20Sopenharmony_ci	batadv_orig_ifinfo_put(orig_ifinfo);
13038c2ecf20Sopenharmony_ci	return ret;
13048c2ecf20Sopenharmony_ci}
13058c2ecf20Sopenharmony_ci
13068c2ecf20Sopenharmony_ci/**
13078c2ecf20Sopenharmony_ci * batadv_iv_ogm_process_per_outif() - process a batman iv OGM for an outgoing
13088c2ecf20Sopenharmony_ci *  interface
13098c2ecf20Sopenharmony_ci * @skb: the skb containing the OGM
13108c2ecf20Sopenharmony_ci * @ogm_offset: offset from skb->data to start of ogm header
13118c2ecf20Sopenharmony_ci * @orig_node: the (cached) orig node for the originator of this OGM
13128c2ecf20Sopenharmony_ci * @if_incoming: the interface where this packet was received
13138c2ecf20Sopenharmony_ci * @if_outgoing: the interface for which the packet should be considered
13148c2ecf20Sopenharmony_ci */
13158c2ecf20Sopenharmony_cistatic void
13168c2ecf20Sopenharmony_cibatadv_iv_ogm_process_per_outif(const struct sk_buff *skb, int ogm_offset,
13178c2ecf20Sopenharmony_ci				struct batadv_orig_node *orig_node,
13188c2ecf20Sopenharmony_ci				struct batadv_hard_iface *if_incoming,
13198c2ecf20Sopenharmony_ci				struct batadv_hard_iface *if_outgoing)
13208c2ecf20Sopenharmony_ci{
13218c2ecf20Sopenharmony_ci	struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
13228c2ecf20Sopenharmony_ci	struct batadv_hardif_neigh_node *hardif_neigh = NULL;
13238c2ecf20Sopenharmony_ci	struct batadv_neigh_node *router = NULL;
13248c2ecf20Sopenharmony_ci	struct batadv_neigh_node *router_router = NULL;
13258c2ecf20Sopenharmony_ci	struct batadv_orig_node *orig_neigh_node;
13268c2ecf20Sopenharmony_ci	struct batadv_orig_ifinfo *orig_ifinfo;
13278c2ecf20Sopenharmony_ci	struct batadv_neigh_node *orig_neigh_router = NULL;
13288c2ecf20Sopenharmony_ci	struct batadv_neigh_ifinfo *router_ifinfo = NULL;
13298c2ecf20Sopenharmony_ci	struct batadv_ogm_packet *ogm_packet;
13308c2ecf20Sopenharmony_ci	enum batadv_dup_status dup_status;
13318c2ecf20Sopenharmony_ci	bool is_from_best_next_hop = false;
13328c2ecf20Sopenharmony_ci	bool is_single_hop_neigh = false;
13338c2ecf20Sopenharmony_ci	bool sameseq, similar_ttl;
13348c2ecf20Sopenharmony_ci	struct sk_buff *skb_priv;
13358c2ecf20Sopenharmony_ci	struct ethhdr *ethhdr;
13368c2ecf20Sopenharmony_ci	u8 *prev_sender;
13378c2ecf20Sopenharmony_ci	bool is_bidirect;
13388c2ecf20Sopenharmony_ci
13398c2ecf20Sopenharmony_ci	/* create a private copy of the skb, as some functions change tq value
13408c2ecf20Sopenharmony_ci	 * and/or flags.
13418c2ecf20Sopenharmony_ci	 */
13428c2ecf20Sopenharmony_ci	skb_priv = skb_copy(skb, GFP_ATOMIC);
13438c2ecf20Sopenharmony_ci	if (!skb_priv)
13448c2ecf20Sopenharmony_ci		return;
13458c2ecf20Sopenharmony_ci
13468c2ecf20Sopenharmony_ci	ethhdr = eth_hdr(skb_priv);
13478c2ecf20Sopenharmony_ci	ogm_packet = (struct batadv_ogm_packet *)(skb_priv->data + ogm_offset);
13488c2ecf20Sopenharmony_ci
13498c2ecf20Sopenharmony_ci	dup_status = batadv_iv_ogm_update_seqnos(ethhdr, ogm_packet,
13508c2ecf20Sopenharmony_ci						 if_incoming, if_outgoing);
13518c2ecf20Sopenharmony_ci	if (batadv_compare_eth(ethhdr->h_source, ogm_packet->orig))
13528c2ecf20Sopenharmony_ci		is_single_hop_neigh = true;
13538c2ecf20Sopenharmony_ci
13548c2ecf20Sopenharmony_ci	if (dup_status == BATADV_PROTECTED) {
13558c2ecf20Sopenharmony_ci		batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
13568c2ecf20Sopenharmony_ci			   "Drop packet: packet within seqno protection time (sender: %pM)\n",
13578c2ecf20Sopenharmony_ci			   ethhdr->h_source);
13588c2ecf20Sopenharmony_ci		goto out;
13598c2ecf20Sopenharmony_ci	}
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ci	if (ogm_packet->tq == 0) {
13628c2ecf20Sopenharmony_ci		batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
13638c2ecf20Sopenharmony_ci			   "Drop packet: originator packet with tq equal 0\n");
13648c2ecf20Sopenharmony_ci		goto out;
13658c2ecf20Sopenharmony_ci	}
13668c2ecf20Sopenharmony_ci
13678c2ecf20Sopenharmony_ci	if (is_single_hop_neigh) {
13688c2ecf20Sopenharmony_ci		hardif_neigh = batadv_hardif_neigh_get(if_incoming,
13698c2ecf20Sopenharmony_ci						       ethhdr->h_source);
13708c2ecf20Sopenharmony_ci		if (hardif_neigh)
13718c2ecf20Sopenharmony_ci			hardif_neigh->last_seen = jiffies;
13728c2ecf20Sopenharmony_ci	}
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_ci	router = batadv_orig_router_get(orig_node, if_outgoing);
13758c2ecf20Sopenharmony_ci	if (router) {
13768c2ecf20Sopenharmony_ci		router_router = batadv_orig_router_get(router->orig_node,
13778c2ecf20Sopenharmony_ci						       if_outgoing);
13788c2ecf20Sopenharmony_ci		router_ifinfo = batadv_neigh_ifinfo_get(router, if_outgoing);
13798c2ecf20Sopenharmony_ci	}
13808c2ecf20Sopenharmony_ci
13818c2ecf20Sopenharmony_ci	if ((router_ifinfo && router_ifinfo->bat_iv.tq_avg != 0) &&
13828c2ecf20Sopenharmony_ci	    (batadv_compare_eth(router->addr, ethhdr->h_source)))
13838c2ecf20Sopenharmony_ci		is_from_best_next_hop = true;
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_ci	prev_sender = ogm_packet->prev_sender;
13868c2ecf20Sopenharmony_ci	/* avoid temporary routing loops */
13878c2ecf20Sopenharmony_ci	if (router && router_router &&
13888c2ecf20Sopenharmony_ci	    (batadv_compare_eth(router->addr, prev_sender)) &&
13898c2ecf20Sopenharmony_ci	    !(batadv_compare_eth(ogm_packet->orig, prev_sender)) &&
13908c2ecf20Sopenharmony_ci	    (batadv_compare_eth(router->addr, router_router->addr))) {
13918c2ecf20Sopenharmony_ci		batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
13928c2ecf20Sopenharmony_ci			   "Drop packet: ignoring all rebroadcast packets that may make me loop (sender: %pM)\n",
13938c2ecf20Sopenharmony_ci			   ethhdr->h_source);
13948c2ecf20Sopenharmony_ci		goto out;
13958c2ecf20Sopenharmony_ci	}
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_ci	if (if_outgoing == BATADV_IF_DEFAULT)
13988c2ecf20Sopenharmony_ci		batadv_tvlv_ogm_receive(bat_priv, ogm_packet, orig_node);
13998c2ecf20Sopenharmony_ci
14008c2ecf20Sopenharmony_ci	/* if sender is a direct neighbor the sender mac equals
14018c2ecf20Sopenharmony_ci	 * originator mac
14028c2ecf20Sopenharmony_ci	 */
14038c2ecf20Sopenharmony_ci	if (is_single_hop_neigh)
14048c2ecf20Sopenharmony_ci		orig_neigh_node = orig_node;
14058c2ecf20Sopenharmony_ci	else
14068c2ecf20Sopenharmony_ci		orig_neigh_node = batadv_iv_ogm_orig_get(bat_priv,
14078c2ecf20Sopenharmony_ci							 ethhdr->h_source);
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci	if (!orig_neigh_node)
14108c2ecf20Sopenharmony_ci		goto out;
14118c2ecf20Sopenharmony_ci
14128c2ecf20Sopenharmony_ci	/* Update nc_nodes of the originator */
14138c2ecf20Sopenharmony_ci	batadv_nc_update_nc_node(bat_priv, orig_node, orig_neigh_node,
14148c2ecf20Sopenharmony_ci				 ogm_packet, is_single_hop_neigh);
14158c2ecf20Sopenharmony_ci
14168c2ecf20Sopenharmony_ci	orig_neigh_router = batadv_orig_router_get(orig_neigh_node,
14178c2ecf20Sopenharmony_ci						   if_outgoing);
14188c2ecf20Sopenharmony_ci
14198c2ecf20Sopenharmony_ci	/* drop packet if sender is not a direct neighbor and if we
14208c2ecf20Sopenharmony_ci	 * don't route towards it
14218c2ecf20Sopenharmony_ci	 */
14228c2ecf20Sopenharmony_ci	if (!is_single_hop_neigh && !orig_neigh_router) {
14238c2ecf20Sopenharmony_ci		batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
14248c2ecf20Sopenharmony_ci			   "Drop packet: OGM via unknown neighbor!\n");
14258c2ecf20Sopenharmony_ci		goto out_neigh;
14268c2ecf20Sopenharmony_ci	}
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_ci	is_bidirect = batadv_iv_ogm_calc_tq(orig_node, orig_neigh_node,
14298c2ecf20Sopenharmony_ci					    ogm_packet, if_incoming,
14308c2ecf20Sopenharmony_ci					    if_outgoing);
14318c2ecf20Sopenharmony_ci
14328c2ecf20Sopenharmony_ci	/* update ranking if it is not a duplicate or has the same
14338c2ecf20Sopenharmony_ci	 * seqno and similar ttl as the non-duplicate
14348c2ecf20Sopenharmony_ci	 */
14358c2ecf20Sopenharmony_ci	orig_ifinfo = batadv_orig_ifinfo_new(orig_node, if_outgoing);
14368c2ecf20Sopenharmony_ci	if (!orig_ifinfo)
14378c2ecf20Sopenharmony_ci		goto out_neigh;
14388c2ecf20Sopenharmony_ci
14398c2ecf20Sopenharmony_ci	sameseq = orig_ifinfo->last_real_seqno == ntohl(ogm_packet->seqno);
14408c2ecf20Sopenharmony_ci	similar_ttl = (orig_ifinfo->last_ttl - 3) <= ogm_packet->ttl;
14418c2ecf20Sopenharmony_ci
14428c2ecf20Sopenharmony_ci	if (is_bidirect && (dup_status == BATADV_NO_DUP ||
14438c2ecf20Sopenharmony_ci			    (sameseq && similar_ttl))) {
14448c2ecf20Sopenharmony_ci		batadv_iv_ogm_orig_update(bat_priv, orig_node,
14458c2ecf20Sopenharmony_ci					  orig_ifinfo, ethhdr,
14468c2ecf20Sopenharmony_ci					  ogm_packet, if_incoming,
14478c2ecf20Sopenharmony_ci					  if_outgoing, dup_status);
14488c2ecf20Sopenharmony_ci	}
14498c2ecf20Sopenharmony_ci	batadv_orig_ifinfo_put(orig_ifinfo);
14508c2ecf20Sopenharmony_ci
14518c2ecf20Sopenharmony_ci	/* only forward for specific interface, not for the default one. */
14528c2ecf20Sopenharmony_ci	if (if_outgoing == BATADV_IF_DEFAULT)
14538c2ecf20Sopenharmony_ci		goto out_neigh;
14548c2ecf20Sopenharmony_ci
14558c2ecf20Sopenharmony_ci	/* is single hop (direct) neighbor */
14568c2ecf20Sopenharmony_ci	if (is_single_hop_neigh) {
14578c2ecf20Sopenharmony_ci		/* OGMs from secondary interfaces should only scheduled once
14588c2ecf20Sopenharmony_ci		 * per interface where it has been received, not multiple times
14598c2ecf20Sopenharmony_ci		 */
14608c2ecf20Sopenharmony_ci		if (ogm_packet->ttl <= 2 &&
14618c2ecf20Sopenharmony_ci		    if_incoming != if_outgoing) {
14628c2ecf20Sopenharmony_ci			batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
14638c2ecf20Sopenharmony_ci				   "Drop packet: OGM from secondary interface and wrong outgoing interface\n");
14648c2ecf20Sopenharmony_ci			goto out_neigh;
14658c2ecf20Sopenharmony_ci		}
14668c2ecf20Sopenharmony_ci		/* mark direct link on incoming interface */
14678c2ecf20Sopenharmony_ci		batadv_iv_ogm_forward(orig_node, ethhdr, ogm_packet,
14688c2ecf20Sopenharmony_ci				      is_single_hop_neigh,
14698c2ecf20Sopenharmony_ci				      is_from_best_next_hop, if_incoming,
14708c2ecf20Sopenharmony_ci				      if_outgoing);
14718c2ecf20Sopenharmony_ci
14728c2ecf20Sopenharmony_ci		batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
14738c2ecf20Sopenharmony_ci			   "Forwarding packet: rebroadcast neighbor packet with direct link flag\n");
14748c2ecf20Sopenharmony_ci		goto out_neigh;
14758c2ecf20Sopenharmony_ci	}
14768c2ecf20Sopenharmony_ci
14778c2ecf20Sopenharmony_ci	/* multihop originator */
14788c2ecf20Sopenharmony_ci	if (!is_bidirect) {
14798c2ecf20Sopenharmony_ci		batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
14808c2ecf20Sopenharmony_ci			   "Drop packet: not received via bidirectional link\n");
14818c2ecf20Sopenharmony_ci		goto out_neigh;
14828c2ecf20Sopenharmony_ci	}
14838c2ecf20Sopenharmony_ci
14848c2ecf20Sopenharmony_ci	if (dup_status == BATADV_NEIGH_DUP) {
14858c2ecf20Sopenharmony_ci		batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
14868c2ecf20Sopenharmony_ci			   "Drop packet: duplicate packet received\n");
14878c2ecf20Sopenharmony_ci		goto out_neigh;
14888c2ecf20Sopenharmony_ci	}
14898c2ecf20Sopenharmony_ci
14908c2ecf20Sopenharmony_ci	batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
14918c2ecf20Sopenharmony_ci		   "Forwarding packet: rebroadcast originator packet\n");
14928c2ecf20Sopenharmony_ci	batadv_iv_ogm_forward(orig_node, ethhdr, ogm_packet,
14938c2ecf20Sopenharmony_ci			      is_single_hop_neigh, is_from_best_next_hop,
14948c2ecf20Sopenharmony_ci			      if_incoming, if_outgoing);
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_ciout_neigh:
14978c2ecf20Sopenharmony_ci	if (orig_neigh_node && !is_single_hop_neigh)
14988c2ecf20Sopenharmony_ci		batadv_orig_node_put(orig_neigh_node);
14998c2ecf20Sopenharmony_ciout:
15008c2ecf20Sopenharmony_ci	if (router_ifinfo)
15018c2ecf20Sopenharmony_ci		batadv_neigh_ifinfo_put(router_ifinfo);
15028c2ecf20Sopenharmony_ci	if (router)
15038c2ecf20Sopenharmony_ci		batadv_neigh_node_put(router);
15048c2ecf20Sopenharmony_ci	if (router_router)
15058c2ecf20Sopenharmony_ci		batadv_neigh_node_put(router_router);
15068c2ecf20Sopenharmony_ci	if (orig_neigh_router)
15078c2ecf20Sopenharmony_ci		batadv_neigh_node_put(orig_neigh_router);
15088c2ecf20Sopenharmony_ci	if (hardif_neigh)
15098c2ecf20Sopenharmony_ci		batadv_hardif_neigh_put(hardif_neigh);
15108c2ecf20Sopenharmony_ci
15118c2ecf20Sopenharmony_ci	consume_skb(skb_priv);
15128c2ecf20Sopenharmony_ci}
15138c2ecf20Sopenharmony_ci
15148c2ecf20Sopenharmony_ci/**
15158c2ecf20Sopenharmony_ci * batadv_iv_ogm_process_reply() - Check OGM for direct reply and process it
15168c2ecf20Sopenharmony_ci * @ogm_packet: rebroadcast OGM packet to process
15178c2ecf20Sopenharmony_ci * @if_incoming: the interface where this packet was received
15188c2ecf20Sopenharmony_ci * @orig_node: originator which reproadcasted the OGMs
15198c2ecf20Sopenharmony_ci * @if_incoming_seqno: OGM sequence number when rebroadcast was received
15208c2ecf20Sopenharmony_ci */
15218c2ecf20Sopenharmony_cistatic void batadv_iv_ogm_process_reply(struct batadv_ogm_packet *ogm_packet,
15228c2ecf20Sopenharmony_ci					struct batadv_hard_iface *if_incoming,
15238c2ecf20Sopenharmony_ci					struct batadv_orig_node *orig_node,
15248c2ecf20Sopenharmony_ci					u32 if_incoming_seqno)
15258c2ecf20Sopenharmony_ci{
15268c2ecf20Sopenharmony_ci	struct batadv_orig_ifinfo *orig_ifinfo;
15278c2ecf20Sopenharmony_ci	s32 bit_pos;
15288c2ecf20Sopenharmony_ci	u8 *weight;
15298c2ecf20Sopenharmony_ci
15308c2ecf20Sopenharmony_ci	/* neighbor has to indicate direct link and it has to
15318c2ecf20Sopenharmony_ci	 * come via the corresponding interface
15328c2ecf20Sopenharmony_ci	 */
15338c2ecf20Sopenharmony_ci	if (!(ogm_packet->flags & BATADV_DIRECTLINK))
15348c2ecf20Sopenharmony_ci		return;
15358c2ecf20Sopenharmony_ci
15368c2ecf20Sopenharmony_ci	if (!batadv_compare_eth(if_incoming->net_dev->dev_addr,
15378c2ecf20Sopenharmony_ci				ogm_packet->orig))
15388c2ecf20Sopenharmony_ci		return;
15398c2ecf20Sopenharmony_ci
15408c2ecf20Sopenharmony_ci	orig_ifinfo = batadv_orig_ifinfo_get(orig_node, if_incoming);
15418c2ecf20Sopenharmony_ci	if (!orig_ifinfo)
15428c2ecf20Sopenharmony_ci		return;
15438c2ecf20Sopenharmony_ci
15448c2ecf20Sopenharmony_ci	/* save packet seqno for bidirectional check */
15458c2ecf20Sopenharmony_ci	spin_lock_bh(&orig_node->bat_iv.ogm_cnt_lock);
15468c2ecf20Sopenharmony_ci	bit_pos = if_incoming_seqno - 2;
15478c2ecf20Sopenharmony_ci	bit_pos -= ntohl(ogm_packet->seqno);
15488c2ecf20Sopenharmony_ci	batadv_set_bit(orig_ifinfo->bat_iv.bcast_own, bit_pos);
15498c2ecf20Sopenharmony_ci	weight = &orig_ifinfo->bat_iv.bcast_own_sum;
15508c2ecf20Sopenharmony_ci	*weight = bitmap_weight(orig_ifinfo->bat_iv.bcast_own,
15518c2ecf20Sopenharmony_ci				BATADV_TQ_LOCAL_WINDOW_SIZE);
15528c2ecf20Sopenharmony_ci	spin_unlock_bh(&orig_node->bat_iv.ogm_cnt_lock);
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci	batadv_orig_ifinfo_put(orig_ifinfo);
15558c2ecf20Sopenharmony_ci}
15568c2ecf20Sopenharmony_ci
15578c2ecf20Sopenharmony_ci/**
15588c2ecf20Sopenharmony_ci * batadv_iv_ogm_process() - process an incoming batman iv OGM
15598c2ecf20Sopenharmony_ci * @skb: the skb containing the OGM
15608c2ecf20Sopenharmony_ci * @ogm_offset: offset to the OGM which should be processed (for aggregates)
15618c2ecf20Sopenharmony_ci * @if_incoming: the interface where this packet was received
15628c2ecf20Sopenharmony_ci */
15638c2ecf20Sopenharmony_cistatic void batadv_iv_ogm_process(const struct sk_buff *skb, int ogm_offset,
15648c2ecf20Sopenharmony_ci				  struct batadv_hard_iface *if_incoming)
15658c2ecf20Sopenharmony_ci{
15668c2ecf20Sopenharmony_ci	struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
15678c2ecf20Sopenharmony_ci	struct batadv_orig_node *orig_neigh_node, *orig_node;
15688c2ecf20Sopenharmony_ci	struct batadv_hard_iface *hard_iface;
15698c2ecf20Sopenharmony_ci	struct batadv_ogm_packet *ogm_packet;
15708c2ecf20Sopenharmony_ci	u32 if_incoming_seqno;
15718c2ecf20Sopenharmony_ci	bool has_directlink_flag;
15728c2ecf20Sopenharmony_ci	struct ethhdr *ethhdr;
15738c2ecf20Sopenharmony_ci	bool is_my_oldorig = false;
15748c2ecf20Sopenharmony_ci	bool is_my_addr = false;
15758c2ecf20Sopenharmony_ci	bool is_my_orig = false;
15768c2ecf20Sopenharmony_ci
15778c2ecf20Sopenharmony_ci	ogm_packet = (struct batadv_ogm_packet *)(skb->data + ogm_offset);
15788c2ecf20Sopenharmony_ci	ethhdr = eth_hdr(skb);
15798c2ecf20Sopenharmony_ci
15808c2ecf20Sopenharmony_ci	/* Silently drop when the batman packet is actually not a
15818c2ecf20Sopenharmony_ci	 * correct packet.
15828c2ecf20Sopenharmony_ci	 *
15838c2ecf20Sopenharmony_ci	 * This might happen if a packet is padded (e.g. Ethernet has a
15848c2ecf20Sopenharmony_ci	 * minimum frame length of 64 byte) and the aggregation interprets
15858c2ecf20Sopenharmony_ci	 * it as an additional length.
15868c2ecf20Sopenharmony_ci	 *
15878c2ecf20Sopenharmony_ci	 * TODO: A more sane solution would be to have a bit in the
15888c2ecf20Sopenharmony_ci	 * batadv_ogm_packet to detect whether the packet is the last
15898c2ecf20Sopenharmony_ci	 * packet in an aggregation.  Here we expect that the padding
15908c2ecf20Sopenharmony_ci	 * is always zero (or not 0x01)
15918c2ecf20Sopenharmony_ci	 */
15928c2ecf20Sopenharmony_ci	if (ogm_packet->packet_type != BATADV_IV_OGM)
15938c2ecf20Sopenharmony_ci		return;
15948c2ecf20Sopenharmony_ci
15958c2ecf20Sopenharmony_ci	/* could be changed by schedule_own_packet() */
15968c2ecf20Sopenharmony_ci	if_incoming_seqno = atomic_read(&if_incoming->bat_iv.ogm_seqno);
15978c2ecf20Sopenharmony_ci
15988c2ecf20Sopenharmony_ci	if (ogm_packet->flags & BATADV_DIRECTLINK)
15998c2ecf20Sopenharmony_ci		has_directlink_flag = true;
16008c2ecf20Sopenharmony_ci	else
16018c2ecf20Sopenharmony_ci		has_directlink_flag = false;
16028c2ecf20Sopenharmony_ci
16038c2ecf20Sopenharmony_ci	batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
16048c2ecf20Sopenharmony_ci		   "Received BATMAN packet via NB: %pM, IF: %s [%pM] (from OG: %pM, via prev OG: %pM, seqno %u, tq %d, TTL %d, V %d, IDF %d)\n",
16058c2ecf20Sopenharmony_ci		   ethhdr->h_source, if_incoming->net_dev->name,
16068c2ecf20Sopenharmony_ci		   if_incoming->net_dev->dev_addr, ogm_packet->orig,
16078c2ecf20Sopenharmony_ci		   ogm_packet->prev_sender, ntohl(ogm_packet->seqno),
16088c2ecf20Sopenharmony_ci		   ogm_packet->tq, ogm_packet->ttl,
16098c2ecf20Sopenharmony_ci		   ogm_packet->version, has_directlink_flag);
16108c2ecf20Sopenharmony_ci
16118c2ecf20Sopenharmony_ci	rcu_read_lock();
16128c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
16138c2ecf20Sopenharmony_ci		if (hard_iface->if_status != BATADV_IF_ACTIVE)
16148c2ecf20Sopenharmony_ci			continue;
16158c2ecf20Sopenharmony_ci
16168c2ecf20Sopenharmony_ci		if (hard_iface->soft_iface != if_incoming->soft_iface)
16178c2ecf20Sopenharmony_ci			continue;
16188c2ecf20Sopenharmony_ci
16198c2ecf20Sopenharmony_ci		if (batadv_compare_eth(ethhdr->h_source,
16208c2ecf20Sopenharmony_ci				       hard_iface->net_dev->dev_addr))
16218c2ecf20Sopenharmony_ci			is_my_addr = true;
16228c2ecf20Sopenharmony_ci
16238c2ecf20Sopenharmony_ci		if (batadv_compare_eth(ogm_packet->orig,
16248c2ecf20Sopenharmony_ci				       hard_iface->net_dev->dev_addr))
16258c2ecf20Sopenharmony_ci			is_my_orig = true;
16268c2ecf20Sopenharmony_ci
16278c2ecf20Sopenharmony_ci		if (batadv_compare_eth(ogm_packet->prev_sender,
16288c2ecf20Sopenharmony_ci				       hard_iface->net_dev->dev_addr))
16298c2ecf20Sopenharmony_ci			is_my_oldorig = true;
16308c2ecf20Sopenharmony_ci	}
16318c2ecf20Sopenharmony_ci	rcu_read_unlock();
16328c2ecf20Sopenharmony_ci
16338c2ecf20Sopenharmony_ci	if (is_my_addr) {
16348c2ecf20Sopenharmony_ci		batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
16358c2ecf20Sopenharmony_ci			   "Drop packet: received my own broadcast (sender: %pM)\n",
16368c2ecf20Sopenharmony_ci			   ethhdr->h_source);
16378c2ecf20Sopenharmony_ci		return;
16388c2ecf20Sopenharmony_ci	}
16398c2ecf20Sopenharmony_ci
16408c2ecf20Sopenharmony_ci	if (is_my_orig) {
16418c2ecf20Sopenharmony_ci		orig_neigh_node = batadv_iv_ogm_orig_get(bat_priv,
16428c2ecf20Sopenharmony_ci							 ethhdr->h_source);
16438c2ecf20Sopenharmony_ci		if (!orig_neigh_node)
16448c2ecf20Sopenharmony_ci			return;
16458c2ecf20Sopenharmony_ci
16468c2ecf20Sopenharmony_ci		batadv_iv_ogm_process_reply(ogm_packet, if_incoming,
16478c2ecf20Sopenharmony_ci					    orig_neigh_node, if_incoming_seqno);
16488c2ecf20Sopenharmony_ci
16498c2ecf20Sopenharmony_ci		batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
16508c2ecf20Sopenharmony_ci			   "Drop packet: originator packet from myself (via neighbor)\n");
16518c2ecf20Sopenharmony_ci		batadv_orig_node_put(orig_neigh_node);
16528c2ecf20Sopenharmony_ci		return;
16538c2ecf20Sopenharmony_ci	}
16548c2ecf20Sopenharmony_ci
16558c2ecf20Sopenharmony_ci	if (is_my_oldorig) {
16568c2ecf20Sopenharmony_ci		batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
16578c2ecf20Sopenharmony_ci			   "Drop packet: ignoring all rebroadcast echos (sender: %pM)\n",
16588c2ecf20Sopenharmony_ci			   ethhdr->h_source);
16598c2ecf20Sopenharmony_ci		return;
16608c2ecf20Sopenharmony_ci	}
16618c2ecf20Sopenharmony_ci
16628c2ecf20Sopenharmony_ci	if (ogm_packet->flags & BATADV_NOT_BEST_NEXT_HOP) {
16638c2ecf20Sopenharmony_ci		batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
16648c2ecf20Sopenharmony_ci			   "Drop packet: ignoring all packets not forwarded from the best next hop (sender: %pM)\n",
16658c2ecf20Sopenharmony_ci			   ethhdr->h_source);
16668c2ecf20Sopenharmony_ci		return;
16678c2ecf20Sopenharmony_ci	}
16688c2ecf20Sopenharmony_ci
16698c2ecf20Sopenharmony_ci	orig_node = batadv_iv_ogm_orig_get(bat_priv, ogm_packet->orig);
16708c2ecf20Sopenharmony_ci	if (!orig_node)
16718c2ecf20Sopenharmony_ci		return;
16728c2ecf20Sopenharmony_ci
16738c2ecf20Sopenharmony_ci	batadv_iv_ogm_process_per_outif(skb, ogm_offset, orig_node,
16748c2ecf20Sopenharmony_ci					if_incoming, BATADV_IF_DEFAULT);
16758c2ecf20Sopenharmony_ci
16768c2ecf20Sopenharmony_ci	rcu_read_lock();
16778c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
16788c2ecf20Sopenharmony_ci		if (hard_iface->if_status != BATADV_IF_ACTIVE)
16798c2ecf20Sopenharmony_ci			continue;
16808c2ecf20Sopenharmony_ci
16818c2ecf20Sopenharmony_ci		if (hard_iface->soft_iface != bat_priv->soft_iface)
16828c2ecf20Sopenharmony_ci			continue;
16838c2ecf20Sopenharmony_ci
16848c2ecf20Sopenharmony_ci		if (!kref_get_unless_zero(&hard_iface->refcount))
16858c2ecf20Sopenharmony_ci			continue;
16868c2ecf20Sopenharmony_ci
16878c2ecf20Sopenharmony_ci		batadv_iv_ogm_process_per_outif(skb, ogm_offset, orig_node,
16888c2ecf20Sopenharmony_ci						if_incoming, hard_iface);
16898c2ecf20Sopenharmony_ci
16908c2ecf20Sopenharmony_ci		batadv_hardif_put(hard_iface);
16918c2ecf20Sopenharmony_ci	}
16928c2ecf20Sopenharmony_ci	rcu_read_unlock();
16938c2ecf20Sopenharmony_ci
16948c2ecf20Sopenharmony_ci	batadv_orig_node_put(orig_node);
16958c2ecf20Sopenharmony_ci}
16968c2ecf20Sopenharmony_ci
16978c2ecf20Sopenharmony_cistatic void batadv_iv_send_outstanding_bat_ogm_packet(struct work_struct *work)
16988c2ecf20Sopenharmony_ci{
16998c2ecf20Sopenharmony_ci	struct delayed_work *delayed_work;
17008c2ecf20Sopenharmony_ci	struct batadv_forw_packet *forw_packet;
17018c2ecf20Sopenharmony_ci	struct batadv_priv *bat_priv;
17028c2ecf20Sopenharmony_ci	bool dropped = false;
17038c2ecf20Sopenharmony_ci
17048c2ecf20Sopenharmony_ci	delayed_work = to_delayed_work(work);
17058c2ecf20Sopenharmony_ci	forw_packet = container_of(delayed_work, struct batadv_forw_packet,
17068c2ecf20Sopenharmony_ci				   delayed_work);
17078c2ecf20Sopenharmony_ci	bat_priv = netdev_priv(forw_packet->if_incoming->soft_iface);
17088c2ecf20Sopenharmony_ci
17098c2ecf20Sopenharmony_ci	if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING) {
17108c2ecf20Sopenharmony_ci		dropped = true;
17118c2ecf20Sopenharmony_ci		goto out;
17128c2ecf20Sopenharmony_ci	}
17138c2ecf20Sopenharmony_ci
17148c2ecf20Sopenharmony_ci	batadv_iv_ogm_emit(forw_packet);
17158c2ecf20Sopenharmony_ci
17168c2ecf20Sopenharmony_ci	/* we have to have at least one packet in the queue to determine the
17178c2ecf20Sopenharmony_ci	 * queues wake up time unless we are shutting down.
17188c2ecf20Sopenharmony_ci	 *
17198c2ecf20Sopenharmony_ci	 * only re-schedule if this is the "original" copy, e.g. the OGM of the
17208c2ecf20Sopenharmony_ci	 * primary interface should only be rescheduled once per period, but
17218c2ecf20Sopenharmony_ci	 * this function will be called for the forw_packet instances of the
17228c2ecf20Sopenharmony_ci	 * other secondary interfaces as well.
17238c2ecf20Sopenharmony_ci	 */
17248c2ecf20Sopenharmony_ci	if (forw_packet->own &&
17258c2ecf20Sopenharmony_ci	    forw_packet->if_incoming == forw_packet->if_outgoing)
17268c2ecf20Sopenharmony_ci		batadv_iv_ogm_schedule(forw_packet->if_incoming);
17278c2ecf20Sopenharmony_ci
17288c2ecf20Sopenharmony_ciout:
17298c2ecf20Sopenharmony_ci	/* do we get something for free()? */
17308c2ecf20Sopenharmony_ci	if (batadv_forw_packet_steal(forw_packet,
17318c2ecf20Sopenharmony_ci				     &bat_priv->forw_bat_list_lock))
17328c2ecf20Sopenharmony_ci		batadv_forw_packet_free(forw_packet, dropped);
17338c2ecf20Sopenharmony_ci}
17348c2ecf20Sopenharmony_ci
17358c2ecf20Sopenharmony_cistatic int batadv_iv_ogm_receive(struct sk_buff *skb,
17368c2ecf20Sopenharmony_ci				 struct batadv_hard_iface *if_incoming)
17378c2ecf20Sopenharmony_ci{
17388c2ecf20Sopenharmony_ci	struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
17398c2ecf20Sopenharmony_ci	struct batadv_ogm_packet *ogm_packet;
17408c2ecf20Sopenharmony_ci	u8 *packet_pos;
17418c2ecf20Sopenharmony_ci	int ogm_offset;
17428c2ecf20Sopenharmony_ci	bool res;
17438c2ecf20Sopenharmony_ci	int ret = NET_RX_DROP;
17448c2ecf20Sopenharmony_ci
17458c2ecf20Sopenharmony_ci	res = batadv_check_management_packet(skb, if_incoming, BATADV_OGM_HLEN);
17468c2ecf20Sopenharmony_ci	if (!res)
17478c2ecf20Sopenharmony_ci		goto free_skb;
17488c2ecf20Sopenharmony_ci
17498c2ecf20Sopenharmony_ci	/* did we receive a B.A.T.M.A.N. IV OGM packet on an interface
17508c2ecf20Sopenharmony_ci	 * that does not have B.A.T.M.A.N. IV enabled ?
17518c2ecf20Sopenharmony_ci	 */
17528c2ecf20Sopenharmony_ci	if (bat_priv->algo_ops->iface.enable != batadv_iv_ogm_iface_enable)
17538c2ecf20Sopenharmony_ci		goto free_skb;
17548c2ecf20Sopenharmony_ci
17558c2ecf20Sopenharmony_ci	batadv_inc_counter(bat_priv, BATADV_CNT_MGMT_RX);
17568c2ecf20Sopenharmony_ci	batadv_add_counter(bat_priv, BATADV_CNT_MGMT_RX_BYTES,
17578c2ecf20Sopenharmony_ci			   skb->len + ETH_HLEN);
17588c2ecf20Sopenharmony_ci
17598c2ecf20Sopenharmony_ci	ogm_offset = 0;
17608c2ecf20Sopenharmony_ci	ogm_packet = (struct batadv_ogm_packet *)skb->data;
17618c2ecf20Sopenharmony_ci
17628c2ecf20Sopenharmony_ci	/* unpack the aggregated packets and process them one by one */
17638c2ecf20Sopenharmony_ci	while (batadv_iv_ogm_aggr_packet(ogm_offset, skb_headlen(skb),
17648c2ecf20Sopenharmony_ci					 ogm_packet)) {
17658c2ecf20Sopenharmony_ci		batadv_iv_ogm_process(skb, ogm_offset, if_incoming);
17668c2ecf20Sopenharmony_ci
17678c2ecf20Sopenharmony_ci		ogm_offset += BATADV_OGM_HLEN;
17688c2ecf20Sopenharmony_ci		ogm_offset += ntohs(ogm_packet->tvlv_len);
17698c2ecf20Sopenharmony_ci
17708c2ecf20Sopenharmony_ci		packet_pos = skb->data + ogm_offset;
17718c2ecf20Sopenharmony_ci		ogm_packet = (struct batadv_ogm_packet *)packet_pos;
17728c2ecf20Sopenharmony_ci	}
17738c2ecf20Sopenharmony_ci
17748c2ecf20Sopenharmony_ci	ret = NET_RX_SUCCESS;
17758c2ecf20Sopenharmony_ci
17768c2ecf20Sopenharmony_cifree_skb:
17778c2ecf20Sopenharmony_ci	if (ret == NET_RX_SUCCESS)
17788c2ecf20Sopenharmony_ci		consume_skb(skb);
17798c2ecf20Sopenharmony_ci	else
17808c2ecf20Sopenharmony_ci		kfree_skb(skb);
17818c2ecf20Sopenharmony_ci
17828c2ecf20Sopenharmony_ci	return ret;
17838c2ecf20Sopenharmony_ci}
17848c2ecf20Sopenharmony_ci
17858c2ecf20Sopenharmony_ci#ifdef CONFIG_BATMAN_ADV_DEBUGFS
17868c2ecf20Sopenharmony_ci/**
17878c2ecf20Sopenharmony_ci * batadv_iv_ogm_orig_print_neigh() - print neighbors for the originator table
17888c2ecf20Sopenharmony_ci * @orig_node: the orig_node for which the neighbors are printed
17898c2ecf20Sopenharmony_ci * @if_outgoing: outgoing interface for these entries
17908c2ecf20Sopenharmony_ci * @seq: debugfs table seq_file struct
17918c2ecf20Sopenharmony_ci *
17928c2ecf20Sopenharmony_ci * Must be called while holding an rcu lock.
17938c2ecf20Sopenharmony_ci */
17948c2ecf20Sopenharmony_cistatic void
17958c2ecf20Sopenharmony_cibatadv_iv_ogm_orig_print_neigh(struct batadv_orig_node *orig_node,
17968c2ecf20Sopenharmony_ci			       struct batadv_hard_iface *if_outgoing,
17978c2ecf20Sopenharmony_ci			       struct seq_file *seq)
17988c2ecf20Sopenharmony_ci{
17998c2ecf20Sopenharmony_ci	struct batadv_neigh_node *neigh_node;
18008c2ecf20Sopenharmony_ci	struct batadv_neigh_ifinfo *n_ifinfo;
18018c2ecf20Sopenharmony_ci
18028c2ecf20Sopenharmony_ci	hlist_for_each_entry_rcu(neigh_node, &orig_node->neigh_list, list) {
18038c2ecf20Sopenharmony_ci		n_ifinfo = batadv_neigh_ifinfo_get(neigh_node, if_outgoing);
18048c2ecf20Sopenharmony_ci		if (!n_ifinfo)
18058c2ecf20Sopenharmony_ci			continue;
18068c2ecf20Sopenharmony_ci
18078c2ecf20Sopenharmony_ci		seq_printf(seq, " %pM (%3i)",
18088c2ecf20Sopenharmony_ci			   neigh_node->addr,
18098c2ecf20Sopenharmony_ci			   n_ifinfo->bat_iv.tq_avg);
18108c2ecf20Sopenharmony_ci
18118c2ecf20Sopenharmony_ci		batadv_neigh_ifinfo_put(n_ifinfo);
18128c2ecf20Sopenharmony_ci	}
18138c2ecf20Sopenharmony_ci}
18148c2ecf20Sopenharmony_ci
18158c2ecf20Sopenharmony_ci/**
18168c2ecf20Sopenharmony_ci * batadv_iv_ogm_orig_print() - print the originator table
18178c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
18188c2ecf20Sopenharmony_ci * @seq: debugfs table seq_file struct
18198c2ecf20Sopenharmony_ci * @if_outgoing: the outgoing interface for which this should be printed
18208c2ecf20Sopenharmony_ci */
18218c2ecf20Sopenharmony_cistatic void batadv_iv_ogm_orig_print(struct batadv_priv *bat_priv,
18228c2ecf20Sopenharmony_ci				     struct seq_file *seq,
18238c2ecf20Sopenharmony_ci				     struct batadv_hard_iface *if_outgoing)
18248c2ecf20Sopenharmony_ci{
18258c2ecf20Sopenharmony_ci	struct batadv_neigh_node *neigh_node;
18268c2ecf20Sopenharmony_ci	struct batadv_hashtable *hash = bat_priv->orig_hash;
18278c2ecf20Sopenharmony_ci	int last_seen_msecs, last_seen_secs;
18288c2ecf20Sopenharmony_ci	struct batadv_orig_node *orig_node;
18298c2ecf20Sopenharmony_ci	struct batadv_neigh_ifinfo *n_ifinfo;
18308c2ecf20Sopenharmony_ci	unsigned long last_seen_jiffies;
18318c2ecf20Sopenharmony_ci	struct hlist_head *head;
18328c2ecf20Sopenharmony_ci	int batman_count = 0;
18338c2ecf20Sopenharmony_ci	u32 i;
18348c2ecf20Sopenharmony_ci
18358c2ecf20Sopenharmony_ci	seq_puts(seq,
18368c2ecf20Sopenharmony_ci		 "  Originator      last-seen (#/255)           Nexthop [outgoingIF]:   Potential nexthops ...\n");
18378c2ecf20Sopenharmony_ci
18388c2ecf20Sopenharmony_ci	for (i = 0; i < hash->size; i++) {
18398c2ecf20Sopenharmony_ci		head = &hash->table[i];
18408c2ecf20Sopenharmony_ci
18418c2ecf20Sopenharmony_ci		rcu_read_lock();
18428c2ecf20Sopenharmony_ci		hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
18438c2ecf20Sopenharmony_ci			neigh_node = batadv_orig_router_get(orig_node,
18448c2ecf20Sopenharmony_ci							    if_outgoing);
18458c2ecf20Sopenharmony_ci			if (!neigh_node)
18468c2ecf20Sopenharmony_ci				continue;
18478c2ecf20Sopenharmony_ci
18488c2ecf20Sopenharmony_ci			n_ifinfo = batadv_neigh_ifinfo_get(neigh_node,
18498c2ecf20Sopenharmony_ci							   if_outgoing);
18508c2ecf20Sopenharmony_ci			if (!n_ifinfo)
18518c2ecf20Sopenharmony_ci				goto next;
18528c2ecf20Sopenharmony_ci
18538c2ecf20Sopenharmony_ci			if (n_ifinfo->bat_iv.tq_avg == 0)
18548c2ecf20Sopenharmony_ci				goto next;
18558c2ecf20Sopenharmony_ci
18568c2ecf20Sopenharmony_ci			last_seen_jiffies = jiffies - orig_node->last_seen;
18578c2ecf20Sopenharmony_ci			last_seen_msecs = jiffies_to_msecs(last_seen_jiffies);
18588c2ecf20Sopenharmony_ci			last_seen_secs = last_seen_msecs / 1000;
18598c2ecf20Sopenharmony_ci			last_seen_msecs = last_seen_msecs % 1000;
18608c2ecf20Sopenharmony_ci
18618c2ecf20Sopenharmony_ci			seq_printf(seq, "%pM %4i.%03is   (%3i) %pM [%10s]:",
18628c2ecf20Sopenharmony_ci				   orig_node->orig, last_seen_secs,
18638c2ecf20Sopenharmony_ci				   last_seen_msecs, n_ifinfo->bat_iv.tq_avg,
18648c2ecf20Sopenharmony_ci				   neigh_node->addr,
18658c2ecf20Sopenharmony_ci				   neigh_node->if_incoming->net_dev->name);
18668c2ecf20Sopenharmony_ci
18678c2ecf20Sopenharmony_ci			batadv_iv_ogm_orig_print_neigh(orig_node, if_outgoing,
18688c2ecf20Sopenharmony_ci						       seq);
18698c2ecf20Sopenharmony_ci			seq_putc(seq, '\n');
18708c2ecf20Sopenharmony_ci			batman_count++;
18718c2ecf20Sopenharmony_ci
18728c2ecf20Sopenharmony_cinext:
18738c2ecf20Sopenharmony_ci			batadv_neigh_node_put(neigh_node);
18748c2ecf20Sopenharmony_ci			if (n_ifinfo)
18758c2ecf20Sopenharmony_ci				batadv_neigh_ifinfo_put(n_ifinfo);
18768c2ecf20Sopenharmony_ci		}
18778c2ecf20Sopenharmony_ci		rcu_read_unlock();
18788c2ecf20Sopenharmony_ci	}
18798c2ecf20Sopenharmony_ci
18808c2ecf20Sopenharmony_ci	if (batman_count == 0)
18818c2ecf20Sopenharmony_ci		seq_puts(seq, "No batman nodes in range ...\n");
18828c2ecf20Sopenharmony_ci}
18838c2ecf20Sopenharmony_ci#endif
18848c2ecf20Sopenharmony_ci
18858c2ecf20Sopenharmony_ci/**
18868c2ecf20Sopenharmony_ci * batadv_iv_ogm_neigh_get_tq_avg() - Get the TQ average for a neighbour on a
18878c2ecf20Sopenharmony_ci *  given outgoing interface.
18888c2ecf20Sopenharmony_ci * @neigh_node: Neighbour of interest
18898c2ecf20Sopenharmony_ci * @if_outgoing: Outgoing interface of interest
18908c2ecf20Sopenharmony_ci * @tq_avg: Pointer of where to store the TQ average
18918c2ecf20Sopenharmony_ci *
18928c2ecf20Sopenharmony_ci * Return: False if no average TQ available, otherwise true.
18938c2ecf20Sopenharmony_ci */
18948c2ecf20Sopenharmony_cistatic bool
18958c2ecf20Sopenharmony_cibatadv_iv_ogm_neigh_get_tq_avg(struct batadv_neigh_node *neigh_node,
18968c2ecf20Sopenharmony_ci			       struct batadv_hard_iface *if_outgoing,
18978c2ecf20Sopenharmony_ci			       u8 *tq_avg)
18988c2ecf20Sopenharmony_ci{
18998c2ecf20Sopenharmony_ci	struct batadv_neigh_ifinfo *n_ifinfo;
19008c2ecf20Sopenharmony_ci
19018c2ecf20Sopenharmony_ci	n_ifinfo = batadv_neigh_ifinfo_get(neigh_node, if_outgoing);
19028c2ecf20Sopenharmony_ci	if (!n_ifinfo)
19038c2ecf20Sopenharmony_ci		return false;
19048c2ecf20Sopenharmony_ci
19058c2ecf20Sopenharmony_ci	*tq_avg = n_ifinfo->bat_iv.tq_avg;
19068c2ecf20Sopenharmony_ci	batadv_neigh_ifinfo_put(n_ifinfo);
19078c2ecf20Sopenharmony_ci
19088c2ecf20Sopenharmony_ci	return true;
19098c2ecf20Sopenharmony_ci}
19108c2ecf20Sopenharmony_ci
19118c2ecf20Sopenharmony_ci/**
19128c2ecf20Sopenharmony_ci * batadv_iv_ogm_orig_dump_subentry() - Dump an originator subentry into a
19138c2ecf20Sopenharmony_ci *  message
19148c2ecf20Sopenharmony_ci * @msg: Netlink message to dump into
19158c2ecf20Sopenharmony_ci * @portid: Port making netlink request
19168c2ecf20Sopenharmony_ci * @seq: Sequence number of netlink message
19178c2ecf20Sopenharmony_ci * @bat_priv: The bat priv with all the soft interface information
19188c2ecf20Sopenharmony_ci * @if_outgoing: Limit dump to entries with this outgoing interface
19198c2ecf20Sopenharmony_ci * @orig_node: Originator to dump
19208c2ecf20Sopenharmony_ci * @neigh_node: Single hops neighbour
19218c2ecf20Sopenharmony_ci * @best: Is the best originator
19228c2ecf20Sopenharmony_ci *
19238c2ecf20Sopenharmony_ci * Return: Error code, or 0 on success
19248c2ecf20Sopenharmony_ci */
19258c2ecf20Sopenharmony_cistatic int
19268c2ecf20Sopenharmony_cibatadv_iv_ogm_orig_dump_subentry(struct sk_buff *msg, u32 portid, u32 seq,
19278c2ecf20Sopenharmony_ci				 struct batadv_priv *bat_priv,
19288c2ecf20Sopenharmony_ci				 struct batadv_hard_iface *if_outgoing,
19298c2ecf20Sopenharmony_ci				 struct batadv_orig_node *orig_node,
19308c2ecf20Sopenharmony_ci				 struct batadv_neigh_node *neigh_node,
19318c2ecf20Sopenharmony_ci				 bool best)
19328c2ecf20Sopenharmony_ci{
19338c2ecf20Sopenharmony_ci	void *hdr;
19348c2ecf20Sopenharmony_ci	u8 tq_avg;
19358c2ecf20Sopenharmony_ci	unsigned int last_seen_msecs;
19368c2ecf20Sopenharmony_ci
19378c2ecf20Sopenharmony_ci	last_seen_msecs = jiffies_to_msecs(jiffies - orig_node->last_seen);
19388c2ecf20Sopenharmony_ci
19398c2ecf20Sopenharmony_ci	if (!batadv_iv_ogm_neigh_get_tq_avg(neigh_node, if_outgoing, &tq_avg))
19408c2ecf20Sopenharmony_ci		return 0;
19418c2ecf20Sopenharmony_ci
19428c2ecf20Sopenharmony_ci	if (if_outgoing != BATADV_IF_DEFAULT &&
19438c2ecf20Sopenharmony_ci	    if_outgoing != neigh_node->if_incoming)
19448c2ecf20Sopenharmony_ci		return 0;
19458c2ecf20Sopenharmony_ci
19468c2ecf20Sopenharmony_ci	hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family,
19478c2ecf20Sopenharmony_ci			  NLM_F_MULTI, BATADV_CMD_GET_ORIGINATORS);
19488c2ecf20Sopenharmony_ci	if (!hdr)
19498c2ecf20Sopenharmony_ci		return -ENOBUFS;
19508c2ecf20Sopenharmony_ci
19518c2ecf20Sopenharmony_ci	if (nla_put(msg, BATADV_ATTR_ORIG_ADDRESS, ETH_ALEN,
19528c2ecf20Sopenharmony_ci		    orig_node->orig) ||
19538c2ecf20Sopenharmony_ci	    nla_put(msg, BATADV_ATTR_NEIGH_ADDRESS, ETH_ALEN,
19548c2ecf20Sopenharmony_ci		    neigh_node->addr) ||
19558c2ecf20Sopenharmony_ci	    nla_put_u32(msg, BATADV_ATTR_HARD_IFINDEX,
19568c2ecf20Sopenharmony_ci			neigh_node->if_incoming->net_dev->ifindex) ||
19578c2ecf20Sopenharmony_ci	    nla_put_u8(msg, BATADV_ATTR_TQ, tq_avg) ||
19588c2ecf20Sopenharmony_ci	    nla_put_u32(msg, BATADV_ATTR_LAST_SEEN_MSECS,
19598c2ecf20Sopenharmony_ci			last_seen_msecs))
19608c2ecf20Sopenharmony_ci		goto nla_put_failure;
19618c2ecf20Sopenharmony_ci
19628c2ecf20Sopenharmony_ci	if (best && nla_put_flag(msg, BATADV_ATTR_FLAG_BEST))
19638c2ecf20Sopenharmony_ci		goto nla_put_failure;
19648c2ecf20Sopenharmony_ci
19658c2ecf20Sopenharmony_ci	genlmsg_end(msg, hdr);
19668c2ecf20Sopenharmony_ci	return 0;
19678c2ecf20Sopenharmony_ci
19688c2ecf20Sopenharmony_ci nla_put_failure:
19698c2ecf20Sopenharmony_ci	genlmsg_cancel(msg, hdr);
19708c2ecf20Sopenharmony_ci	return -EMSGSIZE;
19718c2ecf20Sopenharmony_ci}
19728c2ecf20Sopenharmony_ci
19738c2ecf20Sopenharmony_ci/**
19748c2ecf20Sopenharmony_ci * batadv_iv_ogm_orig_dump_entry() - Dump an originator entry into a message
19758c2ecf20Sopenharmony_ci * @msg: Netlink message to dump into
19768c2ecf20Sopenharmony_ci * @portid: Port making netlink request
19778c2ecf20Sopenharmony_ci * @seq: Sequence number of netlink message
19788c2ecf20Sopenharmony_ci * @bat_priv: The bat priv with all the soft interface information
19798c2ecf20Sopenharmony_ci * @if_outgoing: Limit dump to entries with this outgoing interface
19808c2ecf20Sopenharmony_ci * @orig_node: Originator to dump
19818c2ecf20Sopenharmony_ci * @sub_s: Number of sub entries to skip
19828c2ecf20Sopenharmony_ci *
19838c2ecf20Sopenharmony_ci * This function assumes the caller holds rcu_read_lock().
19848c2ecf20Sopenharmony_ci *
19858c2ecf20Sopenharmony_ci * Return: Error code, or 0 on success
19868c2ecf20Sopenharmony_ci */
19878c2ecf20Sopenharmony_cistatic int
19888c2ecf20Sopenharmony_cibatadv_iv_ogm_orig_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
19898c2ecf20Sopenharmony_ci			      struct batadv_priv *bat_priv,
19908c2ecf20Sopenharmony_ci			      struct batadv_hard_iface *if_outgoing,
19918c2ecf20Sopenharmony_ci			      struct batadv_orig_node *orig_node, int *sub_s)
19928c2ecf20Sopenharmony_ci{
19938c2ecf20Sopenharmony_ci	struct batadv_neigh_node *neigh_node_best;
19948c2ecf20Sopenharmony_ci	struct batadv_neigh_node *neigh_node;
19958c2ecf20Sopenharmony_ci	int sub = 0;
19968c2ecf20Sopenharmony_ci	bool best;
19978c2ecf20Sopenharmony_ci	u8 tq_avg_best;
19988c2ecf20Sopenharmony_ci
19998c2ecf20Sopenharmony_ci	neigh_node_best = batadv_orig_router_get(orig_node, if_outgoing);
20008c2ecf20Sopenharmony_ci	if (!neigh_node_best)
20018c2ecf20Sopenharmony_ci		goto out;
20028c2ecf20Sopenharmony_ci
20038c2ecf20Sopenharmony_ci	if (!batadv_iv_ogm_neigh_get_tq_avg(neigh_node_best, if_outgoing,
20048c2ecf20Sopenharmony_ci					    &tq_avg_best))
20058c2ecf20Sopenharmony_ci		goto out;
20068c2ecf20Sopenharmony_ci
20078c2ecf20Sopenharmony_ci	if (tq_avg_best == 0)
20088c2ecf20Sopenharmony_ci		goto out;
20098c2ecf20Sopenharmony_ci
20108c2ecf20Sopenharmony_ci	hlist_for_each_entry_rcu(neigh_node, &orig_node->neigh_list, list) {
20118c2ecf20Sopenharmony_ci		if (sub++ < *sub_s)
20128c2ecf20Sopenharmony_ci			continue;
20138c2ecf20Sopenharmony_ci
20148c2ecf20Sopenharmony_ci		best = (neigh_node == neigh_node_best);
20158c2ecf20Sopenharmony_ci
20168c2ecf20Sopenharmony_ci		if (batadv_iv_ogm_orig_dump_subentry(msg, portid, seq,
20178c2ecf20Sopenharmony_ci						     bat_priv, if_outgoing,
20188c2ecf20Sopenharmony_ci						     orig_node, neigh_node,
20198c2ecf20Sopenharmony_ci						     best)) {
20208c2ecf20Sopenharmony_ci			batadv_neigh_node_put(neigh_node_best);
20218c2ecf20Sopenharmony_ci
20228c2ecf20Sopenharmony_ci			*sub_s = sub - 1;
20238c2ecf20Sopenharmony_ci			return -EMSGSIZE;
20248c2ecf20Sopenharmony_ci		}
20258c2ecf20Sopenharmony_ci	}
20268c2ecf20Sopenharmony_ci
20278c2ecf20Sopenharmony_ci out:
20288c2ecf20Sopenharmony_ci	if (neigh_node_best)
20298c2ecf20Sopenharmony_ci		batadv_neigh_node_put(neigh_node_best);
20308c2ecf20Sopenharmony_ci
20318c2ecf20Sopenharmony_ci	*sub_s = 0;
20328c2ecf20Sopenharmony_ci	return 0;
20338c2ecf20Sopenharmony_ci}
20348c2ecf20Sopenharmony_ci
20358c2ecf20Sopenharmony_ci/**
20368c2ecf20Sopenharmony_ci * batadv_iv_ogm_orig_dump_bucket() - Dump an originator bucket into a
20378c2ecf20Sopenharmony_ci *  message
20388c2ecf20Sopenharmony_ci * @msg: Netlink message to dump into
20398c2ecf20Sopenharmony_ci * @portid: Port making netlink request
20408c2ecf20Sopenharmony_ci * @seq: Sequence number of netlink message
20418c2ecf20Sopenharmony_ci * @bat_priv: The bat priv with all the soft interface information
20428c2ecf20Sopenharmony_ci * @if_outgoing: Limit dump to entries with this outgoing interface
20438c2ecf20Sopenharmony_ci * @head: Bucket to be dumped
20448c2ecf20Sopenharmony_ci * @idx_s: Number of entries to be skipped
20458c2ecf20Sopenharmony_ci * @sub: Number of sub entries to be skipped
20468c2ecf20Sopenharmony_ci *
20478c2ecf20Sopenharmony_ci * Return: Error code, or 0 on success
20488c2ecf20Sopenharmony_ci */
20498c2ecf20Sopenharmony_cistatic int
20508c2ecf20Sopenharmony_cibatadv_iv_ogm_orig_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq,
20518c2ecf20Sopenharmony_ci			       struct batadv_priv *bat_priv,
20528c2ecf20Sopenharmony_ci			       struct batadv_hard_iface *if_outgoing,
20538c2ecf20Sopenharmony_ci			       struct hlist_head *head, int *idx_s, int *sub)
20548c2ecf20Sopenharmony_ci{
20558c2ecf20Sopenharmony_ci	struct batadv_orig_node *orig_node;
20568c2ecf20Sopenharmony_ci	int idx = 0;
20578c2ecf20Sopenharmony_ci
20588c2ecf20Sopenharmony_ci	rcu_read_lock();
20598c2ecf20Sopenharmony_ci	hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
20608c2ecf20Sopenharmony_ci		if (idx++ < *idx_s)
20618c2ecf20Sopenharmony_ci			continue;
20628c2ecf20Sopenharmony_ci
20638c2ecf20Sopenharmony_ci		if (batadv_iv_ogm_orig_dump_entry(msg, portid, seq, bat_priv,
20648c2ecf20Sopenharmony_ci						  if_outgoing, orig_node,
20658c2ecf20Sopenharmony_ci						  sub)) {
20668c2ecf20Sopenharmony_ci			rcu_read_unlock();
20678c2ecf20Sopenharmony_ci			*idx_s = idx - 1;
20688c2ecf20Sopenharmony_ci			return -EMSGSIZE;
20698c2ecf20Sopenharmony_ci		}
20708c2ecf20Sopenharmony_ci	}
20718c2ecf20Sopenharmony_ci	rcu_read_unlock();
20728c2ecf20Sopenharmony_ci
20738c2ecf20Sopenharmony_ci	*idx_s = 0;
20748c2ecf20Sopenharmony_ci	*sub = 0;
20758c2ecf20Sopenharmony_ci	return 0;
20768c2ecf20Sopenharmony_ci}
20778c2ecf20Sopenharmony_ci
20788c2ecf20Sopenharmony_ci/**
20798c2ecf20Sopenharmony_ci * batadv_iv_ogm_orig_dump() - Dump the originators into a message
20808c2ecf20Sopenharmony_ci * @msg: Netlink message to dump into
20818c2ecf20Sopenharmony_ci * @cb: Control block containing additional options
20828c2ecf20Sopenharmony_ci * @bat_priv: The bat priv with all the soft interface information
20838c2ecf20Sopenharmony_ci * @if_outgoing: Limit dump to entries with this outgoing interface
20848c2ecf20Sopenharmony_ci */
20858c2ecf20Sopenharmony_cistatic void
20868c2ecf20Sopenharmony_cibatadv_iv_ogm_orig_dump(struct sk_buff *msg, struct netlink_callback *cb,
20878c2ecf20Sopenharmony_ci			struct batadv_priv *bat_priv,
20888c2ecf20Sopenharmony_ci			struct batadv_hard_iface *if_outgoing)
20898c2ecf20Sopenharmony_ci{
20908c2ecf20Sopenharmony_ci	struct batadv_hashtable *hash = bat_priv->orig_hash;
20918c2ecf20Sopenharmony_ci	struct hlist_head *head;
20928c2ecf20Sopenharmony_ci	int bucket = cb->args[0];
20938c2ecf20Sopenharmony_ci	int idx = cb->args[1];
20948c2ecf20Sopenharmony_ci	int sub = cb->args[2];
20958c2ecf20Sopenharmony_ci	int portid = NETLINK_CB(cb->skb).portid;
20968c2ecf20Sopenharmony_ci
20978c2ecf20Sopenharmony_ci	while (bucket < hash->size) {
20988c2ecf20Sopenharmony_ci		head = &hash->table[bucket];
20998c2ecf20Sopenharmony_ci
21008c2ecf20Sopenharmony_ci		if (batadv_iv_ogm_orig_dump_bucket(msg, portid,
21018c2ecf20Sopenharmony_ci						   cb->nlh->nlmsg_seq,
21028c2ecf20Sopenharmony_ci						   bat_priv, if_outgoing, head,
21038c2ecf20Sopenharmony_ci						   &idx, &sub))
21048c2ecf20Sopenharmony_ci			break;
21058c2ecf20Sopenharmony_ci
21068c2ecf20Sopenharmony_ci		bucket++;
21078c2ecf20Sopenharmony_ci	}
21088c2ecf20Sopenharmony_ci
21098c2ecf20Sopenharmony_ci	cb->args[0] = bucket;
21108c2ecf20Sopenharmony_ci	cb->args[1] = idx;
21118c2ecf20Sopenharmony_ci	cb->args[2] = sub;
21128c2ecf20Sopenharmony_ci}
21138c2ecf20Sopenharmony_ci
21148c2ecf20Sopenharmony_ci#ifdef CONFIG_BATMAN_ADV_DEBUGFS
21158c2ecf20Sopenharmony_ci/**
21168c2ecf20Sopenharmony_ci * batadv_iv_hardif_neigh_print() - print a single hop neighbour node
21178c2ecf20Sopenharmony_ci * @seq: neighbour table seq_file struct
21188c2ecf20Sopenharmony_ci * @hardif_neigh: hardif neighbour information
21198c2ecf20Sopenharmony_ci */
21208c2ecf20Sopenharmony_cistatic void
21218c2ecf20Sopenharmony_cibatadv_iv_hardif_neigh_print(struct seq_file *seq,
21228c2ecf20Sopenharmony_ci			     struct batadv_hardif_neigh_node *hardif_neigh)
21238c2ecf20Sopenharmony_ci{
21248c2ecf20Sopenharmony_ci	int last_secs, last_msecs;
21258c2ecf20Sopenharmony_ci
21268c2ecf20Sopenharmony_ci	last_secs = jiffies_to_msecs(jiffies - hardif_neigh->last_seen) / 1000;
21278c2ecf20Sopenharmony_ci	last_msecs = jiffies_to_msecs(jiffies - hardif_neigh->last_seen) % 1000;
21288c2ecf20Sopenharmony_ci
21298c2ecf20Sopenharmony_ci	seq_printf(seq, "   %10s   %pM %4i.%03is\n",
21308c2ecf20Sopenharmony_ci		   hardif_neigh->if_incoming->net_dev->name,
21318c2ecf20Sopenharmony_ci		   hardif_neigh->addr, last_secs, last_msecs);
21328c2ecf20Sopenharmony_ci}
21338c2ecf20Sopenharmony_ci
21348c2ecf20Sopenharmony_ci/**
21358c2ecf20Sopenharmony_ci * batadv_iv_ogm_neigh_print() - print the single hop neighbour list
21368c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
21378c2ecf20Sopenharmony_ci * @seq: neighbour table seq_file struct
21388c2ecf20Sopenharmony_ci */
21398c2ecf20Sopenharmony_cistatic void batadv_iv_neigh_print(struct batadv_priv *bat_priv,
21408c2ecf20Sopenharmony_ci				  struct seq_file *seq)
21418c2ecf20Sopenharmony_ci{
21428c2ecf20Sopenharmony_ci	struct net_device *net_dev = (struct net_device *)seq->private;
21438c2ecf20Sopenharmony_ci	struct batadv_hardif_neigh_node *hardif_neigh;
21448c2ecf20Sopenharmony_ci	struct batadv_hard_iface *hard_iface;
21458c2ecf20Sopenharmony_ci	int batman_count = 0;
21468c2ecf20Sopenharmony_ci
21478c2ecf20Sopenharmony_ci	seq_puts(seq, "           IF        Neighbor      last-seen\n");
21488c2ecf20Sopenharmony_ci
21498c2ecf20Sopenharmony_ci	rcu_read_lock();
21508c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
21518c2ecf20Sopenharmony_ci		if (hard_iface->soft_iface != net_dev)
21528c2ecf20Sopenharmony_ci			continue;
21538c2ecf20Sopenharmony_ci
21548c2ecf20Sopenharmony_ci		hlist_for_each_entry_rcu(hardif_neigh,
21558c2ecf20Sopenharmony_ci					 &hard_iface->neigh_list, list) {
21568c2ecf20Sopenharmony_ci			batadv_iv_hardif_neigh_print(seq, hardif_neigh);
21578c2ecf20Sopenharmony_ci			batman_count++;
21588c2ecf20Sopenharmony_ci		}
21598c2ecf20Sopenharmony_ci	}
21608c2ecf20Sopenharmony_ci	rcu_read_unlock();
21618c2ecf20Sopenharmony_ci
21628c2ecf20Sopenharmony_ci	if (batman_count == 0)
21638c2ecf20Sopenharmony_ci		seq_puts(seq, "No batman nodes in range ...\n");
21648c2ecf20Sopenharmony_ci}
21658c2ecf20Sopenharmony_ci#endif
21668c2ecf20Sopenharmony_ci
21678c2ecf20Sopenharmony_ci/**
21688c2ecf20Sopenharmony_ci * batadv_iv_ogm_neigh_diff() - calculate tq difference of two neighbors
21698c2ecf20Sopenharmony_ci * @neigh1: the first neighbor object of the comparison
21708c2ecf20Sopenharmony_ci * @if_outgoing1: outgoing interface for the first neighbor
21718c2ecf20Sopenharmony_ci * @neigh2: the second neighbor object of the comparison
21728c2ecf20Sopenharmony_ci * @if_outgoing2: outgoing interface for the second neighbor
21738c2ecf20Sopenharmony_ci * @diff: pointer to integer receiving the calculated difference
21748c2ecf20Sopenharmony_ci *
21758c2ecf20Sopenharmony_ci * The content of *@diff is only valid when this function returns true.
21768c2ecf20Sopenharmony_ci * It is less, equal to or greater than 0 if the metric via neigh1 is lower,
21778c2ecf20Sopenharmony_ci * the same as or higher than the metric via neigh2
21788c2ecf20Sopenharmony_ci *
21798c2ecf20Sopenharmony_ci * Return: true when the difference could be calculated, false otherwise
21808c2ecf20Sopenharmony_ci */
21818c2ecf20Sopenharmony_cistatic bool batadv_iv_ogm_neigh_diff(struct batadv_neigh_node *neigh1,
21828c2ecf20Sopenharmony_ci				     struct batadv_hard_iface *if_outgoing1,
21838c2ecf20Sopenharmony_ci				     struct batadv_neigh_node *neigh2,
21848c2ecf20Sopenharmony_ci				     struct batadv_hard_iface *if_outgoing2,
21858c2ecf20Sopenharmony_ci				     int *diff)
21868c2ecf20Sopenharmony_ci{
21878c2ecf20Sopenharmony_ci	struct batadv_neigh_ifinfo *neigh1_ifinfo, *neigh2_ifinfo;
21888c2ecf20Sopenharmony_ci	u8 tq1, tq2;
21898c2ecf20Sopenharmony_ci	bool ret = true;
21908c2ecf20Sopenharmony_ci
21918c2ecf20Sopenharmony_ci	neigh1_ifinfo = batadv_neigh_ifinfo_get(neigh1, if_outgoing1);
21928c2ecf20Sopenharmony_ci	neigh2_ifinfo = batadv_neigh_ifinfo_get(neigh2, if_outgoing2);
21938c2ecf20Sopenharmony_ci
21948c2ecf20Sopenharmony_ci	if (!neigh1_ifinfo || !neigh2_ifinfo) {
21958c2ecf20Sopenharmony_ci		ret = false;
21968c2ecf20Sopenharmony_ci		goto out;
21978c2ecf20Sopenharmony_ci	}
21988c2ecf20Sopenharmony_ci
21998c2ecf20Sopenharmony_ci	tq1 = neigh1_ifinfo->bat_iv.tq_avg;
22008c2ecf20Sopenharmony_ci	tq2 = neigh2_ifinfo->bat_iv.tq_avg;
22018c2ecf20Sopenharmony_ci	*diff = (int)tq1 - (int)tq2;
22028c2ecf20Sopenharmony_ci
22038c2ecf20Sopenharmony_ciout:
22048c2ecf20Sopenharmony_ci	if (neigh1_ifinfo)
22058c2ecf20Sopenharmony_ci		batadv_neigh_ifinfo_put(neigh1_ifinfo);
22068c2ecf20Sopenharmony_ci	if (neigh2_ifinfo)
22078c2ecf20Sopenharmony_ci		batadv_neigh_ifinfo_put(neigh2_ifinfo);
22088c2ecf20Sopenharmony_ci
22098c2ecf20Sopenharmony_ci	return ret;
22108c2ecf20Sopenharmony_ci}
22118c2ecf20Sopenharmony_ci
22128c2ecf20Sopenharmony_ci/**
22138c2ecf20Sopenharmony_ci * batadv_iv_ogm_neigh_dump_neigh() - Dump a neighbour into a netlink message
22148c2ecf20Sopenharmony_ci * @msg: Netlink message to dump into
22158c2ecf20Sopenharmony_ci * @portid: Port making netlink request
22168c2ecf20Sopenharmony_ci * @seq: Sequence number of netlink message
22178c2ecf20Sopenharmony_ci * @hardif_neigh: Neighbour to be dumped
22188c2ecf20Sopenharmony_ci *
22198c2ecf20Sopenharmony_ci * Return: Error code, or 0 on success
22208c2ecf20Sopenharmony_ci */
22218c2ecf20Sopenharmony_cistatic int
22228c2ecf20Sopenharmony_cibatadv_iv_ogm_neigh_dump_neigh(struct sk_buff *msg, u32 portid, u32 seq,
22238c2ecf20Sopenharmony_ci			       struct batadv_hardif_neigh_node *hardif_neigh)
22248c2ecf20Sopenharmony_ci{
22258c2ecf20Sopenharmony_ci	void *hdr;
22268c2ecf20Sopenharmony_ci	unsigned int last_seen_msecs;
22278c2ecf20Sopenharmony_ci
22288c2ecf20Sopenharmony_ci	last_seen_msecs = jiffies_to_msecs(jiffies - hardif_neigh->last_seen);
22298c2ecf20Sopenharmony_ci
22308c2ecf20Sopenharmony_ci	hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family,
22318c2ecf20Sopenharmony_ci			  NLM_F_MULTI, BATADV_CMD_GET_NEIGHBORS);
22328c2ecf20Sopenharmony_ci	if (!hdr)
22338c2ecf20Sopenharmony_ci		return -ENOBUFS;
22348c2ecf20Sopenharmony_ci
22358c2ecf20Sopenharmony_ci	if (nla_put(msg, BATADV_ATTR_NEIGH_ADDRESS, ETH_ALEN,
22368c2ecf20Sopenharmony_ci		    hardif_neigh->addr) ||
22378c2ecf20Sopenharmony_ci	    nla_put_u32(msg, BATADV_ATTR_HARD_IFINDEX,
22388c2ecf20Sopenharmony_ci			hardif_neigh->if_incoming->net_dev->ifindex) ||
22398c2ecf20Sopenharmony_ci	    nla_put_u32(msg, BATADV_ATTR_LAST_SEEN_MSECS,
22408c2ecf20Sopenharmony_ci			last_seen_msecs))
22418c2ecf20Sopenharmony_ci		goto nla_put_failure;
22428c2ecf20Sopenharmony_ci
22438c2ecf20Sopenharmony_ci	genlmsg_end(msg, hdr);
22448c2ecf20Sopenharmony_ci	return 0;
22458c2ecf20Sopenharmony_ci
22468c2ecf20Sopenharmony_ci nla_put_failure:
22478c2ecf20Sopenharmony_ci	genlmsg_cancel(msg, hdr);
22488c2ecf20Sopenharmony_ci	return -EMSGSIZE;
22498c2ecf20Sopenharmony_ci}
22508c2ecf20Sopenharmony_ci
22518c2ecf20Sopenharmony_ci/**
22528c2ecf20Sopenharmony_ci * batadv_iv_ogm_neigh_dump_hardif() - Dump the neighbours of a hard interface
22538c2ecf20Sopenharmony_ci *  into a message
22548c2ecf20Sopenharmony_ci * @msg: Netlink message to dump into
22558c2ecf20Sopenharmony_ci * @portid: Port making netlink request
22568c2ecf20Sopenharmony_ci * @seq: Sequence number of netlink message
22578c2ecf20Sopenharmony_ci * @bat_priv: The bat priv with all the soft interface information
22588c2ecf20Sopenharmony_ci * @hard_iface: Hard interface to dump the neighbours for
22598c2ecf20Sopenharmony_ci * @idx_s: Number of entries to skip
22608c2ecf20Sopenharmony_ci *
22618c2ecf20Sopenharmony_ci * This function assumes the caller holds rcu_read_lock().
22628c2ecf20Sopenharmony_ci *
22638c2ecf20Sopenharmony_ci * Return: Error code, or 0 on success
22648c2ecf20Sopenharmony_ci */
22658c2ecf20Sopenharmony_cistatic int
22668c2ecf20Sopenharmony_cibatadv_iv_ogm_neigh_dump_hardif(struct sk_buff *msg, u32 portid, u32 seq,
22678c2ecf20Sopenharmony_ci				struct batadv_priv *bat_priv,
22688c2ecf20Sopenharmony_ci				struct batadv_hard_iface *hard_iface,
22698c2ecf20Sopenharmony_ci				int *idx_s)
22708c2ecf20Sopenharmony_ci{
22718c2ecf20Sopenharmony_ci	struct batadv_hardif_neigh_node *hardif_neigh;
22728c2ecf20Sopenharmony_ci	int idx = 0;
22738c2ecf20Sopenharmony_ci
22748c2ecf20Sopenharmony_ci	hlist_for_each_entry_rcu(hardif_neigh,
22758c2ecf20Sopenharmony_ci				 &hard_iface->neigh_list, list) {
22768c2ecf20Sopenharmony_ci		if (idx++ < *idx_s)
22778c2ecf20Sopenharmony_ci			continue;
22788c2ecf20Sopenharmony_ci
22798c2ecf20Sopenharmony_ci		if (batadv_iv_ogm_neigh_dump_neigh(msg, portid, seq,
22808c2ecf20Sopenharmony_ci						   hardif_neigh)) {
22818c2ecf20Sopenharmony_ci			*idx_s = idx - 1;
22828c2ecf20Sopenharmony_ci			return -EMSGSIZE;
22838c2ecf20Sopenharmony_ci		}
22848c2ecf20Sopenharmony_ci	}
22858c2ecf20Sopenharmony_ci
22868c2ecf20Sopenharmony_ci	*idx_s = 0;
22878c2ecf20Sopenharmony_ci	return 0;
22888c2ecf20Sopenharmony_ci}
22898c2ecf20Sopenharmony_ci
22908c2ecf20Sopenharmony_ci/**
22918c2ecf20Sopenharmony_ci * batadv_iv_ogm_neigh_dump() - Dump the neighbours into a message
22928c2ecf20Sopenharmony_ci * @msg: Netlink message to dump into
22938c2ecf20Sopenharmony_ci * @cb: Control block containing additional options
22948c2ecf20Sopenharmony_ci * @bat_priv: The bat priv with all the soft interface information
22958c2ecf20Sopenharmony_ci * @single_hardif: Limit dump to this hard interface
22968c2ecf20Sopenharmony_ci */
22978c2ecf20Sopenharmony_cistatic void
22988c2ecf20Sopenharmony_cibatadv_iv_ogm_neigh_dump(struct sk_buff *msg, struct netlink_callback *cb,
22998c2ecf20Sopenharmony_ci			 struct batadv_priv *bat_priv,
23008c2ecf20Sopenharmony_ci			 struct batadv_hard_iface *single_hardif)
23018c2ecf20Sopenharmony_ci{
23028c2ecf20Sopenharmony_ci	struct batadv_hard_iface *hard_iface;
23038c2ecf20Sopenharmony_ci	int i_hardif = 0;
23048c2ecf20Sopenharmony_ci	int i_hardif_s = cb->args[0];
23058c2ecf20Sopenharmony_ci	int idx = cb->args[1];
23068c2ecf20Sopenharmony_ci	int portid = NETLINK_CB(cb->skb).portid;
23078c2ecf20Sopenharmony_ci
23088c2ecf20Sopenharmony_ci	rcu_read_lock();
23098c2ecf20Sopenharmony_ci	if (single_hardif) {
23108c2ecf20Sopenharmony_ci		if (i_hardif_s == 0) {
23118c2ecf20Sopenharmony_ci			if (batadv_iv_ogm_neigh_dump_hardif(msg, portid,
23128c2ecf20Sopenharmony_ci							    cb->nlh->nlmsg_seq,
23138c2ecf20Sopenharmony_ci							    bat_priv,
23148c2ecf20Sopenharmony_ci							    single_hardif,
23158c2ecf20Sopenharmony_ci							    &idx) == 0)
23168c2ecf20Sopenharmony_ci				i_hardif++;
23178c2ecf20Sopenharmony_ci		}
23188c2ecf20Sopenharmony_ci	} else {
23198c2ecf20Sopenharmony_ci		list_for_each_entry_rcu(hard_iface, &batadv_hardif_list,
23208c2ecf20Sopenharmony_ci					list) {
23218c2ecf20Sopenharmony_ci			if (hard_iface->soft_iface != bat_priv->soft_iface)
23228c2ecf20Sopenharmony_ci				continue;
23238c2ecf20Sopenharmony_ci
23248c2ecf20Sopenharmony_ci			if (i_hardif++ < i_hardif_s)
23258c2ecf20Sopenharmony_ci				continue;
23268c2ecf20Sopenharmony_ci
23278c2ecf20Sopenharmony_ci			if (batadv_iv_ogm_neigh_dump_hardif(msg, portid,
23288c2ecf20Sopenharmony_ci							    cb->nlh->nlmsg_seq,
23298c2ecf20Sopenharmony_ci							    bat_priv,
23308c2ecf20Sopenharmony_ci							    hard_iface, &idx)) {
23318c2ecf20Sopenharmony_ci				i_hardif--;
23328c2ecf20Sopenharmony_ci				break;
23338c2ecf20Sopenharmony_ci			}
23348c2ecf20Sopenharmony_ci		}
23358c2ecf20Sopenharmony_ci	}
23368c2ecf20Sopenharmony_ci	rcu_read_unlock();
23378c2ecf20Sopenharmony_ci
23388c2ecf20Sopenharmony_ci	cb->args[0] = i_hardif;
23398c2ecf20Sopenharmony_ci	cb->args[1] = idx;
23408c2ecf20Sopenharmony_ci}
23418c2ecf20Sopenharmony_ci
23428c2ecf20Sopenharmony_ci/**
23438c2ecf20Sopenharmony_ci * batadv_iv_ogm_neigh_cmp() - compare the metrics of two neighbors
23448c2ecf20Sopenharmony_ci * @neigh1: the first neighbor object of the comparison
23458c2ecf20Sopenharmony_ci * @if_outgoing1: outgoing interface for the first neighbor
23468c2ecf20Sopenharmony_ci * @neigh2: the second neighbor object of the comparison
23478c2ecf20Sopenharmony_ci * @if_outgoing2: outgoing interface for the second neighbor
23488c2ecf20Sopenharmony_ci *
23498c2ecf20Sopenharmony_ci * Return: a value less, equal to or greater than 0 if the metric via neigh1 is
23508c2ecf20Sopenharmony_ci * lower, the same as or higher than the metric via neigh2
23518c2ecf20Sopenharmony_ci */
23528c2ecf20Sopenharmony_cistatic int batadv_iv_ogm_neigh_cmp(struct batadv_neigh_node *neigh1,
23538c2ecf20Sopenharmony_ci				   struct batadv_hard_iface *if_outgoing1,
23548c2ecf20Sopenharmony_ci				   struct batadv_neigh_node *neigh2,
23558c2ecf20Sopenharmony_ci				   struct batadv_hard_iface *if_outgoing2)
23568c2ecf20Sopenharmony_ci{
23578c2ecf20Sopenharmony_ci	bool ret;
23588c2ecf20Sopenharmony_ci	int diff;
23598c2ecf20Sopenharmony_ci
23608c2ecf20Sopenharmony_ci	ret = batadv_iv_ogm_neigh_diff(neigh1, if_outgoing1, neigh2,
23618c2ecf20Sopenharmony_ci				       if_outgoing2, &diff);
23628c2ecf20Sopenharmony_ci	if (!ret)
23638c2ecf20Sopenharmony_ci		return 0;
23648c2ecf20Sopenharmony_ci
23658c2ecf20Sopenharmony_ci	return diff;
23668c2ecf20Sopenharmony_ci}
23678c2ecf20Sopenharmony_ci
23688c2ecf20Sopenharmony_ci/**
23698c2ecf20Sopenharmony_ci * batadv_iv_ogm_neigh_is_sob() - check if neigh1 is similarly good or better
23708c2ecf20Sopenharmony_ci *  than neigh2 from the metric prospective
23718c2ecf20Sopenharmony_ci * @neigh1: the first neighbor object of the comparison
23728c2ecf20Sopenharmony_ci * @if_outgoing1: outgoing interface for the first neighbor
23738c2ecf20Sopenharmony_ci * @neigh2: the second neighbor object of the comparison
23748c2ecf20Sopenharmony_ci * @if_outgoing2: outgoing interface for the second neighbor
23758c2ecf20Sopenharmony_ci *
23768c2ecf20Sopenharmony_ci * Return: true if the metric via neigh1 is equally good or better than
23778c2ecf20Sopenharmony_ci * the metric via neigh2, false otherwise.
23788c2ecf20Sopenharmony_ci */
23798c2ecf20Sopenharmony_cistatic bool
23808c2ecf20Sopenharmony_cibatadv_iv_ogm_neigh_is_sob(struct batadv_neigh_node *neigh1,
23818c2ecf20Sopenharmony_ci			   struct batadv_hard_iface *if_outgoing1,
23828c2ecf20Sopenharmony_ci			   struct batadv_neigh_node *neigh2,
23838c2ecf20Sopenharmony_ci			   struct batadv_hard_iface *if_outgoing2)
23848c2ecf20Sopenharmony_ci{
23858c2ecf20Sopenharmony_ci	bool ret;
23868c2ecf20Sopenharmony_ci	int diff;
23878c2ecf20Sopenharmony_ci
23888c2ecf20Sopenharmony_ci	ret = batadv_iv_ogm_neigh_diff(neigh1, if_outgoing1, neigh2,
23898c2ecf20Sopenharmony_ci				       if_outgoing2, &diff);
23908c2ecf20Sopenharmony_ci	if (!ret)
23918c2ecf20Sopenharmony_ci		return false;
23928c2ecf20Sopenharmony_ci
23938c2ecf20Sopenharmony_ci	ret = diff > -BATADV_TQ_SIMILARITY_THRESHOLD;
23948c2ecf20Sopenharmony_ci	return ret;
23958c2ecf20Sopenharmony_ci}
23968c2ecf20Sopenharmony_ci
23978c2ecf20Sopenharmony_cistatic void batadv_iv_iface_enabled(struct batadv_hard_iface *hard_iface)
23988c2ecf20Sopenharmony_ci{
23998c2ecf20Sopenharmony_ci	/* begin scheduling originator messages on that interface */
24008c2ecf20Sopenharmony_ci	batadv_iv_ogm_schedule(hard_iface);
24018c2ecf20Sopenharmony_ci}
24028c2ecf20Sopenharmony_ci
24038c2ecf20Sopenharmony_ci/**
24048c2ecf20Sopenharmony_ci * batadv_iv_init_sel_class() - initialize GW selection class
24058c2ecf20Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
24068c2ecf20Sopenharmony_ci */
24078c2ecf20Sopenharmony_cistatic void batadv_iv_init_sel_class(struct batadv_priv *bat_priv)
24088c2ecf20Sopenharmony_ci{
24098c2ecf20Sopenharmony_ci	/* set default TQ difference threshold to 20 */
24108c2ecf20Sopenharmony_ci	atomic_set(&bat_priv->gw.sel_class, 20);
24118c2ecf20Sopenharmony_ci}
24128c2ecf20Sopenharmony_ci
24138c2ecf20Sopenharmony_cistatic struct batadv_gw_node *
24148c2ecf20Sopenharmony_cibatadv_iv_gw_get_best_gw_node(struct batadv_priv *bat_priv)
24158c2ecf20Sopenharmony_ci{
24168c2ecf20Sopenharmony_ci	struct batadv_neigh_node *router;
24178c2ecf20Sopenharmony_ci	struct batadv_neigh_ifinfo *router_ifinfo;
24188c2ecf20Sopenharmony_ci	struct batadv_gw_node *gw_node, *curr_gw = NULL;
24198c2ecf20Sopenharmony_ci	u64 max_gw_factor = 0;
24208c2ecf20Sopenharmony_ci	u64 tmp_gw_factor = 0;
24218c2ecf20Sopenharmony_ci	u8 max_tq = 0;
24228c2ecf20Sopenharmony_ci	u8 tq_avg;
24238c2ecf20Sopenharmony_ci	struct batadv_orig_node *orig_node;
24248c2ecf20Sopenharmony_ci
24258c2ecf20Sopenharmony_ci	rcu_read_lock();
24268c2ecf20Sopenharmony_ci	hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.gateway_list, list) {
24278c2ecf20Sopenharmony_ci		orig_node = gw_node->orig_node;
24288c2ecf20Sopenharmony_ci		router = batadv_orig_router_get(orig_node, BATADV_IF_DEFAULT);
24298c2ecf20Sopenharmony_ci		if (!router)
24308c2ecf20Sopenharmony_ci			continue;
24318c2ecf20Sopenharmony_ci
24328c2ecf20Sopenharmony_ci		router_ifinfo = batadv_neigh_ifinfo_get(router,
24338c2ecf20Sopenharmony_ci							BATADV_IF_DEFAULT);
24348c2ecf20Sopenharmony_ci		if (!router_ifinfo)
24358c2ecf20Sopenharmony_ci			goto next;
24368c2ecf20Sopenharmony_ci
24378c2ecf20Sopenharmony_ci		if (!kref_get_unless_zero(&gw_node->refcount))
24388c2ecf20Sopenharmony_ci			goto next;
24398c2ecf20Sopenharmony_ci
24408c2ecf20Sopenharmony_ci		tq_avg = router_ifinfo->bat_iv.tq_avg;
24418c2ecf20Sopenharmony_ci
24428c2ecf20Sopenharmony_ci		switch (atomic_read(&bat_priv->gw.sel_class)) {
24438c2ecf20Sopenharmony_ci		case 1: /* fast connection */
24448c2ecf20Sopenharmony_ci			tmp_gw_factor = tq_avg * tq_avg;
24458c2ecf20Sopenharmony_ci			tmp_gw_factor *= gw_node->bandwidth_down;
24468c2ecf20Sopenharmony_ci			tmp_gw_factor *= 100 * 100;
24478c2ecf20Sopenharmony_ci			tmp_gw_factor >>= 18;
24488c2ecf20Sopenharmony_ci
24498c2ecf20Sopenharmony_ci			if (tmp_gw_factor > max_gw_factor ||
24508c2ecf20Sopenharmony_ci			    (tmp_gw_factor == max_gw_factor &&
24518c2ecf20Sopenharmony_ci			     tq_avg > max_tq)) {
24528c2ecf20Sopenharmony_ci				if (curr_gw)
24538c2ecf20Sopenharmony_ci					batadv_gw_node_put(curr_gw);
24548c2ecf20Sopenharmony_ci				curr_gw = gw_node;
24558c2ecf20Sopenharmony_ci				kref_get(&curr_gw->refcount);
24568c2ecf20Sopenharmony_ci			}
24578c2ecf20Sopenharmony_ci			break;
24588c2ecf20Sopenharmony_ci
24598c2ecf20Sopenharmony_ci		default: /* 2:  stable connection (use best statistic)
24608c2ecf20Sopenharmony_ci			  * 3:  fast-switch (use best statistic but change as
24618c2ecf20Sopenharmony_ci			  *     soon as a better gateway appears)
24628c2ecf20Sopenharmony_ci			  * XX: late-switch (use best statistic but change as
24638c2ecf20Sopenharmony_ci			  *     soon as a better gateway appears which has
24648c2ecf20Sopenharmony_ci			  *     $routing_class more tq points)
24658c2ecf20Sopenharmony_ci			  */
24668c2ecf20Sopenharmony_ci			if (tq_avg > max_tq) {
24678c2ecf20Sopenharmony_ci				if (curr_gw)
24688c2ecf20Sopenharmony_ci					batadv_gw_node_put(curr_gw);
24698c2ecf20Sopenharmony_ci				curr_gw = gw_node;
24708c2ecf20Sopenharmony_ci				kref_get(&curr_gw->refcount);
24718c2ecf20Sopenharmony_ci			}
24728c2ecf20Sopenharmony_ci			break;
24738c2ecf20Sopenharmony_ci		}
24748c2ecf20Sopenharmony_ci
24758c2ecf20Sopenharmony_ci		if (tq_avg > max_tq)
24768c2ecf20Sopenharmony_ci			max_tq = tq_avg;
24778c2ecf20Sopenharmony_ci
24788c2ecf20Sopenharmony_ci		if (tmp_gw_factor > max_gw_factor)
24798c2ecf20Sopenharmony_ci			max_gw_factor = tmp_gw_factor;
24808c2ecf20Sopenharmony_ci
24818c2ecf20Sopenharmony_ci		batadv_gw_node_put(gw_node);
24828c2ecf20Sopenharmony_ci
24838c2ecf20Sopenharmony_cinext:
24848c2ecf20Sopenharmony_ci		batadv_neigh_node_put(router);
24858c2ecf20Sopenharmony_ci		if (router_ifinfo)
24868c2ecf20Sopenharmony_ci			batadv_neigh_ifinfo_put(router_ifinfo);
24878c2ecf20Sopenharmony_ci	}
24888c2ecf20Sopenharmony_ci	rcu_read_unlock();
24898c2ecf20Sopenharmony_ci
24908c2ecf20Sopenharmony_ci	return curr_gw;
24918c2ecf20Sopenharmony_ci}
24928c2ecf20Sopenharmony_ci
24938c2ecf20Sopenharmony_cistatic bool batadv_iv_gw_is_eligible(struct batadv_priv *bat_priv,
24948c2ecf20Sopenharmony_ci				     struct batadv_orig_node *curr_gw_orig,
24958c2ecf20Sopenharmony_ci				     struct batadv_orig_node *orig_node)
24968c2ecf20Sopenharmony_ci{
24978c2ecf20Sopenharmony_ci	struct batadv_neigh_ifinfo *router_orig_ifinfo = NULL;
24988c2ecf20Sopenharmony_ci	struct batadv_neigh_ifinfo *router_gw_ifinfo = NULL;
24998c2ecf20Sopenharmony_ci	struct batadv_neigh_node *router_gw = NULL;
25008c2ecf20Sopenharmony_ci	struct batadv_neigh_node *router_orig = NULL;
25018c2ecf20Sopenharmony_ci	u8 gw_tq_avg, orig_tq_avg;
25028c2ecf20Sopenharmony_ci	bool ret = false;
25038c2ecf20Sopenharmony_ci
25048c2ecf20Sopenharmony_ci	/* dynamic re-election is performed only on fast or late switch */
25058c2ecf20Sopenharmony_ci	if (atomic_read(&bat_priv->gw.sel_class) <= 2)
25068c2ecf20Sopenharmony_ci		return false;
25078c2ecf20Sopenharmony_ci
25088c2ecf20Sopenharmony_ci	router_gw = batadv_orig_router_get(curr_gw_orig, BATADV_IF_DEFAULT);
25098c2ecf20Sopenharmony_ci	if (!router_gw) {
25108c2ecf20Sopenharmony_ci		ret = true;
25118c2ecf20Sopenharmony_ci		goto out;
25128c2ecf20Sopenharmony_ci	}
25138c2ecf20Sopenharmony_ci
25148c2ecf20Sopenharmony_ci	router_gw_ifinfo = batadv_neigh_ifinfo_get(router_gw,
25158c2ecf20Sopenharmony_ci						   BATADV_IF_DEFAULT);
25168c2ecf20Sopenharmony_ci	if (!router_gw_ifinfo) {
25178c2ecf20Sopenharmony_ci		ret = true;
25188c2ecf20Sopenharmony_ci		goto out;
25198c2ecf20Sopenharmony_ci	}
25208c2ecf20Sopenharmony_ci
25218c2ecf20Sopenharmony_ci	router_orig = batadv_orig_router_get(orig_node, BATADV_IF_DEFAULT);
25228c2ecf20Sopenharmony_ci	if (!router_orig)
25238c2ecf20Sopenharmony_ci		goto out;
25248c2ecf20Sopenharmony_ci
25258c2ecf20Sopenharmony_ci	router_orig_ifinfo = batadv_neigh_ifinfo_get(router_orig,
25268c2ecf20Sopenharmony_ci						     BATADV_IF_DEFAULT);
25278c2ecf20Sopenharmony_ci	if (!router_orig_ifinfo)
25288c2ecf20Sopenharmony_ci		goto out;
25298c2ecf20Sopenharmony_ci
25308c2ecf20Sopenharmony_ci	gw_tq_avg = router_gw_ifinfo->bat_iv.tq_avg;
25318c2ecf20Sopenharmony_ci	orig_tq_avg = router_orig_ifinfo->bat_iv.tq_avg;
25328c2ecf20Sopenharmony_ci
25338c2ecf20Sopenharmony_ci	/* the TQ value has to be better */
25348c2ecf20Sopenharmony_ci	if (orig_tq_avg < gw_tq_avg)
25358c2ecf20Sopenharmony_ci		goto out;
25368c2ecf20Sopenharmony_ci
25378c2ecf20Sopenharmony_ci	/* if the routing class is greater than 3 the value tells us how much
25388c2ecf20Sopenharmony_ci	 * greater the TQ value of the new gateway must be
25398c2ecf20Sopenharmony_ci	 */
25408c2ecf20Sopenharmony_ci	if ((atomic_read(&bat_priv->gw.sel_class) > 3) &&
25418c2ecf20Sopenharmony_ci	    (orig_tq_avg - gw_tq_avg < atomic_read(&bat_priv->gw.sel_class)))
25428c2ecf20Sopenharmony_ci		goto out;
25438c2ecf20Sopenharmony_ci
25448c2ecf20Sopenharmony_ci	batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
25458c2ecf20Sopenharmony_ci		   "Restarting gateway selection: better gateway found (tq curr: %i, tq new: %i)\n",
25468c2ecf20Sopenharmony_ci		   gw_tq_avg, orig_tq_avg);
25478c2ecf20Sopenharmony_ci
25488c2ecf20Sopenharmony_ci	ret = true;
25498c2ecf20Sopenharmony_ciout:
25508c2ecf20Sopenharmony_ci	if (router_gw_ifinfo)
25518c2ecf20Sopenharmony_ci		batadv_neigh_ifinfo_put(router_gw_ifinfo);
25528c2ecf20Sopenharmony_ci	if (router_orig_ifinfo)
25538c2ecf20Sopenharmony_ci		batadv_neigh_ifinfo_put(router_orig_ifinfo);
25548c2ecf20Sopenharmony_ci	if (router_gw)
25558c2ecf20Sopenharmony_ci		batadv_neigh_node_put(router_gw);
25568c2ecf20Sopenharmony_ci	if (router_orig)
25578c2ecf20Sopenharmony_ci		batadv_neigh_node_put(router_orig);
25588c2ecf20Sopenharmony_ci
25598c2ecf20Sopenharmony_ci	return ret;
25608c2ecf20Sopenharmony_ci}
25618c2ecf20Sopenharmony_ci
25628c2ecf20Sopenharmony_ci#ifdef CONFIG_BATMAN_ADV_DEBUGFS
25638c2ecf20Sopenharmony_ci/* fails if orig_node has no router */
25648c2ecf20Sopenharmony_cistatic int batadv_iv_gw_write_buffer_text(struct batadv_priv *bat_priv,
25658c2ecf20Sopenharmony_ci					  struct seq_file *seq,
25668c2ecf20Sopenharmony_ci					  const struct batadv_gw_node *gw_node)
25678c2ecf20Sopenharmony_ci{
25688c2ecf20Sopenharmony_ci	struct batadv_gw_node *curr_gw;
25698c2ecf20Sopenharmony_ci	struct batadv_neigh_node *router;
25708c2ecf20Sopenharmony_ci	struct batadv_neigh_ifinfo *router_ifinfo = NULL;
25718c2ecf20Sopenharmony_ci	int ret = -1;
25728c2ecf20Sopenharmony_ci
25738c2ecf20Sopenharmony_ci	router = batadv_orig_router_get(gw_node->orig_node, BATADV_IF_DEFAULT);
25748c2ecf20Sopenharmony_ci	if (!router)
25758c2ecf20Sopenharmony_ci		goto out;
25768c2ecf20Sopenharmony_ci
25778c2ecf20Sopenharmony_ci	router_ifinfo = batadv_neigh_ifinfo_get(router, BATADV_IF_DEFAULT);
25788c2ecf20Sopenharmony_ci	if (!router_ifinfo)
25798c2ecf20Sopenharmony_ci		goto out;
25808c2ecf20Sopenharmony_ci
25818c2ecf20Sopenharmony_ci	curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
25828c2ecf20Sopenharmony_ci
25838c2ecf20Sopenharmony_ci	seq_printf(seq, "%s %pM (%3i) %pM [%10s]: %u.%u/%u.%u MBit\n",
25848c2ecf20Sopenharmony_ci		   (curr_gw == gw_node ? "=>" : "  "),
25858c2ecf20Sopenharmony_ci		   gw_node->orig_node->orig,
25868c2ecf20Sopenharmony_ci		   router_ifinfo->bat_iv.tq_avg, router->addr,
25878c2ecf20Sopenharmony_ci		   router->if_incoming->net_dev->name,
25888c2ecf20Sopenharmony_ci		   gw_node->bandwidth_down / 10,
25898c2ecf20Sopenharmony_ci		   gw_node->bandwidth_down % 10,
25908c2ecf20Sopenharmony_ci		   gw_node->bandwidth_up / 10,
25918c2ecf20Sopenharmony_ci		   gw_node->bandwidth_up % 10);
25928c2ecf20Sopenharmony_ci	ret = seq_has_overflowed(seq) ? -1 : 0;
25938c2ecf20Sopenharmony_ci
25948c2ecf20Sopenharmony_ci	if (curr_gw)
25958c2ecf20Sopenharmony_ci		batadv_gw_node_put(curr_gw);
25968c2ecf20Sopenharmony_ciout:
25978c2ecf20Sopenharmony_ci	if (router_ifinfo)
25988c2ecf20Sopenharmony_ci		batadv_neigh_ifinfo_put(router_ifinfo);
25998c2ecf20Sopenharmony_ci	if (router)
26008c2ecf20Sopenharmony_ci		batadv_neigh_node_put(router);
26018c2ecf20Sopenharmony_ci	return ret;
26028c2ecf20Sopenharmony_ci}
26038c2ecf20Sopenharmony_ci
26048c2ecf20Sopenharmony_cistatic void batadv_iv_gw_print(struct batadv_priv *bat_priv,
26058c2ecf20Sopenharmony_ci			       struct seq_file *seq)
26068c2ecf20Sopenharmony_ci{
26078c2ecf20Sopenharmony_ci	struct batadv_gw_node *gw_node;
26088c2ecf20Sopenharmony_ci	int gw_count = 0;
26098c2ecf20Sopenharmony_ci
26108c2ecf20Sopenharmony_ci	seq_puts(seq,
26118c2ecf20Sopenharmony_ci		 "      Gateway      (#/255)           Nexthop [outgoingIF]: advertised uplink bandwidth\n");
26128c2ecf20Sopenharmony_ci
26138c2ecf20Sopenharmony_ci	rcu_read_lock();
26148c2ecf20Sopenharmony_ci	hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.gateway_list, list) {
26158c2ecf20Sopenharmony_ci		/* fails if orig_node has no router */
26168c2ecf20Sopenharmony_ci		if (batadv_iv_gw_write_buffer_text(bat_priv, seq, gw_node) < 0)
26178c2ecf20Sopenharmony_ci			continue;
26188c2ecf20Sopenharmony_ci
26198c2ecf20Sopenharmony_ci		gw_count++;
26208c2ecf20Sopenharmony_ci	}
26218c2ecf20Sopenharmony_ci	rcu_read_unlock();
26228c2ecf20Sopenharmony_ci
26238c2ecf20Sopenharmony_ci	if (gw_count == 0)
26248c2ecf20Sopenharmony_ci		seq_puts(seq, "No gateways in range ...\n");
26258c2ecf20Sopenharmony_ci}
26268c2ecf20Sopenharmony_ci#endif
26278c2ecf20Sopenharmony_ci
26288c2ecf20Sopenharmony_ci/**
26298c2ecf20Sopenharmony_ci * batadv_iv_gw_dump_entry() - Dump a gateway into a message
26308c2ecf20Sopenharmony_ci * @msg: Netlink message to dump into
26318c2ecf20Sopenharmony_ci * @portid: Port making netlink request
26328c2ecf20Sopenharmony_ci * @cb: Control block containing additional options
26338c2ecf20Sopenharmony_ci * @bat_priv: The bat priv with all the soft interface information
26348c2ecf20Sopenharmony_ci * @gw_node: Gateway to be dumped
26358c2ecf20Sopenharmony_ci *
26368c2ecf20Sopenharmony_ci * Return: Error code, or 0 on success
26378c2ecf20Sopenharmony_ci */
26388c2ecf20Sopenharmony_cistatic int batadv_iv_gw_dump_entry(struct sk_buff *msg, u32 portid,
26398c2ecf20Sopenharmony_ci				   struct netlink_callback *cb,
26408c2ecf20Sopenharmony_ci				   struct batadv_priv *bat_priv,
26418c2ecf20Sopenharmony_ci				   struct batadv_gw_node *gw_node)
26428c2ecf20Sopenharmony_ci{
26438c2ecf20Sopenharmony_ci	struct batadv_neigh_ifinfo *router_ifinfo = NULL;
26448c2ecf20Sopenharmony_ci	struct batadv_neigh_node *router;
26458c2ecf20Sopenharmony_ci	struct batadv_gw_node *curr_gw = NULL;
26468c2ecf20Sopenharmony_ci	int ret = 0;
26478c2ecf20Sopenharmony_ci	void *hdr;
26488c2ecf20Sopenharmony_ci
26498c2ecf20Sopenharmony_ci	router = batadv_orig_router_get(gw_node->orig_node, BATADV_IF_DEFAULT);
26508c2ecf20Sopenharmony_ci	if (!router)
26518c2ecf20Sopenharmony_ci		goto out;
26528c2ecf20Sopenharmony_ci
26538c2ecf20Sopenharmony_ci	router_ifinfo = batadv_neigh_ifinfo_get(router, BATADV_IF_DEFAULT);
26548c2ecf20Sopenharmony_ci	if (!router_ifinfo)
26558c2ecf20Sopenharmony_ci		goto out;
26568c2ecf20Sopenharmony_ci
26578c2ecf20Sopenharmony_ci	curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
26588c2ecf20Sopenharmony_ci
26598c2ecf20Sopenharmony_ci	hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq,
26608c2ecf20Sopenharmony_ci			  &batadv_netlink_family, NLM_F_MULTI,
26618c2ecf20Sopenharmony_ci			  BATADV_CMD_GET_GATEWAYS);
26628c2ecf20Sopenharmony_ci	if (!hdr) {
26638c2ecf20Sopenharmony_ci		ret = -ENOBUFS;
26648c2ecf20Sopenharmony_ci		goto out;
26658c2ecf20Sopenharmony_ci	}
26668c2ecf20Sopenharmony_ci
26678c2ecf20Sopenharmony_ci	genl_dump_check_consistent(cb, hdr);
26688c2ecf20Sopenharmony_ci
26698c2ecf20Sopenharmony_ci	ret = -EMSGSIZE;
26708c2ecf20Sopenharmony_ci
26718c2ecf20Sopenharmony_ci	if (curr_gw == gw_node)
26728c2ecf20Sopenharmony_ci		if (nla_put_flag(msg, BATADV_ATTR_FLAG_BEST)) {
26738c2ecf20Sopenharmony_ci			genlmsg_cancel(msg, hdr);
26748c2ecf20Sopenharmony_ci			goto out;
26758c2ecf20Sopenharmony_ci		}
26768c2ecf20Sopenharmony_ci
26778c2ecf20Sopenharmony_ci	if (nla_put(msg, BATADV_ATTR_ORIG_ADDRESS, ETH_ALEN,
26788c2ecf20Sopenharmony_ci		    gw_node->orig_node->orig) ||
26798c2ecf20Sopenharmony_ci	    nla_put_u8(msg, BATADV_ATTR_TQ, router_ifinfo->bat_iv.tq_avg) ||
26808c2ecf20Sopenharmony_ci	    nla_put(msg, BATADV_ATTR_ROUTER, ETH_ALEN,
26818c2ecf20Sopenharmony_ci		    router->addr) ||
26828c2ecf20Sopenharmony_ci	    nla_put_string(msg, BATADV_ATTR_HARD_IFNAME,
26838c2ecf20Sopenharmony_ci			   router->if_incoming->net_dev->name) ||
26848c2ecf20Sopenharmony_ci	    nla_put_u32(msg, BATADV_ATTR_BANDWIDTH_DOWN,
26858c2ecf20Sopenharmony_ci			gw_node->bandwidth_down) ||
26868c2ecf20Sopenharmony_ci	    nla_put_u32(msg, BATADV_ATTR_BANDWIDTH_UP,
26878c2ecf20Sopenharmony_ci			gw_node->bandwidth_up)) {
26888c2ecf20Sopenharmony_ci		genlmsg_cancel(msg, hdr);
26898c2ecf20Sopenharmony_ci		goto out;
26908c2ecf20Sopenharmony_ci	}
26918c2ecf20Sopenharmony_ci
26928c2ecf20Sopenharmony_ci	genlmsg_end(msg, hdr);
26938c2ecf20Sopenharmony_ci	ret = 0;
26948c2ecf20Sopenharmony_ci
26958c2ecf20Sopenharmony_ciout:
26968c2ecf20Sopenharmony_ci	if (curr_gw)
26978c2ecf20Sopenharmony_ci		batadv_gw_node_put(curr_gw);
26988c2ecf20Sopenharmony_ci	if (router_ifinfo)
26998c2ecf20Sopenharmony_ci		batadv_neigh_ifinfo_put(router_ifinfo);
27008c2ecf20Sopenharmony_ci	if (router)
27018c2ecf20Sopenharmony_ci		batadv_neigh_node_put(router);
27028c2ecf20Sopenharmony_ci	return ret;
27038c2ecf20Sopenharmony_ci}
27048c2ecf20Sopenharmony_ci
27058c2ecf20Sopenharmony_ci/**
27068c2ecf20Sopenharmony_ci * batadv_iv_gw_dump() - Dump gateways into a message
27078c2ecf20Sopenharmony_ci * @msg: Netlink message to dump into
27088c2ecf20Sopenharmony_ci * @cb: Control block containing additional options
27098c2ecf20Sopenharmony_ci * @bat_priv: The bat priv with all the soft interface information
27108c2ecf20Sopenharmony_ci */
27118c2ecf20Sopenharmony_cistatic void batadv_iv_gw_dump(struct sk_buff *msg, struct netlink_callback *cb,
27128c2ecf20Sopenharmony_ci			      struct batadv_priv *bat_priv)
27138c2ecf20Sopenharmony_ci{
27148c2ecf20Sopenharmony_ci	int portid = NETLINK_CB(cb->skb).portid;
27158c2ecf20Sopenharmony_ci	struct batadv_gw_node *gw_node;
27168c2ecf20Sopenharmony_ci	int idx_skip = cb->args[0];
27178c2ecf20Sopenharmony_ci	int idx = 0;
27188c2ecf20Sopenharmony_ci
27198c2ecf20Sopenharmony_ci	spin_lock_bh(&bat_priv->gw.list_lock);
27208c2ecf20Sopenharmony_ci	cb->seq = bat_priv->gw.generation << 1 | 1;
27218c2ecf20Sopenharmony_ci
27228c2ecf20Sopenharmony_ci	hlist_for_each_entry(gw_node, &bat_priv->gw.gateway_list, list) {
27238c2ecf20Sopenharmony_ci		if (idx++ < idx_skip)
27248c2ecf20Sopenharmony_ci			continue;
27258c2ecf20Sopenharmony_ci
27268c2ecf20Sopenharmony_ci		if (batadv_iv_gw_dump_entry(msg, portid, cb, bat_priv,
27278c2ecf20Sopenharmony_ci					    gw_node)) {
27288c2ecf20Sopenharmony_ci			idx_skip = idx - 1;
27298c2ecf20Sopenharmony_ci			goto unlock;
27308c2ecf20Sopenharmony_ci		}
27318c2ecf20Sopenharmony_ci	}
27328c2ecf20Sopenharmony_ci
27338c2ecf20Sopenharmony_ci	idx_skip = idx;
27348c2ecf20Sopenharmony_ciunlock:
27358c2ecf20Sopenharmony_ci	spin_unlock_bh(&bat_priv->gw.list_lock);
27368c2ecf20Sopenharmony_ci
27378c2ecf20Sopenharmony_ci	cb->args[0] = idx_skip;
27388c2ecf20Sopenharmony_ci}
27398c2ecf20Sopenharmony_ci
27408c2ecf20Sopenharmony_cistatic struct batadv_algo_ops batadv_batman_iv __read_mostly = {
27418c2ecf20Sopenharmony_ci	.name = "BATMAN_IV",
27428c2ecf20Sopenharmony_ci	.iface = {
27438c2ecf20Sopenharmony_ci		.enable = batadv_iv_ogm_iface_enable,
27448c2ecf20Sopenharmony_ci		.enabled = batadv_iv_iface_enabled,
27458c2ecf20Sopenharmony_ci		.disable = batadv_iv_ogm_iface_disable,
27468c2ecf20Sopenharmony_ci		.update_mac = batadv_iv_ogm_iface_update_mac,
27478c2ecf20Sopenharmony_ci		.primary_set = batadv_iv_ogm_primary_iface_set,
27488c2ecf20Sopenharmony_ci	},
27498c2ecf20Sopenharmony_ci	.neigh = {
27508c2ecf20Sopenharmony_ci		.cmp = batadv_iv_ogm_neigh_cmp,
27518c2ecf20Sopenharmony_ci		.is_similar_or_better = batadv_iv_ogm_neigh_is_sob,
27528c2ecf20Sopenharmony_ci#ifdef CONFIG_BATMAN_ADV_DEBUGFS
27538c2ecf20Sopenharmony_ci		.print = batadv_iv_neigh_print,
27548c2ecf20Sopenharmony_ci#endif
27558c2ecf20Sopenharmony_ci		.dump = batadv_iv_ogm_neigh_dump,
27568c2ecf20Sopenharmony_ci	},
27578c2ecf20Sopenharmony_ci	.orig = {
27588c2ecf20Sopenharmony_ci#ifdef CONFIG_BATMAN_ADV_DEBUGFS
27598c2ecf20Sopenharmony_ci		.print = batadv_iv_ogm_orig_print,
27608c2ecf20Sopenharmony_ci#endif
27618c2ecf20Sopenharmony_ci		.dump = batadv_iv_ogm_orig_dump,
27628c2ecf20Sopenharmony_ci	},
27638c2ecf20Sopenharmony_ci	.gw = {
27648c2ecf20Sopenharmony_ci		.init_sel_class = batadv_iv_init_sel_class,
27658c2ecf20Sopenharmony_ci		.get_best_gw_node = batadv_iv_gw_get_best_gw_node,
27668c2ecf20Sopenharmony_ci		.is_eligible = batadv_iv_gw_is_eligible,
27678c2ecf20Sopenharmony_ci#ifdef CONFIG_BATMAN_ADV_DEBUGFS
27688c2ecf20Sopenharmony_ci		.print = batadv_iv_gw_print,
27698c2ecf20Sopenharmony_ci#endif
27708c2ecf20Sopenharmony_ci		.dump = batadv_iv_gw_dump,
27718c2ecf20Sopenharmony_ci	},
27728c2ecf20Sopenharmony_ci};
27738c2ecf20Sopenharmony_ci
27748c2ecf20Sopenharmony_ci/**
27758c2ecf20Sopenharmony_ci * batadv_iv_init() - B.A.T.M.A.N. IV initialization function
27768c2ecf20Sopenharmony_ci *
27778c2ecf20Sopenharmony_ci * Return: 0 on success or negative error number in case of failure
27788c2ecf20Sopenharmony_ci */
27798c2ecf20Sopenharmony_ciint __init batadv_iv_init(void)
27808c2ecf20Sopenharmony_ci{
27818c2ecf20Sopenharmony_ci	int ret;
27828c2ecf20Sopenharmony_ci
27838c2ecf20Sopenharmony_ci	/* batman originator packet */
27848c2ecf20Sopenharmony_ci	ret = batadv_recv_handler_register(BATADV_IV_OGM,
27858c2ecf20Sopenharmony_ci					   batadv_iv_ogm_receive);
27868c2ecf20Sopenharmony_ci	if (ret < 0)
27878c2ecf20Sopenharmony_ci		goto out;
27888c2ecf20Sopenharmony_ci
27898c2ecf20Sopenharmony_ci	ret = batadv_algo_register(&batadv_batman_iv);
27908c2ecf20Sopenharmony_ci	if (ret < 0)
27918c2ecf20Sopenharmony_ci		goto handler_unregister;
27928c2ecf20Sopenharmony_ci
27938c2ecf20Sopenharmony_ci	goto out;
27948c2ecf20Sopenharmony_ci
27958c2ecf20Sopenharmony_cihandler_unregister:
27968c2ecf20Sopenharmony_ci	batadv_recv_handler_unregister(BATADV_IV_OGM);
27978c2ecf20Sopenharmony_ciout:
27988c2ecf20Sopenharmony_ci	return ret;
27998c2ecf20Sopenharmony_ci}
2800