diff --git a/net/batman-adv/bat_v.c b/net/batman-adv/bat_v.c
index 992e428..764d4e1 100644
--- a/net/batman-adv/bat_v.c
+++ b/net/batman-adv/bat_v.c
@@ -126,6 +126,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);
 }
@@ -172,14 +175,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);
 }
 
 /**
@@ -196,7 +218,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) {
@@ -1039,6 +1061,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
@@ -1081,6 +1104,9 @@ void batadv_v_hardif_init(struct batadv_hard_iface *hard_iface)
 
 	bat_v->min_throughput = 0;
 	bat_v->max_throughput = U32_MAX;
+	bat_v->min_throughput_other = 0;
+	bat_v->max_throughput_other = U32_MAX;
+
 	memset(bat_v->neigh_hash, 0, sizeof(bat_v->neigh_hash));
 }
 
@@ -1095,9 +1121,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 df7bc64..752789f 100644
--- a/net/batman-adv/bat_v_elp.c
+++ b/net/batman-adv/bat_v_elp.c
@@ -20,6 +20,7 @@
 
 #include <crypto/hash.h>
 #include <linux/atomic.h>
+#include <linux/bug.h>
 #include <linux/byteorder/generic.h>
 #include <linux/err.h>
 #include <linux/errno.h>
@@ -51,6 +52,7 @@
 #include "packet.h"
 #include "routing.h"
 #include "send.h"
+#include "tvlv.h"
 
 static struct crypto_shash *tfm;
 
@@ -88,6 +90,8 @@ static void batadv_v_elp_update_neigh_hash(struct batadv_hard_iface *hard_iface)
 	u8 *own_addr = hard_iface->net_dev->dev_addr;
 	u32 min_throughput = U32_MAX;
 	u32 max_throughput = 0;
+	u32 min_throughput_other = U32_MAX;
+	u32 max_throughput_other = 0;
 	u32 throughput;
 	int ret;
 
@@ -129,6 +133,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();
 
@@ -144,14 +158,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;
 
@@ -160,6 +178,8 @@ err:
 	       sizeof(hard_iface->bat_v.neigh_hash));
 	hard_iface->bat_v.min_throughput = 0;
 	hard_iface->bat_v.max_throughput = U32_MAX;
+	hard_iface->bat_v.min_throughput_other = 0;
+	hard_iface->bat_v.max_throughput_other = U32_MAX;
 
 	pr_warn_once("An error occurred while calculating neighbor hash for %s\n",
 		     hard_iface->net_dev->name);
@@ -581,8 +601,27 @@ void batadv_v_elp_primary_iface_set(struct batadv_hard_iface *primary_iface)
 }
 
 /**
+ * batadv_v_elp_get_tvlv_len - get tvlv_len of an elp packet
+ * @skb: the elp packet to parse
+ *
+ * Return: Length of the tvlv data of the given skb.
+ */
+static u16 batadv_v_elp_get_tvlv_len(struct sk_buff *skb)
+{
+	unsigned int tvlv_len_offset;
+	__be16 *tvlv_len, tvlv_len_buff;
+
+	tvlv_len_offset = offsetof(struct batadv_elp_packet, tvlv_len);
+	tvlv_len = skb_header_pointer(skb, tvlv_len_offset,
+				      sizeof(tvlv_len_buff), &tvlv_len_buff);
+
+	return tvlv_len ? ntohs(*tvlv_len) : 0;
+}
+
+/**
  * batadv_v_elp_neigh_update - update an ELP neighbour node
  * @bat_priv: the bat priv with all the soft interface information
+ * @skb: the received packet
  * @neigh_addr: the neighbour interface address
  * @if_incoming: the interface the packet was received through
  * @elp_packet: the received ELP packet
@@ -591,6 +630,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)
@@ -599,6 +639,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);
+	u16 tvlv_len = batadv_v_elp_get_tvlv_len(skb);
 	s32 seqno_diff;
 	s32 elp_latest_seqno;
 
@@ -615,6 +657,9 @@ static void batadv_v_elp_neigh_update(struct batadv_priv *bat_priv,
 	if (!hardif_neigh)
 		goto neigh_free;
 
+	batadv_tvlv_containers_process2(bat_priv, skb, BATADV_ELP, tvlv_offset,
+					tvlv_len, hardif_neigh);
+
 	elp_latest_seqno = hardif_neigh->bat_v.elp_latest_seqno;
 	seqno_diff = ntohl(elp_packet->seqno) - elp_latest_seqno;
 
@@ -686,7 +731,7 @@ int batadv_v_elp_packet_recv(struct sk_buff *skb,
 	if (!primary_if)
 		goto free_skb;
 
-	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);
 
 	ret = NET_RX_SUCCESS;
@@ -702,6 +747,259 @@ free_skb:
 }
 
 /**
+ * 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 = ctx;
+	struct batadv_tvlv_nhh_data *nhh_data;
+	u32 min_throughput = 0;
+	u32 max_throughput = U32_MAX;
+
+	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 2e73047..88e0507 100644
--- a/net/batman-adv/bat_v_elp.h
+++ b/net/batman-adv/bat_v_elp.h
@@ -20,9 +20,13 @@
 
 #include "main.h"
 
+#include <linux/types.h>
+
 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 +36,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 cfc9228..6be707d 100644
--- a/net/batman-adv/bat_v_ogm.c
+++ b/net/batman-adv/bat_v_ogm.c
@@ -183,7 +183,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 +197,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 +292,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 +755,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 +770,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 2068770..f89445d 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 e348f76..c502c41 100644
--- a/net/batman-adv/hard-interface.c
+++ b/net/batman-adv/hard-interface.c
@@ -379,8 +379,9 @@ bool batadv_is_wifi_hardif(struct batadv_hard_iface *hard_iface)
  * 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.
  *
@@ -388,12 +389,16 @@ bool batadv_is_wifi_hardif(struct batadv_hard_iface *hard_iface)
  *	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;
 
@@ -406,22 +411,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 9f9890f..931ed88 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,
 };
 
 /**
@@ -79,7 +81,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 1489ec2..b60efbb 100644
--- a/net/batman-adv/send.c
+++ b/net/batman-adv/send.c
@@ -799,7 +799,6 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work)
 	unsigned long send_time = jiffies + msecs_to_jiffies(5);
 	bool dropped = false;
 	u8 *neigh_addr;
-	u8 *orig_neigh;
 	int ret = 0;
 
 	delayed_work = to_delayed_work(work);
@@ -837,10 +836,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;
@@ -855,6 +852,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 6dbdbf6..d189860 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];
 };
 
@@ -418,6 +422,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
  */
@@ -425,6 +433,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;
 };
@@ -1445,6 +1456,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
@@ -1454,6 +1467,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,
