[3/3] batman-adv: forward broadcast packets via unicast transmissions

Message ID 20190514073859.2053-4-linus.luessing@c0d3.blue
State Changes Requested
Delegated to: Simon Wunderlich
Headers show
Series
  • [1/3] batman-adv: bcast: queue per interface, if needed
Related show

Commit Message

Linus Lüssing May 14, 2019, 7:38 a.m.
With the high bitrates of modern wireless devices these days multiple
unicast transmissions are usually less costly than a broadcast
transmission on a low bitrate.

Furthermore unicast transmissions come with the advantage of being
acknowledged and potentially retried automatically, hence with a better
reliability.

The compromise so far was to (re-)transmit a broadcast packet three
times in batman-adv and advise the user to set a higher multicast rate.
However the multicast still needs to be chosen somewhat conservatively
(typically something between 12MBit/s and 24MBit/s are chosen) to ensure
robustness. Which might be too low for some applications.

With this patch a broadcast packet is forwarded on a link via individual
unicast transmissions as long as the number of receivers is smaller or
equal to the configurable multicast fanout (default: 16).

Unicasts are only send to neighboring nodes which are direct
neighbors. So a neighbor node which we see on a link but which has a
better reachability via a multi-hop path will not receive a unicast
transmission.

Furthermore, the broadcast avoidance checks still apply (no echo to
previous sender, no echo to initial originator) to further reduce
unicast transmissions.

Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue>
---
 net/batman-adv/hard-interface.c |   1 +
 net/batman-adv/routing.c        |  38 ++++++++
 net/batman-adv/send.c           | 156 ++++++++++++++++++++++++++++++++
 net/batman-adv/types.h          |   8 ++
 4 files changed, 203 insertions(+)

Comments

Sven Eckelmann May 25, 2019, 10:21 a.m. | #1
On Tuesday, 14 May 2019 09:38:59 CEST Linus Lüssing wrote:
> +static bool batadv_forw_bcast_may_ucast(struct batadv_priv *bat_priv,
> +                                       struct batadv_hard_iface *if_out)
> +{
> +       unsigned long num_direct_orig;
> +       unsigned long num_bcast_no_urcv;
> +
> +       num_direct_orig = atomic_read(&if_out->num_direct_orig);
> +       num_bcast_no_urcv = atomic_read(&if_out->num_bcast_no_urcv);
> +
> +       return !num_bcast_no_urcv &&
> +              (num_direct_orig <= atomic_read(&bat_priv->multicast_fanout));
> +}

ecsv/pu|sparse linux-3.16.66 cfg: BLA=y DAT=y DEBUGFS=n DEBUG=y TRACING=y NC=n MCAST=n BATMAN_V=y SYSFS=y|/home/build_test/build_env/tmp.I4uiDZgE1M/net/batman-adv/send.c:928:57: error: no member 'multicast_fanout' in struct batadv_priv
/home/build_test/build_env/tmp.I4uiDZgE1M/net/batman-adv/send.c:928:47: warning: call with no type!
/home/build_test/build_env/tmp.I4uiDZgE1M/net/batman-adv/send.c: In function ‘batadv_forw_bcast_may_ucast’:
/home/build_test/build_env/tmp.I4uiDZgE1M/net/batman-adv/send.c:928:50: error: ‘struct batadv_priv’ has no member named ‘multicast_fanout’
         (num_direct_orig <= atomic_read(&bat_priv->multicast_fanout));
                                                  ^~
/home/build_test/build_env/tmp.I4uiDZgE1M/net/batman-adv/send.c:929:1: warning: control reaches end of non-void function [-Wreturn-type]
 }

Kind regards,
	Sven

Patch

diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c
index b71d8efc..11864f77 100644
--- a/net/batman-adv/hard-interface.c
+++ b/net/batman-adv/hard-interface.c
@@ -937,6 +937,7 @@  batadv_hardif_add_interface(struct net_device *net_dev)
 
 	batadv_v_hardif_init(hard_iface);
 	atomic_set(&hard_iface->num_bcast_no_urcv, 0);
+	atomic_set(&hard_iface->num_direct_orig, 0);
 
 	batadv_check_known_mac_addr(hard_iface->net_dev);
 	kref_get(&hard_iface->refcount);
diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
index 03b4e609..99a138ae 100644
--- a/net/batman-adv/routing.c
+++ b/net/batman-adv/routing.c
@@ -42,6 +42,40 @@ 
 static int batadv_route_unicast_packet(struct sk_buff *skb,
 				       struct batadv_hard_iface *recv_if);
 
+/**
+ * batadv_route_update_direct_neigh_count() - update the direct neighbor counter
+ * @oldrouter: the previously selected router
+ * @newrouter: the newly selected router
+ * @orig: the originator (MAC) the router is updated for
+ *
+ * Updates to direct neighbor originator counter of the hard interface the old
+ * and new router belongs to.
+ */
+static void
+batadv_route_update_direct_neigh_count(struct batadv_neigh_node *oldrouter,
+				       struct batadv_neigh_node *newrouter,
+				       u8 *orig)
+{
+	bool oldrouter_is_direct = false;
+	bool newrouter_is_direct = false;
+	u8 *neigh_orig;
+
+	if (oldrouter) {
+		neigh_orig = oldrouter->hardif_neigh->orig;
+		oldrouter_is_direct = batadv_compare_eth(neigh_orig, orig);
+	}
+
+	if (newrouter) {
+		neigh_orig = newrouter->hardif_neigh->orig;
+		newrouter_is_direct = batadv_compare_eth(neigh_orig, orig);
+	}
+
+	if (oldrouter_is_direct && !newrouter_is_direct)
+		atomic_dec(&oldrouter->if_incoming->num_direct_orig);
+	else if (!oldrouter_is_direct && newrouter_is_direct)
+		atomic_inc(&newrouter->if_incoming->num_direct_orig);
+}
+
 /**
  * _batadv_update_route() - set the router for this originator
  * @bat_priv: the bat priv with all the soft interface information
@@ -77,6 +111,10 @@  static void _batadv_update_route(struct batadv_priv *bat_priv,
 	if (neigh_node)
 		kref_get(&neigh_node->refcount);
 
+	if (recv_if == BATADV_IF_DEFAULT)
+		batadv_route_update_direct_neigh_count(curr_router, neigh_node,
+						       orig_node->orig);
+
 	rcu_assign_pointer(orig_ifinfo->router, neigh_node);
 	spin_unlock_bh(&orig_node->neigh_list_lock);
 	batadv_orig_ifinfo_put(orig_ifinfo);
diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c
index 1341b9ac..c83f14b2 100644
--- a/net/batman-adv/send.c
+++ b/net/batman-adv/send.c
@@ -905,6 +905,146 @@  static bool batadv_send_no_broadcast(struct batadv_priv *bat_priv,
 	return true;
 }
 
+/**
+ * batadv_forw_bcast_may_ucast() - check if a broadcast packet can be unicasted
+ * @bat_priv: the bat priv with all the soft interface information
+ * @if_out: the outgoing interface to check for
+ *
+ * Return: True if the number of 1-hop, direct neighbor originators on the given
+ * interface is smaller than or equal to the configured multicast fanout limit
+ * and all neighbor nodes support the reception of batman-adv broadcast
+ * packets with a unicast ethernet frame destination. Otherwise returns false.
+ */
+static bool batadv_forw_bcast_may_ucast(struct batadv_priv *bat_priv,
+					struct batadv_hard_iface *if_out)
+{
+	unsigned long num_direct_orig;
+	unsigned long num_bcast_no_urcv;
+
+	num_direct_orig = atomic_read(&if_out->num_direct_orig);
+	num_bcast_no_urcv = atomic_read(&if_out->num_bcast_no_urcv);
+
+	return !num_bcast_no_urcv &&
+	       (num_direct_orig <= atomic_read(&bat_priv->multicast_fanout));
+}
+
+/**
+ * batadv_forw_bcasts_via_ucasts_check() - check if a neighbor needs a unicast
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: broadcast packet to check
+ * @if_out: the outgoing interface to check for
+ * @if_neigh: the neighbor node to check for
+ * @own_packet: true if it is a self-generated broadcast packet
+ *
+ * Return: True if a packet needs to be transmitted to the given neighbor,
+ * false otherwise.
+ */
+static bool
+batadv_forw_bcasts_via_ucasts_check(struct batadv_priv *bat_priv,
+				    struct sk_buff *skb,
+				    struct batadv_hard_iface *if_out,
+				    struct batadv_hardif_neigh_node *if_neigh,
+				    bool own_packet)
+{
+	u8 *bcast_orig = ((struct batadv_bcast_packet *)skb->data)->orig;
+	struct batadv_hardif_neigh_node *neigh_node = NULL;
+	struct batadv_neigh_node *router = NULL;
+	struct batadv_orig_node *orig_node;
+	bool ret = false;
+	u8 *router_addr;
+	u8 *neigh_addr;
+	u8 *orig_neigh;
+
+	if (!own_packet) {
+		neigh_addr = eth_hdr(skb)->h_source;
+		neigh_node = batadv_hardif_neigh_get(if_out,
+						     neigh_addr);
+	}
+
+	orig_neigh = neigh_node ? neigh_node->orig : NULL;
+
+	orig_node = batadv_orig_hash_find(bat_priv, if_neigh->orig);
+	if (orig_node) {
+		router = batadv_orig_router_get(orig_node, BATADV_IF_DEFAULT);
+		router_addr = router ? router->addr : NULL;
+	}
+
+	/* is the originator -> no rebroadcast */
+	if (batadv_compare_eth(if_neigh->orig, bcast_orig)) {
+		goto out;
+	/* is the one we received from -> no rebroadcast */
+	} else if (orig_neigh &&
+		   batadv_compare_eth(if_neigh->orig, orig_neigh)) {
+		goto out;
+	/* only 1-hop, direct neighbor originators */
+	} else if (router && !batadv_compare_eth(router_addr, if_neigh->addr)) {
+		goto out;
+	}
+
+	ret = true;
+out:
+	if (router)
+		batadv_neigh_node_put(router);
+	if (orig_node)
+		batadv_orig_node_put(orig_node);
+	if (neigh_node)
+		batadv_hardif_neigh_put(neigh_node);
+
+	return ret;
+}
+
+/**
+ * batadv_forw_bcast_packet_via_ucasts() - unicast a broadcast packet
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: broadcast packet to send
+ * @own_packet: true if it is a self-generated broadcast packet
+ * @if_out: the outgoing interface to forward to
+ *
+ * Forwards a broadcast packet on the specified interface via unicast
+ * transmissions.
+ *
+ * This call clones the given skb, hence the caller needs to take into
+ * account that the data segment of the original skb might not be
+ * modifiable anymore.
+ *
+ * Return: NETDEV_TX_OK on success and NETDEV_TX_BUSY on errors.
+ */
+static int batadv_forw_bcast_via_ucasts(struct batadv_priv *bat_priv,
+					struct sk_buff *skb,
+					bool own_packet,
+					struct batadv_hard_iface *if_out)
+{
+	struct batadv_hardif_neigh_node *hardif_neigh;
+	struct sk_buff *newskb;
+	int ret = NETDEV_TX_OK;
+
+	rcu_read_lock();
+	hlist_for_each_entry_rcu(hardif_neigh, &if_out->neigh_list, list) {
+		if (!kref_get_unless_zero(&hardif_neigh->refcount))
+			continue;
+
+		if (!batadv_forw_bcasts_via_ucasts_check(bat_priv, skb, if_out,
+							 hardif_neigh,
+							 own_packet)) {
+			batadv_hardif_neigh_put(hardif_neigh);
+			continue;
+		}
+
+		newskb = skb_clone(skb, GFP_ATOMIC);
+		if (!newskb) {
+			batadv_hardif_neigh_put(hardif_neigh);
+			ret = NETDEV_TX_BUSY;
+			break;
+		}
+
+		batadv_send_skb_packet(newskb, if_out, hardif_neigh->addr);
+		batadv_hardif_neigh_put(hardif_neigh);
+	}
+	rcu_read_unlock();
+
+	return ret;
+}
+
 /**
  * __batadv_forw_bcast_packet() - forward and queue a broadcast packet
  * @bat_priv: the bat priv with all the soft interface information
@@ -949,6 +1089,22 @@  static int __batadv_forw_bcast_packet(struct batadv_priv *bat_priv,
 			continue;
 		}
 
+		/* try individual unicasts first */
+		if (!delay && batadv_forw_bcast_may_ucast(bat_priv,
+							  hard_iface)) {
+			ret = batadv_forw_bcast_via_ucasts(bat_priv, skb,
+							   own_packet,
+							   hard_iface);
+
+			if (ret == NETDEV_TX_BUSY) {
+				batadv_hardif_put(hard_iface);
+				break;
+			}
+
+			batadv_hardif_put(hard_iface);
+			continue;
+		} /* else: transmit via broadcast */
+
 		ret = batadv_forw_bcast_packet_if(bat_priv, skb, delay,
 						  own_packet, primary_if,
 						  hard_iface);
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index 877a2762..52a51861 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -200,6 +200,14 @@  struct batadv_hard_iface {
 	 */
 	atomic_t num_bcast_no_urcv;
 
+	/**
+	 * @num_direct_orig: number of neighboring originators on this
+	 * interface which have a direct, 1-hop path (which is equivalent
+	 * to the number of neighbor nodes on this interface which are a
+	 * selected router)
+	 */
+	atomic_t num_direct_orig;
+
 	/** @bat_iv: per hard-interface B.A.T.M.A.N. IV data */
 	struct batadv_hard_iface_bat_iv bat_iv;