• Vladimir Oltean's avatar
    net: dsa: sja1105: fix multicast forwarding working only for last added mdb entry · 7cef293b
    Vladimir Oltean authored
    The commit cited in Fixes: did 2 things: it refactored the read-back
    polling from sja1105_dynamic_config_read() into a new function,
    sja1105_dynamic_config_wait_complete(), and it called that from
    sja1105_dynamic_config_write() too.
    
    What is problematic is the refactoring.
    
    The refactored code from sja1105_dynamic_config_poll_valid() works like
    the previous one, but the problem is that it uses another packed_buf[]
    SPI buffer, and there was code at the end of sja1105_dynamic_config_read()
    which was relying on the read-back packed_buf[]:
    
    	/* Don't dereference possibly NULL pointer - maybe caller
    	 * only wanted to see whether the entry existed or not.
    	 */
    	if (entry)
    		ops->entry_packing(packed_buf, entry, UNPACK);
    
    After the change, the packed_buf[] that this code sees is no longer the
    entry read back from hardware, but the original entry that the caller
    passed to the sja1105_dynamic_config_read(), packed into this buffer.
    
    This difference is the most notable with the SJA1105_SEARCH uses from
    sja1105pqrs_fdb_add() - used for both fdb and mdb. There, we have logic
    added by commit 728db843 ("net: dsa: sja1105: ignore the FDB entry
    for unknown multicast when adding a new address") to figure out whether
    the address we're trying to add matches on any existing hardware entry,
    with the exception of the catch-all multicast address.
    
    That logic was broken, because with sja1105_dynamic_config_read() not
    working properly, it doesn't return us the entry read back from
    hardware, but the entry that we passed to it. And, since for multicast,
    a match will always exist, it will tell us that any mdb entry already
    exists at index=0 L2 Address Lookup table. It is index=0 because the
    caller doesn't know the index - it wants to find it out, and
    sja1105_dynamic_config_read() does:
    
    	if (index < 0) { // SJA1105_SEARCH
    		/* Avoid copying a signed negative number to an u64 */
    		cmd.index = 0; // <- this
    		cmd.search = true;
    	} else {
    		cmd.index = index;
    		cmd.search = false;
    	}
    
    So, to the caller of sja1105_dynamic_config_read(), the returned info
    looks entirely legit, and it will add all mdb entries to FDB index 0.
    There, they will always overwrite each other (not to mention,
    potentially they can also overwrite a pre-existing bridge fdb entry),
    and the user-visible impact will be that only the last mdb entry will be
    forwarded as it should. The others won't (will be flooded or dropped,
    depending on the egress flood settings).
    
    Fixing is a bit more complicated, and involves either passing the same
    packed_buf[] to sja1105_dynamic_config_wait_complete(), or moving all
    the extra processing on the packed_buf[] to
    sja1105_dynamic_config_wait_complete(). I've opted for the latter,
    because it makes sja1105_dynamic_config_wait_complete() a bit more
    self-contained.
    
    Fixes: df405910 ("net: dsa: sja1105: wait for dynamic config command completion on writes too")
    Reported-by: default avatarYanan Yang <yanan.yang@nxp.com>
    Signed-off-by: default avatarVladimir Oltean <vladimir.oltean@nxp.com>
    Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
    7cef293b
sja1105_dynamic_config.c 48.7 KB