162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Copyright (C) B.A.T.M.A.N. contributors:
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Antonio Quartulli
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include "distributed-arp-table.h"
862306a36Sopenharmony_ci#include "main.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <asm/unaligned.h>
1162306a36Sopenharmony_ci#include <linux/atomic.h>
1262306a36Sopenharmony_ci#include <linux/bitops.h>
1362306a36Sopenharmony_ci#include <linux/byteorder/generic.h>
1462306a36Sopenharmony_ci#include <linux/container_of.h>
1562306a36Sopenharmony_ci#include <linux/errno.h>
1662306a36Sopenharmony_ci#include <linux/etherdevice.h>
1762306a36Sopenharmony_ci#include <linux/gfp.h>
1862306a36Sopenharmony_ci#include <linux/if_arp.h>
1962306a36Sopenharmony_ci#include <linux/if_ether.h>
2062306a36Sopenharmony_ci#include <linux/if_vlan.h>
2162306a36Sopenharmony_ci#include <linux/in.h>
2262306a36Sopenharmony_ci#include <linux/ip.h>
2362306a36Sopenharmony_ci#include <linux/jiffies.h>
2462306a36Sopenharmony_ci#include <linux/kref.h>
2562306a36Sopenharmony_ci#include <linux/list.h>
2662306a36Sopenharmony_ci#include <linux/netlink.h>
2762306a36Sopenharmony_ci#include <linux/rculist.h>
2862306a36Sopenharmony_ci#include <linux/rcupdate.h>
2962306a36Sopenharmony_ci#include <linux/skbuff.h>
3062306a36Sopenharmony_ci#include <linux/slab.h>
3162306a36Sopenharmony_ci#include <linux/spinlock.h>
3262306a36Sopenharmony_ci#include <linux/stddef.h>
3362306a36Sopenharmony_ci#include <linux/string.h>
3462306a36Sopenharmony_ci#include <linux/udp.h>
3562306a36Sopenharmony_ci#include <linux/workqueue.h>
3662306a36Sopenharmony_ci#include <net/arp.h>
3762306a36Sopenharmony_ci#include <net/genetlink.h>
3862306a36Sopenharmony_ci#include <net/netlink.h>
3962306a36Sopenharmony_ci#include <net/sock.h>
4062306a36Sopenharmony_ci#include <uapi/linux/batman_adv.h>
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#include "bridge_loop_avoidance.h"
4362306a36Sopenharmony_ci#include "hard-interface.h"
4462306a36Sopenharmony_ci#include "hash.h"
4562306a36Sopenharmony_ci#include "log.h"
4662306a36Sopenharmony_ci#include "netlink.h"
4762306a36Sopenharmony_ci#include "originator.h"
4862306a36Sopenharmony_ci#include "send.h"
4962306a36Sopenharmony_ci#include "soft-interface.h"
5062306a36Sopenharmony_ci#include "translation-table.h"
5162306a36Sopenharmony_ci#include "tvlv.h"
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cienum batadv_bootpop {
5462306a36Sopenharmony_ci	BATADV_BOOTREPLY	= 2,
5562306a36Sopenharmony_ci};
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cienum batadv_boothtype {
5862306a36Sopenharmony_ci	BATADV_HTYPE_ETHERNET	= 1,
5962306a36Sopenharmony_ci};
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cienum batadv_dhcpoptioncode {
6262306a36Sopenharmony_ci	BATADV_DHCP_OPT_PAD		= 0,
6362306a36Sopenharmony_ci	BATADV_DHCP_OPT_MSG_TYPE	= 53,
6462306a36Sopenharmony_ci	BATADV_DHCP_OPT_END		= 255,
6562306a36Sopenharmony_ci};
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cienum batadv_dhcptype {
6862306a36Sopenharmony_ci	BATADV_DHCPACK		= 5,
6962306a36Sopenharmony_ci};
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci/* { 99, 130, 83, 99 } */
7262306a36Sopenharmony_ci#define BATADV_DHCP_MAGIC 1669485411
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistruct batadv_dhcp_packet {
7562306a36Sopenharmony_ci	__u8 op;
7662306a36Sopenharmony_ci	__u8 htype;
7762306a36Sopenharmony_ci	__u8 hlen;
7862306a36Sopenharmony_ci	__u8 hops;
7962306a36Sopenharmony_ci	__be32 xid;
8062306a36Sopenharmony_ci	__be16 secs;
8162306a36Sopenharmony_ci	__be16 flags;
8262306a36Sopenharmony_ci	__be32 ciaddr;
8362306a36Sopenharmony_ci	__be32 yiaddr;
8462306a36Sopenharmony_ci	__be32 siaddr;
8562306a36Sopenharmony_ci	__be32 giaddr;
8662306a36Sopenharmony_ci	__u8 chaddr[16];
8762306a36Sopenharmony_ci	__u8 sname[64];
8862306a36Sopenharmony_ci	__u8 file[128];
8962306a36Sopenharmony_ci	__be32 magic;
9062306a36Sopenharmony_ci	/* __u8 options[]; */
9162306a36Sopenharmony_ci};
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci#define BATADV_DHCP_YIADDR_LEN sizeof(((struct batadv_dhcp_packet *)0)->yiaddr)
9462306a36Sopenharmony_ci#define BATADV_DHCP_CHADDR_LEN sizeof(((struct batadv_dhcp_packet *)0)->chaddr)
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic void batadv_dat_purge(struct work_struct *work);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci/**
9962306a36Sopenharmony_ci * batadv_dat_start_timer() - initialise the DAT periodic worker
10062306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
10162306a36Sopenharmony_ci */
10262306a36Sopenharmony_cistatic void batadv_dat_start_timer(struct batadv_priv *bat_priv)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	queue_delayed_work(batadv_event_workqueue, &bat_priv->dat.work,
10562306a36Sopenharmony_ci			   msecs_to_jiffies(10000));
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci/**
10962306a36Sopenharmony_ci * batadv_dat_entry_release() - release dat_entry from lists and queue for free
11062306a36Sopenharmony_ci *  after rcu grace period
11162306a36Sopenharmony_ci * @ref: kref pointer of the dat_entry
11262306a36Sopenharmony_ci */
11362306a36Sopenharmony_cistatic void batadv_dat_entry_release(struct kref *ref)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	struct batadv_dat_entry *dat_entry;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	dat_entry = container_of(ref, struct batadv_dat_entry, refcount);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	kfree_rcu(dat_entry, rcu);
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci/**
12362306a36Sopenharmony_ci * batadv_dat_entry_put() - decrement the dat_entry refcounter and possibly
12462306a36Sopenharmony_ci *  release it
12562306a36Sopenharmony_ci * @dat_entry: dat_entry to be free'd
12662306a36Sopenharmony_ci */
12762306a36Sopenharmony_cistatic void batadv_dat_entry_put(struct batadv_dat_entry *dat_entry)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	if (!dat_entry)
13062306a36Sopenharmony_ci		return;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	kref_put(&dat_entry->refcount, batadv_dat_entry_release);
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci/**
13662306a36Sopenharmony_ci * batadv_dat_to_purge() - check whether a dat_entry has to be purged or not
13762306a36Sopenharmony_ci * @dat_entry: the entry to check
13862306a36Sopenharmony_ci *
13962306a36Sopenharmony_ci * Return: true if the entry has to be purged now, false otherwise.
14062306a36Sopenharmony_ci */
14162306a36Sopenharmony_cistatic bool batadv_dat_to_purge(struct batadv_dat_entry *dat_entry)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	return batadv_has_timed_out(dat_entry->last_update,
14462306a36Sopenharmony_ci				    BATADV_DAT_ENTRY_TIMEOUT);
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci/**
14862306a36Sopenharmony_ci * __batadv_dat_purge() - delete entries from the DAT local storage
14962306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
15062306a36Sopenharmony_ci * @to_purge: function in charge to decide whether an entry has to be purged or
15162306a36Sopenharmony_ci *	      not. This function takes the dat_entry as argument and has to
15262306a36Sopenharmony_ci *	      returns a boolean value: true is the entry has to be deleted,
15362306a36Sopenharmony_ci *	      false otherwise
15462306a36Sopenharmony_ci *
15562306a36Sopenharmony_ci * Loops over each entry in the DAT local storage and deletes it if and only if
15662306a36Sopenharmony_ci * the to_purge function passed as argument returns true.
15762306a36Sopenharmony_ci */
15862306a36Sopenharmony_cistatic void __batadv_dat_purge(struct batadv_priv *bat_priv,
15962306a36Sopenharmony_ci			       bool (*to_purge)(struct batadv_dat_entry *))
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	spinlock_t *list_lock; /* protects write access to the hash lists */
16262306a36Sopenharmony_ci	struct batadv_dat_entry *dat_entry;
16362306a36Sopenharmony_ci	struct hlist_node *node_tmp;
16462306a36Sopenharmony_ci	struct hlist_head *head;
16562306a36Sopenharmony_ci	u32 i;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	if (!bat_priv->dat.hash)
16862306a36Sopenharmony_ci		return;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	for (i = 0; i < bat_priv->dat.hash->size; i++) {
17162306a36Sopenharmony_ci		head = &bat_priv->dat.hash->table[i];
17262306a36Sopenharmony_ci		list_lock = &bat_priv->dat.hash->list_locks[i];
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci		spin_lock_bh(list_lock);
17562306a36Sopenharmony_ci		hlist_for_each_entry_safe(dat_entry, node_tmp, head,
17662306a36Sopenharmony_ci					  hash_entry) {
17762306a36Sopenharmony_ci			/* if a helper function has been passed as parameter,
17862306a36Sopenharmony_ci			 * ask it if the entry has to be purged or not
17962306a36Sopenharmony_ci			 */
18062306a36Sopenharmony_ci			if (to_purge && !to_purge(dat_entry))
18162306a36Sopenharmony_ci				continue;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci			hlist_del_rcu(&dat_entry->hash_entry);
18462306a36Sopenharmony_ci			batadv_dat_entry_put(dat_entry);
18562306a36Sopenharmony_ci		}
18662306a36Sopenharmony_ci		spin_unlock_bh(list_lock);
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci/**
19162306a36Sopenharmony_ci * batadv_dat_purge() - periodic task that deletes old entries from the local
19262306a36Sopenharmony_ci *  DAT hash table
19362306a36Sopenharmony_ci * @work: kernel work struct
19462306a36Sopenharmony_ci */
19562306a36Sopenharmony_cistatic void batadv_dat_purge(struct work_struct *work)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	struct delayed_work *delayed_work;
19862306a36Sopenharmony_ci	struct batadv_priv_dat *priv_dat;
19962306a36Sopenharmony_ci	struct batadv_priv *bat_priv;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	delayed_work = to_delayed_work(work);
20262306a36Sopenharmony_ci	priv_dat = container_of(delayed_work, struct batadv_priv_dat, work);
20362306a36Sopenharmony_ci	bat_priv = container_of(priv_dat, struct batadv_priv, dat);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	__batadv_dat_purge(bat_priv, batadv_dat_to_purge);
20662306a36Sopenharmony_ci	batadv_dat_start_timer(bat_priv);
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci/**
21062306a36Sopenharmony_ci * batadv_compare_dat() - comparing function used in the local DAT hash table
21162306a36Sopenharmony_ci * @node: node in the local table
21262306a36Sopenharmony_ci * @data2: second object to compare the node to
21362306a36Sopenharmony_ci *
21462306a36Sopenharmony_ci * Return: true if the two entries are the same, false otherwise.
21562306a36Sopenharmony_ci */
21662306a36Sopenharmony_cistatic bool batadv_compare_dat(const struct hlist_node *node, const void *data2)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	const void *data1 = container_of(node, struct batadv_dat_entry,
21962306a36Sopenharmony_ci					 hash_entry);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	return memcmp(data1, data2, sizeof(__be32)) == 0;
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci/**
22562306a36Sopenharmony_ci * batadv_arp_hw_src() - extract the hw_src field from an ARP packet
22662306a36Sopenharmony_ci * @skb: ARP packet
22762306a36Sopenharmony_ci * @hdr_size: size of the possible header before the ARP packet
22862306a36Sopenharmony_ci *
22962306a36Sopenharmony_ci * Return: the value of the hw_src field in the ARP packet.
23062306a36Sopenharmony_ci */
23162306a36Sopenharmony_cistatic u8 *batadv_arp_hw_src(struct sk_buff *skb, int hdr_size)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	u8 *addr;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	addr = (u8 *)(skb->data + hdr_size);
23662306a36Sopenharmony_ci	addr += ETH_HLEN + sizeof(struct arphdr);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	return addr;
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci/**
24262306a36Sopenharmony_ci * batadv_arp_ip_src() - extract the ip_src field from an ARP packet
24362306a36Sopenharmony_ci * @skb: ARP packet
24462306a36Sopenharmony_ci * @hdr_size: size of the possible header before the ARP packet
24562306a36Sopenharmony_ci *
24662306a36Sopenharmony_ci * Return: the value of the ip_src field in the ARP packet.
24762306a36Sopenharmony_ci */
24862306a36Sopenharmony_cistatic __be32 batadv_arp_ip_src(struct sk_buff *skb, int hdr_size)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	return *(__force __be32 *)(batadv_arp_hw_src(skb, hdr_size) + ETH_ALEN);
25162306a36Sopenharmony_ci}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci/**
25462306a36Sopenharmony_ci * batadv_arp_hw_dst() - extract the hw_dst field from an ARP packet
25562306a36Sopenharmony_ci * @skb: ARP packet
25662306a36Sopenharmony_ci * @hdr_size: size of the possible header before the ARP packet
25762306a36Sopenharmony_ci *
25862306a36Sopenharmony_ci * Return: the value of the hw_dst field in the ARP packet.
25962306a36Sopenharmony_ci */
26062306a36Sopenharmony_cistatic u8 *batadv_arp_hw_dst(struct sk_buff *skb, int hdr_size)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	return batadv_arp_hw_src(skb, hdr_size) + ETH_ALEN + 4;
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci/**
26662306a36Sopenharmony_ci * batadv_arp_ip_dst() - extract the ip_dst field from an ARP packet
26762306a36Sopenharmony_ci * @skb: ARP packet
26862306a36Sopenharmony_ci * @hdr_size: size of the possible header before the ARP packet
26962306a36Sopenharmony_ci *
27062306a36Sopenharmony_ci * Return: the value of the ip_dst field in the ARP packet.
27162306a36Sopenharmony_ci */
27262306a36Sopenharmony_cistatic __be32 batadv_arp_ip_dst(struct sk_buff *skb, int hdr_size)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	u8 *dst = batadv_arp_hw_src(skb, hdr_size) + ETH_ALEN * 2 + 4;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	return *(__force __be32 *)dst;
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci/**
28062306a36Sopenharmony_ci * batadv_hash_dat() - compute the hash value for an IP address
28162306a36Sopenharmony_ci * @data: data to hash
28262306a36Sopenharmony_ci * @size: size of the hash table
28362306a36Sopenharmony_ci *
28462306a36Sopenharmony_ci * Return: the selected index in the hash table for the given data.
28562306a36Sopenharmony_ci */
28662306a36Sopenharmony_cistatic u32 batadv_hash_dat(const void *data, u32 size)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	u32 hash = 0;
28962306a36Sopenharmony_ci	const struct batadv_dat_entry *dat = data;
29062306a36Sopenharmony_ci	const unsigned char *key;
29162306a36Sopenharmony_ci	__be16 vid;
29262306a36Sopenharmony_ci	u32 i;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	key = (__force const unsigned char *)&dat->ip;
29562306a36Sopenharmony_ci	for (i = 0; i < sizeof(dat->ip); i++) {
29662306a36Sopenharmony_ci		hash += key[i];
29762306a36Sopenharmony_ci		hash += (hash << 10);
29862306a36Sopenharmony_ci		hash ^= (hash >> 6);
29962306a36Sopenharmony_ci	}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	vid = htons(dat->vid);
30262306a36Sopenharmony_ci	key = (__force const unsigned char *)&vid;
30362306a36Sopenharmony_ci	for (i = 0; i < sizeof(dat->vid); i++) {
30462306a36Sopenharmony_ci		hash += key[i];
30562306a36Sopenharmony_ci		hash += (hash << 10);
30662306a36Sopenharmony_ci		hash ^= (hash >> 6);
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	hash += (hash << 3);
31062306a36Sopenharmony_ci	hash ^= (hash >> 11);
31162306a36Sopenharmony_ci	hash += (hash << 15);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	return hash % size;
31462306a36Sopenharmony_ci}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci/**
31762306a36Sopenharmony_ci * batadv_dat_entry_hash_find() - look for a given dat_entry in the local hash
31862306a36Sopenharmony_ci * table
31962306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
32062306a36Sopenharmony_ci * @ip: search key
32162306a36Sopenharmony_ci * @vid: VLAN identifier
32262306a36Sopenharmony_ci *
32362306a36Sopenharmony_ci * Return: the dat_entry if found, NULL otherwise.
32462306a36Sopenharmony_ci */
32562306a36Sopenharmony_cistatic struct batadv_dat_entry *
32662306a36Sopenharmony_cibatadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip,
32762306a36Sopenharmony_ci			   unsigned short vid)
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	struct hlist_head *head;
33062306a36Sopenharmony_ci	struct batadv_dat_entry to_find, *dat_entry, *dat_entry_tmp = NULL;
33162306a36Sopenharmony_ci	struct batadv_hashtable *hash = bat_priv->dat.hash;
33262306a36Sopenharmony_ci	u32 index;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	if (!hash)
33562306a36Sopenharmony_ci		return NULL;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	to_find.ip = ip;
33862306a36Sopenharmony_ci	to_find.vid = vid;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	index = batadv_hash_dat(&to_find, hash->size);
34162306a36Sopenharmony_ci	head = &hash->table[index];
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	rcu_read_lock();
34462306a36Sopenharmony_ci	hlist_for_each_entry_rcu(dat_entry, head, hash_entry) {
34562306a36Sopenharmony_ci		if (dat_entry->ip != ip)
34662306a36Sopenharmony_ci			continue;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci		if (!kref_get_unless_zero(&dat_entry->refcount))
34962306a36Sopenharmony_ci			continue;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci		dat_entry_tmp = dat_entry;
35262306a36Sopenharmony_ci		break;
35362306a36Sopenharmony_ci	}
35462306a36Sopenharmony_ci	rcu_read_unlock();
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	return dat_entry_tmp;
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci/**
36062306a36Sopenharmony_ci * batadv_dat_entry_add() - add a new dat entry or update it if already exists
36162306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
36262306a36Sopenharmony_ci * @ip: ipv4 to add/edit
36362306a36Sopenharmony_ci * @mac_addr: mac address to assign to the given ipv4
36462306a36Sopenharmony_ci * @vid: VLAN identifier
36562306a36Sopenharmony_ci */
36662306a36Sopenharmony_cistatic void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip,
36762306a36Sopenharmony_ci				 u8 *mac_addr, unsigned short vid)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	struct batadv_dat_entry *dat_entry;
37062306a36Sopenharmony_ci	int hash_added;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	dat_entry = batadv_dat_entry_hash_find(bat_priv, ip, vid);
37362306a36Sopenharmony_ci	/* if this entry is already known, just update it */
37462306a36Sopenharmony_ci	if (dat_entry) {
37562306a36Sopenharmony_ci		if (!batadv_compare_eth(dat_entry->mac_addr, mac_addr))
37662306a36Sopenharmony_ci			ether_addr_copy(dat_entry->mac_addr, mac_addr);
37762306a36Sopenharmony_ci		dat_entry->last_update = jiffies;
37862306a36Sopenharmony_ci		batadv_dbg(BATADV_DBG_DAT, bat_priv,
37962306a36Sopenharmony_ci			   "Entry updated: %pI4 %pM (vid: %d)\n",
38062306a36Sopenharmony_ci			   &dat_entry->ip, dat_entry->mac_addr,
38162306a36Sopenharmony_ci			   batadv_print_vid(vid));
38262306a36Sopenharmony_ci		goto out;
38362306a36Sopenharmony_ci	}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	dat_entry = kmalloc(sizeof(*dat_entry), GFP_ATOMIC);
38662306a36Sopenharmony_ci	if (!dat_entry)
38762306a36Sopenharmony_ci		goto out;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	dat_entry->ip = ip;
39062306a36Sopenharmony_ci	dat_entry->vid = vid;
39162306a36Sopenharmony_ci	ether_addr_copy(dat_entry->mac_addr, mac_addr);
39262306a36Sopenharmony_ci	dat_entry->last_update = jiffies;
39362306a36Sopenharmony_ci	kref_init(&dat_entry->refcount);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	kref_get(&dat_entry->refcount);
39662306a36Sopenharmony_ci	hash_added = batadv_hash_add(bat_priv->dat.hash, batadv_compare_dat,
39762306a36Sopenharmony_ci				     batadv_hash_dat, dat_entry,
39862306a36Sopenharmony_ci				     &dat_entry->hash_entry);
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	if (unlikely(hash_added != 0)) {
40162306a36Sopenharmony_ci		/* remove the reference for the hash */
40262306a36Sopenharmony_ci		batadv_dat_entry_put(dat_entry);
40362306a36Sopenharmony_ci		goto out;
40462306a36Sopenharmony_ci	}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	batadv_dbg(BATADV_DBG_DAT, bat_priv, "New entry added: %pI4 %pM (vid: %d)\n",
40762306a36Sopenharmony_ci		   &dat_entry->ip, dat_entry->mac_addr, batadv_print_vid(vid));
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ciout:
41062306a36Sopenharmony_ci	batadv_dat_entry_put(dat_entry);
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci#ifdef CONFIG_BATMAN_ADV_DEBUG
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci/**
41662306a36Sopenharmony_ci * batadv_dbg_arp() - print a debug message containing all the ARP packet
41762306a36Sopenharmony_ci *  details
41862306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
41962306a36Sopenharmony_ci * @skb: ARP packet
42062306a36Sopenharmony_ci * @hdr_size: size of the possible header before the ARP packet
42162306a36Sopenharmony_ci * @msg: message to print together with the debugging information
42262306a36Sopenharmony_ci */
42362306a36Sopenharmony_cistatic void batadv_dbg_arp(struct batadv_priv *bat_priv, struct sk_buff *skb,
42462306a36Sopenharmony_ci			   int hdr_size, char *msg)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	struct batadv_unicast_4addr_packet *unicast_4addr_packet;
42762306a36Sopenharmony_ci	struct batadv_bcast_packet *bcast_pkt;
42862306a36Sopenharmony_ci	u8 *orig_addr;
42962306a36Sopenharmony_ci	__be32 ip_src, ip_dst;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	if (msg)
43262306a36Sopenharmony_ci		batadv_dbg(BATADV_DBG_DAT, bat_priv, "%s\n", msg);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	ip_src = batadv_arp_ip_src(skb, hdr_size);
43562306a36Sopenharmony_ci	ip_dst = batadv_arp_ip_dst(skb, hdr_size);
43662306a36Sopenharmony_ci	batadv_dbg(BATADV_DBG_DAT, bat_priv,
43762306a36Sopenharmony_ci		   "ARP MSG = [src: %pM-%pI4 dst: %pM-%pI4]\n",
43862306a36Sopenharmony_ci		   batadv_arp_hw_src(skb, hdr_size), &ip_src,
43962306a36Sopenharmony_ci		   batadv_arp_hw_dst(skb, hdr_size), &ip_dst);
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	if (hdr_size < sizeof(struct batadv_unicast_packet))
44262306a36Sopenharmony_ci		return;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	unicast_4addr_packet = (struct batadv_unicast_4addr_packet *)skb->data;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	switch (unicast_4addr_packet->u.packet_type) {
44762306a36Sopenharmony_ci	case BATADV_UNICAST:
44862306a36Sopenharmony_ci		batadv_dbg(BATADV_DBG_DAT, bat_priv,
44962306a36Sopenharmony_ci			   "* encapsulated within a UNICAST packet\n");
45062306a36Sopenharmony_ci		break;
45162306a36Sopenharmony_ci	case BATADV_UNICAST_4ADDR:
45262306a36Sopenharmony_ci		batadv_dbg(BATADV_DBG_DAT, bat_priv,
45362306a36Sopenharmony_ci			   "* encapsulated within a UNICAST_4ADDR packet (src: %pM)\n",
45462306a36Sopenharmony_ci			   unicast_4addr_packet->src);
45562306a36Sopenharmony_ci		switch (unicast_4addr_packet->subtype) {
45662306a36Sopenharmony_ci		case BATADV_P_DAT_DHT_PUT:
45762306a36Sopenharmony_ci			batadv_dbg(BATADV_DBG_DAT, bat_priv, "* type: DAT_DHT_PUT\n");
45862306a36Sopenharmony_ci			break;
45962306a36Sopenharmony_ci		case BATADV_P_DAT_DHT_GET:
46062306a36Sopenharmony_ci			batadv_dbg(BATADV_DBG_DAT, bat_priv, "* type: DAT_DHT_GET\n");
46162306a36Sopenharmony_ci			break;
46262306a36Sopenharmony_ci		case BATADV_P_DAT_CACHE_REPLY:
46362306a36Sopenharmony_ci			batadv_dbg(BATADV_DBG_DAT, bat_priv,
46462306a36Sopenharmony_ci				   "* type: DAT_CACHE_REPLY\n");
46562306a36Sopenharmony_ci			break;
46662306a36Sopenharmony_ci		case BATADV_P_DATA:
46762306a36Sopenharmony_ci			batadv_dbg(BATADV_DBG_DAT, bat_priv, "* type: DATA\n");
46862306a36Sopenharmony_ci			break;
46962306a36Sopenharmony_ci		default:
47062306a36Sopenharmony_ci			batadv_dbg(BATADV_DBG_DAT, bat_priv, "* type: Unknown (%u)!\n",
47162306a36Sopenharmony_ci				   unicast_4addr_packet->u.packet_type);
47262306a36Sopenharmony_ci		}
47362306a36Sopenharmony_ci		break;
47462306a36Sopenharmony_ci	case BATADV_BCAST:
47562306a36Sopenharmony_ci		bcast_pkt = (struct batadv_bcast_packet *)unicast_4addr_packet;
47662306a36Sopenharmony_ci		orig_addr = bcast_pkt->orig;
47762306a36Sopenharmony_ci		batadv_dbg(BATADV_DBG_DAT, bat_priv,
47862306a36Sopenharmony_ci			   "* encapsulated within a BCAST packet (src: %pM)\n",
47962306a36Sopenharmony_ci			   orig_addr);
48062306a36Sopenharmony_ci		break;
48162306a36Sopenharmony_ci	default:
48262306a36Sopenharmony_ci		batadv_dbg(BATADV_DBG_DAT, bat_priv,
48362306a36Sopenharmony_ci			   "* encapsulated within an unknown packet type (0x%x)\n",
48462306a36Sopenharmony_ci			   unicast_4addr_packet->u.packet_type);
48562306a36Sopenharmony_ci	}
48662306a36Sopenharmony_ci}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci#else
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_cistatic void batadv_dbg_arp(struct batadv_priv *bat_priv, struct sk_buff *skb,
49162306a36Sopenharmony_ci			   int hdr_size, char *msg)
49262306a36Sopenharmony_ci{
49362306a36Sopenharmony_ci}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci#endif /* CONFIG_BATMAN_ADV_DEBUG */
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci/**
49862306a36Sopenharmony_ci * batadv_is_orig_node_eligible() - check whether a node can be a DHT candidate
49962306a36Sopenharmony_ci * @res: the array with the already selected candidates
50062306a36Sopenharmony_ci * @select: number of already selected candidates
50162306a36Sopenharmony_ci * @tmp_max: address of the currently evaluated node
50262306a36Sopenharmony_ci * @max: current round max address
50362306a36Sopenharmony_ci * @last_max: address of the last selected candidate
50462306a36Sopenharmony_ci * @candidate: orig_node under evaluation
50562306a36Sopenharmony_ci * @max_orig_node: last selected candidate
50662306a36Sopenharmony_ci *
50762306a36Sopenharmony_ci * Return: true if the node has been elected as next candidate or false
50862306a36Sopenharmony_ci * otherwise.
50962306a36Sopenharmony_ci */
51062306a36Sopenharmony_cistatic bool batadv_is_orig_node_eligible(struct batadv_dat_candidate *res,
51162306a36Sopenharmony_ci					 int select, batadv_dat_addr_t tmp_max,
51262306a36Sopenharmony_ci					 batadv_dat_addr_t max,
51362306a36Sopenharmony_ci					 batadv_dat_addr_t last_max,
51462306a36Sopenharmony_ci					 struct batadv_orig_node *candidate,
51562306a36Sopenharmony_ci					 struct batadv_orig_node *max_orig_node)
51662306a36Sopenharmony_ci{
51762306a36Sopenharmony_ci	bool ret = false;
51862306a36Sopenharmony_ci	int j;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	/* check if orig node candidate is running DAT */
52162306a36Sopenharmony_ci	if (!test_bit(BATADV_ORIG_CAPA_HAS_DAT, &candidate->capabilities))
52262306a36Sopenharmony_ci		goto out;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	/* Check if this node has already been selected... */
52562306a36Sopenharmony_ci	for (j = 0; j < select; j++)
52662306a36Sopenharmony_ci		if (res[j].orig_node == candidate)
52762306a36Sopenharmony_ci			break;
52862306a36Sopenharmony_ci	/* ..and possibly skip it */
52962306a36Sopenharmony_ci	if (j < select)
53062306a36Sopenharmony_ci		goto out;
53162306a36Sopenharmony_ci	/* sanity check: has it already been selected? This should not happen */
53262306a36Sopenharmony_ci	if (tmp_max > last_max)
53362306a36Sopenharmony_ci		goto out;
53462306a36Sopenharmony_ci	/* check if during this iteration an originator with a closer dht
53562306a36Sopenharmony_ci	 * address has already been found
53662306a36Sopenharmony_ci	 */
53762306a36Sopenharmony_ci	if (tmp_max < max)
53862306a36Sopenharmony_ci		goto out;
53962306a36Sopenharmony_ci	/* this is an hash collision with the temporary selected node. Choose
54062306a36Sopenharmony_ci	 * the one with the lowest address
54162306a36Sopenharmony_ci	 */
54262306a36Sopenharmony_ci	if (tmp_max == max && max_orig_node &&
54362306a36Sopenharmony_ci	    batadv_compare_eth(candidate->orig, max_orig_node->orig))
54462306a36Sopenharmony_ci		goto out;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	ret = true;
54762306a36Sopenharmony_ciout:
54862306a36Sopenharmony_ci	return ret;
54962306a36Sopenharmony_ci}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci/**
55262306a36Sopenharmony_ci * batadv_choose_next_candidate() - select the next DHT candidate
55362306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
55462306a36Sopenharmony_ci * @cands: candidates array
55562306a36Sopenharmony_ci * @select: number of candidates already present in the array
55662306a36Sopenharmony_ci * @ip_key: key to look up in the DHT
55762306a36Sopenharmony_ci * @last_max: pointer where the address of the selected candidate will be saved
55862306a36Sopenharmony_ci */
55962306a36Sopenharmony_cistatic void batadv_choose_next_candidate(struct batadv_priv *bat_priv,
56062306a36Sopenharmony_ci					 struct batadv_dat_candidate *cands,
56162306a36Sopenharmony_ci					 int select, batadv_dat_addr_t ip_key,
56262306a36Sopenharmony_ci					 batadv_dat_addr_t *last_max)
56362306a36Sopenharmony_ci{
56462306a36Sopenharmony_ci	batadv_dat_addr_t max = 0;
56562306a36Sopenharmony_ci	batadv_dat_addr_t tmp_max = 0;
56662306a36Sopenharmony_ci	struct batadv_orig_node *orig_node, *max_orig_node = NULL;
56762306a36Sopenharmony_ci	struct batadv_hashtable *hash = bat_priv->orig_hash;
56862306a36Sopenharmony_ci	struct hlist_head *head;
56962306a36Sopenharmony_ci	int i;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	/* if no node is eligible as candidate, leave the candidate type as
57262306a36Sopenharmony_ci	 * NOT_FOUND
57362306a36Sopenharmony_ci	 */
57462306a36Sopenharmony_ci	cands[select].type = BATADV_DAT_CANDIDATE_NOT_FOUND;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	/* iterate over the originator list and find the node with the closest
57762306a36Sopenharmony_ci	 * dat_address which has not been selected yet
57862306a36Sopenharmony_ci	 */
57962306a36Sopenharmony_ci	for (i = 0; i < hash->size; i++) {
58062306a36Sopenharmony_ci		head = &hash->table[i];
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci		rcu_read_lock();
58362306a36Sopenharmony_ci		hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
58462306a36Sopenharmony_ci			/* the dht space is a ring using unsigned addresses */
58562306a36Sopenharmony_ci			tmp_max = BATADV_DAT_ADDR_MAX - orig_node->dat_addr +
58662306a36Sopenharmony_ci				  ip_key;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci			if (!batadv_is_orig_node_eligible(cands, select,
58962306a36Sopenharmony_ci							  tmp_max, max,
59062306a36Sopenharmony_ci							  *last_max, orig_node,
59162306a36Sopenharmony_ci							  max_orig_node))
59262306a36Sopenharmony_ci				continue;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci			if (!kref_get_unless_zero(&orig_node->refcount))
59562306a36Sopenharmony_ci				continue;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci			max = tmp_max;
59862306a36Sopenharmony_ci			batadv_orig_node_put(max_orig_node);
59962306a36Sopenharmony_ci			max_orig_node = orig_node;
60062306a36Sopenharmony_ci		}
60162306a36Sopenharmony_ci		rcu_read_unlock();
60262306a36Sopenharmony_ci	}
60362306a36Sopenharmony_ci	if (max_orig_node) {
60462306a36Sopenharmony_ci		cands[select].type = BATADV_DAT_CANDIDATE_ORIG;
60562306a36Sopenharmony_ci		cands[select].orig_node = max_orig_node;
60662306a36Sopenharmony_ci		batadv_dbg(BATADV_DBG_DAT, bat_priv,
60762306a36Sopenharmony_ci			   "dat_select_candidates() %d: selected %pM addr=%u dist=%u\n",
60862306a36Sopenharmony_ci			   select, max_orig_node->orig, max_orig_node->dat_addr,
60962306a36Sopenharmony_ci			   max);
61062306a36Sopenharmony_ci	}
61162306a36Sopenharmony_ci	*last_max = max;
61262306a36Sopenharmony_ci}
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci/**
61562306a36Sopenharmony_ci * batadv_dat_select_candidates() - select the nodes which the DHT message has
61662306a36Sopenharmony_ci *  to be sent to
61762306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
61862306a36Sopenharmony_ci * @ip_dst: ipv4 to look up in the DHT
61962306a36Sopenharmony_ci * @vid: VLAN identifier
62062306a36Sopenharmony_ci *
62162306a36Sopenharmony_ci * An originator O is selected if and only if its DHT_ID value is one of three
62262306a36Sopenharmony_ci * closest values (from the LEFT, with wrap around if needed) then the hash
62362306a36Sopenharmony_ci * value of the key. ip_dst is the key.
62462306a36Sopenharmony_ci *
62562306a36Sopenharmony_ci * Return: the candidate array of size BATADV_DAT_CANDIDATE_NUM.
62662306a36Sopenharmony_ci */
62762306a36Sopenharmony_cistatic struct batadv_dat_candidate *
62862306a36Sopenharmony_cibatadv_dat_select_candidates(struct batadv_priv *bat_priv, __be32 ip_dst,
62962306a36Sopenharmony_ci			     unsigned short vid)
63062306a36Sopenharmony_ci{
63162306a36Sopenharmony_ci	int select;
63262306a36Sopenharmony_ci	batadv_dat_addr_t last_max = BATADV_DAT_ADDR_MAX, ip_key;
63362306a36Sopenharmony_ci	struct batadv_dat_candidate *res;
63462306a36Sopenharmony_ci	struct batadv_dat_entry dat;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	if (!bat_priv->orig_hash)
63762306a36Sopenharmony_ci		return NULL;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	res = kmalloc_array(BATADV_DAT_CANDIDATES_NUM, sizeof(*res),
64062306a36Sopenharmony_ci			    GFP_ATOMIC);
64162306a36Sopenharmony_ci	if (!res)
64262306a36Sopenharmony_ci		return NULL;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	dat.ip = ip_dst;
64562306a36Sopenharmony_ci	dat.vid = vid;
64662306a36Sopenharmony_ci	ip_key = (batadv_dat_addr_t)batadv_hash_dat(&dat,
64762306a36Sopenharmony_ci						    BATADV_DAT_ADDR_MAX);
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	batadv_dbg(BATADV_DBG_DAT, bat_priv,
65062306a36Sopenharmony_ci		   "%s(): IP=%pI4 hash(IP)=%u\n", __func__, &ip_dst,
65162306a36Sopenharmony_ci		   ip_key);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	for (select = 0; select < BATADV_DAT_CANDIDATES_NUM; select++)
65462306a36Sopenharmony_ci		batadv_choose_next_candidate(bat_priv, res, select, ip_key,
65562306a36Sopenharmony_ci					     &last_max);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	return res;
65862306a36Sopenharmony_ci}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci/**
66162306a36Sopenharmony_ci * batadv_dat_forward_data() - copy and send payload to the selected candidates
66262306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
66362306a36Sopenharmony_ci * @skb: payload to send
66462306a36Sopenharmony_ci * @ip: the DHT key
66562306a36Sopenharmony_ci * @vid: VLAN identifier
66662306a36Sopenharmony_ci * @packet_subtype: unicast4addr packet subtype to use
66762306a36Sopenharmony_ci *
66862306a36Sopenharmony_ci * This function copies the skb with pskb_copy() and is sent as a unicast packet
66962306a36Sopenharmony_ci * to each of the selected candidates.
67062306a36Sopenharmony_ci *
67162306a36Sopenharmony_ci * Return: true if the packet is sent to at least one candidate, false
67262306a36Sopenharmony_ci * otherwise.
67362306a36Sopenharmony_ci */
67462306a36Sopenharmony_cistatic bool batadv_dat_forward_data(struct batadv_priv *bat_priv,
67562306a36Sopenharmony_ci				    struct sk_buff *skb, __be32 ip,
67662306a36Sopenharmony_ci				    unsigned short vid, int packet_subtype)
67762306a36Sopenharmony_ci{
67862306a36Sopenharmony_ci	int i;
67962306a36Sopenharmony_ci	bool ret = false;
68062306a36Sopenharmony_ci	int send_status;
68162306a36Sopenharmony_ci	struct batadv_neigh_node *neigh_node = NULL;
68262306a36Sopenharmony_ci	struct sk_buff *tmp_skb;
68362306a36Sopenharmony_ci	struct batadv_dat_candidate *cand;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	cand = batadv_dat_select_candidates(bat_priv, ip, vid);
68662306a36Sopenharmony_ci	if (!cand)
68762306a36Sopenharmony_ci		goto out;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	batadv_dbg(BATADV_DBG_DAT, bat_priv, "DHT_SEND for %pI4\n", &ip);
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	for (i = 0; i < BATADV_DAT_CANDIDATES_NUM; i++) {
69262306a36Sopenharmony_ci		if (cand[i].type == BATADV_DAT_CANDIDATE_NOT_FOUND)
69362306a36Sopenharmony_ci			continue;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci		neigh_node = batadv_orig_router_get(cand[i].orig_node,
69662306a36Sopenharmony_ci						    BATADV_IF_DEFAULT);
69762306a36Sopenharmony_ci		if (!neigh_node)
69862306a36Sopenharmony_ci			goto free_orig;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci		tmp_skb = pskb_copy_for_clone(skb, GFP_ATOMIC);
70162306a36Sopenharmony_ci		if (!batadv_send_skb_prepare_unicast_4addr(bat_priv, tmp_skb,
70262306a36Sopenharmony_ci							   cand[i].orig_node,
70362306a36Sopenharmony_ci							   packet_subtype)) {
70462306a36Sopenharmony_ci			kfree_skb(tmp_skb);
70562306a36Sopenharmony_ci			goto free_neigh;
70662306a36Sopenharmony_ci		}
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci		send_status = batadv_send_unicast_skb(tmp_skb, neigh_node);
70962306a36Sopenharmony_ci		if (send_status == NET_XMIT_SUCCESS) {
71062306a36Sopenharmony_ci			/* count the sent packet */
71162306a36Sopenharmony_ci			switch (packet_subtype) {
71262306a36Sopenharmony_ci			case BATADV_P_DAT_DHT_GET:
71362306a36Sopenharmony_ci				batadv_inc_counter(bat_priv,
71462306a36Sopenharmony_ci						   BATADV_CNT_DAT_GET_TX);
71562306a36Sopenharmony_ci				break;
71662306a36Sopenharmony_ci			case BATADV_P_DAT_DHT_PUT:
71762306a36Sopenharmony_ci				batadv_inc_counter(bat_priv,
71862306a36Sopenharmony_ci						   BATADV_CNT_DAT_PUT_TX);
71962306a36Sopenharmony_ci				break;
72062306a36Sopenharmony_ci			}
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci			/* packet sent to a candidate: return true */
72362306a36Sopenharmony_ci			ret = true;
72462306a36Sopenharmony_ci		}
72562306a36Sopenharmony_cifree_neigh:
72662306a36Sopenharmony_ci		batadv_neigh_node_put(neigh_node);
72762306a36Sopenharmony_cifree_orig:
72862306a36Sopenharmony_ci		batadv_orig_node_put(cand[i].orig_node);
72962306a36Sopenharmony_ci	}
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ciout:
73262306a36Sopenharmony_ci	kfree(cand);
73362306a36Sopenharmony_ci	return ret;
73462306a36Sopenharmony_ci}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci/**
73762306a36Sopenharmony_ci * batadv_dat_tvlv_container_update() - update the dat tvlv container after dat
73862306a36Sopenharmony_ci *  setting change
73962306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
74062306a36Sopenharmony_ci */
74162306a36Sopenharmony_cistatic void batadv_dat_tvlv_container_update(struct batadv_priv *bat_priv)
74262306a36Sopenharmony_ci{
74362306a36Sopenharmony_ci	char dat_mode;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	dat_mode = atomic_read(&bat_priv->distributed_arp_table);
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	switch (dat_mode) {
74862306a36Sopenharmony_ci	case 0:
74962306a36Sopenharmony_ci		batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_DAT, 1);
75062306a36Sopenharmony_ci		break;
75162306a36Sopenharmony_ci	case 1:
75262306a36Sopenharmony_ci		batadv_tvlv_container_register(bat_priv, BATADV_TVLV_DAT, 1,
75362306a36Sopenharmony_ci					       NULL, 0);
75462306a36Sopenharmony_ci		break;
75562306a36Sopenharmony_ci	}
75662306a36Sopenharmony_ci}
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci/**
75962306a36Sopenharmony_ci * batadv_dat_status_update() - update the dat tvlv container after dat
76062306a36Sopenharmony_ci *  setting change
76162306a36Sopenharmony_ci * @net_dev: the soft interface net device
76262306a36Sopenharmony_ci */
76362306a36Sopenharmony_civoid batadv_dat_status_update(struct net_device *net_dev)
76462306a36Sopenharmony_ci{
76562306a36Sopenharmony_ci	struct batadv_priv *bat_priv = netdev_priv(net_dev);
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	batadv_dat_tvlv_container_update(bat_priv);
76862306a36Sopenharmony_ci}
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci/**
77162306a36Sopenharmony_ci * batadv_dat_tvlv_ogm_handler_v1() - process incoming dat tvlv container
77262306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
77362306a36Sopenharmony_ci * @orig: the orig_node of the ogm
77462306a36Sopenharmony_ci * @flags: flags indicating the tvlv state (see batadv_tvlv_handler_flags)
77562306a36Sopenharmony_ci * @tvlv_value: tvlv buffer containing the gateway data
77662306a36Sopenharmony_ci * @tvlv_value_len: tvlv buffer length
77762306a36Sopenharmony_ci */
77862306a36Sopenharmony_cistatic void batadv_dat_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,
77962306a36Sopenharmony_ci					   struct batadv_orig_node *orig,
78062306a36Sopenharmony_ci					   u8 flags,
78162306a36Sopenharmony_ci					   void *tvlv_value, u16 tvlv_value_len)
78262306a36Sopenharmony_ci{
78362306a36Sopenharmony_ci	if (flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND)
78462306a36Sopenharmony_ci		clear_bit(BATADV_ORIG_CAPA_HAS_DAT, &orig->capabilities);
78562306a36Sopenharmony_ci	else
78662306a36Sopenharmony_ci		set_bit(BATADV_ORIG_CAPA_HAS_DAT, &orig->capabilities);
78762306a36Sopenharmony_ci}
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci/**
79062306a36Sopenharmony_ci * batadv_dat_hash_free() - free the local DAT hash table
79162306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
79262306a36Sopenharmony_ci */
79362306a36Sopenharmony_cistatic void batadv_dat_hash_free(struct batadv_priv *bat_priv)
79462306a36Sopenharmony_ci{
79562306a36Sopenharmony_ci	if (!bat_priv->dat.hash)
79662306a36Sopenharmony_ci		return;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	__batadv_dat_purge(bat_priv, NULL);
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	batadv_hash_destroy(bat_priv->dat.hash);
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	bat_priv->dat.hash = NULL;
80362306a36Sopenharmony_ci}
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci/**
80662306a36Sopenharmony_ci * batadv_dat_init() - initialise the DAT internals
80762306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
80862306a36Sopenharmony_ci *
80962306a36Sopenharmony_ci * Return: 0 in case of success, a negative error code otherwise
81062306a36Sopenharmony_ci */
81162306a36Sopenharmony_ciint batadv_dat_init(struct batadv_priv *bat_priv)
81262306a36Sopenharmony_ci{
81362306a36Sopenharmony_ci	if (bat_priv->dat.hash)
81462306a36Sopenharmony_ci		return 0;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	bat_priv->dat.hash = batadv_hash_new(1024);
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	if (!bat_priv->dat.hash)
81962306a36Sopenharmony_ci		return -ENOMEM;
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	INIT_DELAYED_WORK(&bat_priv->dat.work, batadv_dat_purge);
82262306a36Sopenharmony_ci	batadv_dat_start_timer(bat_priv);
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	batadv_tvlv_handler_register(bat_priv, batadv_dat_tvlv_ogm_handler_v1,
82562306a36Sopenharmony_ci				     NULL, NULL, BATADV_TVLV_DAT, 1,
82662306a36Sopenharmony_ci				     BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
82762306a36Sopenharmony_ci	batadv_dat_tvlv_container_update(bat_priv);
82862306a36Sopenharmony_ci	return 0;
82962306a36Sopenharmony_ci}
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci/**
83262306a36Sopenharmony_ci * batadv_dat_free() - free the DAT internals
83362306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
83462306a36Sopenharmony_ci */
83562306a36Sopenharmony_civoid batadv_dat_free(struct batadv_priv *bat_priv)
83662306a36Sopenharmony_ci{
83762306a36Sopenharmony_ci	batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_DAT, 1);
83862306a36Sopenharmony_ci	batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_DAT, 1);
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	cancel_delayed_work_sync(&bat_priv->dat.work);
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	batadv_dat_hash_free(bat_priv);
84362306a36Sopenharmony_ci}
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci/**
84662306a36Sopenharmony_ci * batadv_dat_cache_dump_entry() - dump one entry of the DAT cache table to a
84762306a36Sopenharmony_ci *  netlink socket
84862306a36Sopenharmony_ci * @msg: buffer for the message
84962306a36Sopenharmony_ci * @portid: netlink port
85062306a36Sopenharmony_ci * @cb: Control block containing additional options
85162306a36Sopenharmony_ci * @dat_entry: entry to dump
85262306a36Sopenharmony_ci *
85362306a36Sopenharmony_ci * Return: 0 or error code.
85462306a36Sopenharmony_ci */
85562306a36Sopenharmony_cistatic int
85662306a36Sopenharmony_cibatadv_dat_cache_dump_entry(struct sk_buff *msg, u32 portid,
85762306a36Sopenharmony_ci			    struct netlink_callback *cb,
85862306a36Sopenharmony_ci			    struct batadv_dat_entry *dat_entry)
85962306a36Sopenharmony_ci{
86062306a36Sopenharmony_ci	int msecs;
86162306a36Sopenharmony_ci	void *hdr;
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq,
86462306a36Sopenharmony_ci			  &batadv_netlink_family, NLM_F_MULTI,
86562306a36Sopenharmony_ci			  BATADV_CMD_GET_DAT_CACHE);
86662306a36Sopenharmony_ci	if (!hdr)
86762306a36Sopenharmony_ci		return -ENOBUFS;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	genl_dump_check_consistent(cb, hdr);
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	msecs = jiffies_to_msecs(jiffies - dat_entry->last_update);
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	if (nla_put_in_addr(msg, BATADV_ATTR_DAT_CACHE_IP4ADDRESS,
87462306a36Sopenharmony_ci			    dat_entry->ip) ||
87562306a36Sopenharmony_ci	    nla_put(msg, BATADV_ATTR_DAT_CACHE_HWADDRESS, ETH_ALEN,
87662306a36Sopenharmony_ci		    dat_entry->mac_addr) ||
87762306a36Sopenharmony_ci	    nla_put_u16(msg, BATADV_ATTR_DAT_CACHE_VID, dat_entry->vid) ||
87862306a36Sopenharmony_ci	    nla_put_u32(msg, BATADV_ATTR_LAST_SEEN_MSECS, msecs)) {
87962306a36Sopenharmony_ci		genlmsg_cancel(msg, hdr);
88062306a36Sopenharmony_ci		return -EMSGSIZE;
88162306a36Sopenharmony_ci	}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	genlmsg_end(msg, hdr);
88462306a36Sopenharmony_ci	return 0;
88562306a36Sopenharmony_ci}
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci/**
88862306a36Sopenharmony_ci * batadv_dat_cache_dump_bucket() - dump one bucket of the DAT cache table to
88962306a36Sopenharmony_ci *  a netlink socket
89062306a36Sopenharmony_ci * @msg: buffer for the message
89162306a36Sopenharmony_ci * @portid: netlink port
89262306a36Sopenharmony_ci * @cb: Control block containing additional options
89362306a36Sopenharmony_ci * @hash: hash to dump
89462306a36Sopenharmony_ci * @bucket: bucket index to dump
89562306a36Sopenharmony_ci * @idx_skip: How many entries to skip
89662306a36Sopenharmony_ci *
89762306a36Sopenharmony_ci * Return: 0 or error code.
89862306a36Sopenharmony_ci */
89962306a36Sopenharmony_cistatic int
90062306a36Sopenharmony_cibatadv_dat_cache_dump_bucket(struct sk_buff *msg, u32 portid,
90162306a36Sopenharmony_ci			     struct netlink_callback *cb,
90262306a36Sopenharmony_ci			     struct batadv_hashtable *hash, unsigned int bucket,
90362306a36Sopenharmony_ci			     int *idx_skip)
90462306a36Sopenharmony_ci{
90562306a36Sopenharmony_ci	struct batadv_dat_entry *dat_entry;
90662306a36Sopenharmony_ci	int idx = 0;
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	spin_lock_bh(&hash->list_locks[bucket]);
90962306a36Sopenharmony_ci	cb->seq = atomic_read(&hash->generation) << 1 | 1;
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	hlist_for_each_entry(dat_entry, &hash->table[bucket], hash_entry) {
91262306a36Sopenharmony_ci		if (idx < *idx_skip)
91362306a36Sopenharmony_ci			goto skip;
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci		if (batadv_dat_cache_dump_entry(msg, portid, cb, dat_entry)) {
91662306a36Sopenharmony_ci			spin_unlock_bh(&hash->list_locks[bucket]);
91762306a36Sopenharmony_ci			*idx_skip = idx;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci			return -EMSGSIZE;
92062306a36Sopenharmony_ci		}
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ciskip:
92362306a36Sopenharmony_ci		idx++;
92462306a36Sopenharmony_ci	}
92562306a36Sopenharmony_ci	spin_unlock_bh(&hash->list_locks[bucket]);
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	return 0;
92862306a36Sopenharmony_ci}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci/**
93162306a36Sopenharmony_ci * batadv_dat_cache_dump() - dump DAT cache table to a netlink socket
93262306a36Sopenharmony_ci * @msg: buffer for the message
93362306a36Sopenharmony_ci * @cb: callback structure containing arguments
93462306a36Sopenharmony_ci *
93562306a36Sopenharmony_ci * Return: message length.
93662306a36Sopenharmony_ci */
93762306a36Sopenharmony_ciint batadv_dat_cache_dump(struct sk_buff *msg, struct netlink_callback *cb)
93862306a36Sopenharmony_ci{
93962306a36Sopenharmony_ci	struct batadv_hard_iface *primary_if = NULL;
94062306a36Sopenharmony_ci	int portid = NETLINK_CB(cb->skb).portid;
94162306a36Sopenharmony_ci	struct net *net = sock_net(cb->skb->sk);
94262306a36Sopenharmony_ci	struct net_device *soft_iface;
94362306a36Sopenharmony_ci	struct batadv_hashtable *hash;
94462306a36Sopenharmony_ci	struct batadv_priv *bat_priv;
94562306a36Sopenharmony_ci	int bucket = cb->args[0];
94662306a36Sopenharmony_ci	int idx = cb->args[1];
94762306a36Sopenharmony_ci	int ifindex;
94862306a36Sopenharmony_ci	int ret = 0;
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	ifindex = batadv_netlink_get_ifindex(cb->nlh,
95162306a36Sopenharmony_ci					     BATADV_ATTR_MESH_IFINDEX);
95262306a36Sopenharmony_ci	if (!ifindex)
95362306a36Sopenharmony_ci		return -EINVAL;
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	soft_iface = dev_get_by_index(net, ifindex);
95662306a36Sopenharmony_ci	if (!soft_iface || !batadv_softif_is_valid(soft_iface)) {
95762306a36Sopenharmony_ci		ret = -ENODEV;
95862306a36Sopenharmony_ci		goto out;
95962306a36Sopenharmony_ci	}
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	bat_priv = netdev_priv(soft_iface);
96262306a36Sopenharmony_ci	hash = bat_priv->dat.hash;
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	primary_if = batadv_primary_if_get_selected(bat_priv);
96562306a36Sopenharmony_ci	if (!primary_if || primary_if->if_status != BATADV_IF_ACTIVE) {
96662306a36Sopenharmony_ci		ret = -ENOENT;
96762306a36Sopenharmony_ci		goto out;
96862306a36Sopenharmony_ci	}
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	while (bucket < hash->size) {
97162306a36Sopenharmony_ci		if (batadv_dat_cache_dump_bucket(msg, portid, cb, hash, bucket,
97262306a36Sopenharmony_ci						 &idx))
97362306a36Sopenharmony_ci			break;
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci		bucket++;
97662306a36Sopenharmony_ci		idx = 0;
97762306a36Sopenharmony_ci	}
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	cb->args[0] = bucket;
98062306a36Sopenharmony_ci	cb->args[1] = idx;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	ret = msg->len;
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ciout:
98562306a36Sopenharmony_ci	batadv_hardif_put(primary_if);
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	dev_put(soft_iface);
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	return ret;
99062306a36Sopenharmony_ci}
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci/**
99362306a36Sopenharmony_ci * batadv_arp_get_type() - parse an ARP packet and gets the type
99462306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
99562306a36Sopenharmony_ci * @skb: packet to analyse
99662306a36Sopenharmony_ci * @hdr_size: size of the possible header before the ARP packet in the skb
99762306a36Sopenharmony_ci *
99862306a36Sopenharmony_ci * Return: the ARP type if the skb contains a valid ARP packet, 0 otherwise.
99962306a36Sopenharmony_ci */
100062306a36Sopenharmony_cistatic u16 batadv_arp_get_type(struct batadv_priv *bat_priv,
100162306a36Sopenharmony_ci			       struct sk_buff *skb, int hdr_size)
100262306a36Sopenharmony_ci{
100362306a36Sopenharmony_ci	struct arphdr *arphdr;
100462306a36Sopenharmony_ci	struct ethhdr *ethhdr;
100562306a36Sopenharmony_ci	__be32 ip_src, ip_dst;
100662306a36Sopenharmony_ci	u8 *hw_src, *hw_dst;
100762306a36Sopenharmony_ci	u16 type = 0;
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	/* pull the ethernet header */
101062306a36Sopenharmony_ci	if (unlikely(!pskb_may_pull(skb, hdr_size + ETH_HLEN)))
101162306a36Sopenharmony_ci		goto out;
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	ethhdr = (struct ethhdr *)(skb->data + hdr_size);
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	if (ethhdr->h_proto != htons(ETH_P_ARP))
101662306a36Sopenharmony_ci		goto out;
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	/* pull the ARP payload */
101962306a36Sopenharmony_ci	if (unlikely(!pskb_may_pull(skb, hdr_size + ETH_HLEN +
102062306a36Sopenharmony_ci				    arp_hdr_len(skb->dev))))
102162306a36Sopenharmony_ci		goto out;
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	arphdr = (struct arphdr *)(skb->data + hdr_size + ETH_HLEN);
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	/* check whether the ARP packet carries a valid IP information */
102662306a36Sopenharmony_ci	if (arphdr->ar_hrd != htons(ARPHRD_ETHER))
102762306a36Sopenharmony_ci		goto out;
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	if (arphdr->ar_pro != htons(ETH_P_IP))
103062306a36Sopenharmony_ci		goto out;
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	if (arphdr->ar_hln != ETH_ALEN)
103362306a36Sopenharmony_ci		goto out;
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	if (arphdr->ar_pln != 4)
103662306a36Sopenharmony_ci		goto out;
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	/* Check for bad reply/request. If the ARP message is not sane, DAT
103962306a36Sopenharmony_ci	 * will simply ignore it
104062306a36Sopenharmony_ci	 */
104162306a36Sopenharmony_ci	ip_src = batadv_arp_ip_src(skb, hdr_size);
104262306a36Sopenharmony_ci	ip_dst = batadv_arp_ip_dst(skb, hdr_size);
104362306a36Sopenharmony_ci	if (ipv4_is_loopback(ip_src) || ipv4_is_multicast(ip_src) ||
104462306a36Sopenharmony_ci	    ipv4_is_loopback(ip_dst) || ipv4_is_multicast(ip_dst) ||
104562306a36Sopenharmony_ci	    ipv4_is_zeronet(ip_src) || ipv4_is_lbcast(ip_src) ||
104662306a36Sopenharmony_ci	    ipv4_is_zeronet(ip_dst) || ipv4_is_lbcast(ip_dst))
104762306a36Sopenharmony_ci		goto out;
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	hw_src = batadv_arp_hw_src(skb, hdr_size);
105062306a36Sopenharmony_ci	if (is_zero_ether_addr(hw_src) || is_multicast_ether_addr(hw_src))
105162306a36Sopenharmony_ci		goto out;
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	/* don't care about the destination MAC address in ARP requests */
105462306a36Sopenharmony_ci	if (arphdr->ar_op != htons(ARPOP_REQUEST)) {
105562306a36Sopenharmony_ci		hw_dst = batadv_arp_hw_dst(skb, hdr_size);
105662306a36Sopenharmony_ci		if (is_zero_ether_addr(hw_dst) ||
105762306a36Sopenharmony_ci		    is_multicast_ether_addr(hw_dst))
105862306a36Sopenharmony_ci			goto out;
105962306a36Sopenharmony_ci	}
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	type = ntohs(arphdr->ar_op);
106262306a36Sopenharmony_ciout:
106362306a36Sopenharmony_ci	return type;
106462306a36Sopenharmony_ci}
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci/**
106762306a36Sopenharmony_ci * batadv_dat_get_vid() - extract the VLAN identifier from skb if any
106862306a36Sopenharmony_ci * @skb: the buffer containing the packet to extract the VID from
106962306a36Sopenharmony_ci * @hdr_size: the size of the batman-adv header encapsulating the packet
107062306a36Sopenharmony_ci *
107162306a36Sopenharmony_ci * Return: If the packet embedded in the skb is vlan tagged this function
107262306a36Sopenharmony_ci * returns the VID with the BATADV_VLAN_HAS_TAG flag. Otherwise BATADV_NO_FLAGS
107362306a36Sopenharmony_ci * is returned.
107462306a36Sopenharmony_ci */
107562306a36Sopenharmony_cistatic unsigned short batadv_dat_get_vid(struct sk_buff *skb, int *hdr_size)
107662306a36Sopenharmony_ci{
107762306a36Sopenharmony_ci	unsigned short vid;
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	vid = batadv_get_vid(skb, *hdr_size);
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	/* ARP parsing functions jump forward of hdr_size + ETH_HLEN.
108262306a36Sopenharmony_ci	 * If the header contained in the packet is a VLAN one (which is longer)
108362306a36Sopenharmony_ci	 * hdr_size is updated so that the functions will still skip the
108462306a36Sopenharmony_ci	 * correct amount of bytes.
108562306a36Sopenharmony_ci	 */
108662306a36Sopenharmony_ci	if (vid & BATADV_VLAN_HAS_TAG)
108762306a36Sopenharmony_ci		*hdr_size += VLAN_HLEN;
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	return vid;
109062306a36Sopenharmony_ci}
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci/**
109362306a36Sopenharmony_ci * batadv_dat_arp_create_reply() - create an ARP Reply
109462306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
109562306a36Sopenharmony_ci * @ip_src: ARP sender IP
109662306a36Sopenharmony_ci * @ip_dst: ARP target IP
109762306a36Sopenharmony_ci * @hw_src: Ethernet source and ARP sender MAC
109862306a36Sopenharmony_ci * @hw_dst: Ethernet destination and ARP target MAC
109962306a36Sopenharmony_ci * @vid: VLAN identifier (optional, set to zero otherwise)
110062306a36Sopenharmony_ci *
110162306a36Sopenharmony_ci * Creates an ARP Reply from the given values, optionally encapsulated in a
110262306a36Sopenharmony_ci * VLAN header.
110362306a36Sopenharmony_ci *
110462306a36Sopenharmony_ci * Return: An skb containing an ARP Reply.
110562306a36Sopenharmony_ci */
110662306a36Sopenharmony_cistatic struct sk_buff *
110762306a36Sopenharmony_cibatadv_dat_arp_create_reply(struct batadv_priv *bat_priv, __be32 ip_src,
110862306a36Sopenharmony_ci			    __be32 ip_dst, u8 *hw_src, u8 *hw_dst,
110962306a36Sopenharmony_ci			    unsigned short vid)
111062306a36Sopenharmony_ci{
111162306a36Sopenharmony_ci	struct sk_buff *skb;
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	skb = arp_create(ARPOP_REPLY, ETH_P_ARP, ip_dst, bat_priv->soft_iface,
111462306a36Sopenharmony_ci			 ip_src, hw_dst, hw_src, hw_dst);
111562306a36Sopenharmony_ci	if (!skb)
111662306a36Sopenharmony_ci		return NULL;
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	skb_reset_mac_header(skb);
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	if (vid & BATADV_VLAN_HAS_TAG)
112162306a36Sopenharmony_ci		skb = vlan_insert_tag(skb, htons(ETH_P_8021Q),
112262306a36Sopenharmony_ci				      vid & VLAN_VID_MASK);
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	return skb;
112562306a36Sopenharmony_ci}
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci/**
112862306a36Sopenharmony_ci * batadv_dat_snoop_outgoing_arp_request() - snoop the ARP request and try to
112962306a36Sopenharmony_ci * answer using DAT
113062306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
113162306a36Sopenharmony_ci * @skb: packet to check
113262306a36Sopenharmony_ci *
113362306a36Sopenharmony_ci * Return: true if the message has been sent to the dht candidates, false
113462306a36Sopenharmony_ci * otherwise. In case of a positive return value the message has to be enqueued
113562306a36Sopenharmony_ci * to permit the fallback.
113662306a36Sopenharmony_ci */
113762306a36Sopenharmony_cibool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
113862306a36Sopenharmony_ci					   struct sk_buff *skb)
113962306a36Sopenharmony_ci{
114062306a36Sopenharmony_ci	u16 type = 0;
114162306a36Sopenharmony_ci	__be32 ip_dst, ip_src;
114262306a36Sopenharmony_ci	u8 *hw_src;
114362306a36Sopenharmony_ci	bool ret = false;
114462306a36Sopenharmony_ci	struct batadv_dat_entry *dat_entry = NULL;
114562306a36Sopenharmony_ci	struct sk_buff *skb_new;
114662306a36Sopenharmony_ci	struct net_device *soft_iface = bat_priv->soft_iface;
114762306a36Sopenharmony_ci	int hdr_size = 0;
114862306a36Sopenharmony_ci	unsigned short vid;
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	if (!atomic_read(&bat_priv->distributed_arp_table))
115162306a36Sopenharmony_ci		goto out;
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	vid = batadv_dat_get_vid(skb, &hdr_size);
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	type = batadv_arp_get_type(bat_priv, skb, hdr_size);
115662306a36Sopenharmony_ci	/* If the node gets an ARP_REQUEST it has to send a DHT_GET unicast
115762306a36Sopenharmony_ci	 * message to the selected DHT candidates
115862306a36Sopenharmony_ci	 */
115962306a36Sopenharmony_ci	if (type != ARPOP_REQUEST)
116062306a36Sopenharmony_ci		goto out;
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	batadv_dbg_arp(bat_priv, skb, hdr_size, "Parsing outgoing ARP REQUEST");
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	ip_src = batadv_arp_ip_src(skb, hdr_size);
116562306a36Sopenharmony_ci	hw_src = batadv_arp_hw_src(skb, hdr_size);
116662306a36Sopenharmony_ci	ip_dst = batadv_arp_ip_dst(skb, hdr_size);
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid);
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci	dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst, vid);
117162306a36Sopenharmony_ci	if (dat_entry) {
117262306a36Sopenharmony_ci		/* If the ARP request is destined for a local client the local
117362306a36Sopenharmony_ci		 * client will answer itself. DAT would only generate a
117462306a36Sopenharmony_ci		 * duplicate packet.
117562306a36Sopenharmony_ci		 *
117662306a36Sopenharmony_ci		 * Moreover, if the soft-interface is enslaved into a bridge, an
117762306a36Sopenharmony_ci		 * additional DAT answer may trigger kernel warnings about
117862306a36Sopenharmony_ci		 * a packet coming from the wrong port.
117962306a36Sopenharmony_ci		 */
118062306a36Sopenharmony_ci		if (batadv_is_my_client(bat_priv, dat_entry->mac_addr, vid)) {
118162306a36Sopenharmony_ci			ret = true;
118262306a36Sopenharmony_ci			goto out;
118362306a36Sopenharmony_ci		}
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci		/* If BLA is enabled, only send ARP replies if we have claimed
118662306a36Sopenharmony_ci		 * the destination for the ARP request or if no one else of
118762306a36Sopenharmony_ci		 * the backbone gws belonging to our backbone has claimed the
118862306a36Sopenharmony_ci		 * destination.
118962306a36Sopenharmony_ci		 */
119062306a36Sopenharmony_ci		if (!batadv_bla_check_claim(bat_priv,
119162306a36Sopenharmony_ci					    dat_entry->mac_addr, vid)) {
119262306a36Sopenharmony_ci			batadv_dbg(BATADV_DBG_DAT, bat_priv,
119362306a36Sopenharmony_ci				   "Device %pM claimed by another backbone gw. Don't send ARP reply!",
119462306a36Sopenharmony_ci				   dat_entry->mac_addr);
119562306a36Sopenharmony_ci			ret = true;
119662306a36Sopenharmony_ci			goto out;
119762306a36Sopenharmony_ci		}
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci		skb_new = batadv_dat_arp_create_reply(bat_priv, ip_dst, ip_src,
120062306a36Sopenharmony_ci						      dat_entry->mac_addr,
120162306a36Sopenharmony_ci						      hw_src, vid);
120262306a36Sopenharmony_ci		if (!skb_new)
120362306a36Sopenharmony_ci			goto out;
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci		skb_new->protocol = eth_type_trans(skb_new, soft_iface);
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci		batadv_inc_counter(bat_priv, BATADV_CNT_RX);
120862306a36Sopenharmony_ci		batadv_add_counter(bat_priv, BATADV_CNT_RX_BYTES,
120962306a36Sopenharmony_ci				   skb->len + ETH_HLEN + hdr_size);
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci		netif_rx(skb_new);
121262306a36Sopenharmony_ci		batadv_dbg(BATADV_DBG_DAT, bat_priv, "ARP request replied locally\n");
121362306a36Sopenharmony_ci		ret = true;
121462306a36Sopenharmony_ci	} else {
121562306a36Sopenharmony_ci		/* Send the request to the DHT */
121662306a36Sopenharmony_ci		ret = batadv_dat_forward_data(bat_priv, skb, ip_dst, vid,
121762306a36Sopenharmony_ci					      BATADV_P_DAT_DHT_GET);
121862306a36Sopenharmony_ci	}
121962306a36Sopenharmony_ciout:
122062306a36Sopenharmony_ci	batadv_dat_entry_put(dat_entry);
122162306a36Sopenharmony_ci	return ret;
122262306a36Sopenharmony_ci}
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci/**
122562306a36Sopenharmony_ci * batadv_dat_snoop_incoming_arp_request() - snoop the ARP request and try to
122662306a36Sopenharmony_ci * answer using the local DAT storage
122762306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
122862306a36Sopenharmony_ci * @skb: packet to check
122962306a36Sopenharmony_ci * @hdr_size: size of the encapsulation header
123062306a36Sopenharmony_ci *
123162306a36Sopenharmony_ci * Return: true if the request has been answered, false otherwise.
123262306a36Sopenharmony_ci */
123362306a36Sopenharmony_cibool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
123462306a36Sopenharmony_ci					   struct sk_buff *skb, int hdr_size)
123562306a36Sopenharmony_ci{
123662306a36Sopenharmony_ci	u16 type;
123762306a36Sopenharmony_ci	__be32 ip_src, ip_dst;
123862306a36Sopenharmony_ci	u8 *hw_src;
123962306a36Sopenharmony_ci	struct sk_buff *skb_new;
124062306a36Sopenharmony_ci	struct batadv_dat_entry *dat_entry = NULL;
124162306a36Sopenharmony_ci	bool ret = false;
124262306a36Sopenharmony_ci	unsigned short vid;
124362306a36Sopenharmony_ci	int err;
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	if (!atomic_read(&bat_priv->distributed_arp_table))
124662306a36Sopenharmony_ci		goto out;
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	vid = batadv_dat_get_vid(skb, &hdr_size);
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci	type = batadv_arp_get_type(bat_priv, skb, hdr_size);
125162306a36Sopenharmony_ci	if (type != ARPOP_REQUEST)
125262306a36Sopenharmony_ci		goto out;
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	hw_src = batadv_arp_hw_src(skb, hdr_size);
125562306a36Sopenharmony_ci	ip_src = batadv_arp_ip_src(skb, hdr_size);
125662306a36Sopenharmony_ci	ip_dst = batadv_arp_ip_dst(skb, hdr_size);
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	batadv_dbg_arp(bat_priv, skb, hdr_size, "Parsing incoming ARP REQUEST");
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid);
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst, vid);
126362306a36Sopenharmony_ci	if (!dat_entry)
126462306a36Sopenharmony_ci		goto out;
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	skb_new = batadv_dat_arp_create_reply(bat_priv, ip_dst, ip_src,
126762306a36Sopenharmony_ci					      dat_entry->mac_addr, hw_src, vid);
126862306a36Sopenharmony_ci	if (!skb_new)
126962306a36Sopenharmony_ci		goto out;
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	/* To preserve backwards compatibility, the node has choose the outgoing
127262306a36Sopenharmony_ci	 * format based on the incoming request packet type. The assumption is
127362306a36Sopenharmony_ci	 * that a node not using the 4addr packet format doesn't support it.
127462306a36Sopenharmony_ci	 */
127562306a36Sopenharmony_ci	if (hdr_size == sizeof(struct batadv_unicast_4addr_packet))
127662306a36Sopenharmony_ci		err = batadv_send_skb_via_tt_4addr(bat_priv, skb_new,
127762306a36Sopenharmony_ci						   BATADV_P_DAT_CACHE_REPLY,
127862306a36Sopenharmony_ci						   NULL, vid);
127962306a36Sopenharmony_ci	else
128062306a36Sopenharmony_ci		err = batadv_send_skb_via_tt(bat_priv, skb_new, NULL, vid);
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	if (err != NET_XMIT_DROP) {
128362306a36Sopenharmony_ci		batadv_inc_counter(bat_priv, BATADV_CNT_DAT_CACHED_REPLY_TX);
128462306a36Sopenharmony_ci		ret = true;
128562306a36Sopenharmony_ci	}
128662306a36Sopenharmony_ciout:
128762306a36Sopenharmony_ci	batadv_dat_entry_put(dat_entry);
128862306a36Sopenharmony_ci	if (ret)
128962306a36Sopenharmony_ci		kfree_skb(skb);
129062306a36Sopenharmony_ci	return ret;
129162306a36Sopenharmony_ci}
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci/**
129462306a36Sopenharmony_ci * batadv_dat_snoop_outgoing_arp_reply() - snoop the ARP reply and fill the DHT
129562306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
129662306a36Sopenharmony_ci * @skb: packet to check
129762306a36Sopenharmony_ci */
129862306a36Sopenharmony_civoid batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv,
129962306a36Sopenharmony_ci					 struct sk_buff *skb)
130062306a36Sopenharmony_ci{
130162306a36Sopenharmony_ci	u16 type;
130262306a36Sopenharmony_ci	__be32 ip_src, ip_dst;
130362306a36Sopenharmony_ci	u8 *hw_src, *hw_dst;
130462306a36Sopenharmony_ci	int hdr_size = 0;
130562306a36Sopenharmony_ci	unsigned short vid;
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	if (!atomic_read(&bat_priv->distributed_arp_table))
130862306a36Sopenharmony_ci		return;
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci	vid = batadv_dat_get_vid(skb, &hdr_size);
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci	type = batadv_arp_get_type(bat_priv, skb, hdr_size);
131362306a36Sopenharmony_ci	if (type != ARPOP_REPLY)
131462306a36Sopenharmony_ci		return;
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	batadv_dbg_arp(bat_priv, skb, hdr_size, "Parsing outgoing ARP REPLY");
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	hw_src = batadv_arp_hw_src(skb, hdr_size);
131962306a36Sopenharmony_ci	ip_src = batadv_arp_ip_src(skb, hdr_size);
132062306a36Sopenharmony_ci	hw_dst = batadv_arp_hw_dst(skb, hdr_size);
132162306a36Sopenharmony_ci	ip_dst = batadv_arp_ip_dst(skb, hdr_size);
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci	batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid);
132462306a36Sopenharmony_ci	batadv_dat_entry_add(bat_priv, ip_dst, hw_dst, vid);
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	/* Send the ARP reply to the candidates for both the IP addresses that
132762306a36Sopenharmony_ci	 * the node obtained from the ARP reply
132862306a36Sopenharmony_ci	 */
132962306a36Sopenharmony_ci	batadv_dat_forward_data(bat_priv, skb, ip_src, vid,
133062306a36Sopenharmony_ci				BATADV_P_DAT_DHT_PUT);
133162306a36Sopenharmony_ci	batadv_dat_forward_data(bat_priv, skb, ip_dst, vid,
133262306a36Sopenharmony_ci				BATADV_P_DAT_DHT_PUT);
133362306a36Sopenharmony_ci}
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci/**
133662306a36Sopenharmony_ci * batadv_dat_snoop_incoming_arp_reply() - snoop the ARP reply and fill the
133762306a36Sopenharmony_ci *  local DAT storage only
133862306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
133962306a36Sopenharmony_ci * @skb: packet to check
134062306a36Sopenharmony_ci * @hdr_size: size of the encapsulation header
134162306a36Sopenharmony_ci *
134262306a36Sopenharmony_ci * Return: true if the packet was snooped and consumed by DAT. False if the
134362306a36Sopenharmony_ci * packet has to be delivered to the interface
134462306a36Sopenharmony_ci */
134562306a36Sopenharmony_cibool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv,
134662306a36Sopenharmony_ci					 struct sk_buff *skb, int hdr_size)
134762306a36Sopenharmony_ci{
134862306a36Sopenharmony_ci	struct batadv_dat_entry *dat_entry = NULL;
134962306a36Sopenharmony_ci	u16 type;
135062306a36Sopenharmony_ci	__be32 ip_src, ip_dst;
135162306a36Sopenharmony_ci	u8 *hw_src, *hw_dst;
135262306a36Sopenharmony_ci	bool dropped = false;
135362306a36Sopenharmony_ci	unsigned short vid;
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	if (!atomic_read(&bat_priv->distributed_arp_table))
135662306a36Sopenharmony_ci		goto out;
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	vid = batadv_dat_get_vid(skb, &hdr_size);
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci	type = batadv_arp_get_type(bat_priv, skb, hdr_size);
136162306a36Sopenharmony_ci	if (type != ARPOP_REPLY)
136262306a36Sopenharmony_ci		goto out;
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	batadv_dbg_arp(bat_priv, skb, hdr_size, "Parsing incoming ARP REPLY");
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	hw_src = batadv_arp_hw_src(skb, hdr_size);
136762306a36Sopenharmony_ci	ip_src = batadv_arp_ip_src(skb, hdr_size);
136862306a36Sopenharmony_ci	hw_dst = batadv_arp_hw_dst(skb, hdr_size);
136962306a36Sopenharmony_ci	ip_dst = batadv_arp_ip_dst(skb, hdr_size);
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci	/* If ip_dst is already in cache and has the right mac address,
137262306a36Sopenharmony_ci	 * drop this frame if this ARP reply is destined for us because it's
137362306a36Sopenharmony_ci	 * most probably an ARP reply generated by another node of the DHT.
137462306a36Sopenharmony_ci	 * We have most probably received already a reply earlier. Delivering
137562306a36Sopenharmony_ci	 * this frame would lead to doubled receive of an ARP reply.
137662306a36Sopenharmony_ci	 */
137762306a36Sopenharmony_ci	dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_src, vid);
137862306a36Sopenharmony_ci	if (dat_entry && batadv_compare_eth(hw_src, dat_entry->mac_addr)) {
137962306a36Sopenharmony_ci		batadv_dbg(BATADV_DBG_DAT, bat_priv, "Doubled ARP reply removed: ARP MSG = [src: %pM-%pI4 dst: %pM-%pI4]; dat_entry: %pM-%pI4\n",
138062306a36Sopenharmony_ci			   hw_src, &ip_src, hw_dst, &ip_dst,
138162306a36Sopenharmony_ci			   dat_entry->mac_addr,	&dat_entry->ip);
138262306a36Sopenharmony_ci		dropped = true;
138362306a36Sopenharmony_ci	}
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_ci	/* Update our internal cache with both the IP addresses the node got
138662306a36Sopenharmony_ci	 * within the ARP reply
138762306a36Sopenharmony_ci	 */
138862306a36Sopenharmony_ci	batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid);
138962306a36Sopenharmony_ci	batadv_dat_entry_add(bat_priv, ip_dst, hw_dst, vid);
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci	if (dropped)
139262306a36Sopenharmony_ci		goto out;
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci	/* If BLA is enabled, only forward ARP replies if we have claimed the
139562306a36Sopenharmony_ci	 * source of the ARP reply or if no one else of the same backbone has
139662306a36Sopenharmony_ci	 * already claimed that client. This prevents that different gateways
139762306a36Sopenharmony_ci	 * to the same backbone all forward the ARP reply leading to multiple
139862306a36Sopenharmony_ci	 * replies in the backbone.
139962306a36Sopenharmony_ci	 */
140062306a36Sopenharmony_ci	if (!batadv_bla_check_claim(bat_priv, hw_src, vid)) {
140162306a36Sopenharmony_ci		batadv_dbg(BATADV_DBG_DAT, bat_priv,
140262306a36Sopenharmony_ci			   "Device %pM claimed by another backbone gw. Drop ARP reply.\n",
140362306a36Sopenharmony_ci			   hw_src);
140462306a36Sopenharmony_ci		dropped = true;
140562306a36Sopenharmony_ci		goto out;
140662306a36Sopenharmony_ci	}
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci	/* if this REPLY is directed to a client of mine, let's deliver the
140962306a36Sopenharmony_ci	 * packet to the interface
141062306a36Sopenharmony_ci	 */
141162306a36Sopenharmony_ci	dropped = !batadv_is_my_client(bat_priv, hw_dst, vid);
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci	/* if this REPLY is sent on behalf of a client of mine, let's drop the
141462306a36Sopenharmony_ci	 * packet because the client will reply by itself
141562306a36Sopenharmony_ci	 */
141662306a36Sopenharmony_ci	dropped |= batadv_is_my_client(bat_priv, hw_src, vid);
141762306a36Sopenharmony_ciout:
141862306a36Sopenharmony_ci	if (dropped)
141962306a36Sopenharmony_ci		kfree_skb(skb);
142062306a36Sopenharmony_ci	batadv_dat_entry_put(dat_entry);
142162306a36Sopenharmony_ci	/* if dropped == false -> deliver to the interface */
142262306a36Sopenharmony_ci	return dropped;
142362306a36Sopenharmony_ci}
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci/**
142662306a36Sopenharmony_ci * batadv_dat_check_dhcp_ipudp() - check skb for IP+UDP headers valid for DHCP
142762306a36Sopenharmony_ci * @skb: the packet to check
142862306a36Sopenharmony_ci * @ip_src: a buffer to store the IPv4 source address in
142962306a36Sopenharmony_ci *
143062306a36Sopenharmony_ci * Checks whether the given skb has an IP and UDP header valid for a DHCP
143162306a36Sopenharmony_ci * message from a DHCP server. And if so, stores the IPv4 source address in
143262306a36Sopenharmony_ci * the provided buffer.
143362306a36Sopenharmony_ci *
143462306a36Sopenharmony_ci * Return: True if valid, false otherwise.
143562306a36Sopenharmony_ci */
143662306a36Sopenharmony_cistatic bool
143762306a36Sopenharmony_cibatadv_dat_check_dhcp_ipudp(struct sk_buff *skb, __be32 *ip_src)
143862306a36Sopenharmony_ci{
143962306a36Sopenharmony_ci	unsigned int offset = skb_network_offset(skb);
144062306a36Sopenharmony_ci	struct udphdr *udphdr, _udphdr;
144162306a36Sopenharmony_ci	struct iphdr *iphdr, _iphdr;
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci	iphdr = skb_header_pointer(skb, offset, sizeof(_iphdr), &_iphdr);
144462306a36Sopenharmony_ci	if (!iphdr || iphdr->version != 4 || iphdr->ihl * 4 < sizeof(_iphdr))
144562306a36Sopenharmony_ci		return false;
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci	if (iphdr->protocol != IPPROTO_UDP)
144862306a36Sopenharmony_ci		return false;
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	offset += iphdr->ihl * 4;
145162306a36Sopenharmony_ci	skb_set_transport_header(skb, offset);
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci	udphdr = skb_header_pointer(skb, offset, sizeof(_udphdr), &_udphdr);
145462306a36Sopenharmony_ci	if (!udphdr || udphdr->source != htons(67))
145562306a36Sopenharmony_ci		return false;
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	*ip_src = get_unaligned(&iphdr->saddr);
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci	return true;
146062306a36Sopenharmony_ci}
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci/**
146362306a36Sopenharmony_ci * batadv_dat_check_dhcp() - examine packet for valid DHCP message
146462306a36Sopenharmony_ci * @skb: the packet to check
146562306a36Sopenharmony_ci * @proto: ethernet protocol hint (behind a potential vlan)
146662306a36Sopenharmony_ci * @ip_src: a buffer to store the IPv4 source address in
146762306a36Sopenharmony_ci *
146862306a36Sopenharmony_ci * Checks whether the given skb is a valid DHCP packet. And if so, stores the
146962306a36Sopenharmony_ci * IPv4 source address in the provided buffer.
147062306a36Sopenharmony_ci *
147162306a36Sopenharmony_ci * Caller needs to ensure that the skb network header is set correctly.
147262306a36Sopenharmony_ci *
147362306a36Sopenharmony_ci * Return: If skb is a valid DHCP packet, then returns its op code
147462306a36Sopenharmony_ci * (e.g. BOOTREPLY vs. BOOTREQUEST). Otherwise returns -EINVAL.
147562306a36Sopenharmony_ci */
147662306a36Sopenharmony_cistatic int
147762306a36Sopenharmony_cibatadv_dat_check_dhcp(struct sk_buff *skb, __be16 proto, __be32 *ip_src)
147862306a36Sopenharmony_ci{
147962306a36Sopenharmony_ci	__be32 *magic, _magic;
148062306a36Sopenharmony_ci	unsigned int offset;
148162306a36Sopenharmony_ci	struct {
148262306a36Sopenharmony_ci		__u8 op;
148362306a36Sopenharmony_ci		__u8 htype;
148462306a36Sopenharmony_ci		__u8 hlen;
148562306a36Sopenharmony_ci		__u8 hops;
148662306a36Sopenharmony_ci	} *dhcp_h, _dhcp_h;
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci	if (proto != htons(ETH_P_IP))
148962306a36Sopenharmony_ci		return -EINVAL;
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_ci	if (!batadv_dat_check_dhcp_ipudp(skb, ip_src))
149262306a36Sopenharmony_ci		return -EINVAL;
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci	offset = skb_transport_offset(skb) + sizeof(struct udphdr);
149562306a36Sopenharmony_ci	if (skb->len < offset + sizeof(struct batadv_dhcp_packet))
149662306a36Sopenharmony_ci		return -EINVAL;
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	dhcp_h = skb_header_pointer(skb, offset, sizeof(_dhcp_h), &_dhcp_h);
149962306a36Sopenharmony_ci	if (!dhcp_h || dhcp_h->htype != BATADV_HTYPE_ETHERNET ||
150062306a36Sopenharmony_ci	    dhcp_h->hlen != ETH_ALEN)
150162306a36Sopenharmony_ci		return -EINVAL;
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci	offset += offsetof(struct batadv_dhcp_packet, magic);
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci	magic = skb_header_pointer(skb, offset, sizeof(_magic), &_magic);
150662306a36Sopenharmony_ci	if (!magic || get_unaligned(magic) != htonl(BATADV_DHCP_MAGIC))
150762306a36Sopenharmony_ci		return -EINVAL;
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci	return dhcp_h->op;
151062306a36Sopenharmony_ci}
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci/**
151362306a36Sopenharmony_ci * batadv_dat_get_dhcp_message_type() - get message type of a DHCP packet
151462306a36Sopenharmony_ci * @skb: the DHCP packet to parse
151562306a36Sopenharmony_ci *
151662306a36Sopenharmony_ci * Iterates over the DHCP options of the given DHCP packet to find a
151762306a36Sopenharmony_ci * DHCP Message Type option and parse it.
151862306a36Sopenharmony_ci *
151962306a36Sopenharmony_ci * Caller needs to ensure that the given skb is a valid DHCP packet and
152062306a36Sopenharmony_ci * that the skb transport header is set correctly.
152162306a36Sopenharmony_ci *
152262306a36Sopenharmony_ci * Return: The found DHCP message type value, if found. -EINVAL otherwise.
152362306a36Sopenharmony_ci */
152462306a36Sopenharmony_cistatic int batadv_dat_get_dhcp_message_type(struct sk_buff *skb)
152562306a36Sopenharmony_ci{
152662306a36Sopenharmony_ci	unsigned int offset = skb_transport_offset(skb) + sizeof(struct udphdr);
152762306a36Sopenharmony_ci	u8 *type, _type;
152862306a36Sopenharmony_ci	struct {
152962306a36Sopenharmony_ci		u8 type;
153062306a36Sopenharmony_ci		u8 len;
153162306a36Sopenharmony_ci	} *tl, _tl;
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_ci	offset += sizeof(struct batadv_dhcp_packet);
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ci	while ((tl = skb_header_pointer(skb, offset, sizeof(_tl), &_tl))) {
153662306a36Sopenharmony_ci		if (tl->type == BATADV_DHCP_OPT_MSG_TYPE)
153762306a36Sopenharmony_ci			break;
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci		if (tl->type == BATADV_DHCP_OPT_END)
154062306a36Sopenharmony_ci			break;
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ci		if (tl->type == BATADV_DHCP_OPT_PAD)
154362306a36Sopenharmony_ci			offset++;
154462306a36Sopenharmony_ci		else
154562306a36Sopenharmony_ci			offset += tl->len + sizeof(_tl);
154662306a36Sopenharmony_ci	}
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci	/* Option Overload Code not supported */
154962306a36Sopenharmony_ci	if (!tl || tl->type != BATADV_DHCP_OPT_MSG_TYPE ||
155062306a36Sopenharmony_ci	    tl->len != sizeof(_type))
155162306a36Sopenharmony_ci		return -EINVAL;
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci	offset += sizeof(_tl);
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci	type = skb_header_pointer(skb, offset, sizeof(_type), &_type);
155662306a36Sopenharmony_ci	if (!type)
155762306a36Sopenharmony_ci		return -EINVAL;
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci	return *type;
156062306a36Sopenharmony_ci}
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci/**
156362306a36Sopenharmony_ci * batadv_dat_dhcp_get_yiaddr() - get yiaddr from a DHCP packet
156462306a36Sopenharmony_ci * @skb: the DHCP packet to parse
156562306a36Sopenharmony_ci * @buf: a buffer to store the yiaddr in
156662306a36Sopenharmony_ci *
156762306a36Sopenharmony_ci * Caller needs to ensure that the given skb is a valid DHCP packet and
156862306a36Sopenharmony_ci * that the skb transport header is set correctly.
156962306a36Sopenharmony_ci *
157062306a36Sopenharmony_ci * Return: True on success, false otherwise.
157162306a36Sopenharmony_ci */
157262306a36Sopenharmony_cistatic bool batadv_dat_dhcp_get_yiaddr(struct sk_buff *skb, __be32 *buf)
157362306a36Sopenharmony_ci{
157462306a36Sopenharmony_ci	unsigned int offset = skb_transport_offset(skb) + sizeof(struct udphdr);
157562306a36Sopenharmony_ci	__be32 *yiaddr;
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci	offset += offsetof(struct batadv_dhcp_packet, yiaddr);
157862306a36Sopenharmony_ci	yiaddr = skb_header_pointer(skb, offset, BATADV_DHCP_YIADDR_LEN, buf);
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_ci	if (!yiaddr)
158162306a36Sopenharmony_ci		return false;
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci	if (yiaddr != buf)
158462306a36Sopenharmony_ci		*buf = get_unaligned(yiaddr);
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ci	return true;
158762306a36Sopenharmony_ci}
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci/**
159062306a36Sopenharmony_ci * batadv_dat_get_dhcp_chaddr() - get chaddr from a DHCP packet
159162306a36Sopenharmony_ci * @skb: the DHCP packet to parse
159262306a36Sopenharmony_ci * @buf: a buffer to store the chaddr in
159362306a36Sopenharmony_ci *
159462306a36Sopenharmony_ci * Caller needs to ensure that the given skb is a valid DHCP packet and
159562306a36Sopenharmony_ci * that the skb transport header is set correctly.
159662306a36Sopenharmony_ci *
159762306a36Sopenharmony_ci * Return: True on success, false otherwise
159862306a36Sopenharmony_ci */
159962306a36Sopenharmony_cistatic bool batadv_dat_get_dhcp_chaddr(struct sk_buff *skb, u8 *buf)
160062306a36Sopenharmony_ci{
160162306a36Sopenharmony_ci	unsigned int offset = skb_transport_offset(skb) + sizeof(struct udphdr);
160262306a36Sopenharmony_ci	u8 *chaddr;
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_ci	offset += offsetof(struct batadv_dhcp_packet, chaddr);
160562306a36Sopenharmony_ci	chaddr = skb_header_pointer(skb, offset, BATADV_DHCP_CHADDR_LEN, buf);
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci	if (!chaddr)
160862306a36Sopenharmony_ci		return false;
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_ci	if (chaddr != buf)
161162306a36Sopenharmony_ci		memcpy(buf, chaddr, BATADV_DHCP_CHADDR_LEN);
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci	return true;
161462306a36Sopenharmony_ci}
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_ci/**
161762306a36Sopenharmony_ci * batadv_dat_put_dhcp() - puts addresses from a DHCP packet into the DHT and
161862306a36Sopenharmony_ci *  DAT cache
161962306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
162062306a36Sopenharmony_ci * @chaddr: the DHCP client MAC address
162162306a36Sopenharmony_ci * @yiaddr: the DHCP client IP address
162262306a36Sopenharmony_ci * @hw_dst: the DHCP server MAC address
162362306a36Sopenharmony_ci * @ip_dst: the DHCP server IP address
162462306a36Sopenharmony_ci * @vid: VLAN identifier
162562306a36Sopenharmony_ci *
162662306a36Sopenharmony_ci * Adds given MAC/IP pairs to the local DAT cache and propagates them further
162762306a36Sopenharmony_ci * into the DHT.
162862306a36Sopenharmony_ci *
162962306a36Sopenharmony_ci * For the DHT propagation, client MAC + IP will appear as the ARP Reply
163062306a36Sopenharmony_ci * transmitter (and hw_dst/ip_dst as the target).
163162306a36Sopenharmony_ci */
163262306a36Sopenharmony_cistatic void batadv_dat_put_dhcp(struct batadv_priv *bat_priv, u8 *chaddr,
163362306a36Sopenharmony_ci				__be32 yiaddr, u8 *hw_dst, __be32 ip_dst,
163462306a36Sopenharmony_ci				unsigned short vid)
163562306a36Sopenharmony_ci{
163662306a36Sopenharmony_ci	struct sk_buff *skb;
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ci	skb = batadv_dat_arp_create_reply(bat_priv, yiaddr, ip_dst, chaddr,
163962306a36Sopenharmony_ci					  hw_dst, vid);
164062306a36Sopenharmony_ci	if (!skb)
164162306a36Sopenharmony_ci		return;
164262306a36Sopenharmony_ci
164362306a36Sopenharmony_ci	skb_set_network_header(skb, ETH_HLEN);
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci	batadv_dat_entry_add(bat_priv, yiaddr, chaddr, vid);
164662306a36Sopenharmony_ci	batadv_dat_entry_add(bat_priv, ip_dst, hw_dst, vid);
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci	batadv_dat_forward_data(bat_priv, skb, yiaddr, vid,
164962306a36Sopenharmony_ci				BATADV_P_DAT_DHT_PUT);
165062306a36Sopenharmony_ci	batadv_dat_forward_data(bat_priv, skb, ip_dst, vid,
165162306a36Sopenharmony_ci				BATADV_P_DAT_DHT_PUT);
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_ci	consume_skb(skb);
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_ci	batadv_dbg(BATADV_DBG_DAT, bat_priv,
165662306a36Sopenharmony_ci		   "Snooped from outgoing DHCPACK (server address): %pI4, %pM (vid: %i)\n",
165762306a36Sopenharmony_ci		   &ip_dst, hw_dst, batadv_print_vid(vid));
165862306a36Sopenharmony_ci	batadv_dbg(BATADV_DBG_DAT, bat_priv,
165962306a36Sopenharmony_ci		   "Snooped from outgoing DHCPACK (client address): %pI4, %pM (vid: %i)\n",
166062306a36Sopenharmony_ci		   &yiaddr, chaddr, batadv_print_vid(vid));
166162306a36Sopenharmony_ci}
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_ci/**
166462306a36Sopenharmony_ci * batadv_dat_check_dhcp_ack() - examine packet for valid DHCP message
166562306a36Sopenharmony_ci * @skb: the packet to check
166662306a36Sopenharmony_ci * @proto: ethernet protocol hint (behind a potential vlan)
166762306a36Sopenharmony_ci * @ip_src: a buffer to store the IPv4 source address in
166862306a36Sopenharmony_ci * @chaddr: a buffer to store the DHCP Client Hardware Address in
166962306a36Sopenharmony_ci * @yiaddr: a buffer to store the DHCP Your IP Address in
167062306a36Sopenharmony_ci *
167162306a36Sopenharmony_ci * Checks whether the given skb is a valid DHCPACK. And if so, stores the
167262306a36Sopenharmony_ci * IPv4 server source address (ip_src), client MAC address (chaddr) and client
167362306a36Sopenharmony_ci * IPv4 address (yiaddr) in the provided buffers.
167462306a36Sopenharmony_ci *
167562306a36Sopenharmony_ci * Caller needs to ensure that the skb network header is set correctly.
167662306a36Sopenharmony_ci *
167762306a36Sopenharmony_ci * Return: True if the skb is a valid DHCPACK. False otherwise.
167862306a36Sopenharmony_ci */
167962306a36Sopenharmony_cistatic bool
168062306a36Sopenharmony_cibatadv_dat_check_dhcp_ack(struct sk_buff *skb, __be16 proto, __be32 *ip_src,
168162306a36Sopenharmony_ci			  u8 *chaddr, __be32 *yiaddr)
168262306a36Sopenharmony_ci{
168362306a36Sopenharmony_ci	int type;
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ci	type = batadv_dat_check_dhcp(skb, proto, ip_src);
168662306a36Sopenharmony_ci	if (type != BATADV_BOOTREPLY)
168762306a36Sopenharmony_ci		return false;
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_ci	type = batadv_dat_get_dhcp_message_type(skb);
169062306a36Sopenharmony_ci	if (type != BATADV_DHCPACK)
169162306a36Sopenharmony_ci		return false;
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_ci	if (!batadv_dat_dhcp_get_yiaddr(skb, yiaddr))
169462306a36Sopenharmony_ci		return false;
169562306a36Sopenharmony_ci
169662306a36Sopenharmony_ci	if (!batadv_dat_get_dhcp_chaddr(skb, chaddr))
169762306a36Sopenharmony_ci		return false;
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_ci	return true;
170062306a36Sopenharmony_ci}
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_ci/**
170362306a36Sopenharmony_ci * batadv_dat_snoop_outgoing_dhcp_ack() - snoop DHCPACK and fill DAT with it
170462306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
170562306a36Sopenharmony_ci * @skb: the packet to snoop
170662306a36Sopenharmony_ci * @proto: ethernet protocol hint (behind a potential vlan)
170762306a36Sopenharmony_ci * @vid: VLAN identifier
170862306a36Sopenharmony_ci *
170962306a36Sopenharmony_ci * This function first checks whether the given skb is a valid DHCPACK. If
171062306a36Sopenharmony_ci * so then its source MAC and IP as well as its DHCP Client Hardware Address
171162306a36Sopenharmony_ci * field and DHCP Your IP Address field are added to the local DAT cache and
171262306a36Sopenharmony_ci * propagated into the DHT.
171362306a36Sopenharmony_ci *
171462306a36Sopenharmony_ci * Caller needs to ensure that the skb mac and network headers are set
171562306a36Sopenharmony_ci * correctly.
171662306a36Sopenharmony_ci */
171762306a36Sopenharmony_civoid batadv_dat_snoop_outgoing_dhcp_ack(struct batadv_priv *bat_priv,
171862306a36Sopenharmony_ci					struct sk_buff *skb,
171962306a36Sopenharmony_ci					__be16 proto,
172062306a36Sopenharmony_ci					unsigned short vid)
172162306a36Sopenharmony_ci{
172262306a36Sopenharmony_ci	u8 chaddr[BATADV_DHCP_CHADDR_LEN];
172362306a36Sopenharmony_ci	__be32 ip_src, yiaddr;
172462306a36Sopenharmony_ci
172562306a36Sopenharmony_ci	if (!atomic_read(&bat_priv->distributed_arp_table))
172662306a36Sopenharmony_ci		return;
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_ci	if (!batadv_dat_check_dhcp_ack(skb, proto, &ip_src, chaddr, &yiaddr))
172962306a36Sopenharmony_ci		return;
173062306a36Sopenharmony_ci
173162306a36Sopenharmony_ci	batadv_dat_put_dhcp(bat_priv, chaddr, yiaddr, eth_hdr(skb)->h_source,
173262306a36Sopenharmony_ci			    ip_src, vid);
173362306a36Sopenharmony_ci}
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_ci/**
173662306a36Sopenharmony_ci * batadv_dat_snoop_incoming_dhcp_ack() - snoop DHCPACK and fill DAT cache
173762306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
173862306a36Sopenharmony_ci * @skb: the packet to snoop
173962306a36Sopenharmony_ci * @hdr_size: header size, up to the tail of the batman-adv header
174062306a36Sopenharmony_ci *
174162306a36Sopenharmony_ci * This function first checks whether the given skb is a valid DHCPACK. If
174262306a36Sopenharmony_ci * so then its source MAC and IP as well as its DHCP Client Hardware Address
174362306a36Sopenharmony_ci * field and DHCP Your IP Address field are added to the local DAT cache.
174462306a36Sopenharmony_ci */
174562306a36Sopenharmony_civoid batadv_dat_snoop_incoming_dhcp_ack(struct batadv_priv *bat_priv,
174662306a36Sopenharmony_ci					struct sk_buff *skb, int hdr_size)
174762306a36Sopenharmony_ci{
174862306a36Sopenharmony_ci	u8 chaddr[BATADV_DHCP_CHADDR_LEN];
174962306a36Sopenharmony_ci	struct ethhdr *ethhdr;
175062306a36Sopenharmony_ci	__be32 ip_src, yiaddr;
175162306a36Sopenharmony_ci	unsigned short vid;
175262306a36Sopenharmony_ci	__be16 proto;
175362306a36Sopenharmony_ci	u8 *hw_src;
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_ci	if (!atomic_read(&bat_priv->distributed_arp_table))
175662306a36Sopenharmony_ci		return;
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci	if (unlikely(!pskb_may_pull(skb, hdr_size + ETH_HLEN)))
175962306a36Sopenharmony_ci		return;
176062306a36Sopenharmony_ci
176162306a36Sopenharmony_ci	ethhdr = (struct ethhdr *)(skb->data + hdr_size);
176262306a36Sopenharmony_ci	skb_set_network_header(skb, hdr_size + ETH_HLEN);
176362306a36Sopenharmony_ci	proto = ethhdr->h_proto;
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_ci	if (!batadv_dat_check_dhcp_ack(skb, proto, &ip_src, chaddr, &yiaddr))
176662306a36Sopenharmony_ci		return;
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_ci	hw_src = ethhdr->h_source;
176962306a36Sopenharmony_ci	vid = batadv_dat_get_vid(skb, &hdr_size);
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci	batadv_dat_entry_add(bat_priv, yiaddr, chaddr, vid);
177262306a36Sopenharmony_ci	batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid);
177362306a36Sopenharmony_ci
177462306a36Sopenharmony_ci	batadv_dbg(BATADV_DBG_DAT, bat_priv,
177562306a36Sopenharmony_ci		   "Snooped from incoming DHCPACK (server address): %pI4, %pM (vid: %i)\n",
177662306a36Sopenharmony_ci		   &ip_src, hw_src, batadv_print_vid(vid));
177762306a36Sopenharmony_ci	batadv_dbg(BATADV_DBG_DAT, bat_priv,
177862306a36Sopenharmony_ci		   "Snooped from incoming DHCPACK (client address): %pI4, %pM (vid: %i)\n",
177962306a36Sopenharmony_ci		   &yiaddr, chaddr, batadv_print_vid(vid));
178062306a36Sopenharmony_ci}
178162306a36Sopenharmony_ci
178262306a36Sopenharmony_ci/**
178362306a36Sopenharmony_ci * batadv_dat_drop_broadcast_packet() - check if an ARP request has to be
178462306a36Sopenharmony_ci *  dropped (because the node has already obtained the reply via DAT) or not
178562306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
178662306a36Sopenharmony_ci * @forw_packet: the broadcast packet
178762306a36Sopenharmony_ci *
178862306a36Sopenharmony_ci * Return: true if the node can drop the packet, false otherwise.
178962306a36Sopenharmony_ci */
179062306a36Sopenharmony_cibool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv,
179162306a36Sopenharmony_ci				      struct batadv_forw_packet *forw_packet)
179262306a36Sopenharmony_ci{
179362306a36Sopenharmony_ci	u16 type;
179462306a36Sopenharmony_ci	__be32 ip_dst;
179562306a36Sopenharmony_ci	struct batadv_dat_entry *dat_entry = NULL;
179662306a36Sopenharmony_ci	bool ret = false;
179762306a36Sopenharmony_ci	int hdr_size = sizeof(struct batadv_bcast_packet);
179862306a36Sopenharmony_ci	unsigned short vid;
179962306a36Sopenharmony_ci
180062306a36Sopenharmony_ci	if (!atomic_read(&bat_priv->distributed_arp_table))
180162306a36Sopenharmony_ci		goto out;
180262306a36Sopenharmony_ci
180362306a36Sopenharmony_ci	/* If this packet is an ARP_REQUEST and the node already has the
180462306a36Sopenharmony_ci	 * information that it is going to ask, then the packet can be dropped
180562306a36Sopenharmony_ci	 */
180662306a36Sopenharmony_ci	if (batadv_forw_packet_is_rebroadcast(forw_packet))
180762306a36Sopenharmony_ci		goto out;
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci	vid = batadv_dat_get_vid(forw_packet->skb, &hdr_size);
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_ci	type = batadv_arp_get_type(bat_priv, forw_packet->skb, hdr_size);
181262306a36Sopenharmony_ci	if (type != ARPOP_REQUEST)
181362306a36Sopenharmony_ci		goto out;
181462306a36Sopenharmony_ci
181562306a36Sopenharmony_ci	ip_dst = batadv_arp_ip_dst(forw_packet->skb, hdr_size);
181662306a36Sopenharmony_ci	dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst, vid);
181762306a36Sopenharmony_ci	/* check if the node already got this entry */
181862306a36Sopenharmony_ci	if (!dat_entry) {
181962306a36Sopenharmony_ci		batadv_dbg(BATADV_DBG_DAT, bat_priv,
182062306a36Sopenharmony_ci			   "ARP Request for %pI4: fallback\n", &ip_dst);
182162306a36Sopenharmony_ci		goto out;
182262306a36Sopenharmony_ci	}
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_ci	batadv_dbg(BATADV_DBG_DAT, bat_priv,
182562306a36Sopenharmony_ci		   "ARP Request for %pI4: fallback prevented\n", &ip_dst);
182662306a36Sopenharmony_ci	ret = true;
182762306a36Sopenharmony_ci
182862306a36Sopenharmony_ciout:
182962306a36Sopenharmony_ci	batadv_dat_entry_put(dat_entry);
183062306a36Sopenharmony_ci	return ret;
183162306a36Sopenharmony_ci}
1832