Hi,
Here's the second broadcast avoidance patchset (rebased on the former one).
Hopefully, those two patchsets together should be an automatic approach and replacement for the (non-upstream) no-rebroadcast flag patch and the cases it is currently used for.
Documentation: * https://www.open-mesh.org/projects/batman-adv/wiki/Broadcast-Avoidances
Branch: * https://git.open-mesh.org/batman-adv.git/shortlog/refs/heads/linus/neighhash
Regards, Linus
Adds a sha512 hash as a TVLV to an ELP packet. Hash is the "sum" of all neighbors (ordered alphabetically, concatenated, binary) a node sees on a specific interface.
Furthermore, the best and worst TX metric of all these neighbors on an interface are added to the TVLV.
Signed-off-by: Linus Lüssing linus.luessing@c0d3.blue
---
Note: This patch throws two checkpatch warnings which are faulty, though. --- net/batman-adv/Kconfig | 1 + net/batman-adv/bat_v.c | 27 ++++++- net/batman-adv/bat_v.h | 5 ++ net/batman-adv/bat_v_elp.c | 175 ++++++++++++++++++++++++++++++++++++++++++++ net/batman-adv/bat_v_elp.h | 2 + net/batman-adv/log.c | 4 +- net/batman-adv/main.c | 1 + net/batman-adv/originator.c | 47 +++++++++++- net/batman-adv/packet.h | 26 +++++++ net/batman-adv/types.h | 8 ++ 10 files changed, 289 insertions(+), 7 deletions(-)
diff --git a/net/batman-adv/Kconfig b/net/batman-adv/Kconfig index f20742c..35e220e 100644 --- a/net/batman-adv/Kconfig +++ b/net/batman-adv/Kconfig @@ -18,6 +18,7 @@ config BATMAN_ADV config BATMAN_ADV_BATMAN_V bool "B.A.T.M.A.N. V protocol (experimental)" depends on BATMAN_ADV && CFG80211=y || (CFG80211=m && BATMAN_ADV=m) + depends on CRYPTO_SHA512 default n help This option enables the B.A.T.M.A.N. V protocol, the successor diff --git a/net/batman-adv/bat_v.c b/net/batman-adv/bat_v.c index e79f6f0..762b34c 100644 --- a/net/batman-adv/bat_v.c +++ b/net/batman-adv/bat_v.c @@ -1070,11 +1070,17 @@ static struct batadv_algo_ops batadv_batman_v __read_mostly = { */ void batadv_v_hardif_init(struct batadv_hard_iface *hard_iface) { + struct batadv_hard_iface_bat_v *bat_v = &hard_iface->bat_v; + /* enable link throughput auto-detection by setting the throughput * override to zero */ - atomic_set(&hard_iface->bat_v.throughput_override, 0); - atomic_set(&hard_iface->bat_v.elp_interval, 500); + atomic_set(&bat_v->throughput_override, 0); + atomic_set(&bat_v->elp_interval, 500); + + bat_v->min_throughput = 0; + bat_v->max_throughput = (~(u32)0); + memset(bat_v->neigh_hash, 0, sizeof(bat_v->neigh_hash)); }
/** @@ -1108,6 +1114,14 @@ void batadv_v_mesh_free(struct batadv_priv *bat_priv) }
/** + * batadv_v_free - free the B.A.T.M.A.N. V global, mesh independent resources + */ +void batadv_v_free(void) +{ + batadv_v_elp_free(); +} + +/** * batadv_v_init - B.A.T.M.A.N. V initialization function * * Description: Takes care of initializing all the subcomponents. @@ -1119,11 +1133,15 @@ int __init batadv_v_init(void) { int ret;
+ ret = batadv_v_elp_init(); + if (ret < 0) + return ret; + /* B.A.T.M.A.N. V echo location protocol packet */ ret = batadv_recv_handler_register(BATADV_ELP, batadv_v_elp_packet_recv); if (ret < 0) - return ret; + goto elp_free;
ret = batadv_recv_handler_register(BATADV_OGM2, batadv_v_ogm_packet_recv); @@ -1136,6 +1154,9 @@ int __init batadv_v_init(void)
return ret;
+elp_free: + batadv_v_elp_free(); + ogm_unregister: batadv_recv_handler_unregister(BATADV_OGM2);
diff --git a/net/batman-adv/bat_v.h b/net/batman-adv/bat_v.h index 83b7763..e2645b8 100644 --- a/net/batman-adv/bat_v.h +++ b/net/batman-adv/bat_v.h @@ -23,6 +23,7 @@ #ifdef CONFIG_BATMAN_ADV_BATMAN_V
int batadv_v_init(void); +void batadv_v_free(void); void batadv_v_hardif_init(struct batadv_hard_iface *hardif); int batadv_v_mesh_init(struct batadv_priv *bat_priv); void batadv_v_mesh_free(struct batadv_priv *bat_priv); @@ -34,6 +35,10 @@ static inline int batadv_v_init(void) return 0; }
+static inline void batadv_v_free(void) +{ +} + static inline void batadv_v_hardif_init(struct batadv_hard_iface *hardif) { } diff --git a/net/batman-adv/bat_v_elp.c b/net/batman-adv/bat_v_elp.c index ee08540..d4b5750 100644 --- a/net/batman-adv/bat_v_elp.c +++ b/net/batman-adv/bat_v_elp.c @@ -18,6 +18,8 @@ #include "bat_v_elp.h" #include "main.h"
+#include <crypto/hash.h> +#include <crypto/sha.h> #include <linux/atomic.h> #include <linux/byteorder/generic.h> #include <linux/errno.h> @@ -49,6 +51,8 @@ #include "routing.h" #include "send.h"
+static struct crypto_shash *tfm; + /** * batadv_v_elp_start_timer - restart timer for ELP periodic work * @hard_iface: the interface for which the timer has to be reset @@ -65,6 +69,133 @@ static void batadv_v_elp_start_timer(struct batadv_hard_iface *hard_iface) }
/** + * batadv_v_elp_update_neigh_hash - updates neighborhood hash related data + * @hard_iface: interface which the data has to be prepared for + * + * Firstly, this function updates the neighborhood hash of a hard interface. + * That is it resummarizes the present neighborhood into one compact hash + * representation. + * + * Secondly, minimum and maximum throughput values within this neighorhood are + * updated. + */ +static void batadv_v_elp_update_neigh_hash(struct batadv_hard_iface *hard_iface) +{ + struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); + struct batadv_hardif_neigh_node *hardif_neigh; + struct ewma_throughput *ewma_throughput; + u8 *own_addr = hard_iface->net_dev->dev_addr; + u32 min_throughput = ~((u32)0), max_throughput = 0; + u32 throughput; + int ret; + + SHASH_DESC_ON_STACK(shash, tfm); + + shash->flags = 0; + shash->tfm = tfm; + + ret = crypto_shash_init(shash); + if (ret) + goto err; + + rcu_read_lock(); + hlist_for_each_entry_rcu(hardif_neigh, + &hard_iface->neigh_list, list) { + /* insert own address at the right spot */ + if (own_addr && (memcmp(own_addr, hardif_neigh->addr, + ETH_ALEN) < 0)) { + ret = crypto_shash_update(shash, own_addr, ETH_ALEN); + if (ret) { + rcu_read_unlock(); + goto err; + } + + own_addr = NULL; + } + + ret = crypto_shash_update(shash, hardif_neigh->addr, ETH_ALEN); + if (ret) { + rcu_read_unlock(); + goto err; + } + + ewma_throughput = &hardif_neigh->bat_v.throughput; + throughput = ewma_throughput_read(ewma_throughput); + + if (throughput < min_throughput) + min_throughput = throughput; + + if (throughput > max_throughput) + max_throughput = throughput; + } + rcu_read_unlock(); + + if (own_addr) { + ret = crypto_shash_update(shash, own_addr, ETH_ALEN); + if (ret) + goto err; + } + + ret = crypto_shash_final(shash, hard_iface->bat_v.neigh_hash); + if (ret) + goto err; + + hard_iface->bat_v.min_throughput = min_throughput; + hard_iface->bat_v.max_throughput = max_throughput; + + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Updated neighbor hash on interface %s: %*phN, min_through: %u kbit/s, max_through: %u kbit/s\n", + hard_iface->net_dev->name, + (int)sizeof(hard_iface->bat_v.neigh_hash), + hard_iface->bat_v.neigh_hash, + hard_iface->bat_v.min_throughput * 100, + hard_iface->bat_v.max_throughput * 100); + + return; + +err: + memset(hard_iface->bat_v.neigh_hash, 0, + sizeof(hard_iface->bat_v.neigh_hash)); + hard_iface->bat_v.min_throughput = 0; + hard_iface->bat_v.max_throughput = ~((u32)0); + + pr_warn_once("An error occurred while calculating neighbor hash for %s\n", + hard_iface->net_dev->name); +} + +/** + * batadv_v_elp_update_neigh_hash_tvlv - updates a neighborhood hash tvlv + * @hard_iface: interface which the tvlv is updated for + * @skb: the to be transmitted ELP packet containing the neighborhood tvlv + * + * Prepares the neighborhood hash tvlv of an ELP packet by updating its + * hash as well as minimum and maximum throughput values. + */ +static void +batadv_v_elp_update_neigh_hash_tvlv(struct batadv_hard_iface *hard_iface, + struct sk_buff *skb) +{ + struct batadv_hard_iface_bat_v *hard_iface_v = &hard_iface->bat_v; + struct batadv_elp_packet *elp_packet; + struct batadv_tvlv_hdr *tvlv_hdr; + struct batadv_tvlv_nhh_data *nhh_data; + + elp_packet = (struct batadv_elp_packet *)skb_network_header(skb); + tvlv_hdr = (struct batadv_tvlv_hdr *)(elp_packet + 1); + nhh_data = (struct batadv_tvlv_nhh_data *)(tvlv_hdr + 1); + + if (!hard_iface_v->min_throughput) { + elp_packet->tvlv_len = 0; + skb_trim(skb, skb->len - sizeof(*tvlv_hdr) - sizeof(*nhh_data)); + } else { + nhh_data->min_throughput = htonl(hard_iface_v->min_throughput); + nhh_data->max_throughput = htonl(hard_iface_v->max_throughput); + memcpy(nhh_data->neigh_hash, hard_iface_v->neigh_hash, + sizeof(hard_iface_v->neigh_hash)); + } +} + +/** * batadv_v_elp_get_throughput - get the throughput towards a neighbour * @neigh: the neighbour for which the throughput has to be obtained * @@ -269,6 +400,9 @@ static void batadv_v_elp_periodic_work(struct work_struct *work) elp_interval = atomic_read(&hard_iface->bat_v.elp_interval); elp_packet->elp_interval = htonl(elp_interval);
+ batadv_v_elp_update_neigh_hash(hard_iface); + batadv_v_elp_update_neigh_hash_tvlv(hard_iface, skb); + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "Sending broadcast ELP packet on interface %s, seqno %u\n", hard_iface->net_dev->name, @@ -324,23 +458,42 @@ out: int batadv_v_elp_iface_enable(struct batadv_hard_iface *hard_iface) { struct batadv_elp_packet *elp_packet; + struct batadv_tvlv_hdr *tvlv_hdr; + struct batadv_tvlv_nhh_data *nhh_data; unsigned char *elp_buff; u32 random_seqno; size_t size; int res = -ENOMEM;
size = ETH_HLEN + NET_IP_ALIGN + BATADV_ELP_HLEN; + size += sizeof(*nhh_data) + sizeof(*tvlv_hdr); + hard_iface->bat_v.elp_skb = dev_alloc_skb(size); if (!hard_iface->bat_v.elp_skb) goto out;
skb_reserve(hard_iface->bat_v.elp_skb, ETH_HLEN + NET_IP_ALIGN); + skb_reset_network_header(hard_iface->bat_v.elp_skb); + elp_buff = skb_put(hard_iface->bat_v.elp_skb, BATADV_ELP_HLEN); elp_packet = (struct batadv_elp_packet *)elp_buff; memset(elp_packet, 0, BATADV_ELP_HLEN);
elp_packet->packet_type = BATADV_ELP; elp_packet->version = BATADV_COMPAT_VERSION; + elp_packet->tvlv_len = htons(sizeof(*nhh_data) + sizeof(*tvlv_hdr)); + + elp_buff = skb_put(hard_iface->bat_v.elp_skb, sizeof(*tvlv_hdr)); + tvlv_hdr = (struct batadv_tvlv_hdr *)elp_buff; + tvlv_hdr->type = BATADV_TVLV_NHH; + tvlv_hdr->version = 1; + tvlv_hdr->len = htons(sizeof(*nhh_data)); + + size = sizeof(*nhh_data); + elp_buff = skb_put(hard_iface->bat_v.elp_skb, size); + nhh_data = (struct batadv_tvlv_nhh_data *)elp_buff; + nhh_data->min_throughput = htonl(0); + memset(nhh_data, 0, size);
/* randomize initial seqno to avoid collision */ get_random_bytes(&random_seqno, sizeof(random_seqno)); @@ -527,3 +680,25 @@ out: consume_skb(skb); return NET_RX_SUCCESS; } + +/** + * batadv_v_elp_init - initialize global ELP structures + * + * Return: A negative value on error, zero on success. + */ +int batadv_v_elp_init(void) +{ + tfm = crypto_alloc_shash("sha512", 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + return 0; +} + +/** + * batadv_v_elp_free - free global ELP structures + */ +void batadv_v_elp_free(void) +{ + crypto_free_shash(tfm); +} diff --git a/net/batman-adv/bat_v_elp.h b/net/batman-adv/bat_v_elp.h index be17c0b..ed5936c 100644 --- a/net/batman-adv/bat_v_elp.h +++ b/net/batman-adv/bat_v_elp.h @@ -23,6 +23,8 @@ struct sk_buff; struct work_struct;
+int batadv_v_elp_init(void); +void batadv_v_elp_free(void); int batadv_v_elp_iface_enable(struct batadv_hard_iface *hard_iface); void batadv_v_elp_iface_disable(struct batadv_hard_iface *hard_iface); void batadv_v_elp_iface_activate(struct batadv_hard_iface *primary_iface, diff --git a/net/batman-adv/log.c b/net/batman-adv/log.c index 56dc532..099524e 100644 --- a/net/batman-adv/log.c +++ b/net/batman-adv/log.c @@ -66,7 +66,7 @@ static int batadv_fdebug_log(struct batadv_priv_debug_log *debug_log, const char *fmt, ...) { va_list args; - static char debug_log_buf[256]; + static char debug_log_buf[512]; char *p;
if (!debug_log) @@ -90,7 +90,7 @@ static int batadv_fdebug_log(struct batadv_priv_debug_log *debug_log, int batadv_debug_log(struct batadv_priv *bat_priv, const char *fmt, ...) { va_list args; - char tmp_log_buf[256]; + char tmp_log_buf[512];
va_start(args, fmt); vscnprintf(tmp_log_buf, sizeof(tmp_log_buf), fmt, args); diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index 2c017ab..2570463 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -135,6 +135,7 @@ static void __exit batadv_exit(void) rcu_barrier();
batadv_tt_cache_destroy(); + batadv_v_free(); }
int batadv_mesh_init(struct net_device *soft_iface) diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c index 3f1c286..3a5612d 100644 --- a/net/batman-adv/originator.c +++ b/net/batman-adv/originator.c @@ -509,6 +509,43 @@ batadv_neigh_node_get(const struct batadv_orig_node *orig_node, }
/** + * batadv_hardif_neigh_get_pre - get the predecessor of a neighbor node + * @hard_iface: the interface this neighbour is connected to + * @neigh_addr: address of the neighbour to retrieve the predecessor for + * + * Tries to find the neighbor node which has an address closest to but + * smaller than the neigh_addr provided. In other words, tries to + * find a potential predecessor of a given MAC address. + * + * Return: The alphabetical predecessor of a neighbor node. Returns NULL + * if no such predecessor exists. + */ +static struct batadv_hardif_neigh_node * +batadv_hardif_neigh_get_pre(struct batadv_hard_iface *hard_iface, + const u8 *neigh_addr) +{ + struct batadv_hardif_neigh_node *tmp_hardif_neigh, *hardif_neigh = NULL; + + rcu_read_lock(); + hlist_for_each_entry_rcu(tmp_hardif_neigh, &hard_iface->neigh_list, + list) { + if (memcmp(tmp_hardif_neigh->addr, neigh_addr, ETH_ALEN) >= 0) + break; + + if (!kref_get_unless_zero(&tmp_hardif_neigh->refcount)) + continue; + + if (hardif_neigh) + batadv_hardif_neigh_put(hardif_neigh); + + hardif_neigh = tmp_hardif_neigh; + } + rcu_read_unlock(); + + return hardif_neigh; +} + +/** * batadv_hardif_neigh_create - create a hardif neighbour node * @hard_iface: the interface this neighbour is connected to * @neigh_addr: the interface address of the neighbour to retrieve @@ -522,7 +559,7 @@ batadv_hardif_neigh_create(struct batadv_hard_iface *hard_iface, struct batadv_orig_node *orig_node) { struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); - struct batadv_hardif_neigh_node *hardif_neigh = NULL; + struct batadv_hardif_neigh_node *hardif_neigh = NULL, *pre_neigh;
spin_lock_bh(&hard_iface->neigh_list_lock);
@@ -547,7 +584,13 @@ batadv_hardif_neigh_create(struct batadv_hard_iface *hard_iface, if (bat_priv->algo_ops->neigh.hardif_init) bat_priv->algo_ops->neigh.hardif_init(hardif_neigh);
- hlist_add_head(&hardif_neigh->list, &hard_iface->neigh_list); + pre_neigh = batadv_hardif_neigh_get_pre(hard_iface, neigh_addr); + if (!pre_neigh) { + hlist_add_head(&hardif_neigh->list, &hard_iface->neigh_list); + } else { + hlist_add_behind(&hardif_neigh->list, &pre_neigh->list); + batadv_hardif_neigh_put(pre_neigh); + }
out: spin_unlock_bh(&hard_iface->neigh_list_lock); diff --git a/net/batman-adv/packet.h b/net/batman-adv/packet.h index 6afc0b8..56f1f6f 100644 --- a/net/batman-adv/packet.h +++ b/net/batman-adv/packet.h @@ -19,6 +19,7 @@ #define _NET_BATMAN_ADV_PACKET_H_
#include <asm/byteorder.h> +#include <crypto/sha.h> #include <linux/types.h>
#define batadv_tp_is_error(n) ((u8)n > 127 ? 1 : 0) @@ -163,6 +164,14 @@ enum batadv_tvlv_type { BATADV_TVLV_MCAST = 0x06, };
+/** + * enum batadv_tvlv_elp_type - tvlv type definitions for ELP messages + * @BATADV_TVLV_NHH: neighborhood hash + */ +enum batadv_tvlv_elp_type { + BATADV_TVLV_NHH = 0x01, +}; + #pragma pack(2) /* the destination hardware field in the ARP frame is used to * transport the claim type and the group id @@ -240,6 +249,8 @@ struct batadv_ogm2_packet { * @orig: originator mac address * @seqno: sequence number * @elp_interval: currently used ELP sending interval in ms + * @reserved: reserved bytes for alignment + * @tvlv_len: length of tvlv data following the elp header */ struct batadv_elp_packet { u8 packet_type; @@ -247,6 +258,8 @@ struct batadv_elp_packet { u8 orig[ETH_ALEN]; __be32 seqno; __be32 elp_interval; + __be16 reserved; + __be16 tvlv_len; };
#define BATADV_ELP_HLEN sizeof(struct batadv_elp_packet) @@ -628,4 +641,17 @@ struct batadv_tvlv_mcast_data { u8 reserved[3]; };
+/** + * batadv_tvlv_nhh_data - neighborhood hash data + * @min_throughput: worst of all TX throughputs this neighbor has to others + * @max_throughput: best of all TX throughputs this neighbor has to others + * @neigh_hash: a sha512 hash of all neighbors this neighbor sees + * (hash over the alphabetically ordered, concatenated, binary representation) + */ +struct batadv_tvlv_nhh_data { + __be32 min_throughput; + __be32 max_throughput; + u8 neigh_hash[SHA512_DIGEST_SIZE]; +}; + #endif /* _NET_BATMAN_ADV_PACKET_H_ */ diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index b540cd3..731bdf5 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -22,6 +22,7 @@ #error only "main.h" can be included directly #endif
+#include <crypto/sha.h> #include <linux/average.h> #include <linux/bitops.h> #include <linux/compiler.h> @@ -108,6 +109,10 @@ enum batadv_v_hard_iface_flags { * @elp_wq: workqueue used to schedule ELP transmissions * @throughput_override: throughput override to disable link auto-detection * @flags: interface specific flags + * @min_throughput: worst of all TX throughputs this neighbor has to others + * @max_throughput: best of all TX throughputs this neighbor has to others + * @neigh_hash: a sha512 hash of all neighbors this neighbor sees + * (hash over the alphabetically ordered, concatenated, binary representation) */ struct batadv_hard_iface_bat_v { atomic_t elp_interval; @@ -116,6 +121,9 @@ struct batadv_hard_iface_bat_v { struct delayed_work elp_wq; atomic_t throughput_override; u8 flags; + u32 min_throughput; + u32 max_throughput; + u8 neigh_hash[SHA512_DIGEST_SIZE]; };
/**
[...]
diff --git a/net/batman-adv/Kconfig b/net/batman-adv/Kconfig index f20742c..35e220e 100644 --- a/net/batman-adv/Kconfig +++ b/net/batman-adv/Kconfig @@ -18,6 +18,7 @@ config BATMAN_ADV config BATMAN_ADV_BATMAN_V bool "B.A.T.M.A.N. V protocol (experimental)" depends on BATMAN_ADV && CFG80211=y || (CFG80211=m && BATMAN_ADV=m)
- depends on CRYPTO_SHA512 default n help This option enables the B.A.T.M.A.N. V protocol, the successor
Nearly everyone else seems to select the crypto sha512 module (maybe we should do the same?):
crypto/Kconfig: select CRYPTO_SHA512 crypto/Kconfig: select CRYPTO_SHA512 crypto/Kconfig: select CRYPTO_SHA512 crypto/Kconfig: select CRYPTO_SHA512 drivers/crypto/Kconfig: select CRYPTO_SHA512 drivers/crypto/chelsio/Kconfig: select CRYPTO_SHA512 drivers/crypto/qat/Kconfig: select CRYPTO_SHA512 drivers/staging/lustre/lustre/Kconfig: select CRYPTO_SHA512 init/Kconfig: select CRYPTO_SHA512 init/Kconfig: select CRYPTO_SHA512 security/integrity/ima/Kconfig: depends on CRYPTO_SHA512 && !IMA_TEMPLATE
Kind regards, Sven
On Dienstag, 20. September 2016 14:12:43 CEST Linus Lüssing wrote: [...]
/**
- batadv_hardif_neigh_get_pre - get the predecessor of a neighbor node
- @hard_iface: the interface this neighbour is connected to
- @neigh_addr: address of the neighbour to retrieve the predecessor for
- Tries to find the neighbor node which has an address closest to but
- smaller than the neigh_addr provided. In other words, tries to
- find a potential predecessor of a given MAC address.
- Return: The alphabetical predecessor of a neighbor node. Returns NULL
- if no such predecessor exists.
- */
+static struct batadv_hardif_neigh_node * +batadv_hardif_neigh_get_pre(struct batadv_hard_iface *hard_iface,
const u8 *neigh_addr)
+{
- struct batadv_hardif_neigh_node *tmp_hardif_neigh, *hardif_neigh = NULL;
- rcu_read_lock();
- hlist_for_each_entry_rcu(tmp_hardif_neigh, &hard_iface->neigh_list,
list) {
if (memcmp(tmp_hardif_neigh->addr, neigh_addr, ETH_ALEN) >= 0)
break;
if (!kref_get_unless_zero(&tmp_hardif_neigh->refcount))
continue;
if (hardif_neigh)
batadv_hardif_neigh_put(hardif_neigh);
hardif_neigh = tmp_hardif_neigh;
- }
- rcu_read_unlock();
- return hardif_neigh;
+}
+/**
- batadv_hardif_neigh_create - create a hardif neighbour node
- @hard_iface: the interface this neighbour is connected to
- @neigh_addr: the interface address of the neighbour to retrieve
@@ -522,7 +559,7 @@ batadv_hardif_neigh_create(struct batadv_hard_iface *hard_iface, struct batadv_orig_node *orig_node) { struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
- struct batadv_hardif_neigh_node *hardif_neigh = NULL;
struct batadv_hardif_neigh_node *hardif_neigh = NULL, *pre_neigh;
spin_lock_bh(&hard_iface->neigh_list_lock);
@@ -547,7 +584,13 @@ batadv_hardif_neigh_create(struct batadv_hard_iface *hard_iface, if (bat_priv->algo_ops->neigh.hardif_init) bat_priv->algo_ops->neigh.hardif_init(hardif_neigh);
- hlist_add_head(&hardif_neigh->list, &hard_iface->neigh_list);
- pre_neigh = batadv_hardif_neigh_get_pre(hard_iface, neigh_addr);
- if (!pre_neigh) {
hlist_add_head(&hardif_neigh->list, &hard_iface->neigh_list);
- } else {
hlist_add_behind(&hardif_neigh->list, &pre_neigh->list);
batadv_hardif_neigh_put(pre_neigh);
- }
I personally would have liked to see this in a separate patch. But there are more important things here.
First you have to use hlist_add_head_rcu and hlist_add_behind_rcu here because the readers use RCU to access the list. I think it would also be more appropriate to replace the rcu_read_lock in the batadv_hardif_neigh_get_pre function and instead use lockdep to mark the function as "requires hard_iface->neigh_list_lock".
Stuff to other parts will follow later.
Kind regards, Sven
On Thu, Sep 29, 2016 at 05:31:27PM +0200, Sven Eckelmann wrote:
First you have to use hlist_add_head_rcu and hlist_add_behind_rcu here because the readers use RCU to access the list. I think it would also be more appropriate to replace the rcu_read_lock in the batadv_hardif_neigh_get_pre function and instead use lockdep to mark the function as "requires hard_iface->neigh_list_lock".
Hm, I'm currently wondering... isn't this a bug we already have now, then? Shouldn't this have been an hlist_add_head_rcu() in the first place?
On Thu, Oct 06, 2016 at 03:55:21AM +0200, Linus Lüssing wrote:
On Thu, Sep 29, 2016 at 05:31:27PM +0200, Sven Eckelmann wrote:
First you have to use hlist_add_head_rcu and hlist_add_behind_rcu here because the readers use RCU to access the list. I think it would also be more appropriate to replace the rcu_read_lock in the batadv_hardif_neigh_get_pre function and instead use lockdep to mark the function as "requires hard_iface->neigh_list_lock".
Hm, I'm currently wondering... isn't this a bug we already have now, then? Shouldn't this have been an hlist_add_head_rcu() in the first place?
Now saw your patch for maint - so ignore my last comment :D.
On Thu, Sep 29, 2016 at 05:31:27PM +0200, Sven Eckelmann wrote:
On Dienstag, 20. September 2016 14:12:43 CEST Linus Lüssing wrote: [...]
/**
- batadv_hardif_neigh_get_pre - get the predecessor of a neighbor node
- @hard_iface: the interface this neighbour is connected to
- @neigh_addr: address of the neighbour to retrieve the predecessor for
- Tries to find the neighbor node which has an address closest to but
- smaller than the neigh_addr provided. In other words, tries to
- find a potential predecessor of a given MAC address.
- Return: The alphabetical predecessor of a neighbor node. Returns NULL
- if no such predecessor exists.
- */
+static struct batadv_hardif_neigh_node * +batadv_hardif_neigh_get_pre(struct batadv_hard_iface *hard_iface,
const u8 *neigh_addr)
+{
- struct batadv_hardif_neigh_node *tmp_hardif_neigh, *hardif_neigh = NULL;
- rcu_read_lock();
- hlist_for_each_entry_rcu(tmp_hardif_neigh, &hard_iface->neigh_list,
list) {
if (memcmp(tmp_hardif_neigh->addr, neigh_addr, ETH_ALEN) >= 0)
break;
if (!kref_get_unless_zero(&tmp_hardif_neigh->refcount))
continue;
if (hardif_neigh)
batadv_hardif_neigh_put(hardif_neigh);
hardif_neigh = tmp_hardif_neigh;
- }
- rcu_read_unlock();
- return hardif_neigh;
+}
+/**
- batadv_hardif_neigh_create - create a hardif neighbour node
- @hard_iface: the interface this neighbour is connected to
- @neigh_addr: the interface address of the neighbour to retrieve
@@ -522,7 +559,7 @@ batadv_hardif_neigh_create(struct batadv_hard_iface *hard_iface, struct batadv_orig_node *orig_node) { struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
- struct batadv_hardif_neigh_node *hardif_neigh = NULL;
struct batadv_hardif_neigh_node *hardif_neigh = NULL, *pre_neigh;
spin_lock_bh(&hard_iface->neigh_list_lock);
@@ -547,7 +584,13 @@ batadv_hardif_neigh_create(struct batadv_hard_iface *hard_iface, if (bat_priv->algo_ops->neigh.hardif_init) bat_priv->algo_ops->neigh.hardif_init(hardif_neigh);
- hlist_add_head(&hardif_neigh->list, &hard_iface->neigh_list);
- pre_neigh = batadv_hardif_neigh_get_pre(hard_iface, neigh_addr);
- if (!pre_neigh) {
hlist_add_head(&hardif_neigh->list, &hard_iface->neigh_list);
- } else {
hlist_add_behind(&hardif_neigh->list, &pre_neigh->list);
batadv_hardif_neigh_put(pre_neigh);
- }
I personally would have liked to see this in a separate patch. But there are more important things here.
First you have to use hlist_add_head_rcu and hlist_add_behind_rcu here because the readers use RCU to access the list. I think it would also be more appropriate to replace the rcu_read_lock in the batadv_hardif_neigh_get_pre function and instead use lockdep to mark the function as "requires hard_iface->neigh_list_lock".
PS: In v2, I haven't changed batadv_hardif_neigh_get_pre() to a non-rcu variant with lockdep. You are right, rcu isn't really necessary in this context, but I kind of like being able to have a function on one screen and seeing immediately whether it works or not, without needing to scroll.
And I liked keeping it similar to batadv_hardif_neigh_get().
Does that make sense?
On Dienstag, 20. September 2016 14:12:43 CEST Linus Lüssing wrote: [...]
/**
- batadv_v_elp_update_neigh_hash - updates neighborhood hash related data
- @hard_iface: interface which the data has to be prepared for
- Firstly, this function updates the neighborhood hash of a hard interface.
- That is it resummarizes the present neighborhood into one compact hash
- representation.
- Secondly, minimum and maximum throughput values within this neighorhood are
- updated.
- */
+static void batadv_v_elp_update_neigh_hash(struct batadv_hard_iface *hard_iface) +{
- struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
- struct batadv_hardif_neigh_node *hardif_neigh;
- struct ewma_throughput *ewma_throughput;
- u8 *own_addr = hard_iface->net_dev->dev_addr;
- u32 min_throughput = ~((u32)0), max_throughput = 0;
- u32 throughput;
- int ret;
- SHASH_DESC_ON_STACK(shash, tfm);
This macro doesn't exist on Linux < v3.18. Can you please add it to compat-include/crypto/hash.h?
[...]
+/**
- batadv_tvlv_nhh_data - neighborhood hash data
- @min_throughput: worst of all TX throughputs this neighbor has to others
- @max_throughput: best of all TX throughputs this neighbor has to others
- @neigh_hash: a sha512 hash of all neighbors this neighbor sees
- (hash over the alphabetically ordered, concatenated, binary representation)
- */
+struct batadv_tvlv_nhh_data {
- __be32 min_throughput;
- __be32 max_throughput;
- u8 neigh_hash[SHA512_DIGEST_SIZE];
+};
Please use "struct batadv_tvlv_nhh_data - neighborhood hash data" in kernel-doc block
Kind regards, Sven
On Dienstag, 20. September 2016 14:12:43 CEST Linus Lüssing wrote: [...]
diff --git a/net/batman-adv/bat_v.c b/net/batman-adv/bat_v.c index e79f6f0..762b34c 100644 --- a/net/batman-adv/bat_v.c +++ b/net/batman-adv/bat_v.c
[...]
- memset(bat_v->neigh_hash, 0, sizeof(bat_v->neigh_hash));
Include <linux/string.h> is missing for it. Can you please add while resending the patch?
[...]
diff --git a/net/batman-adv/bat_v_elp.c b/net/batman-adv/bat_v_elp.c index ee08540..d4b5750 100644 --- a/net/batman-adv/bat_v_elp.c +++ b/net/batman-adv/bat_v_elp.c
[...]
- pr_warn_once("An error occurred while calculating neighbor hash for %s\n",
hard_iface->net_dev->name);
+}
Please include linux/printk.h for pr_warn_once.
[...]
+int batadv_v_elp_init(void) +{
- tfm = crypto_alloc_shash("sha512", 0, 0);
- if (IS_ERR(tfm))
return PTR_ERR(tfm);
These two require linux/err.h
[...]
int batadv_mesh_init(struct net_device *soft_iface) diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c index 3f1c286..3a5612d 100644 --- a/net/batman-adv/originator.c +++ b/net/batman-adv/originator.c
[...]
if (memcmp(tmp_hardif_neigh->addr, neigh_addr, ETH_ALEN) >= 0)
break;
memset requires linux/string.h
Kind regards, Sven
Instead of having one TVLV registration function for OGMs and one for unicast packets, this patch adds a new, generic tvlv handler registratiorn function, which simply uses a packet type as parameter.
For now, this patch only migrates the multicast and gateway tvlv handlers to this new API, as these two have been tested and verified to work by the author.
The benefits of this new API:
* A more unified TVLV handling * Easier to add TVLV capabilities to any new, upcoming packet type * Does not rely on linearized skb data
Further ToDos (for later patches):
* Insert handler hooks for unicast_tvlv packets too and migrate BATADV_TVLV_{DAT,NC,TT,ROAM} to new API after further testing. * Remove old TVLV handler API
Signed-off-by: Linus Lüssing linus.luessing@c0d3.blue --- net/batman-adv/bat_iv_ogm.c | 8 +- net/batman-adv/bat_v_ogm.c | 17 +- net/batman-adv/distributed-arp-table.c | 4 +- net/batman-adv/gateway_common.c | 44 +++-- net/batman-adv/multicast.c | 37 ++-- net/batman-adv/network-coding.c | 4 +- net/batman-adv/tvlv.c | 324 ++++++++++++++++++++++++++++++--- net/batman-adv/tvlv.h | 22 ++- net/batman-adv/types.h | 24 ++- 9 files changed, 405 insertions(+), 79 deletions(-)
diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index 9c723cf..4f09b70 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -1491,7 +1491,7 @@ batadv_iv_ogm_process_per_outif(const struct sk_buff *skb, int ogm_offset, }
if (if_outgoing == BATADV_IF_DEFAULT) - batadv_tvlv_ogm_receive(bat_priv, ogm_packet, orig_node); + batadv_tvlv_ogm_receive(bat_priv, skb, orig_node);
/* if sender is a direct neighbor the sender mac equals * originator mac @@ -1832,6 +1832,8 @@ static int batadv_iv_ogm_receive(struct sk_buff *skb, ogm_offset = 0; ogm_packet = (struct batadv_ogm_packet *)skb->data;
+ WARN_ON(skb_network_offset(skb) != 0); + /* unpack the aggregated packets and process them one by one */ while (batadv_iv_ogm_aggr_packet(ogm_offset, skb_headlen(skb), ogm_packet->tvlv_len)) { @@ -1842,8 +1844,12 @@ static int batadv_iv_ogm_receive(struct sk_buff *skb,
packet_pos = skb->data + ogm_offset; ogm_packet = (struct batadv_ogm_packet *)packet_pos; + + skb_set_network_header(skb, ogm_offset); }
+ skb_reset_network_header(skb); + kfree_skb(skb); return NET_RX_SUCCESS; } diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c index eaa2e2d..606f899 100644 --- a/net/batman-adv/bat_v_ogm.c +++ b/net/batman-adv/bat_v_ogm.c @@ -594,7 +594,7 @@ out: /** * batadv_v_ogm_process_per_outif - process a batman v OGM for an outgoing if * @bat_priv: the bat priv with all the soft interface information - * @ethhdr: the Ethernet header of the OGM2 + * @skb: the skb containing the OGM2 * @ogm2: OGM2 structure * @orig_node: Originator structure for which the OGM has been received * @neigh_node: the neigh_node through with the OGM has been received @@ -603,13 +603,16 @@ out: */ static void batadv_v_ogm_process_per_outif(struct batadv_priv *bat_priv, - const struct ethhdr *ethhdr, + const struct sk_buff *skb, const struct batadv_ogm2_packet *ogm2, struct batadv_orig_node *orig_node, struct batadv_neigh_node *neigh_node, struct batadv_hard_iface *if_incoming, struct batadv_hard_iface *if_outgoing) { + void *ctx = batadv_tvlv_ogm_pack_ctx(orig_node); + const struct ethhdr *ethhdr = eth_hdr(skb); + unsigned int tvlv_offset = sizeof(*ogm2); int seqno_age; bool forward;
@@ -623,11 +626,15 @@ batadv_v_ogm_process_per_outif(struct batadv_priv *bat_priv, return;
/* only unknown & newer OGMs contain TVLVs we are interested in */ - if ((seqno_age > 0) && (if_outgoing == BATADV_IF_DEFAULT)) + if ((seqno_age > 0) && (if_outgoing == BATADV_IF_DEFAULT)) { + batadv_tvlv_containers_process2(bat_priv, skb, BATADV_OGM2, + tvlv_offset, + ntohs(ogm2->tvlv_len), ctx); batadv_tvlv_containers_process(bat_priv, true, orig_node, NULL, NULL, (unsigned char *)(ogm2 + 1), ntohs(ogm2->tvlv_len)); + }
/* if the metric update went through, update routes if needed */ forward = batadv_v_ogm_route_update(bat_priv, ethhdr, ogm2, orig_node, @@ -728,7 +735,7 @@ static void batadv_v_ogm_process(const struct sk_buff *skb, int ogm_offset, path_throughput = min_t(u32, link_throughput, ogm_throughput); ogm_packet->throughput = htonl(path_throughput);
- batadv_v_ogm_process_per_outif(bat_priv, ethhdr, ogm_packet, orig_node, + batadv_v_ogm_process_per_outif(bat_priv, skb, ogm_packet, orig_node, neigh_node, if_incoming, BATADV_IF_DEFAULT);
@@ -772,7 +779,7 @@ static void batadv_v_ogm_process(const struct sk_buff *skb, int ogm_offset, continue; }
- batadv_v_ogm_process_per_outif(bat_priv, ethhdr, ogm_packet, + batadv_v_ogm_process_per_outif(bat_priv, skb, ogm_packet, orig_node, neigh_node, if_incoming, hard_iface);
diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index e257efd..4fd912c 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -734,7 +734,7 @@ static void batadv_dat_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv, u8 flags, void *tvlv_value, u16 tvlv_value_len) { - if (flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) + if (flags & BATADV_TVLV_HANDLER_CIFNOTFND) clear_bit(BATADV_ORIG_CAPA_HAS_DAT, &orig->capabilities); else set_bit(BATADV_ORIG_CAPA_HAS_DAT, &orig->capabilities); @@ -776,7 +776,7 @@ int batadv_dat_init(struct batadv_priv *bat_priv)
batadv_tvlv_handler_register(bat_priv, batadv_dat_tvlv_ogm_handler_v1, NULL, BATADV_TVLV_DAT, 1, - BATADV_TVLV_HANDLER_OGM_CIFNOTFND); + BATADV_TVLV_HANDLER_CIFNOTFND); batadv_dat_tvlv_container_update(bat_priv); return 0; } diff --git a/net/batman-adv/gateway_common.c b/net/batman-adv/gateway_common.c index 2118481..6f7cb51 100644 --- a/net/batman-adv/gateway_common.c +++ b/net/batman-adv/gateway_common.c @@ -207,25 +207,26 @@ ssize_t batadv_gw_bandwidth_set(struct net_device *net_dev, char *buff, }
/** - * batadv_gw_tvlv_ogm_handler_v1 - process incoming gateway tvlv container + * batadv_gw_tvlv_ogm_handler - process incoming gateway tvlv container * @bat_priv: the bat priv with all the soft interface information - * @orig: the orig_node of the ogm - * @flags: flags indicating the tvlv state (see batadv_tvlv_handler_flags) * @tvlv_value: tvlv buffer containing the gateway data * @tvlv_value_len: tvlv buffer length + * @ctx: handler specific context information (here: orig_node) + * + * Return: Always NET_RX_SUCCESS. */ -static void batadv_gw_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv, - struct batadv_orig_node *orig, - u8 flags, - void *tvlv_value, u16 tvlv_value_len) +static int batadv_gw_tvlv_ogm_handler(struct batadv_priv *bat_priv, + void *tvlv_value, + u16 tvlv_value_len, + void *ctx) { + struct batadv_orig_node *orig_node = batadv_tvlv_ogm_unpack_ctx(ctx); struct batadv_tvlv_gateway_data gateway, *gateway_ptr;
- /* only fetch the tvlv value if the handler wasn't called via the - * CIFNOTFND flag and if there is data to fetch + /* might either be too small due to a broken packet, + * or zero because no matching TVLV was found in the provided OGM */ - if ((flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) || - (tvlv_value_len < sizeof(gateway))) { + if (tvlv_value_len < sizeof(gateway)) { gateway.bandwidth_down = 0; gateway.bandwidth_up = 0; } else { @@ -239,12 +240,14 @@ static void batadv_gw_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv, } }
- batadv_gw_node_update(bat_priv, orig, &gateway); + batadv_gw_node_update(bat_priv, orig_node, &gateway);
/* restart gateway selection */ if ((gateway.bandwidth_down != 0) && (atomic_read(&bat_priv->gw.mode) == BATADV_GW_MODE_CLIENT)) - batadv_gw_check_election(bat_priv, orig); + batadv_gw_check_election(bat_priv, orig_node); + + return NET_RX_SUCCESS; }
/** @@ -253,9 +256,12 @@ static void batadv_gw_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv, */ void batadv_gw_init(struct batadv_priv *bat_priv) { - batadv_tvlv_handler_register(bat_priv, batadv_gw_tvlv_ogm_handler_v1, - NULL, BATADV_TVLV_GW, 1, - BATADV_TVLV_HANDLER_OGM_CIFNOTFND); + batadv_tvlv_handler_register2(bat_priv, batadv_gw_tvlv_ogm_handler, + BATADV_IV_OGM, BATADV_TVLV_GW, 1, + BATADV_TVLV_HANDLER_CIFNOTFND); + batadv_tvlv_handler_register2(bat_priv, batadv_gw_tvlv_ogm_handler, + BATADV_OGM2, BATADV_TVLV_GW, 1, + BATADV_TVLV_HANDLER_CIFNOTFND); }
/** @@ -265,5 +271,9 @@ void batadv_gw_init(struct batadv_priv *bat_priv) void batadv_gw_free(struct batadv_priv *bat_priv) { batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_GW, 1); - batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_GW, 1); + + batadv_tvlv_handler_unregister2(bat_priv, BATADV_IV_OGM, BATADV_TVLV_GW, + 1); + batadv_tvlv_handler_unregister2(bat_priv, BATADV_OGM2, BATADV_TVLV_GW, + 1); } diff --git a/net/batman-adv/multicast.c b/net/batman-adv/multicast.c index 13661f4..9a962eb 100644 --- a/net/batman-adv/multicast.c +++ b/net/batman-adv/multicast.c @@ -1070,23 +1070,23 @@ static void batadv_mcast_want_ipv6_update(struct batadv_priv *bat_priv, /** * batadv_mcast_tvlv_ogm_handler - process incoming multicast tvlv container * @bat_priv: the bat priv with all the soft interface information - * @orig: the orig_node of the ogm - * @flags: flags indicating the tvlv state (see batadv_tvlv_handler_flags) * @tvlv_value: tvlv buffer containing the multicast data * @tvlv_value_len: tvlv buffer length + * @ctx: handler specific context information (here: orig_node) + * + * Return: Always NET_RX_SUCCESS. */ -static void batadv_mcast_tvlv_ogm_handler(struct batadv_priv *bat_priv, - struct batadv_orig_node *orig, - u8 flags, - void *tvlv_value, - u16 tvlv_value_len) +static int batadv_mcast_tvlv_ogm_handler(struct batadv_priv *bat_priv, + void *tvlv_value, + u16 tvlv_value_len, + void *ctx) { - bool orig_mcast_enabled = !(flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND); + struct batadv_orig_node *orig = batadv_tvlv_ogm_unpack_ctx(ctx); + bool orig_mcast_enabled = !!tvlv_value; u8 mcast_flags = BATADV_NO_FLAGS; bool orig_initialized;
- if (orig_mcast_enabled && tvlv_value && - (tvlv_value_len >= sizeof(mcast_flags))) + if (orig_mcast_enabled && (tvlv_value_len >= sizeof(mcast_flags))) mcast_flags = *(u8 *)tvlv_value;
spin_lock_bh(&orig->mcast_handler_lock); @@ -1121,6 +1121,8 @@ static void batadv_mcast_tvlv_ogm_handler(struct batadv_priv *bat_priv,
orig->mcast_flags = mcast_flags; spin_unlock_bh(&orig->mcast_handler_lock); + + return NET_RX_SUCCESS; }
/** @@ -1129,9 +1131,12 @@ static void batadv_mcast_tvlv_ogm_handler(struct batadv_priv *bat_priv, */ void batadv_mcast_init(struct batadv_priv *bat_priv) { - batadv_tvlv_handler_register(bat_priv, batadv_mcast_tvlv_ogm_handler, - NULL, BATADV_TVLV_MCAST, 2, - BATADV_TVLV_HANDLER_OGM_CIFNOTFND); + batadv_tvlv_handler_register2(bat_priv, batadv_mcast_tvlv_ogm_handler, + BATADV_IV_OGM, BATADV_TVLV_MCAST, 2, + BATADV_TVLV_HANDLER_CIFNOTFND); + batadv_tvlv_handler_register2(bat_priv, batadv_mcast_tvlv_ogm_handler, + BATADV_OGM2, BATADV_TVLV_MCAST, 2, + BATADV_TVLV_HANDLER_CIFNOTFND); }
#ifdef CONFIG_BATMAN_ADV_DEBUGFS @@ -1244,7 +1249,11 @@ int batadv_mcast_flags_seq_print_text(struct seq_file *seq, void *offset) void batadv_mcast_free(struct batadv_priv *bat_priv) { batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_MCAST, 2); - batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_MCAST, 2); + + batadv_tvlv_handler_unregister2(bat_priv, BATADV_IV_OGM, + BATADV_TVLV_MCAST, 2); + batadv_tvlv_handler_unregister2(bat_priv, BATADV_OGM2, + BATADV_TVLV_MCAST, 2);
spin_lock_bh(&bat_priv->tt.commit_lock); batadv_mcast_mla_tt_retract(bat_priv, NULL); diff --git a/net/batman-adv/network-coding.c b/net/batman-adv/network-coding.c index e3baf69..2fe44d8 100644 --- a/net/batman-adv/network-coding.c +++ b/net/batman-adv/network-coding.c @@ -138,7 +138,7 @@ static void batadv_nc_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv, u8 flags, void *tvlv_value, u16 tvlv_value_len) { - if (flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) + if (flags & BATADV_TVLV_HANDLER_CIFNOTFND) clear_bit(BATADV_ORIG_CAPA_HAS_NC, &orig->capabilities); else set_bit(BATADV_ORIG_CAPA_HAS_NC, &orig->capabilities); @@ -177,7 +177,7 @@ int batadv_nc_mesh_init(struct batadv_priv *bat_priv)
batadv_tvlv_handler_register(bat_priv, batadv_nc_tvlv_ogm_handler_v1, NULL, BATADV_TVLV_NC, 1, - BATADV_TVLV_HANDLER_OGM_CIFNOTFND); + BATADV_TVLV_HANDLER_CIFNOTFND); batadv_nc_tvlv_container_update(bat_priv); return 0;
diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c index 77654f0..b4a0442 100644 --- a/net/batman-adv/tvlv.c +++ b/net/batman-adv/tvlv.c @@ -68,23 +68,28 @@ static void batadv_tvlv_handler_put(struct batadv_tvlv_handler *tvlv_handler) * batadv_tvlv_handler_get - retrieve tvlv handler from the tvlv handler list * based on the provided type and version (both need to match) * @bat_priv: the bat priv with all the soft interface information - * @type: tvlv handler type to look for - * @version: tvlv handler version to look for + * @packet_type: packet type to look for + * @tvlv_type: tvlv handler type to look for + * @tvlv_version: tvlv handler version to look for * * Return: tvlv handler if found or NULL otherwise. */ static struct batadv_tvlv_handler * -batadv_tvlv_handler_get(struct batadv_priv *bat_priv, u8 type, u8 version) +batadv_tvlv_handler_get(struct batadv_priv *bat_priv, int packet_type, + u8 tvlv_type, u8 tvlv_version) { struct batadv_tvlv_handler *tvlv_handler_tmp, *tvlv_handler = NULL;
rcu_read_lock(); hlist_for_each_entry_rcu(tvlv_handler_tmp, &bat_priv->tvlv.handler_list, list) { - if (tvlv_handler_tmp->type != type) + if (tvlv_handler_tmp->packet_type != packet_type) continue;
- if (tvlv_handler_tmp->version != version) + if (tvlv_handler_tmp->tvlv_type != tvlv_type) + continue; + + if (tvlv_handler_tmp->tvlv_version != tvlv_version) continue;
if (!kref_get_unless_zero(&tvlv_handler_tmp->refcount)) @@ -387,7 +392,7 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv, tvlv_handler->ogm_handler(bat_priv, orig_node, BATADV_NO_FLAGS, tvlv_value, tvlv_value_len); - tvlv_handler->flags |= BATADV_TVLV_HANDLER_OGM_CALLED; + tvlv_handler->flags |= BATADV_TVLV_HANDLER_CALLED; } else { if (!src) return NET_RX_SUCCESS; @@ -407,6 +412,139 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv, }
/** + * batadv_tvlv_call_handler2 - call the appropriate tvlv handler + * @bat_priv: the bat priv with all the soft interface information + * @packet_type: packet type to look and call for + * @tvlv_type: tvlv handler type to look and call for + * @tvlv_version: tvlv handler version to look and call for + * @tvlv_value: tvlv content + * @tvlv_value_len: tvlv content length + * @ctx: handler specific context information + * + * Return: NET_RX_SUCCESS if handler was found and called successfully, + * NET_RX_DROP otherwise. + */ +static int batadv_tvlv_call_handler2(struct batadv_priv *bat_priv, + u8 packet_type, u8 tvlv_type, + u8 tvlv_version, void *tvlv_value, + u16 tvlv_value_len, void *ctx) +{ + struct batadv_tvlv_handler *tvlv_handler; + int ret; + + tvlv_handler = batadv_tvlv_handler_get(bat_priv, packet_type, tvlv_type, + tvlv_version); + if (!tvlv_handler) + return NET_RX_DROP; + + ret = tvlv_handler->handler(bat_priv, tvlv_value, tvlv_value_len, ctx); + tvlv_handler->flags |= BATADV_TVLV_HANDLER_CALLED; + + batadv_tvlv_handler_put(tvlv_handler); + + return ret; +} + +/** + * batadv_tvlv_call_unfound_handlers - call any handler not called yet + * @bat_priv: the bat priv with all the soft interface information + * @packet_type: the packet type to call handlers of unfound TVLVs for + * @ctx: handler specific context information + * + * For any registered TVLV handler with a CIFNOTFND flag: If a matching + * tvlv type was not found in a specific packet (type) then this calls the + * according handler with an empty (NULL) tvlv_value and tvlv_value_len of + * zero now. + */ +static void batadv_tvlv_call_unfound_handlers(struct batadv_priv *bat_priv, + int packet_type, void *ctx) +{ + struct batadv_tvlv_handler *tvlv_handler; + + rcu_read_lock(); + hlist_for_each_entry_rcu(tvlv_handler, + &bat_priv->tvlv.handler_list, list) { + if (tvlv_handler->packet_type != packet_type) + continue; + + if ((tvlv_handler->flags & BATADV_TVLV_HANDLER_CIFNOTFND) && + !(tvlv_handler->flags & BATADV_TVLV_HANDLER_CALLED)) + tvlv_handler->handler(bat_priv, NULL, 0, ctx); + + tvlv_handler->flags &= ~BATADV_TVLV_HANDLER_CALLED; + } + rcu_read_unlock(); +} + +/** + * batadv_tvlv_containers_process2 - parse and process TVLV content of a packet + * @bat_priv: the bat priv with all the soft interface information + * @skb: the packet to parse and process TVLV data from + * @packet_type: the packet type to call handlers for + * @tvlv_offset: offset from the skb data pointer to the first tvlv header + * @tvlv_value_len: total tvlv content length (sum of all tvlv headers+values) + * @ctx: handler specific context information + * + * This function parses TVLV options of the given skb and tries to call the + * appropriate, registered handlers. + * + * In the end, all not yet called handlers (because no appropriate TVLV was + * found in the packet) which were registered with a CIFNOTFND flag are + * called with empty tvlv_value pointers. + * + * Return: NET_RX_SUCCESS if all TVLVs were known and parsed, as well as + * any TVLV handler called successfully. Returns NET_RX_DROP otherwise. + */ +int batadv_tvlv_containers_process2(struct batadv_priv *bat_priv, + const struct sk_buff *skb, u8 packet_type, + unsigned int tvlv_offset, + u16 tvlv_value_len, void *ctx) +{ + struct batadv_tvlv_hdr *tvlv_hdr, tvlv_hdr_buff; + u8 *tvlv_value, tvlv_value_buff[128]; + u16 tvlv_value_cont_len; + int ret = NET_RX_SUCCESS; + + while (tvlv_value_len >= sizeof(*tvlv_hdr)) { + tvlv_hdr = skb_header_pointer(skb, tvlv_offset, + sizeof(tvlv_hdr_buff), + &tvlv_hdr_buff); + if (!tvlv_hdr) + return NET_RX_DROP; + + tvlv_value_cont_len = ntohs(tvlv_hdr->len); + tvlv_offset += sizeof(*tvlv_hdr); + tvlv_value_len -= sizeof(*tvlv_hdr); + + if (tvlv_value_cont_len > sizeof(tvlv_value_buff)) { + pr_warn_once("batman-adv: TVLVs greater than 128 bytes unsupported for now, ignoring\n"); + goto skip_handler_call; + } + + if (tvlv_value_cont_len > tvlv_value_len) + return NET_RX_DROP; + + tvlv_value = skb_header_pointer(skb, tvlv_offset, + tvlv_value_cont_len, + tvlv_value_buff); + if (!tvlv_value) + return NET_RX_DROP; + + ret |= batadv_tvlv_call_handler2(bat_priv, packet_type, + tvlv_hdr->type, + tvlv_hdr->version, tvlv_value, + tvlv_value_cont_len, ctx); +skip_handler_call: + tvlv_offset += tvlv_value_cont_len; + tvlv_value_len -= tvlv_value_cont_len; + } + + batadv_tvlv_call_unfound_handlers(bat_priv, packet_type, ctx); + + return ret; +} + +/** * batadv_tvlv_containers_process - parse the given tvlv buffer to call the * appropriate handlers * @bat_priv: the bat priv with all the soft interface information @@ -429,7 +567,7 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, struct batadv_tvlv_handler *tvlv_handler; struct batadv_tvlv_hdr *tvlv_hdr; u16 tvlv_value_cont_len; - u8 cifnotfound = BATADV_TVLV_HANDLER_OGM_CIFNOTFND; + u8 cifnotfound = BATADV_TVLV_HANDLER_CIFNOTFND; int ret = NET_RX_SUCCESS;
while (tvlv_value_len >= sizeof(*tvlv_hdr)) { @@ -441,7 +579,7 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, if (tvlv_value_cont_len > tvlv_value_len) break;
- tvlv_handler = batadv_tvlv_handler_get(bat_priv, + tvlv_handler = batadv_tvlv_handler_get(bat_priv, -1, tvlv_hdr->type, tvlv_hdr->version);
@@ -461,12 +599,15 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, rcu_read_lock(); hlist_for_each_entry_rcu(tvlv_handler, &bat_priv->tvlv.handler_list, list) { - if ((tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) && - !(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CALLED)) + if (tvlv_handler->packet_type != -1) + continue; + + if ((tvlv_handler->flags & BATADV_TVLV_HANDLER_CIFNOTFND) && + !(tvlv_handler->flags & BATADV_TVLV_HANDLER_CALLED)) tvlv_handler->ogm_handler(bat_priv, orig_node, cifnotfound, NULL, 0);
- tvlv_handler->flags &= ~BATADV_TVLV_HANDLER_OGM_CALLED; + tvlv_handler->flags &= ~BATADV_TVLV_HANDLER_CALLED; } rcu_read_unlock();
@@ -474,30 +615,128 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, }
/** + * batadv_tvlv_ogm_pack_ctx - pack context to be passed to OGM TVLV handlers + * @orig_node: An orig_node to pack (mandatory, may *not* be NULL!) + * + * This packs the context, here the orig_node the packet came from, so that + * it is later available to the to be called OGM TVLV handlers. + * + * Return: The wrapped context. + */ +void *batadv_tvlv_ogm_pack_ctx(struct batadv_orig_node *orig_node) +{ + WARN_ON(!orig_node); + return (void *)orig_node; +} + +/** + * batadv_tvlv_ogm_unpack_ctx - unpack context received with a TVLV handler call + * @orig_node: An orig_node to pack + * + * This unpacks the context received within an OGM TVLV handler, here the + * orig_node the packet came from. + * + * Return: The orig_node the packet came from. + */ +struct batadv_orig_node *batadv_tvlv_ogm_unpack_ctx(void *ctx) +{ + return (struct batadv_orig_node *)ctx; +} + +/** * batadv_tvlv_ogm_receive - process an incoming ogm and call the appropriate * handlers * @bat_priv: the bat priv with all the soft interface information - * @batadv_ogm_packet: ogm packet containing the tvlv containers + * @skb: ogm packet containing the tvlv containers * @orig_node: orig node emitting the ogm packet + * + * Caller needs to ensure that the skb network header points to the appropriate + * OGM header. */ void batadv_tvlv_ogm_receive(struct batadv_priv *bat_priv, - struct batadv_ogm_packet *batadv_ogm_packet, + const struct sk_buff *skb, struct batadv_orig_node *orig_node) { + void *ctx = batadv_tvlv_ogm_pack_ctx(orig_node); + struct batadv_ogm_packet *ogm_packet; + unsigned int tvlv_offset; void *tvlv_value; u16 tvlv_value_len;
- if (!batadv_ogm_packet) + ogm_packet = (struct batadv_ogm_packet *)skb_network_header(skb); + if (!ogm_packet) return;
- tvlv_value_len = ntohs(batadv_ogm_packet->tvlv_len); + tvlv_value_len = ntohs(ogm_packet->tvlv_len); if (!tvlv_value_len) return;
- tvlv_value = batadv_ogm_packet + 1; + tvlv_offset = skb_network_offset(skb) + sizeof(*ogm_packet); + tvlv_value = ogm_packet + 1;
batadv_tvlv_containers_process(bat_priv, true, orig_node, NULL, NULL, tvlv_value, tvlv_value_len); + batadv_tvlv_containers_process2(bat_priv, skb, BATADV_IV_OGM, + tvlv_offset, tvlv_value_len, ctx); +} + +/** + * batadv_tvlv_handler_register - register a tvlv handler + * @bat_priv: the bat priv with all the soft interface information + * @handler: TVLV handler callback function + * @packet_type: packet type to register this handler for + * @tvlv_type: tvlv handler type to be registered + * @tvlv_version: tvlv handler version to be registered + * @flags: flags to enable or disable TVLV API behavior + * + * Registers a handler for incoming packets of the provided packet type. + * When a packet of this type with a matching TVLV (both tvlv type and version) + * is received then the registered handler is called with the according TVLV + * value, length and packet context. + * + * If 'flags' is set to BATADV_TVLV_HANDLER_CIFNOTFND: + * Then the handler might be called with an empty tvlv_value (NULL) and + * tvlv_value_len (zero) if a packet with a matching packet type but no + * matching TVLV was received. + */ +void batadv_tvlv_handler_register2(struct batadv_priv *bat_priv, + int (*handler)(struct batadv_priv *bat_priv, + void *tvlv_value, + u16 tvlv_value_len, + void *ctx), + u8 packet_type, u8 tvlv_type, + u8 tvlv_version, u8 flags) +{ + struct batadv_tvlv_handler *tvlv_handler; + + tvlv_handler = batadv_tvlv_handler_get(bat_priv, packet_type, tvlv_type, + tvlv_version); + if (tvlv_handler) { + batadv_tvlv_handler_put(tvlv_handler); + return; + } + + tvlv_handler = kzalloc(sizeof(*tvlv_handler), GFP_ATOMIC); + if (!tvlv_handler) + return; + + tvlv_handler->ogm_handler = NULL; + tvlv_handler->unicast_handler = NULL; + tvlv_handler->handler = handler; + tvlv_handler->packet_type = packet_type; + tvlv_handler->tvlv_type = tvlv_type; + tvlv_handler->tvlv_version = tvlv_version; + tvlv_handler->flags = flags; + kref_init(&tvlv_handler->refcount); + INIT_HLIST_NODE(&tvlv_handler->list); + + spin_lock_bh(&bat_priv->tvlv.handler_list_lock); + kref_get(&tvlv_handler->refcount); + hlist_add_head_rcu(&tvlv_handler->list, &bat_priv->tvlv.handler_list); + spin_unlock_bh(&bat_priv->tvlv.handler_list_lock); + + /* don't return reference to new tvlv_handler */ + batadv_tvlv_handler_put(tvlv_handler); }
/** @@ -510,8 +749,8 @@ void batadv_tvlv_ogm_receive(struct batadv_priv *bat_priv, * @uptr: unicast tvlv handler callback function. This function receives the * source & destination of the unicast packet as well as the tvlv content * to process. - * @type: tvlv handler type to be registered - * @version: tvlv handler version to be registered + * @tvlv_type: tvlv handler type to be registered + * @tvlv_version: tvlv handler version to be registered * @flags: flags to enable or disable TVLV API behavior */ void batadv_tvlv_handler_register(struct batadv_priv *bat_priv, @@ -524,11 +763,13 @@ void batadv_tvlv_handler_register(struct batadv_priv *bat_priv, u8 *src, u8 *dst, void *tvlv_value, u16 tvlv_value_len), - u8 type, u8 version, u8 flags) + u8 tvlv_type, u8 tvlv_version, + u8 flags) { struct batadv_tvlv_handler *tvlv_handler;
- tvlv_handler = batadv_tvlv_handler_get(bat_priv, type, version); + tvlv_handler = batadv_tvlv_handler_get(bat_priv, -1, tvlv_type, + tvlv_version); if (tvlv_handler) { batadv_tvlv_handler_put(tvlv_handler); return; @@ -540,8 +781,10 @@ void batadv_tvlv_handler_register(struct batadv_priv *bat_priv,
tvlv_handler->ogm_handler = optr; tvlv_handler->unicast_handler = uptr; - tvlv_handler->type = type; - tvlv_handler->version = version; + tvlv_handler->handler = NULL; + tvlv_handler->packet_type = -1; + tvlv_handler->tvlv_type = tvlv_type; + tvlv_handler->tvlv_version = tvlv_version; tvlv_handler->flags = flags; kref_init(&tvlv_handler->refcount); INIT_HLIST_NODE(&tvlv_handler->list); @@ -556,18 +799,47 @@ void batadv_tvlv_handler_register(struct batadv_priv *bat_priv, }
/** + * batadv_tvlv_handler_unregister2 - unregister a tvlv handler + * @bat_priv: the bat priv with all the soft interface information + * @tvlv_type: packet type to unregistered for + * @tvlv_type: tvlv handler type to be unregistered + * @tvlv_version: tvlv handler version to be unregistered + * + * Unregisters a TVLV handler based on the provided packet type, tvlv type + * and version (all need to match). + */ +void batadv_tvlv_handler_unregister2(struct batadv_priv *bat_priv, + u8 packet_type, u8 tvlv_type, + u8 tvlv_version) +{ + struct batadv_tvlv_handler *tvlv_handler; + + tvlv_handler = batadv_tvlv_handler_get(bat_priv, packet_type, tvlv_type, + tvlv_version); + if (!tvlv_handler) + return; + + batadv_tvlv_handler_put(tvlv_handler); + spin_lock_bh(&bat_priv->tvlv.handler_list_lock); + hlist_del_rcu(&tvlv_handler->list); + spin_unlock_bh(&bat_priv->tvlv.handler_list_lock); + batadv_tvlv_handler_put(tvlv_handler); +} + +/** * batadv_tvlv_handler_unregister - unregister tvlv handler based on the * provided type and version (both need to match) * @bat_priv: the bat priv with all the soft interface information - * @type: tvlv handler type to be unregistered - * @version: tvlv handler version to be unregistered + * @tvlv_type: tvlv handler type to be unregistered + * @tvlv_version: tvlv handler version to be unregistered */ void batadv_tvlv_handler_unregister(struct batadv_priv *bat_priv, - u8 type, u8 version) + u8 tvlv_type, u8 tvlv_version) { struct batadv_tvlv_handler *tvlv_handler;
- tvlv_handler = batadv_tvlv_handler_get(bat_priv, type, version); + tvlv_handler = batadv_tvlv_handler_get(bat_priv, -1, tvlv_type, + tvlv_version); if (!tvlv_handler) return;
diff --git a/net/batman-adv/tvlv.h b/net/batman-adv/tvlv.h index e4369b5..b3d604f 100644 --- a/net/batman-adv/tvlv.h +++ b/net/batman-adv/tvlv.h @@ -30,12 +30,21 @@ void batadv_tvlv_container_register(struct batadv_priv *bat_priv, u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv, unsigned char **packet_buff, int *packet_buff_len, int packet_min_len); +void *batadv_tvlv_ogm_pack_ctx(struct batadv_orig_node *orig_node); +struct batadv_orig_node *batadv_tvlv_ogm_unpack_ctx(void *ctx); void batadv_tvlv_ogm_receive(struct batadv_priv *bat_priv, - struct batadv_ogm_packet *batadv_ogm_packet, + const struct sk_buff *skb, struct batadv_orig_node *orig_node); void batadv_tvlv_container_unregister(struct batadv_priv *bat_priv, u8 type, u8 version);
+void batadv_tvlv_handler_register2(struct batadv_priv *bat_priv, + int (*handler)(struct batadv_priv *bat_priv, + void *tvlv_value, + u16 tvlv_value_len, + void *ctx), + u8 packet_type, u8 tvlv_type, + u8 tvlv_version, u8 flags); void batadv_tvlv_handler_register(struct batadv_priv *bat_priv, void (*optr)(struct batadv_priv *bat_priv, struct batadv_orig_node *orig, @@ -46,14 +55,21 @@ void batadv_tvlv_handler_register(struct batadv_priv *bat_priv, u8 *src, u8 *dst, void *tvlv_value, u16 tvlv_value_len), - u8 type, u8 version, u8 flags); + u8 tvlv_type, u8 tvlv_version, u8 flags); +void batadv_tvlv_handler_unregister2(struct batadv_priv *bat_priv, + u8 packet_type, u8 tvlv_type, + u8 tvlv_version); void batadv_tvlv_handler_unregister(struct batadv_priv *bat_priv, - u8 type, u8 version); + u8 tvlv_type, u8 tvlv_version); int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, bool ogm_source, struct batadv_orig_node *orig_node, u8 *src, u8 *dst, void *tvlv_buff, u16 tvlv_buff_len); +int batadv_tvlv_containers_process2(struct batadv_priv *bat_priv, + const struct sk_buff *skb, u8 packet_type, + unsigned int tvlv_offset, + u16 tvlv_value_len, void *ctx); void batadv_tvlv_unicast_send(struct batadv_priv *bat_priv, u8 *src, u8 *dst, u8 type, u8 version, void *tvlv_value, u16 tvlv_value_len); diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 731bdf5..6a71522 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -1584,8 +1584,11 @@ struct batadv_tvlv_container { * incoming OGM packets * @unicast_handler: handler callback which is given the tvlv payload to process * on incoming unicast tvlv packets - * @type: tvlv type this handler feels responsible for - * @version: tvlv version this handler feels responsible for + * @handler: handler callback which is given the tvlv payload to process on + * incoming packets of the given packet type + * @packet_type: packet type this handler feels responsible for + * @tvlv_type: tvlv type this handler feels responsible for + * @tvlv_version: tvlv version this handler feels responsible for * @flags: tvlv handler flags * @refcount: number of contexts the object is used * @rcu: struct used for freeing in an RCU-safe manner @@ -1598,8 +1601,11 @@ struct batadv_tvlv_handler { int (*unicast_handler)(struct batadv_priv *bat_priv, u8 *src, u8 *dst, void *tvlv_value, u16 tvlv_value_len); - u8 type; - u8 version; + int (*handler)(struct batadv_priv *bat_priv, void *tvlv_value, + u16 tvlv_value_len, void *ctx); + int packet_type; + u8 tvlv_type; + u8 tvlv_version; u8 flags; struct kref refcount; struct rcu_head rcu; @@ -1607,15 +1613,15 @@ struct batadv_tvlv_handler {
/** * enum batadv_tvlv_handler_flags - tvlv handler flags definitions - * @BATADV_TVLV_HANDLER_OGM_CIFNOTFND: tvlv ogm processing function will call + * @BATADV_TVLV_HANDLER_CIFNOTFND: tvlv processing function will call * this handler even if its type was not found (with no data) - * @BATADV_TVLV_HANDLER_OGM_CALLED: interval tvlv handling flag - the API marks + * @BATADV_TVLV_HANDLER_CALLED: internal tvlv handling flag - the API marks * a handler as being called, so it won't be called if the - * BATADV_TVLV_HANDLER_OGM_CIFNOTFND flag was set + * BATADV_TVLV_HANDLER_CIFNOTFND flag was set */ enum batadv_tvlv_handler_flags { - BATADV_TVLV_HANDLER_OGM_CIFNOTFND = BIT(1), - BATADV_TVLV_HANDLER_OGM_CALLED = BIT(2), + BATADV_TVLV_HANDLER_CIFNOTFND = BIT(1), + BATADV_TVLV_HANDLER_CALLED = BIT(2), };
/**
On Dienstag, 20. September 2016 14:12:44 CEST Linus Lüssing wrote: [...]
+/**
- batadv_tvlv_ogm_unpack_ctx - unpack context received with a TVLV handler call
- @orig_node: An orig_node to pack
- This unpacks the context received within an OGM TVLV handler, here the
- orig_node the packet came from.
- Return: The orig_node the packet came from.
- */
+struct batadv_orig_node *batadv_tvlv_ogm_unpack_ctx(void *ctx) +{
- return (struct batadv_orig_node *)ctx;
+}
The function doesn't have a parameter orig_node but it is documented in the kernel-doc block. Instead the ctx should be documented.
[...]
/**
- batadv_tvlv_handler_unregister2 - unregister a tvlv handler
- @bat_priv: the bat priv with all the soft interface information
- @tvlv_type: packet type to unregistered for
- @tvlv_type: tvlv handler type to be unregistered
tvlv_type is documented twice - and differently.
Kind regards, Sven
On Dienstag, 20. September 2016 14:12:44 CEST Linus Lüssing wrote: [...]
diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c index 77654f0..b4a0442 100644 --- a/net/batman-adv/tvlv.c +++ b/net/batman-adv/tvlv.c
[...]
if (tvlv_value_cont_len > sizeof(tvlv_value_buff)) {
pr_warn_once("batman-adv: TVLVs greater than 128 bytes unsupported for now, ignoring\n");
goto skip_handler_call;
}
Please add the include of linux/printk.h for pr_warn_once.
[...]
- WARN_ON(!orig_node);
Please add the include of linux/bug.h for pr_warn_once.
diff --git a/net/batman-adv/tvlv.h b/net/batman-adv/tvlv.h index e4369b5..b3d604f 100644 --- a/net/batman-adv/tvlv.h +++ b/net/batman-adv/tvlv.h @@ -30,12 +30,21 @@ void batadv_tvlv_container_register(struct batadv_priv *bat_priv, u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv, unsigned char **packet_buff, int *packet_buff_len, int packet_min_len); +void *batadv_tvlv_ogm_pack_ctx(struct batadv_orig_node *orig_node); +struct batadv_orig_node *batadv_tvlv_ogm_unpack_ctx(void *ctx); void batadv_tvlv_ogm_receive(struct batadv_priv *bat_priv,
struct batadv_ogm_packet *batadv_ogm_packet,
const struct sk_buff *skb, struct batadv_orig_node *orig_node);
The forward declaration for "struct batadv_ogm_packet;" should now be replaced with "struct sk_buff;"
Thanks, Sven
This patch aims to reduce protocol overhead for BATMAN V by avoiding rebroadcasts of OGM2 and broadcast packets in neighborhoods where every neighbor "sees" another.
This is done by parsing and storing the new information provided by the ELP specific neighborhood hash TVLV.
We then use this new information to detect certain scenarios where a rebroadcast of OGM2 and broadcast packets on the incoming interface is unncessary.
In a nutshell the detection checks whether all neighbors see each other by comparing their with a neighbors hash. If so, then a node deciding to rebroadcast will check whether either the ingress side or the egress side might be a bottleneck metric wise. In other words, whether a rebroadcast might not enhance any throughput.
Signed-off-by: Linus Lüssing linus.luessing@c0d3.blue --- net/batman-adv/bat_v.c | 40 ++++- net/batman-adv/bat_v_elp.c | 315 +++++++++++++++++++++++++++++++++++++++- net/batman-adv/bat_v_elp.h | 10 ++ net/batman-adv/bat_v_ogm.c | 19 ++- net/batman-adv/bat_v_ogm.h | 4 + net/batman-adv/hard-interface.c | 47 +++--- net/batman-adv/hard-interface.h | 6 +- net/batman-adv/send.c | 8 +- net/batman-adv/types.h | 17 +++ 9 files changed, 431 insertions(+), 35 deletions(-)
diff --git a/net/batman-adv/bat_v.c b/net/batman-adv/bat_v.c index 762b34c..75a4147 100644 --- a/net/batman-adv/bat_v.c +++ b/net/batman-adv/bat_v.c @@ -125,6 +125,9 @@ static void batadv_v_hardif_neigh_init(struct batadv_hardif_neigh_node *hardif_neigh) { ewma_throughput_init(&hardif_neigh->bat_v.throughput); + hardif_neigh->bat_v.min_throughput = 0; + memset(hardif_neigh->bat_v.neigh_hash, 0, + sizeof(hardif_neigh->bat_v.neigh_hash)); INIT_WORK(&hardif_neigh->bat_v.metric_work, batadv_v_elp_throughput_metric_update); } @@ -171,14 +174,33 @@ batadv_v_hardif_neigh_print(struct seq_file *seq, { int last_secs, last_msecs; u32 throughput; + char nhh_avoid_ogm = '.', nhh_avoid_bcast = '.'; + bool same_hash; + + same_hash = batadv_v_elp_nhh_cmp(hardif_neigh); + + if (same_hash) { + if (batadv_v_elp_rx_ingress_bad(hardif_neigh)) + nhh_avoid_ogm = 'A'; + + if (batadv_v_elp_rx_egress_bad(hardif_neigh)) + nhh_avoid_ogm = (nhh_avoid_ogm == '.') ? 'B' : 'C'; + + if (batadv_v_elp_tx_ingress_bad(hardif_neigh)) + nhh_avoid_bcast = 'X'; + + if (batadv_v_elp_tx_egress_bad(hardif_neigh)) + nhh_avoid_bcast = (nhh_avoid_bcast == '.') ? 'Y' : 'Z'; + }
last_secs = jiffies_to_msecs(jiffies - hardif_neigh->last_seen) / 1000; last_msecs = jiffies_to_msecs(jiffies - hardif_neigh->last_seen) % 1000; throughput = ewma_throughput_read(&hardif_neigh->bat_v.throughput);
- seq_printf(seq, "%pM %4i.%03is (%9u.%1u) [%10s]\n", + seq_printf(seq, "%pM %4i.%03is (%9u.%1u) [%10s] [%c%c%c]\n", hardif_neigh->addr, last_secs, last_msecs, throughput / 10, - throughput % 10, hardif_neigh->if_incoming->net_dev->name); + throughput % 10, hardif_neigh->if_incoming->net_dev->name, + same_hash ? 'H' : '.', nhh_avoid_ogm, nhh_avoid_bcast); }
/** @@ -195,7 +217,7 @@ static void batadv_v_neigh_print(struct batadv_priv *bat_priv, int batman_count = 0;
seq_puts(seq, - " Neighbor last-seen ( throughput) [ IF]\n"); + " Neighbor last-seen ( throughput) [ IF] NHH-flags\n");
rcu_read_lock(); list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { @@ -1038,6 +1060,7 @@ static struct batadv_algo_ops batadv_batman_v __read_mostly = { }, .neigh = { .hardif_init = batadv_v_hardif_neigh_init, + .hardif_no_broadcast = batadv_v_elp_no_broadcast, .cmp = batadv_v_neigh_cmp, .is_similar_or_better = batadv_v_neigh_is_sob, #ifdef CONFIG_BATMAN_ADV_DEBUGFS @@ -1080,6 +1103,9 @@ void batadv_v_hardif_init(struct batadv_hard_iface *hard_iface)
bat_v->min_throughput = 0; bat_v->max_throughput = (~(u32)0); + bat_v->min_throughput_other = 0; + bat_v->max_throughput_other = ~((u32)0); + memset(bat_v->neigh_hash, 0, sizeof(bat_v->neigh_hash)); }
@@ -1094,9 +1120,15 @@ int batadv_v_mesh_init(struct batadv_priv *bat_priv) { int ret = 0;
+ ret = batadv_v_elp_mesh_init(bat_priv); + if (ret < 0) + return ret; + ret = batadv_v_ogm_init(bat_priv); - if (ret < 0) + if (ret < 0) { + batadv_v_elp_mesh_free(bat_priv); return ret; + }
/* set default throughput difference threshold to 5Mbps */ atomic_set(&bat_priv->gw.sel_class, 50); diff --git a/net/batman-adv/bat_v_elp.c b/net/batman-adv/bat_v_elp.c index d4b5750..d386075 100644 --- a/net/batman-adv/bat_v_elp.c +++ b/net/batman-adv/bat_v_elp.c @@ -50,6 +50,7 @@ #include "packet.h" #include "routing.h" #include "send.h" +#include "tvlv.h"
static struct crypto_shash *tfm;
@@ -86,6 +87,7 @@ static void batadv_v_elp_update_neigh_hash(struct batadv_hard_iface *hard_iface) struct ewma_throughput *ewma_throughput; u8 *own_addr = hard_iface->net_dev->dev_addr; u32 min_throughput = ~((u32)0), max_throughput = 0; + u32 min_throughput_other = ~((u32)0), max_throughput_other = 0; u32 throughput; int ret;
@@ -127,6 +129,16 @@ static void batadv_v_elp_update_neigh_hash(struct batadv_hard_iface *hard_iface)
if (throughput > max_throughput) max_throughput = throughput; + + throughput = hardif_neigh->bat_v.min_throughput; + + if (throughput < min_throughput_other) + min_throughput_other = throughput; + + throughput = hardif_neigh->bat_v.max_throughput; + + if (throughput > max_throughput_other) + max_throughput_other = throughput; } rcu_read_unlock();
@@ -142,14 +154,18 @@ static void batadv_v_elp_update_neigh_hash(struct batadv_hard_iface *hard_iface)
hard_iface->bat_v.min_throughput = min_throughput; hard_iface->bat_v.max_throughput = max_throughput; + hard_iface->bat_v.min_throughput_other = min_throughput_other; + hard_iface->bat_v.max_throughput_other = max_throughput_other;
batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "Updated neighbor hash on interface %s: %*phN, min_through: %u kbit/s, max_through: %u kbit/s\n", + "Updated neighbor hash on interface %s: %*phN, min_through: %u kbit/s, max_through: %u kbit/s, min_through_other: %u kbit/s, max_through_other: %u kbit/s\n", hard_iface->net_dev->name, (int)sizeof(hard_iface->bat_v.neigh_hash), hard_iface->bat_v.neigh_hash, hard_iface->bat_v.min_throughput * 100, - hard_iface->bat_v.max_throughput * 100); + hard_iface->bat_v.max_throughput * 100, + hard_iface->bat_v.min_throughput_other * 100, + hard_iface->bat_v.max_throughput_other * 100);
return;
@@ -158,6 +174,8 @@ err: sizeof(hard_iface->bat_v.neigh_hash)); hard_iface->bat_v.min_throughput = 0; hard_iface->bat_v.max_throughput = ~((u32)0); + hard_iface->bat_v.min_throughput_other = 0; + hard_iface->bat_v.max_throughput_other = ~((u32)0);
pr_warn_once("An error occurred while calculating neighbor hash for %s\n", hard_iface->net_dev->name); @@ -571,6 +589,35 @@ void batadv_v_elp_primary_iface_set(struct batadv_hard_iface *primary_iface) }
/** + * batadv_v_elp_pack_ctx - pack context to be passed to an ELP TVLV handler + * @hardif_neigh: An hardif_neigh to pack (mandatory, may *not* be NULL!) + * + * This packs the context, here the hardif_neigh the packet came from, so that + * it is later available to the to be called ELP TVLV handler. + * + * Return: The wrapped context. + */ +static void * +batadv_v_elp_pack_ctx(struct batadv_hardif_neigh_node *hardif_neigh) +{ + return (void *)hardif_neigh; +} + +/** + * batadv_v_elp_unpack_ctx - unpack context received with a TVLV handler call + * @ctx: The context container to unpack + * + * This unpacks the context received within an ELP TVLV handler, here the + * hardif_neigh the packet came from. + * + * Return: The hardif_neigh the packet came from. + */ +static struct batadv_hardif_neigh_node *batadv_v_elp_unpack_ctx(void *ctx) +{ + return (struct batadv_hardif_neigh_node *)ctx; +} + +/** * batadv_v_elp_neigh_update - update an ELP neighbour node * @bat_priv: the bat priv with all the soft interface information * @neigh_addr: the neighbour interface address @@ -581,6 +628,7 @@ void batadv_v_elp_primary_iface_set(struct batadv_hard_iface *primary_iface) * ELP packet. */ static void batadv_v_elp_neigh_update(struct batadv_priv *bat_priv, + struct sk_buff *skb, u8 *neigh_addr, struct batadv_hard_iface *if_incoming, struct batadv_elp_packet *elp_packet) @@ -589,6 +637,8 @@ static void batadv_v_elp_neigh_update(struct batadv_priv *bat_priv, struct batadv_neigh_node *neigh; struct batadv_orig_node *orig_neigh; struct batadv_hardif_neigh_node *hardif_neigh; + unsigned int tvlv_offset = sizeof(*elp_packet); + void *ctx; s32 seqno_diff; s32 elp_latest_seqno;
@@ -605,6 +655,11 @@ static void batadv_v_elp_neigh_update(struct batadv_priv *bat_priv, if (!hardif_neigh) goto neigh_free;
+ ctx = batadv_v_elp_pack_ctx(hardif_neigh); + + batadv_tvlv_containers_process2(bat_priv, skb, BATADV_ELP, tvlv_offset, + ntohs(elp_packet->tvlv_len), ctx); + elp_latest_seqno = hardif_neigh->bat_v.elp_latest_seqno; seqno_diff = ntohl(elp_packet->seqno) - elp_latest_seqno;
@@ -671,7 +726,7 @@ int batadv_v_elp_packet_recv(struct sk_buff *skb, if (!primary_if) goto out;
- batadv_v_elp_neigh_update(bat_priv, ethhdr->h_source, if_incoming, + batadv_v_elp_neigh_update(bat_priv, skb, ethhdr->h_source, if_incoming, elp_packet);
out: @@ -682,6 +737,260 @@ out: }
/** + * batadv_v_elp_nhh_cmp - compares a neighbor's hash with the own one + * @hardif_neigh: the hardif_neigh to compare with + * + * Checks whether the neighbor hash a neighbor advertised matches our own + * hash, the one we computed for the same interface. + * + * Return: True, if the hashes match, false otherwise. + */ +bool batadv_v_elp_nhh_cmp(struct batadv_hardif_neigh_node *hardif_neigh) +{ + return !memcmp(hardif_neigh->if_incoming->bat_v.neigh_hash, + hardif_neigh->bat_v.neigh_hash, + sizeof(hardif_neigh->bat_v.neigh_hash)); +} + +/** + * batadv_v_elp_rx_ingress_bad - check for ingress RX-metric bottlenecks + * @hardif_neigh: the hardif_neigh a packet was received from + * + * Checks whether we could potentially be a better path for packets + * coming from the given hardif_neigh to any neighbor on the same interface. + * Or whether there is some bottleneck making us unfavourable to become + * a forwarder for packets from this hardif_neigh. + * + * More specifically, this function checks whether our ingress side, that + * is the connection from us to the given hardif_neigh, is worse than the + * direct transmission any other neighbor to this hardif_neigh. + * + * Return: True if our incoming side a bottleneck, false otherwise. + */ + +bool batadv_v_elp_rx_ingress_bad(struct batadv_hardif_neigh_node *hardif_neigh) +{ + struct batadv_hard_iface *iface = hardif_neigh->if_incoming; + struct batadv_priv *bat_priv = netdev_priv(iface->soft_iface); + u32 throughput = ewma_throughput_read(&hardif_neigh->bat_v.throughput); + u32 limit = iface->bat_v.min_throughput_other; + + throughput = batadv_v_forward_penalty(bat_priv, iface, iface, + throughput); + + return throughput < limit; +} + +/** + * batadv_v_elp_rx_egress_bad - check for egress RX-metric bottlenecks + * @hardif_neigh: the hardif_neigh a packet was received from + * + * Checks whether we could potentially be a better path for packets + * coming from the given hardif_neigh to any neighbor on the same interface. + * Or whether there is some bottleneck making us unfavourable to become + * a forwarder for packets from this hardif_neigh. + * + * More specifically, this function checks whether our egress side, that is + * the connection from from any neighbor other than hardif_neigh to us, is + * worse than the direct transmission of any other neighbor to this + * hardif_neigh. + * + * Return: True if our outgoing side a bottleneck, false otherwise. + */ +bool batadv_v_elp_rx_egress_bad(struct batadv_hardif_neigh_node *hardif_neigh) +{ + struct batadv_hard_iface *iface = hardif_neigh->if_incoming; + struct batadv_priv *bat_priv = netdev_priv(iface->soft_iface); + u32 throughput = iface->bat_v.max_throughput_other; + u32 limit = iface->bat_v.min_throughput_other; + + throughput = batadv_v_forward_penalty(bat_priv, iface, iface, + throughput); + + return throughput < limit; +} + +/** + * batadv_v_elp_tx_ingress_bad - check for ingress TX-metric bottlenecks + * @hardif_neigh: the hardif_neigh a packet was received from + * + * Checks whether we could potentially be a better path for packets + * coming from the given hardif_neigh to any neighbor on the same interface. + * Or whether there is some bottleneck making us unfavourable to become + * a forwarder for packets from this hardif_neigh. + * + * More specifically, this function checks whether our ingress side, that + * is the connection from the given hardif_neigh to us, is worse than the + * direct transmission of the hardif_neigh to any other neighbor. + * + * Return: True if our incoming side a bottleneck, false otherwise. + */ +bool batadv_v_elp_tx_ingress_bad(struct batadv_hardif_neigh_node *hardif_neigh) +{ + struct batadv_hard_iface *iface = hardif_neigh->if_incoming; + struct batadv_priv *bat_priv = netdev_priv(iface->soft_iface); + u32 throughput = hardif_neigh->bat_v.max_throughput; + u32 limit = hardif_neigh->bat_v.min_throughput; + + throughput = batadv_v_forward_penalty(bat_priv, iface, iface, + throughput); + + return throughput < limit; +} + +/** + * batadv_v_elp_tx_egress_bad - check for egress TX-metric bottlenecks + * @hardif_neigh: the hardif_neigh a packet was received from + * + * Checks whether we could potentially be a better path for packets + * coming from the given hardif_neigh to any neighbor on the same interface. + * Or whether there is some bottleneck making us unfavourable to become + * a forwarder for packets from this hardif_neigh. + * + * More specifically, this function checks whether our egress side, that is + * the connection from us to any neighbor other than hardif_neigh, is + * worse than the direct transmission of the hardif_neigh to any other neighbor. + * + * Return: True if our outgoing side a bottleneck, false otherwise. + */ +bool batadv_v_elp_tx_egress_bad(struct batadv_hardif_neigh_node *hardif_neigh) +{ + struct batadv_hard_iface *iface = hardif_neigh->if_incoming; + struct batadv_priv *bat_priv = netdev_priv(iface->soft_iface); + u32 throughput = iface->bat_v.max_throughput; + u32 limit = hardif_neigh->bat_v.min_throughput; + + throughput = batadv_v_forward_penalty(bat_priv, iface, iface, + throughput); + + return throughput < limit; +} + +/** + * batadv_v_elp_no_broadcast - checks whether a rebroadcast can be avoided + * @if_outgoing: the outgoing interface to be considered for rebroadcast + * @hardif_neigh: the hardif_neigh the packet came from + * @inverse_metric: the metric direction to use (e.g. "true" for OGMs, "false" + * for broadcast packets + * + * This function checks whether with the information available to/from ELP, a + * rebroadcast of an OGM2 or broadcast packet on an interface can be + * avoided. + * + * The inverse_metric parameter indicates whether the considered packet + * should follow the best RX (inverse_metric = "true", e.g. OGMs) or TX + * path (inverse_metric = "false", e.g. broadcast packets). + * + * Return: True, if a rebroadcast can be avoided, false otherwise. + */ +bool batadv_v_elp_no_broadcast(struct batadv_hard_iface *if_outgoing, + struct batadv_hardif_neigh_node *hardif_neigh, + bool inverse_metric) +{ + if (!if_outgoing || !hardif_neigh) + return false; + + /* ELP does not provide information across inferface domains */ + if (if_outgoing != hardif_neigh->if_incoming) + return false; + + if (!batadv_v_elp_nhh_cmp(hardif_neigh)) + return false; + + /* same neighborhood, is a better path possible? */ + if (inverse_metric) { + /* OGM2 check */ + if (batadv_v_elp_rx_ingress_bad(hardif_neigh) || + batadv_v_elp_rx_egress_bad(hardif_neigh)) + return true; + } else { + /* broadcast packet check */ + if (batadv_v_elp_tx_ingress_bad(hardif_neigh) || + batadv_v_elp_tx_egress_bad(hardif_neigh)) + return true; + } + + return false; +} + +/** + * batadv_v_elp_tvlv_handler_nhh - process incoming NHH tvlv container + * @bat_priv: the bat priv with all the soft interface information + * @tvlv_value: tvlv buffer containing the neighborhood hash data + * @tvlv_value_len: tvlv buffer length + * @ctx: handler specific context information (here: hardif_neigh) + * + * Return: NET_RX_DROP on parsing errors, NET_RX_SUCCESS otherwise. + */ +static int batadv_v_elp_tvlv_handler_nhh(struct batadv_priv *bat_priv, + void *tvlv_value, u16 tvlv_value_len, + void *ctx) +{ + struct batadv_hardif_neigh_node *hardif_neigh; + struct batadv_tvlv_nhh_data *nhh_data; + u32 min_throughput = 0; + u32 max_throughput = (~(u32)0); + + hardif_neigh = batadv_v_elp_unpack_ctx(ctx); + if (WARN_ON(!hardif_neigh)) + return NET_RX_DROP; + + if (tvlv_value) { + if (tvlv_value_len < sizeof(*nhh_data)) + return NET_RX_DROP; + + nhh_data = (struct batadv_tvlv_nhh_data *)tvlv_value; + + memcpy(hardif_neigh->bat_v.neigh_hash, nhh_data->neigh_hash, + sizeof(hardif_neigh->bat_v.neigh_hash)); + min_throughput = ntohl(nhh_data->min_throughput); + max_throughput = ntohl(nhh_data->max_throughput); + } else { + memset(hardif_neigh->bat_v.neigh_hash, 0, + sizeof(hardif_neigh->bat_v.neigh_hash)); + } + + hardif_neigh->bat_v.min_throughput = min_throughput; + hardif_neigh->bat_v.max_throughput = max_throughput; + + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Got neighbor hash on interface %s from %pM: %*phN, min_through: %u kbit/s, max_through: %u kbit/s\n", + hardif_neigh->if_incoming->net_dev->name, + hardif_neigh->addr, + (int)sizeof(hardif_neigh->bat_v.neigh_hash), + hardif_neigh->bat_v.neigh_hash, + hardif_neigh->bat_v.min_throughput * 100, + hardif_neigh->bat_v.max_throughput * 100); + + return NET_RX_SUCCESS; +} + +/** + * batadv_v_elp_mesh_init - initialize the ELP private resources for a mesh + * @bat_priv: the object representing the mesh interface to initialise + * + * Return: Always returns 0. + */ +int batadv_v_elp_mesh_init(struct batadv_priv *bat_priv) +{ + batadv_tvlv_handler_register2(bat_priv, batadv_v_elp_tvlv_handler_nhh, + BATADV_ELP, BATADV_TVLV_NHH, 1, + BATADV_TVLV_HANDLER_CIFNOTFND); + + return 0; +} + +/** + * batadv_v_elp_mesh_free - free the ELP private resources for a mesh + * @bat_priv: the object representing the mesh interface to free + */ +void batadv_v_elp_mesh_free(struct batadv_priv *bat_priv) +{ + batadv_tvlv_handler_unregister2(bat_priv, BATADV_ELP, BATADV_TVLV_NHH, + 1); +} + +/** * batadv_v_elp_init - initialize global ELP structures * * Return: A negative value on error, zero on success. diff --git a/net/batman-adv/bat_v_elp.h b/net/batman-adv/bat_v_elp.h index ed5936c..69a9a94 100644 --- a/net/batman-adv/bat_v_elp.h +++ b/net/batman-adv/bat_v_elp.h @@ -23,6 +23,8 @@ struct sk_buff; struct work_struct;
+int batadv_v_elp_mesh_init(struct batadv_priv *bat_priv); +void batadv_v_elp_mesh_free(struct batadv_priv *bat_priv); int batadv_v_elp_init(void); void batadv_v_elp_free(void); int batadv_v_elp_iface_enable(struct batadv_hard_iface *hard_iface); @@ -32,6 +34,14 @@ void batadv_v_elp_iface_activate(struct batadv_hard_iface *primary_iface, void batadv_v_elp_primary_iface_set(struct batadv_hard_iface *primary_iface); int batadv_v_elp_packet_recv(struct sk_buff *skb, struct batadv_hard_iface *if_incoming); +bool batadv_v_elp_nhh_cmp(struct batadv_hardif_neigh_node *hardif_neigh); +bool batadv_v_elp_rx_ingress_bad(struct batadv_hardif_neigh_node *hardif_neigh); +bool batadv_v_elp_rx_egress_bad(struct batadv_hardif_neigh_node *hardif_neigh); +bool batadv_v_elp_tx_ingress_bad(struct batadv_hardif_neigh_node *hardif_neigh); +bool batadv_v_elp_tx_egress_bad(struct batadv_hardif_neigh_node *hardif_neigh); +bool batadv_v_elp_no_broadcast(struct batadv_hard_iface *if_outgoing, + struct batadv_hardif_neigh_node *hardif_neigh, + bool inverse_metric); void batadv_v_elp_throughput_metric_update(struct work_struct *work);
#endif /* _NET_BATMAN_ADV_BAT_V_ELP_H_ */ diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c index 606f899..147d746 100644 --- a/net/batman-adv/bat_v_ogm.c +++ b/net/batman-adv/bat_v_ogm.c @@ -43,6 +43,7 @@ #include "hard-interface.h" #include "hash.h" #include "log.h" +#include "bat_v_ogm.h" #include "originator.h" #include "packet.h" #include "routing.h" @@ -183,7 +184,7 @@ static void batadv_v_ogm_send(struct work_struct *work) if (!kref_get_unless_zero(&hard_iface->refcount)) continue;
- ret = batadv_hardif_no_broadcast(hard_iface, NULL, NULL); + ret = batadv_hardif_no_broadcast(hard_iface, NULL, NULL, true); if (ret) { char *type;
@@ -197,6 +198,9 @@ static void batadv_v_ogm_send(struct work_struct *work) case BATADV_HARDIF_BCAST_DUPORIG: type = "single neighbor is originator"; break; + case BATADV_HARDIF_BCAST_WORSENHH: + type = "worse neighborhood metric"; + break; default: type = "unknown"; } @@ -289,10 +293,10 @@ void batadv_v_ogm_primary_iface_set(struct batadv_hard_iface *primary_iface) * * Return: the penalised throughput metric. */ -static u32 batadv_v_forward_penalty(struct batadv_priv *bat_priv, - struct batadv_hard_iface *if_incoming, - struct batadv_hard_iface *if_outgoing, - u32 throughput) +u32 batadv_v_forward_penalty(struct batadv_priv *bat_priv, + struct batadv_hard_iface *if_incoming, + struct batadv_hard_iface *if_outgoing, + u32 throughput) { int hop_penalty = atomic_read(&bat_priv->hop_penalty); int hop_penalty_max = BATADV_TQ_MAX_VALUE; @@ -752,7 +756,7 @@ static void batadv_v_ogm_process(const struct sk_buff *skb, int ogm_offset,
ret = batadv_hardif_no_broadcast(hard_iface, ogm_packet->orig, - hardif_neigh->orig); + hardif_neigh, true);
if (ret) { char *type; @@ -767,6 +771,9 @@ static void batadv_v_ogm_process(const struct sk_buff *skb, int ogm_offset, case BATADV_HARDIF_BCAST_DUPORIG: type = "single neighbor is originator"; break; + case BATADV_HARDIF_BCAST_WORSENHH: + type = "worse neighborhood metric"; + break; default: type = "unknown"; } diff --git a/net/batman-adv/bat_v_ogm.h b/net/batman-adv/bat_v_ogm.h index 4c4d45c..de01593 100644 --- a/net/batman-adv/bat_v_ogm.h +++ b/net/batman-adv/bat_v_ogm.h @@ -30,6 +30,10 @@ int batadv_v_ogm_iface_enable(struct batadv_hard_iface *hard_iface); struct batadv_orig_node *batadv_v_ogm_orig_get(struct batadv_priv *bat_priv, const u8 *addr); void batadv_v_ogm_primary_iface_set(struct batadv_hard_iface *primary_iface); +u32 batadv_v_forward_penalty(struct batadv_priv *bat_priv, + struct batadv_hard_iface *if_incoming, + struct batadv_hard_iface *if_outgoing, + u32 throughput); int batadv_v_ogm_packet_recv(struct sk_buff *skb, struct batadv_hard_iface *if_incoming);
diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index dc1816e..7037910 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -232,8 +232,9 @@ bool batadv_is_wifi_netdev(struct net_device *net_device) * batadv_hardif_no_broadcast - check whether (re)broadcast is necessary * @if_outgoing: the outgoing interface checked and considered for (re)broadcast * @orig_addr: the originator of this packet - * @orig_neigh: originator address of the forwarder we just got the packet from - * (NULL if we originated) + * @hardif_neigh: the neighbor we got this packet from (NULL if we originated) + * @inverse_metric: the metric direction to use (e.g. "true" for OGMs, "false" + * for broadcast packets * * Checks whether a packet needs to be (re)broadcasted on the given interface. * @@ -241,12 +242,16 @@ bool batadv_is_wifi_netdev(struct net_device *net_device) * BATADV_HARDIF_BCAST_NORECIPIENT: No neighbor on interface * BATADV_HARDIF_BCAST_DUPFWD: Just one neighbor, but it is the forwarder * BATADV_HARDIF_BCAST_DUPORIG: Just one neighbor, but it is the originator + * BATADV_HARDIF_BCAST_WORSENHH: ELP detected a worse neighborhood metric * BATADV_HARDIF_BCAST_OK: Several neighbors, must broadcast */ int batadv_hardif_no_broadcast(struct batadv_hard_iface *if_outgoing, - u8 *orig_addr, u8 *orig_neigh) + u8 *orig_addr, + struct batadv_hardif_neigh_node *hardif_neigh, + bool inverse_metric) { - struct batadv_hardif_neigh_node *hardif_neigh; + struct batadv_priv *bat_priv = netdev_priv(if_outgoing->soft_iface); + struct batadv_hardif_neigh_node *hardif_neigh_cmp; struct hlist_node *first; int ret = BATADV_HARDIF_BCAST_OK;
@@ -259,22 +264,30 @@ int batadv_hardif_no_broadcast(struct batadv_hard_iface *if_outgoing, goto out; }
- /* >1 neighbors -> (re)brodcast */ - if (rcu_dereference(hlist_next_rcu(first))) - goto out; + /* single neighbor checks */ + if (!rcu_dereference(hlist_next_rcu(first)) && hardif_neigh) { + hardif_neigh_cmp = hlist_entry(first, + struct batadv_hardif_neigh_node, + list);
- hardif_neigh = hlist_entry(first, struct batadv_hardif_neigh_node, - list); - - /* 1 neighbor, is the originator -> no rebroadcast */ - if (orig_addr && batadv_compare_eth(hardif_neigh->orig, orig_addr)) { - ret = BATADV_HARDIF_BCAST_DUPORIG; - /* 1 neighbor, is the one we received from -> no rebroadcast */ - } else if (orig_neigh && - batadv_compare_eth(hardif_neigh->orig, orig_neigh)) { - ret = BATADV_HARDIF_BCAST_DUPFWD; + /* 1 neighbor, is the originator -> no rebroadcast */ + if (batadv_compare_eth(hardif_neigh_cmp->orig, orig_addr)) { + ret = BATADV_HARDIF_BCAST_DUPORIG; + goto out; + /* 1 neighbor, is the one we received from -> no rebroadcast */ + } else if (batadv_compare_eth(hardif_neigh_cmp->orig, + hardif_neigh->orig)) { + ret = BATADV_HARDIF_BCAST_DUPFWD; + goto out; + } }
+ if (bat_priv->algo_ops->neigh.hardif_no_broadcast && + bat_priv->algo_ops->neigh.hardif_no_broadcast(if_outgoing, + hardif_neigh, + inverse_metric)) + ret = BATADV_HARDIF_BCAST_WORSENHH; + out: rcu_read_unlock(); return ret; diff --git a/net/batman-adv/hard-interface.h b/net/batman-adv/hard-interface.h index a043182..d75b767 100644 --- a/net/batman-adv/hard-interface.h +++ b/net/batman-adv/hard-interface.h @@ -45,12 +45,14 @@ enum batadv_hard_if_state { * @BATADV_HARDIF_BCAST_NORECIPIENT: Broadcast not needed, there is no recipient * @BATADV_HARDIF_BCAST_DUPFWD: There is just the neighbor we got it from * @BATADV_HARDIF_BCAST_DUPORIG: There is just the originator + * @BATADV_HARDIF_BCAST_WORSENHH: ELP detected a worse neighborhood metric */ enum batadv_hard_if_bcast { BATADV_HARDIF_BCAST_OK = 0, BATADV_HARDIF_BCAST_NORECIPIENT, BATADV_HARDIF_BCAST_DUPFWD, BATADV_HARDIF_BCAST_DUPORIG, + BATADV_HARDIF_BCAST_WORSENHH, };
/** @@ -78,7 +80,9 @@ int batadv_hardif_min_mtu(struct net_device *soft_iface); void batadv_update_min_mtu(struct net_device *soft_iface); void batadv_hardif_release(struct kref *ref); int batadv_hardif_no_broadcast(struct batadv_hard_iface *if_outgoing, - u8 *orig_addr, u8 *orig_neigh); + u8 *orig_addr, + struct batadv_hardif_neigh_node *hardif_neigh, + bool inverse_metric);
/** * batadv_hardif_put - decrement the hard interface refcounter and possibly diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c index e4d5d73..4e93ad0 100644 --- a/net/batman-adv/send.c +++ b/net/batman-adv/send.c @@ -786,7 +786,6 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work) struct net_device *soft_iface; struct batadv_priv *bat_priv; u8 *neigh_addr; - u8 *orig_neigh; unsigned long send_time = jiffies + msecs_to_jiffies(5); int ret = 0;
@@ -821,10 +820,8 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work) neigh_addr); }
- orig_neigh = neigh_node ? neigh_node->orig : NULL; - ret = batadv_hardif_no_broadcast(hard_iface, bcast_packet->orig, - orig_neigh); + neigh_node, false);
if (ret) { char *type; @@ -839,6 +836,9 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work) case BATADV_HARDIF_BCAST_DUPORIG: type = "single neighbor is originator"; break; + case BATADV_HARDIF_BCAST_WORSENHH: + type = "worse neighborhood metric"; + break; default: type = "unknown"; } diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 6a71522..c7900fd 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -111,6 +111,8 @@ enum batadv_v_hard_iface_flags { * @flags: interface specific flags * @min_throughput: worst of all TX throughputs this neighbor has to others * @max_throughput: best of all TX throughputs this neighbor has to others + * @min_throughput_other: worst of all min_throughput's of other neighbors + * @max_throughput_other: worst of all max_throughput's of other neighbors * @neigh_hash: a sha512 hash of all neighbors this neighbor sees * (hash over the alphabetically ordered, concatenated, binary representation) */ @@ -123,6 +125,8 @@ struct batadv_hard_iface_bat_v { u8 flags; u32 min_throughput; u32 max_throughput; + u32 min_throughput_other; + u32 max_throughput_other; u8 neigh_hash[SHA512_DIGEST_SIZE]; };
@@ -401,6 +405,10 @@ DECLARE_EWMA(throughput, 1024, 8) * @throughput: ewma link throughput towards this neighbor * @elp_interval: time interval between two ELP transmissions * @elp_latest_seqno: latest and best known ELP sequence number + * @min_throughput: worst of all TX throughputs this neighbor has to others + * @max_throughput: best of all TX throughputs this neighbor has to others + * @neigh_hash: a sha512 hash of all neighbors this neighbor sees + * (hash over the alphabetically ordered, concatenated, binary representation) * @last_unicast_tx: when the last unicast packet has been sent to this neighbor * @metric_work: work queue callback item for metric update */ @@ -408,6 +416,9 @@ struct batadv_hardif_neigh_node_bat_v { struct ewma_throughput throughput; u32 elp_interval; u32 elp_latest_seqno; + u32 min_throughput; + u32 max_throughput; + u8 neigh_hash[SHA512_DIGEST_SIZE]; unsigned long last_unicast_tx; struct work_struct metric_work; }; @@ -1426,6 +1437,8 @@ struct batadv_algo_iface_ops { * struct batadv_algo_neigh_ops - mesh algorithm callbacks (neighbour specific) * @hardif_init: called on creation of single hop entry * (optional) + * @hardif_no_broadcast: algorithm specific check(s) regarding rebroadcasts + * (optional) * @cmp: compare the metrics of two neighbors for their respective outgoing * interfaces * @is_similar_or_better: check if neigh1 is equally similar or better than @@ -1435,6 +1448,10 @@ struct batadv_algo_iface_ops { */ struct batadv_algo_neigh_ops { void (*hardif_init)(struct batadv_hardif_neigh_node *neigh); + bool (*hardif_no_broadcast) + (struct batadv_hard_iface *if_outgoing, + struct batadv_hardif_neigh_node *hardif_neigh, + bool inverse_metric); int (*cmp)(struct batadv_neigh_node *neigh1, struct batadv_hard_iface *if_outgoing1, struct batadv_neigh_node *neigh2,
On Dienstag, 20. September 2016 14:12:45 CEST Linus Lüssing wrote:
@@ -1435,6 +1448,10 @@ struct batadv_algo_iface_ops { */ struct batadv_algo_neigh_ops { void (*hardif_init)(struct batadv_hardif_neigh_node *neigh);
bool (*hardif_no_broadcast)
(struct batadv_hard_iface *if_outgoing,
struct batadv_hardif_neigh_node *hardif_neigh,
bool inverse_metric); int (*cmp)(struct batadv_neigh_node *neigh1, struct batadv_hard_iface *if_outgoing1, struct batadv_neigh_node *neigh2,
Can you shorten hardif_neigh a little bit to avoid this slightly confusing line break after "hardif_no_broadcast)"? Many tools like checkpatch or kernel-doc go crazy about it :)
Thanks, Sven
On Dienstag, 20. September 2016 14:12:45 CEST Linus Lüssing wrote: [...]
+/**
- batadv_v_elp_neigh_update - update an ELP neighbour node
- @bat_priv: the bat priv with all the soft interface information
- @neigh_addr: the neighbour interface address
@@ -581,6 +628,7 @@ void batadv_v_elp_primary_iface_set(struct batadv_hard_iface *primary_iface)
- ELP packet.
*/ static void batadv_v_elp_neigh_update(struct batadv_priv *bat_priv,
struct sk_buff *skb, u8 *neigh_addr, struct batadv_hard_iface *if_incoming, struct batadv_elp_packet *elp_packet)
skb parameter was added but the kernel-doc header was not added.
Kind regards, Sven
On Dienstag, 20. September 2016 14:12:45 CEST Linus Lüssing wrote:
diff --git a/net/batman-adv/bat_v.c b/net/batman-adv/bat_v.c index 762b34c..75a4147 100644 --- a/net/batman-adv/bat_v.c +++ b/net/batman-adv/bat_v.c
[...]
- hardif_neigh = batadv_v_elp_unpack_ctx(ctx);
- if (WARN_ON(!hardif_neigh))
return NET_RX_DROP;
Please include linux/bug.h for WARN_ON.
index ed5936c..69a9a94 100644 --- a/net/batman-adv/bat_v_elp.h +++ b/net/batman-adv/bat_v_elp.h
[...]
+bool batadv_v_elp_nhh_cmp(struct batadv_hardif_neigh_node *hardif_neigh); +bool batadv_v_elp_rx_ingress_bad(struct batadv_hardif_neigh_node *hardif_neigh); +bool batadv_v_elp_rx_egress_bad(struct batadv_hardif_neigh_node *hardif_neigh); +bool batadv_v_elp_tx_ingress_bad(struct batadv_hardif_neigh_node *hardif_neigh); +bool batadv_v_elp_tx_egress_bad(struct batadv_hardif_neigh_node *hardif_neigh); +bool batadv_v_elp_no_broadcast(struct batadv_hard_iface *if_outgoing,
struct batadv_hardif_neigh_node *hardif_neigh,
bool inverse_metric);
Please include linux/types.h for bool.
diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c index 606f899..147d746 100644 --- a/net/batman-adv/bat_v_ogm.c +++ b/net/batman-adv/bat_v_ogm.c @@ -43,6 +43,7 @@ #include "hard-interface.h" #include "hash.h" #include "log.h" +#include "bat_v_ogm.h" #include "originator.h" #include "packet.h" #include "routing.h"
Please remove this duplicate include. bat_v_ogm.h is the first include in this file :)
Thanks, Sven
On Tue, Sep 20, 2016 at 02:12:42PM +0200, Linus Lüssing wrote:
Hi,
Here's the second broadcast avoidance patchset (rebased on the former one).
Hopefully, those two patchsets together should be an automatic approach and replacement for the (non-upstream) no-rebroadcast flag patch and the cases it is currently used for.
Documentation:
Branch:
Regards, Linus
PS: Before I forget, Kudos to Henning who had dropped the idea of using a hash while we were discussing this rebroadcasting topic during dinner at WBMv9.
b.a.t.m.a.n@lists.open-mesh.org