From: Antonio Quartulli <a(a)unstable.cc>
The ethtool API is changing in linux-4.6 and the B.A.T.M.A.N. V
code has to be changed accordingly.
Fixes: 5c3245172c01 ("batman-adv: ELP - compute the metric based on the estimated throughput")
Signed-off-by: Antonio Quartulli <a(a)unstable.cc>
[sven(a)narfation.org: Added compat code for __ethtool_get_link_ksettings]
Signed-off-by: Sven Eckelmann <sven(a)narfation.org>
---
resend of "RFC v2" as patch with my Signed-off-by added
compat-include/linux/ethtool.h | 62 ++++++++++++++++++++++++++++++++++++++++++
net/batman-adv/bat_v_elp.c | 12 ++++----
2 files changed, 68 insertions(+), 6 deletions(-)
create mode 100644 compat-include/linux/ethtool.h
diff --git a/compat-include/linux/ethtool.h b/compat-include/linux/ethtool.h
new file mode 100644
index 0000000..87f7577
--- /dev/null
+++ b/compat-include/linux/ethtool.h
@@ -0,0 +1,62 @@
+/* Copyright (C) 2016 B.A.T.M.A.N. contributors:
+ *
+ * Antonio Quartulli
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * This file contains macros for maintaining compatibility with older versions
+ * of the Linux kernel.
+ */
+
+#ifndef _NET_BATMAN_ADV_COMPAT_LINUX_ETHTOOL_H_
+#define _NET_BATMAN_ADV_COMPAT_LINUX_ETHTOOL_H_
+
+#include <linux/version.h>
+#include_next <linux/ethtool.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0)
+
+#define ethtool_link_ksettings batadv_ethtool_link_ksettings
+
+struct batadv_ethtool_link_ksettings {
+ struct {
+ __u32 speed;
+ __u8 duplex;
+ } base;
+};
+
+#define __ethtool_get_link_ksettings(__dev, __link_settings) \
+ batadv_ethtool_get_link_ksettings(__dev, __link_settings)
+
+static inline int
+batadv_ethtool_get_link_ksettings(struct net_device *dev,
+ struct ethtool_link_ksettings *link_ksettings)
+{
+ struct ethtool_cmd cmd;
+ int ret;
+
+ memset(&cmd, 0, sizeof(cmd));
+ ret = __ethtool_get_settings(dev, &cmd);
+
+ if (ret != 0)
+ return ret;
+
+ link_ksettings->base.duplex = cmd.duplex;
+ link_ksettings->base.speed = ethtool_cmd_speed(&cmd);
+
+ return 0;
+}
+
+#endif /* < KERNEL_VERSION(4, 6, 0) */
+
+#endif /* _NET_BATMAN_ADV_COMPAT_LINUX_ETHTOOL_H_ */
diff --git a/net/batman-adv/bat_v_elp.c b/net/batman-adv/bat_v_elp.c
index e94b4a0..3844e7e 100644
--- a/net/batman-adv/bat_v_elp.c
+++ b/net/batman-adv/bat_v_elp.c
@@ -73,8 +73,8 @@ static void batadv_v_elp_start_timer(struct batadv_hard_iface *hard_iface)
static u32 batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh)
{
struct batadv_hard_iface *hard_iface = neigh->if_incoming;
+ struct ethtool_link_ksettings link_settings;
struct station_info sinfo;
- struct ethtool_cmd cmd;
u32 throughput;
int ret;
@@ -110,19 +110,19 @@ static u32 batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh)
/* if not a wifi interface, check if this device provides data via
* ethtool (e.g. an Ethernet adapter)
*/
- memset(&cmd, 0, sizeof(cmd));
+ memset(&link_settings, 0, sizeof(link_settings));
rtnl_lock();
- ret = __ethtool_get_settings(hard_iface->net_dev, &cmd);
+ ret = __ethtool_get_link_ksettings(hard_iface->net_dev, &link_settings);
rtnl_unlock();
if (ret == 0) {
/* link characteristics might change over time */
- if (cmd.duplex == DUPLEX_FULL)
+ if (link_settings.base.duplex == DUPLEX_FULL)
hard_iface->bat_v.flags |= BATADV_FULL_DUPLEX;
else
hard_iface->bat_v.flags &= ~BATADV_FULL_DUPLEX;
- throughput = ethtool_cmd_speed(&cmd);
- if (throughput && throughput != SPEED_UNKNOWN)
+ throughput = link_settings.base.speed;
+ if (throughput && (throughput != SPEED_UNKNOWN))
return throughput * 10;
}
The shutdown of an batman-adv interface can happen with one of its slave
interfaces still being in the BATADV_IF_TO_BE_ACTIVATED state. A possible
reason for it is that the routing algorithm BATMAN_V was selected and
batadv_schedule_bat_ogm was not yet called for this interface. This slave
interface still has to be set to BATADV_IF_INACTIVE or the batman-adv
interface will never reduce its usage counter and thus never gets shutdown.
This problem can be simulated via:
$ modprobe dummy
$ modprobe batman-adv routing_algo=BATMAN_V
$ ip link add bat0 type batadv
$ ip link set dummy0 master bat0
$ ip link set dummy0 up
$ ip link del bat0
unregister_netdevice: waiting for bat0 to become free. Usage count = 3
Fixes: 88861ea9acb7 ("[batman-adv] replacing if up/down timer with kernel notifications")
Reported-by: Matthias Schiffer <mschiffer(a)universe-factory.net>
Signed-off-by: Sven Eckelmann <sven(a)narfation.org>
---
Please check https://www.open-mesh.org/issues/248 to learn more about the
bug
I have not checked why batadv_schedule_bat_ogm isn't called successfully
(which would mark the interface as active) in this situation.
---
net/batman-adv/hard-interface.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c
index 3240a67..f41b472 100644
--- a/net/batman-adv/hard-interface.c
+++ b/net/batman-adv/hard-interface.c
@@ -572,8 +572,7 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface,
struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
struct batadv_hard_iface *primary_if = NULL;
- if (hard_iface->if_status == BATADV_IF_ACTIVE)
- batadv_hardif_deactivate_interface(hard_iface);
+ batadv_hardif_deactivate_interface(hard_iface);
if (hard_iface->if_status != BATADV_IF_INACTIVE)
goto out;
--
2.8.0.rc3
The __ethtool_get_link_ksettings is currently used only for kernels >=
3.15. But the compat code is read by the compiler for each kernel. But
kernels up to 3.1 never had the function __ethtool_get_settings which is
used to emulate this function for kernels < 4.6. Therefore, kernels < 3.2
will fail to compile when this compatibility layer is enabled.
Fixes: 3515604d82d5 ("batman-adv: ELP - use new ethtool_link_get_ksettings API")
Signed-off-by: Sven Eckelmann <sven(a)narfation.org>
---
v2:
- correct version number of the supported kernel
compat-include/linux/ethtool.h | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/compat-include/linux/ethtool.h b/compat-include/linux/ethtool.h
index 87f7577..2e6270e 100644
--- a/compat-include/linux/ethtool.h
+++ b/compat-include/linux/ethtool.h
@@ -24,7 +24,9 @@
#include <linux/version.h>
#include_next <linux/ethtool.h>
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0)
+/* WARNING only enabled on kernels with __ethtool_get_settings support */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0) && \
+ LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
#define ethtool_link_ksettings batadv_ethtool_link_ksettings
--
2.7.0
From: Mihail Costea <mihail.costea90(a)gmail.com>
Mades DAT support more types by making its data a void*, adding type field
to dat_entry and adding data_type to necessary functions.
This change is needed in order to make DAT support any type of data, like IPv6 too.
Adds generic function for transforming DAT data to string.
The function is used in order to avoid defining different debug messages
for different DAT data types. For example, if we had IPv6 as a DAT data,
then "%pI4" should be "%pI6c", but all
the other text of the debug message would be the same.
Also everything is memorized in a struct in order to avoid further
switch cases for all types.
Signed-off-by: Mihail Costea <mihail.costea90(a)gmail.com>
Signed-off-by: Stefan Popa <Stefan.A.Popa(a)intel.com>
Reviewed-by: Stefan Popa <Stefan.A.Popa(a)intel.com>
---
distributed-arp-table.c | 197 +++++++++++++++++++++++++++++++++++------------
distributed-arp-table.h | 1 +
types.h | 24 +++++-
3 files changed, 169 insertions(+), 53 deletions(-)
diff --git a/distributed-arp-table.c b/distributed-arp-table.c
index f2543c2..90565d0 100644
--- a/distributed-arp-table.c
+++ b/distributed-arp-table.c
@@ -31,9 +31,32 @@
#include "types.h"
#include "translation-table.h"
+static struct batadv_dat_type_info batadv_dat_types_info[] = {
+ {
+ .size = sizeof(__be32),
+ .str_fmt = "%pI4",
+ },
+};
+
static void batadv_dat_purge(struct work_struct *work);
/**
+ * batadv_dat_data_to_str: transforms DAT data to string
+ * @data: the DAT data
+ * @type: type of data
+ * @buf: the buf where the data string is stored
+ * @buf_len: buf length
+ *
+ * Returns buf.
+ */
+static char *batadv_dat_data_to_str(void *data, uint8_t type,
+ char *buf, size_t buf_len)
+{
+ snprintf(buf, buf_len, batadv_dat_types_info[type].str_fmt, data);
+return buf;
+}
+
+/**
* batadv_dat_start_timer - initialise the DAT periodic worker
* @bat_priv: the bat priv with all the soft interface information
*/
@@ -45,6 +68,19 @@ static void batadv_dat_start_timer(struct batadv_priv *bat_priv)
}
/**
+ * batadv_dat_entry_free_ref_rcu - free a dat entry using its rcu
+ * @rcu: the dat entry rcu
+ */
+static void batadv_dat_entry_free_ref_rcu(struct rcu_head *rcu)
+{
+ struct batadv_dat_entry *dat_entry;
+
+ dat_entry = container_of(rcu, struct batadv_dat_entry, rcu);
+ kfree(dat_entry->data);
+ kfree(dat_entry);
+}
+
+/**
* batadv_dat_entry_free_ref - decrement the dat_entry refcounter and possibly
* free it
* @dat_entry: the entry to free
@@ -52,7 +88,7 @@ static void batadv_dat_start_timer(struct batadv_priv *bat_priv)
static void batadv_dat_entry_free_ref(struct batadv_dat_entry *dat_entry)
{
if (atomic_dec_and_test(&dat_entry->refcount))
- kfree_rcu(dat_entry, rcu);
+ call_rcu(&dat_entry->rcu, batadv_dat_entry_free_ref_rcu);
}
/**
@@ -136,12 +172,21 @@ static void batadv_dat_purge(struct work_struct *work)
*
* Returns 1 if the two entries are the same, 0 otherwise.
*/
-static int batadv_compare_dat(const struct hlist_node *node, const void *data2)
+static int batadv_compare_dat(const struct hlist_node *node, const void *data2)
{
- const void *data1 = container_of(node, struct batadv_dat_entry,
- hash_entry);
+ struct batadv_dat_entry *dat_entry1 =
+ container_of(node, struct batadv_dat_entry,
+ hash_entry);
+ struct batadv_dat_entry *dat_entry2 =
+ container_of(data2,
+ struct batadv_dat_entry, data);
+ size_t data_size = batadv_dat_types_info[dat_entry1->type].size;
- return (memcmp(data1, data2, sizeof(__be32)) == 0 ? 1 : 0);
+ if (dat_entry1->type != dat_entry2->type)
+ return 0;
+
+ return (memcmp(dat_entry1->data, dat_entry2->data,
+ data_size) == 0 ? 1 : 0);
}
/**
@@ -198,8 +243,9 @@ static __be32 batadv_arp_ip_dst(struct sk_buff *skb, int hdr_size)
}
/**
- * batadv_hash_dat - compute the hash value for an IP address
+ * batadv_hash_dat - compute the hash value for a DAT data
* @data: data to hash
+ * @data_type: type of data
* @size: size of the hash table
*
* Returns the selected index in the hash table for the given data.
@@ -209,7 +255,8 @@ static uint32_t batadv_hash_dat(const void *data, uint32_t size)
uint32_t hash = 0;
const struct batadv_dat_entry *dat = data;
- hash = batadv_hash_bytes(hash, &dat->ip, sizeof(dat->ip));
+ hash = batadv_hash_bytes(hash, dat->data,
+ batadv_dat_types_info[dat->type].size);
hash = batadv_hash_bytes(hash, &dat->vid, sizeof(dat->vid));
hash += (hash << 3);
@@ -223,32 +270,40 @@ static uint32_t batadv_hash_dat(const void *data, uint32_t size)
* batadv_dat_entry_hash_find - look for a given dat_entry in the local hash
* table
* @bat_priv: the bat priv with all the soft interface information
- * @ip: search key
+ * @data: search key
+ * @data_type: type of data
* @vid: VLAN identifier
*
* Returns the dat_entry if found, NULL otherwise.
*/
static struct batadv_dat_entry *
-batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip,
- unsigned short vid)
+batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, void *data,
+ uint8_t data_type, unsigned short vid)
{
struct hlist_head *head;
struct batadv_dat_entry to_find, *dat_entry, *dat_entry_tmp = NULL;
struct batadv_hashtable *hash = bat_priv->dat.hash;
- uint32_t index;
+ uint32_t index, data_size = batadv_dat_types_info[data_type].size;
if (!hash)
return NULL;
- to_find.ip = ip;
+ to_find.data = kmalloc(data_size, GFP_ATOMIC);
+ if (!to_find.data)
+ return NULL;
+ memcpy(to_find.data, data, data_size);
+ to_find.type = data_type;
to_find.vid = vid;
index = batadv_hash_dat(&to_find, hash->size);
head = &hash->table[index];
+ kfree(to_find.data);
rcu_read_lock();
hlist_for_each_entry_rcu(dat_entry, head, hash_entry) {
- if (dat_entry->ip != ip)
+ if (dat_entry->type != data_type)
+ continue;
+ if (memcmp(dat_entry->data, data, data_size))
continue;
if (!atomic_inc_not_zero(&dat_entry->refcount))
@@ -265,25 +320,30 @@ batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip,
/**
* batadv_dat_entry_add - add a new dat entry or update it if already exists
* @bat_priv: the bat priv with all the soft interface information
- * @ip: ipv4 to add/edit
- * @mac_addr: mac address to assign to the given ipv4
+ * @data: the data to add/edit
+ * @data_type: type of the data added to DAT
+ * @mac_addr: mac address to assign to the given data
* @vid: VLAN identifier
*/
-static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip,
- uint8_t *mac_addr, unsigned short vid)
+static void batadv_dat_entry_add(struct batadv_priv *bat_priv, void *data,
+ uint8_t data_type, uint8_t *mac_addr,
+ unsigned short vid)
{
struct batadv_dat_entry *dat_entry;
int hash_added;
+ char dbg_data[BATADV_DAT_DATA_MAX_LEN];
+ size_t data_size = batadv_dat_types_info[data_type].size;
- dat_entry = batadv_dat_entry_hash_find(bat_priv, ip, vid);
+ dat_entry = batadv_dat_entry_hash_find(bat_priv, data, data_type, vid);
/* if this entry is already known, just update it */
if (dat_entry) {
if (!batadv_compare_eth(dat_entry->mac_addr, mac_addr))
memcpy(dat_entry->mac_addr, mac_addr, ETH_ALEN);
dat_entry->last_update = jiffies;
- batadv_dbg(BATADV_DBG_DAT, bat_priv,
- "Entry updated: %pI4 %pM (vid: %u)\n",
- &dat_entry->ip, dat_entry->mac_addr, vid);
+ batadv_dbg(BATADV_DBG_DAT, bat_priv, "Entry updated: %s %pM (vid: %u)\n",
+ batadv_dat_data_to_str(dat_entry->data, data_type,
+ dbg_data, sizeof(dbg_data)),
+ dat_entry->mac_addr, vid);
goto out;
}
@@ -291,7 +351,12 @@ static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip,
if (!dat_entry)
goto out;
- dat_entry->ip = ip;
+ dat_entry->data = kmalloc(data_size, GFP_ATOMIC);
+ if (!dat_entry->data)
+ goto out;
+ memcpy(dat_entry->data, data, data_size);
+
+ dat_entry->type = data_type;
dat_entry->vid = vid;
memcpy(dat_entry->mac_addr, mac_addr, ETH_ALEN);
dat_entry->last_update = jiffies;
@@ -307,8 +372,10 @@ static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip,
goto out;
}
- batadv_dbg(BATADV_DBG_DAT, bat_priv, "New entry added: %pI4 %pM (vid: %u)\n",
- &dat_entry->ip, dat_entry->mac_addr, vid);
+ batadv_dbg(BATADV_DBG_DAT, bat_priv, "New entry added: %s %pM (vid: %u)\n",
+ batadv_dat_data_to_str(dat_entry->data, data_type,
+ dbg_data, sizeof(dbg_data)),
+ dat_entry->mac_addr, vid);
out:
if (dat_entry)
@@ -520,7 +587,8 @@ static void batadv_choose_next_candidate(struct batadv_priv *bat_priv,
* batadv_dat_select_candidates - select the nodes which the DHT message has to
* be sent to
* @bat_priv: the bat priv with all the soft interface information
- * @ip_dst: ipv4 to look up in the DHT
+ * @data: data to look up in the DHT
+ * @data_type: type of data
*
* An originator O is selected if and only if its DHT_ID value is one of three
* closest values (from the LEFT, with wrap around if needed) then the hash
@@ -529,11 +597,15 @@ static void batadv_choose_next_candidate(struct batadv_priv *bat_priv,
* Returns the candidate array of size BATADV_DAT_CANDIDATE_NUM.
*/
static struct batadv_dat_candidate *
-batadv_dat_select_candidates(struct batadv_priv *bat_priv, __be32 ip_dst)
+batadv_dat_select_candidates(struct batadv_priv *bat_priv, void *data,
+ uint8_t data_type)
{
int select;
- batadv_dat_addr_t last_max = BATADV_DAT_ADDR_MAX, ip_key;
+ batadv_dat_addr_t last_max = BATADV_DAT_ADDR_MAX, data_key;
struct batadv_dat_candidate *res;
+ struct batadv_dat_entry to_find;
+ char dbg_data[BATADV_DAT_DATA_MAX_LEN];
+ size_t data_size = batadv_dat_types_info[data_type].size;
if (!bat_priv->orig_hash)
return NULL;
@@ -542,15 +614,23 @@ batadv_dat_select_candidates(struct batadv_priv *bat_priv, __be32 ip_dst)
if (!res)
return NULL;
- ip_key = (batadv_dat_addr_t)batadv_hash_dat(&ip_dst,
- BATADV_DAT_ADDR_MAX);
+ to_find.data = kmalloc(data_size, GFP_ATOMIC);
+ if (!to_find.data)
+ return NULL;
+ memcpy(to_find.data, data, data_size);
+ to_find.type = data_type;
+ data_key = (batadv_dat_addr_t)batadv_hash_dat(&to_find,
+ BATADV_DAT_ADDR_MAX);
+ kfree(to_find.data);
batadv_dbg(BATADV_DBG_DAT, bat_priv,
- "dat_select_candidates(): IP=%pI4 hash(IP)=%u\n", &ip_dst,
- ip_key);
+ "dat_select_candidates(): DATA=%s hash(DATA)=%u\n",
+ batadv_dat_data_to_str(data, data_type, dbg_data,
+ sizeof(dbg_data)),
+ data_key);
for (select = 0; select < BATADV_DAT_CANDIDATES_NUM; select++)
- batadv_choose_next_candidate(bat_priv, res, select, ip_key,
+ batadv_choose_next_candidate(bat_priv, res, select, data_key,
&last_max);
return res;
@@ -560,7 +640,8 @@ batadv_dat_select_candidates(struct batadv_priv *bat_priv, __be32 ip_dst)
* batadv_dat_send_data - send a payload to the selected candidates
* @bat_priv: the bat priv with all the soft interface information
* @skb: payload to send
- * @ip: the DHT key
+ * @data: the DHT key
+ * @data_type: type of data
* @packet_subtype: unicast4addr packet subtype to use
*
* This function copies the skb with pskb_copy() and is sent as unicast packet
@@ -570,8 +651,8 @@ batadv_dat_select_candidates(struct batadv_priv *bat_priv, __be32 ip_dst)
* otherwise.
*/
static bool batadv_dat_send_data(struct batadv_priv *bat_priv,
- struct sk_buff *skb, __be32 ip,
- int packet_subtype)
+ struct sk_buff *skb, void *data,
+ uint8_t data_type, int packet_subtype)
{
int i;
bool ret = false;
@@ -579,12 +660,15 @@ static bool batadv_dat_send_data(struct batadv_priv *bat_priv,
struct batadv_neigh_node *neigh_node = NULL;
struct sk_buff *tmp_skb;
struct batadv_dat_candidate *cand;
+ char dbg_data[BATADV_DAT_DATA_MAX_LEN];
- cand = batadv_dat_select_candidates(bat_priv, ip);
+ cand = batadv_dat_select_candidates(bat_priv, data, data_type);
if (!cand)
goto out;
- batadv_dbg(BATADV_DBG_DAT, bat_priv, "DHT_SEND for %pI4\n", &ip);
+ batadv_dbg(BATADV_DBG_DAT, bat_priv, "DHT_SEND for %s\n",
+ batadv_dat_data_to_str(data, data_type, dbg_data,
+ sizeof(dbg_data)));
for (i = 0; i < BATADV_DAT_CANDIDATES_NUM; i++) {
if (cand[i].type == BATADV_DAT_CANDIDATE_NOT_FOUND)
@@ -754,6 +838,7 @@ int batadv_dat_cache_seq_print_text(struct seq_file *seq, void *offset)
unsigned long last_seen_jiffies;
int last_seen_msecs, last_seen_secs, last_seen_mins;
uint32_t i;
+ char dbg_data[BATADV_DAT_DATA_MAX_LEN];
primary_if = batadv_seq_print_text_primary_if_get(seq);
if (!primary_if)
@@ -774,8 +859,12 @@ int batadv_dat_cache_seq_print_text(struct seq_file *seq, void *offset)
last_seen_msecs = last_seen_msecs % 60000;
last_seen_secs = last_seen_msecs / 1000;
- seq_printf(seq, " * %15pI4 %14pM %6i:%02i\n",
- &dat_entry->ip, dat_entry->mac_addr,
+ seq_printf(seq, " * %15s %14pM %6i:%02i\n",
+ batadv_dat_data_to_str(dat_entry->data,
+ dat_entry->type,
+ dbg_data,
+ sizeof(dbg_data)),
+ dat_entry->mac_addr,
last_seen_mins, last_seen_secs);
}
rcu_read_unlock();
@@ -926,9 +1015,10 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
hw_src = batadv_arp_hw_src(skb, 0);
ip_dst = batadv_arp_ip_dst(skb, 0);
- batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid);
+ batadv_dat_entry_add(bat_priv, &ip_src, BATADV_DAT_IPV4, hw_src, vid);
- dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst, vid);
+ dat_entry = batadv_dat_entry_hash_find(bat_priv, &ip_dst,
+ BATADV_DAT_IPV4, vid);
if (dat_entry) {
/* If the ARP request is destined for a local client the local
* client will answer itself. DAT would only generate a
@@ -962,7 +1052,8 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
ret = true;
} else {
/* Send the request to the DHT */
- ret = batadv_dat_send_data(bat_priv, skb, ip_dst,
+ ret = batadv_dat_send_data(bat_priv, skb, &ip_dst,
+ BATADV_DAT_IPV4,
BATADV_P_DAT_DHT_GET);
}
out:
@@ -1008,9 +1099,10 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
batadv_dbg_arp(bat_priv, skb, type, hdr_size,
"Parsing incoming ARP REQUEST");
- batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid);
+ batadv_dat_entry_add(bat_priv, &ip_src, BATADV_DAT_IPV4, hw_src, vid);
- dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst, vid);
+ dat_entry = batadv_dat_entry_hash_find(bat_priv, &ip_dst,
+ BATADV_DAT_IPV4, vid);
if (!dat_entry)
goto out;
@@ -1074,14 +1166,16 @@ void batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv,
hw_dst = batadv_arp_hw_dst(skb, hdr_size);
ip_dst = batadv_arp_ip_dst(skb, hdr_size);
- batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid);
- batadv_dat_entry_add(bat_priv, ip_dst, hw_dst, vid);
+ batadv_dat_entry_add(bat_priv, &ip_src, BATADV_DAT_IPV4, hw_src, vid);
+ batadv_dat_entry_add(bat_priv, &ip_dst, BATADV_DAT_IPV4, hw_dst, vid);
/* Send the ARP reply to the candidates for both the IP addresses that
* the node obtained from the ARP reply
*/
- batadv_dat_send_data(bat_priv, skb, ip_src, BATADV_P_DAT_DHT_PUT);
- batadv_dat_send_data(bat_priv, skb, ip_dst, BATADV_P_DAT_DHT_PUT);
+ batadv_dat_send_data(bat_priv, skb, &ip_src, BATADV_DAT_IPV4,
+ BATADV_P_DAT_DHT_PUT);
+ batadv_dat_send_data(bat_priv, skb, &ip_dst, BATADV_DAT_IPV4,
+ BATADV_P_DAT_DHT_PUT);
}
/**
* batadv_dat_snoop_incoming_arp_reply - snoop the ARP reply and fill the local
@@ -1119,8 +1213,8 @@ bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv,
/* Update our internal cache with both the IP addresses the node got
* within the ARP reply
*/
- batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid);
- batadv_dat_entry_add(bat_priv, ip_dst, hw_dst, vid);
+ batadv_dat_entry_add(bat_priv, &ip_src, BATADV_DAT_IPV4, hw_src, vid);
+ batadv_dat_entry_add(bat_priv, &ip_dst, BATADV_DAT_IPV4, hw_dst, vid);
/* if this REPLY is directed to a client of mine, let's deliver the
* packet to the interface
@@ -1167,7 +1261,8 @@ bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv,
goto out;
ip_dst = batadv_arp_ip_dst(forw_packet->skb, hdr_size);
- dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst, vid);
+ dat_entry = batadv_dat_entry_hash_find(bat_priv, &ip_dst,
+ BATADV_DAT_IPV4, vid);
/* check if the node already got this entry */
if (!dat_entry) {
batadv_dbg(BATADV_DBG_DAT, bat_priv,
diff --git a/distributed-arp-table.h b/distributed-arp-table.h
index 60d853b..557bab9 100644
--- a/distributed-arp-table.h
+++ b/distributed-arp-table.h
@@ -28,6 +28,7 @@
#include <linux/if_arp.h>
#define BATADV_DAT_ADDR_MAX ((batadv_dat_addr_t)~(batadv_dat_addr_t)0)
+#define BATADV_DAT_DATA_MAX_LEN 16
void batadv_dat_status_update(struct net_device *net_dev);
bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
diff --git a/types.h b/types.h
index 20a1bef..69c187e 100644
--- a/types.h
+++ b/types.h
@@ -931,7 +931,8 @@ struct batadv_algo_ops {
/**
* struct batadv_dat_entry - it is a single entry of batman-adv ARP backend. It
* is used to stored ARP entries needed for the global DAT cache
- * @ip: the IPv4 corresponding to this DAT/ARP entry
+ * @data: the data corresponding to this DAT entry
+ * @type: the type corresponding to this DAT entry
* @mac_addr: the MAC address associated to the stored IPv4
* @vid: the vlan ID associated to this entry
* @last_update: time in jiffies when this entry was refreshed last time
@@ -940,7 +941,8 @@ struct batadv_algo_ops {
* @rcu: struct used for freeing in an RCU-safe manner
*/
struct batadv_dat_entry {
- __be32 ip;
+ void *data;
+ uint8_t type;
uint8_t mac_addr[ETH_ALEN];
unsigned short vid;
unsigned long last_update;
@@ -950,6 +952,24 @@ struct batadv_dat_entry {
};
/**
+ * batadv_dat_types - types used in batadv_dat_entry for IP
+ * @BATADV_DAT_IPv4: IPv4 address type
+ */
+enum batadv_dat_types {
+ BATADV_DAT_IPV4 = 0,
+};
+
+/**
+ * batadv_dat_type_info - info needed for a DAT type data
+ * @size: the size of the type data
+ * @str_fmt: string format used by the data
+ */
+struct batadv_dat_type_info {
+ size_t size;
+ char *str_fmt;
+};
+
+/**
* struct batadv_dat_candidate - candidate destination for DAT operations
* @type: the type of the selected candidate. It can one of the following:
* - BATADV_DAT_CANDIDATE_NOT_FOUND
--
1.7.10.4
_batadv_update_route rcu_derefences orig_ifinfo->router outside of a
spinlock protected region to print some information messages to the debug
log. But this pointer is not checked again when the new pointer is assigned
in the spinlock protected region. Thus is can happen that the value of
orig_ifinfo->router changed in the meantime and thus the reference counter
of the wrong router gets reduced after the spinlock protected region.
Just rcu_dereferencing the value of orig_ifinfo->router inside the spinlock
protected region (which also set the new pointer) is enough to get the
correct old router object.
Fixes: d90ddb94423f ("batman-adv: Make orig_node->router an rcu protected pointer")
Signed-off-by: Sven Eckelmann <sven(a)narfation.org>
---
net/batman-adv/routing.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
index 1fb1be3..61fdefe 100644
--- a/net/batman-adv/routing.c
+++ b/net/batman-adv/routing.c
@@ -104,6 +104,8 @@ static void _batadv_update_route(struct batadv_priv *bat_priv,
neigh_node = NULL;
spin_lock_bh(&orig_node->neigh_list_lock);
+ curr_router = rcu_dereference_protected(orig_ifinfo->router, true);
+
rcu_assign_pointer(orig_ifinfo->router, neigh_node);
spin_unlock_bh(&orig_node->neigh_list_lock);
batadv_orig_ifinfo_free_ref(orig_ifinfo);
--
2.7.0