[maint,v2,4/4] batman-adv: mcast: fix duplicate mcast packets from BLA backbone to mesh

Message ID 20200904182803.8428-5-linus.luessing@c0d3.blue (mailing list archive)
State Superseded, archived
Delegated to: Simon Wunderlich
Headers
Series batman-adv: mcast: TT/BLA fixes |

Commit Message

Linus Lüssing Sept. 4, 2020, 6:28 p.m. UTC
  Scenario:
* Multicast frame send from BLA backbone gateways (multiple nodes
  with their bat0 bridged together, with BLA enabled) sharing the same
  LAN to nodes in the mesh

Issue:
* Nodes receive the frame multiple times on bat0 from the mesh,
  once from each foreign BLA backbone gateway which shares the same LAN
  with another

For multicast frames via batman-adv broadcast packets coming from the
same BLA backbone but from different backbone gateways duplicates are
currently detected via a CRC history of previously received packets.

However this CRC so far was not performed for multicast frames received
via batman-adv unicast packets. Fixing this by appyling the same check
for such packets, too.

Room for improvements in the future: Ideally we would introduce the
possibility to not only claim a client, but a complete originator, too.
This would allow us to only send a multicast-in-unicast packet from a BLA
backbone gateway claiming the node and by that avoid potential redundant
transmissions in the first place.

Fixes: e5cf86d30a9b ("batman-adv: add broadcast duplicate check")
Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue>
---
 net/batman-adv/bridge_loop_avoidance.c | 86 +++++++++++++++++++++-----
 1 file changed, 70 insertions(+), 16 deletions(-)
  

Comments

Simon Wunderlich Sept. 9, 2020, 12:15 p.m. UTC | #1
On Friday, September 4, 2020 8:28:03 PM CEST Linus Lüssing wrote:
> @@ -1626,7 +1626,8 @@ bool batadv_bla_check_bcast_duplist(struct batadv_priv
> *bat_priv, if (entry->crc != crc)
>                         continue;
> 
> -               if (batadv_compare_eth(entry->orig, bcast_packet->orig))
> +               if (!is_zero_ether_addr(entry->orig) &&
> +                   batadv_compare_eth(entry->orig, orig))
>                         continue;
> 
>                 /* this entry seems to match: same crc, not too old,

Shouldn't this check also be skipped if the orig parameter is a zero mac 
address? i.e.:

if (!is_zero_ether_addr(orig)) {
	if (!is_zero_ether_addr(entry->orig) && batadv_compare_eth(entry->orig, 
orig))
		continue;
}

Whether orig is zero can probably be checked once before the loop and the 
result cached in a bool variable.

A little comment may also be nice to explain this part a bit better.

The rest looks good.

Cheers,
      Simon
  
Linus Lüssing Sept. 9, 2020, 3:27 p.m. UTC | #2
On Wed, Sep 09, 2020 at 02:15:51PM +0200, Simon Wunderlich wrote:
> On Friday, September 4, 2020 8:28:03 PM CEST Linus Lüssing wrote:
> > @@ -1626,7 +1626,8 @@ bool batadv_bla_check_bcast_duplist(struct batadv_priv
> > *bat_priv, if (entry->crc != crc)
> >                         continue;
> > 
> > -               if (batadv_compare_eth(entry->orig, bcast_packet->orig))
> > +               if (!is_zero_ether_addr(entry->orig) &&
> > +                   batadv_compare_eth(entry->orig, orig))
> >                         continue;
> > 
> >                 /* this entry seems to match: same crc, not too old,
> 
> Shouldn't this check also be skipped if the orig parameter is a zero mac 
> address? i.e.:
> 
> if (!is_zero_ether_addr(orig)) {
> 	if (!is_zero_ether_addr(entry->orig) && batadv_compare_eth(entry->orig, 
> orig))
> 		continue;
> }

Would be redundant. If entry->orig is non-zero and
the compare_eth() says they are equal, then orig must also be
non-zero.

I initially wanted to leave the code as unchanged as possible for
net / maint. Should I do the restructuring to enhance readability, with
the bool in this patch or in additional patch for net-next?
  
Simon Wunderlich Sept. 10, 2020, 9:32 a.m. UTC | #3
On Wednesday, September 9, 2020 5:27:56 PM CEST Linus Lüssing wrote:
> On Wed, Sep 09, 2020 at 02:15:51PM +0200, Simon Wunderlich wrote:
> > On Friday, September 4, 2020 8:28:03 PM CEST Linus Lüssing wrote:
> > > @@ -1626,7 +1626,8 @@ bool batadv_bla_check_bcast_duplist(struct
> > > batadv_priv *bat_priv, if (entry->crc != crc)
> > > 
> > >                         continue;
> > > 
> > > -               if (batadv_compare_eth(entry->orig, bcast_packet->orig))
> > > +               if (!is_zero_ether_addr(entry->orig) &&
> > > +                   batadv_compare_eth(entry->orig, orig))
> > > 
> > >                         continue;
> > >                 
> > >                 /* this entry seems to match: same crc, not too old,
> > 
> > Shouldn't this check also be skipped if the orig parameter is a zero mac
> > address? i.e.:
> > 
> > if (!is_zero_ether_addr(orig)) {
> > 
> > 	if (!is_zero_ether_addr(entry->orig) && batadv_compare_eth(entry->orig,
> > 
> > orig))
> > 
> > 		continue;
> > 
> > }
> 
> Would be redundant. If entry->orig is non-zero and
> the compare_eth() says they are equal, then orig must also be
> non-zero.

OK good point, that's not really obvious (at least to me).

> 
> I initially wanted to leave the code as unchanged as possible for
> net / maint. Should I do the restructuring to enhance readability, with
> the bool in this patch or in additional patch for net-next?

Personally, I would prefer having a bit more readability or verbose comments 
in front of those kind of logic if statements. Or avoid those logic 
connections and have multiple "ifs" in a row where possible to enhance 
readbility.

This patch is pretty heavy already as is, adding a bool doesn't make a big 
difference IMHO.

Cheers,
       Simon
  

Patch

diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
index 9603a6d0..c509a83d 100644
--- a/net/batman-adv/bridge_loop_avoidance.c
+++ b/net/batman-adv/bridge_loop_avoidance.c
@@ -1580,14 +1580,16 @@  int batadv_bla_init(struct batadv_priv *bat_priv)
 }
 
 /**
- * batadv_bla_check_bcast_duplist() - Check if a frame is in the broadcast dup.
+ * batadv_bla_check_duplist() - Check if a frame is in the broadcast dup.
  * @bat_priv: the bat priv with all the soft interface information
- * @skb: contains the bcast_packet to be checked
- *
- * check if it is on our broadcast list. Another gateway might
- * have sent the same packet because it is connected to the same backbone,
- * so we have to remove this duplicate.
+ * @skb: contains the multicast packet to be checked
+ * @payload_ptr: pointer to position inside the head buffer of the skb
+ *  marking the start of the data to be CRC'ed
  *
+ * Check if it is on our broadcast list. Another gateway might have sent the
+ * same packet because it is connected to the same backbone, so we have to
+ * remove this duplicate.
+
  * This is performed by checking the CRC, which will tell us
  * with a good chance that it is the same packet. If it is furthermore
  * sent by another host, drop it. We allow equal packets from
@@ -1595,19 +1597,17 @@  int batadv_bla_init(struct batadv_priv *bat_priv)
  *
  * Return: true if a packet is in the duplicate list, false otherwise.
  */
-bool batadv_bla_check_bcast_duplist(struct batadv_priv *bat_priv,
-				    struct sk_buff *skb)
+static bool batadv_bla_check_duplist(struct batadv_priv *bat_priv,
+				     struct sk_buff *skb, u8 *payload_ptr,
+				     const u8 *orig)
 {
+	struct batadv_bcast_duplist_entry *entry;
+	bool ret = false;
 	int i, curr;
 	__be32 crc;
-	struct batadv_bcast_packet *bcast_packet;
-	struct batadv_bcast_duplist_entry *entry;
-	bool ret = false;
-
-	bcast_packet = (struct batadv_bcast_packet *)skb->data;
 
 	/* calculate the crc ... */
-	crc = batadv_skb_crc32(skb, (u8 *)(bcast_packet + 1));
+	crc = batadv_skb_crc32(skb, payload_ptr);
 
 	spin_lock_bh(&bat_priv->bla.bcast_duplist_lock);
 
@@ -1626,7 +1626,8 @@  bool batadv_bla_check_bcast_duplist(struct batadv_priv *bat_priv,
 		if (entry->crc != crc)
 			continue;
 
-		if (batadv_compare_eth(entry->orig, bcast_packet->orig))
+		if (!is_zero_ether_addr(entry->orig) &&
+		    batadv_compare_eth(entry->orig, orig))
 			continue;
 
 		/* this entry seems to match: same crc, not too old,
@@ -1643,7 +1644,7 @@  bool batadv_bla_check_bcast_duplist(struct batadv_priv *bat_priv,
 	entry = &bat_priv->bla.bcast_duplist[curr];
 	entry->crc = crc;
 	entry->entrytime = jiffies;
-	ether_addr_copy(entry->orig, bcast_packet->orig);
+	ether_addr_copy(entry->orig, orig);
 	bat_priv->bla.bcast_duplist_curr = curr;
 
 out:
@@ -1652,6 +1653,52 @@  bool batadv_bla_check_bcast_duplist(struct batadv_priv *bat_priv,
 	return ret;
 }
 
+/**
+ * batadv_bla_check_ucast_duplist() - Check if a frame is in the broadcast dup.
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: contains the multicast packet to be checked, decapsulated from a
+ *  unicast_packet
+ *
+ * Check if it is on our broadcast list. Another gateway might have sent the
+ * same packet because it is connected to the same backbone, so we have to
+ * remove this duplicate.
+ *
+ * Return: true if a packet is in the duplicate list, false otherwise.
+ */
+static bool batadv_bla_check_ucast_duplist(struct batadv_priv *bat_priv,
+					   struct sk_buff *skb)
+{
+	u8 orig[ETH_ALEN];
+
+	eth_zero_addr(orig);
+
+	return batadv_bla_check_duplist(bat_priv, skb, (u8 *)skb->data, orig);
+}
+
+/**
+ * batadv_bla_check_bcast_duplist() - Check if a frame is in the broadcast dup.
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: contains the bcast_packet to be checked
+ *
+ * Check if it is on our broadcast list. Another gateway might have sent the
+ * same packet because it is connected to the same backbone, so we have to
+ * remove this duplicate.
+ *
+ * Return: true if a packet is in the duplicate list, false otherwise.
+ */
+bool batadv_bla_check_bcast_duplist(struct batadv_priv *bat_priv,
+				    struct sk_buff *skb)
+{
+	struct batadv_bcast_packet *bcast_packet;
+	u8 *payload_ptr;
+
+	bcast_packet = (struct batadv_bcast_packet *)skb->data;
+	payload_ptr = (u8 *)(bcast_packet + 1);
+
+	return batadv_bla_check_duplist(bat_priv, skb, payload_ptr,
+					bcast_packet->orig);
+}
+
 /**
  * batadv_bla_is_backbone_gw_orig() - Check if the originator is a gateway for
  *  the VLAN identified by vid.
@@ -1852,6 +1899,13 @@  bool batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb,
 		    (!is_broadcast_ether_addr(ethhdr->h_dest) || is_bcast))
 			goto handled;
 
+	/* potential duplicates from foreign BLA backbone gateways via
+	 * multicast-in-unicast packets
+	 */
+	if (is_multicast_ether_addr(ethhdr->h_dest) && !is_bcast &&
+	    batadv_bla_check_ucast_duplist(bat_priv, skb))
+		goto handled;
+
 	ether_addr_copy(search_claim.addr, ethhdr->h_source);
 	search_claim.vid = vid;
 	claim = batadv_claim_hash_find(bat_priv, &search_claim);