Here are a few batman-adv bugfixes:

- fix stale receive device on merged fragments, by Zhiling Zou
 
 the others are written by Sven Eckelmann:
 
  - bla: fix potential CRC corruption issues (2 patches)
 
  - dat: avoid unaligned fault in IP extraction
 
  - dat: atomically update mac addresses
 
  - mcast: fix TX priority extraction for BATADV_FORW_MCAST
 
  - mcast: fix skb sharing and linearization (2 patches)
 
  - bla: fix freeing of claims on meshif deletion
 -----BEGIN PGP SIGNATURE-----
 
 iQJKBAABCgA0FiEE1ilQI7G+y+fdhnrfoSvjmEKSnqEFAmqIHjgWHHN3QHNpbW9u
 d3VuZGVybGljaC5kZQAKCRChK+OYQpKeoXfCEADMvQvyC1/dduuz4dusR76+Hltf
 yreEzZe7heS/Ap1Zn1XdUbtY4DR/5YwRQeKeS+5x0WIGHddvtEiOnRweNoHpgSfE
 szKGFxfqi9sp15WzSyAT/VuzvKDUPbWDQxjXGa8evZUBxkxTH9+6Xj39qeyPH6Jh
 u3GaboHy/TI9uSmwM7w1l+auTmTtf39+fULhhCWzEP4cYbx7XcSy+JaxNibyb09R
 fvCUok//nVqSc5WHeatrmi1b+h4Za3OU1jdj++q7eVW8B6XnrocADE0R4xxW+1qF
 OyUsofjwAUF8Khs+KIiakHUEc59AdgRO7ZV7jbKrpqTgR34CQs1kyZYoM/hYD/0Y
 Nr4qkwr5J2UxLgY7LhjeX/TDb7LvCqL0CT2EN82yxbEBJl0SBZcwXydbTbH/+6nu
 Mggf+f4HX47I1r5bsmOIRV6VhVM95Wx1Cosc9HRCYP2tTDqk8c1jmZ1gRSCJJMKb
 cgG7DBGMbp2rCqAwy+/8cOnQllbPRfFrPinAfNfI+BNYbKlc4H0KPPcsY6PvSHJ+
 A1kNPQ9GctknQfuxID88eR0IkVB718WeO8xecIgSRlSr1f3QdHxRC9tSYok/o+RP
 WP71Yzz1+keiR/l30KsAVEK7neVp/AboGtx295BjATjolxyvj4kxE4UhKXj39nGn
 J6HnPS4V4iw1apKQLQ==
 =YLMX
 -----END PGP SIGNATURE-----

Merge tag 'batadv-net-pullrequest-20260821' of https://git.open-mesh.org/batadv

Simon Wunderlich says:

====================
Here are a few batman-adv bugfixes:

 - fix stale receive device on merged fragments, by Zhiling Zou

the others are written by Sven Eckelmann:

 - bla: fix potential CRC corruption issues (2 patches)

 - dat: avoid unaligned fault in IP extraction

 - dat: atomically update mac addresses

 - mcast: fix TX priority extraction for BATADV_FORW_MCAST

 - mcast: fix skb sharing and linearization (2 patches)

 - bla: fix freeing of claims on meshif deletion

* tag 'batadv-net-pullrequest-20260821' of https://git.open-mesh.org/batadv:
  batman-adv: bla: fix freeing of claims on meshif deletion
  batman-adv: mcast: linearize skbuff for packet generation
  batman-adv: mcast: ensure unshared skb for multicast packets
  batman-adv: fix TX priority extraction for BATADV_FORW_MCAST
  batman-adv: dat: atomically update mac addresses
  batman-adv: dat: avoid unaligned fault in IP extraction
  batman-adv: bla: prevent CRC corruptions after claim flush
  batman-adv: bla: avoid CRC corruption due to parallel claim add
  batman-adv: fix stale receive device on merged fragments
====================

Link: https://patch.msgid.link/20260821094813.201800-1-sw@simonwunderlich.de
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-08-22 12:42:57 -07:00
commit e755276c9f
6 changed files with 101 additions and 65 deletions

View File

@ -325,11 +325,6 @@ batadv_bla_del_backbone_claims(struct batadv_bla_backbone_gw *backbone_gw)
}
spin_unlock_bh(list_lock);
}
/* all claims gone, initialize CRC */
spin_lock_bh(&backbone_gw->crc_lock);
backbone_gw->crc = BATADV_BLA_CRC_INIT;
spin_unlock_bh(&backbone_gw->crc_lock);
}
/**
@ -695,12 +690,14 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
struct batadv_bla_backbone_gw *old_backbone_gw;
struct batadv_bla_claim search_claim;
struct batadv_bla_claim *claim;
bool remove_crc = false;
int hash_added;
u16 claim_crc;
bool changed;
ether_addr_copy(search_claim.addr, mac);
search_claim.vid = vid;
claim = batadv_claim_hash_find(bat_priv, &search_claim);
claim_crc = crc16(0, mac, ETH_ALEN);
/* create a new claim entry if it does not exist yet. */
if (!claim) {
@ -732,43 +729,56 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
kfree(claim);
return;
}
} else {
WRITE_ONCE(claim->lasttime, jiffies);
if (claim->backbone_gw == backbone_gw)
/* no need to register a new backbone */
goto claim_free_ref;
spin_lock_bh(&backbone_gw->crc_lock);
backbone_gw->crc ^= claim_crc;
spin_unlock_bh(&backbone_gw->crc_lock);
WRITE_ONCE(backbone_gw->lasttime, jiffies);
batadv_claim_put(claim);
return;
}
WRITE_ONCE(claim->lasttime, jiffies);
/* replace backbone_gw atomically and adjust reference counters */
spin_lock_bh(&claim->backbone_lock);
if (claim->backbone_gw != backbone_gw) {
changed = true;
old_backbone_gw = claim->backbone_gw;
kref_get(&backbone_gw->refcount);
claim->backbone_gw = backbone_gw;
} else {
old_backbone_gw = NULL;
changed = false;
}
spin_unlock_bh(&claim->backbone_lock);
if (changed) {
batadv_dbg(BATADV_DBG_BLA, bat_priv,
"%s(): changing ownership for %pM, vid %d to gw %pM\n",
__func__, mac, batadv_print_vid(vid),
backbone_gw->orig);
remove_crc = true;
/* add claim address to new backbone_gw */
spin_lock_bh(&backbone_gw->crc_lock);
backbone_gw->crc ^= claim_crc;
spin_unlock_bh(&backbone_gw->crc_lock);
WRITE_ONCE(backbone_gw->lasttime, jiffies);
}
/* replace backbone_gw atomically and adjust reference counters */
spin_lock_bh(&claim->backbone_lock);
old_backbone_gw = claim->backbone_gw;
kref_get(&backbone_gw->refcount);
claim->backbone_gw = backbone_gw;
spin_unlock_bh(&claim->backbone_lock);
if (remove_crc) {
if (old_backbone_gw) {
/* remove claim address from old backbone_gw */
spin_lock_bh(&old_backbone_gw->crc_lock);
old_backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
old_backbone_gw->crc ^= claim_crc;
spin_unlock_bh(&old_backbone_gw->crc_lock);
batadv_backbone_gw_put(old_backbone_gw);
}
batadv_backbone_gw_put(old_backbone_gw);
/* add claim address to new backbone_gw */
spin_lock_bh(&backbone_gw->crc_lock);
backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
spin_unlock_bh(&backbone_gw->crc_lock);
WRITE_ONCE(backbone_gw->lasttime, jiffies);
claim_free_ref:
batadv_claim_put(claim);
}
@ -940,26 +950,18 @@ static bool batadv_handle_unclaim(struct batadv_priv *bat_priv,
const u8 *backbone_addr, const u8 *claim_addr,
unsigned short vid)
{
struct batadv_bla_backbone_gw *backbone_gw;
/* unclaim in any case if it is our own */
if (primary_if && batadv_compare_eth(backbone_addr,
primary_if->net_dev->dev_addr))
batadv_bla_send_claim(bat_priv, claim_addr, vid,
BATADV_CLAIM_TYPE_UNCLAIM);
backbone_gw = batadv_backbone_hash_find(bat_priv, backbone_addr, vid);
if (!backbone_gw)
return true;
/* this must be an UNCLAIM frame */
batadv_dbg(BATADV_DBG_BLA, bat_priv,
"%s(): UNCLAIM %pM on vid %d (sent by %pM)...\n", __func__,
claim_addr, batadv_print_vid(vid), backbone_gw->orig);
claim_addr, batadv_print_vid(vid), backbone_addr);
batadv_bla_del_claim(bat_priv, claim_addr, vid);
batadv_backbone_gw_put(backbone_gw);
return true;
}

View File

@ -316,7 +316,10 @@ static u8 *batadv_arp_hw_src(struct sk_buff *skb, int hdr_size)
*/
static __be32 batadv_arp_ip_src(struct sk_buff *skb, int hdr_size)
{
return *(__force __be32 *)(batadv_arp_hw_src(skb, hdr_size) + ETH_ALEN);
u8 *src = batadv_arp_hw_src(skb, hdr_size) + ETH_ALEN;
__be32 *ip = (__force __be32 *)src;
return get_unaligned(ip);
}
/**
@ -341,8 +344,9 @@ static u8 *batadv_arp_hw_dst(struct sk_buff *skb, int hdr_size)
static __be32 batadv_arp_ip_dst(struct sk_buff *skb, int hdr_size)
{
u8 *dst = batadv_arp_hw_src(skb, hdr_size) + ETH_ALEN * 2 + 4;
__be32 *ip = (__force __be32 *)dst;
return *(__force __be32 *)dst;
return get_unaligned(ip);
}
/**
@ -440,18 +444,19 @@ batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip,
static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip,
u8 *mac_addr, unsigned short vid)
{
u64 u64_mac = ether_addr_to_u64(mac_addr);
struct batadv_dat_entry *dat_entry;
int hash_added;
dat_entry = batadv_dat_entry_hash_find(bat_priv, ip, vid);
/* if this entry is already known, just update it */
if (dat_entry) {
if (!batadv_compare_eth(dat_entry->mac_addr, mac_addr))
ether_addr_copy(dat_entry->mac_addr, mac_addr);
atomic64_set(&dat_entry->mac_addr, u64_mac);
dat_entry->last_update = jiffies;
batadv_dbg(BATADV_DBG_DAT, bat_priv,
"Entry updated: %pI4 %pM (vid: %d)\n",
&dat_entry->ip, dat_entry->mac_addr,
&dat_entry->ip, mac_addr,
batadv_print_vid(vid));
goto out;
}
@ -462,7 +467,7 @@ static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip,
dat_entry->ip = ip;
dat_entry->vid = vid;
ether_addr_copy(dat_entry->mac_addr, mac_addr);
atomic64_set(&dat_entry->mac_addr, u64_mac);
dat_entry->last_update = jiffies;
kref_init(&dat_entry->refcount);
@ -478,7 +483,7 @@ static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip,
}
batadv_dbg(BATADV_DBG_DAT, bat_priv, "New entry added: %pI4 %pM (vid: %d)\n",
&dat_entry->ip, dat_entry->mac_addr, batadv_print_vid(vid));
&dat_entry->ip, mac_addr, batadv_print_vid(vid));
out:
batadv_dat_entry_put(dat_entry);
@ -935,6 +940,8 @@ batadv_dat_cache_dump_entry(struct sk_buff *msg, u32 portid,
struct netlink_callback *cb,
struct batadv_dat_entry *dat_entry)
{
u8 mac[ETH_ALEN];
u64 u64_mac;
int msecs;
void *hdr;
@ -947,11 +954,12 @@ batadv_dat_cache_dump_entry(struct sk_buff *msg, u32 portid,
genl_dump_check_consistent(cb, hdr);
msecs = jiffies_to_msecs(jiffies - dat_entry->last_update);
u64_mac = atomic64_read(&dat_entry->mac_addr);
u64_to_ether_addr(u64_mac, mac);
if (nla_put_in_addr(msg, BATADV_ATTR_DAT_CACHE_IP4ADDRESS,
dat_entry->ip) ||
nla_put(msg, BATADV_ATTR_DAT_CACHE_HWADDRESS, ETH_ALEN,
dat_entry->mac_addr) ||
nla_put(msg, BATADV_ATTR_DAT_CACHE_HWADDRESS, ETH_ALEN, mac) ||
nla_put_u16(msg, BATADV_ATTR_DAT_CACHE_VID, dat_entry->vid) ||
nla_put_u32(msg, BATADV_ATTR_LAST_SEEN_MSECS, msecs)) {
genlmsg_cancel(msg, hdr);
@ -1231,10 +1239,12 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
struct sk_buff *skb_new;
unsigned short vid;
bool ret = false;
u8 mac[ETH_ALEN];
int hdr_size = 0;
__be32 ip_dst;
__be32 ip_src;
u16 type = 0;
u64 u64_mac;
u8 *hw_src;
if (!READ_ONCE(bat_priv->distributed_arp_table))
@ -1263,6 +1273,9 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst, vid);
if (dat_entry) {
u64_mac = atomic64_read(&dat_entry->mac_addr);
u64_to_ether_addr(u64_mac, mac);
/* If the ARP request is destined for a local client the local
* client will answer itself. DAT would only generate a
* duplicate packet.
@ -1271,7 +1284,7 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
* additional DAT answer may trigger kernel warnings about
* a packet coming from the wrong port.
*/
if (batadv_is_my_client(bat_priv, dat_entry->mac_addr, vid)) {
if (batadv_is_my_client(bat_priv, mac, vid)) {
ret = true;
goto out;
}
@ -1281,18 +1294,16 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
* the backbone gws belonging to our backbone has claimed the
* destination.
*/
if (!batadv_bla_check_claim(bat_priv,
dat_entry->mac_addr, vid)) {
if (!batadv_bla_check_claim(bat_priv, mac, vid)) {
batadv_dbg(BATADV_DBG_DAT, bat_priv,
"Device %pM claimed by another backbone gw. Don't send ARP reply!",
dat_entry->mac_addr);
mac);
ret = true;
goto out;
}
skb_new = batadv_dat_arp_create_reply(bat_priv, ip_dst, ip_src,
dat_entry->mac_addr,
hw_src, vid);
mac, hw_src, vid);
if (!skb_new)
goto out;
@ -1336,8 +1347,10 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
struct sk_buff *skb_new;
unsigned short vid;
bool ret = false;
u8 mac[ETH_ALEN];
__be32 ip_src;
__be32 ip_dst;
u64 u64_mac;
u8 *hw_src;
u16 type;
int err;
@ -1367,8 +1380,11 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
if (!dat_entry)
goto out;
u64_mac = atomic64_read(&dat_entry->mac_addr);
u64_to_ether_addr(u64_mac, mac);
skb_new = batadv_dat_arp_create_reply(bat_priv, ip_dst, ip_src,
dat_entry->mac_addr, hw_src, vid);
mac, hw_src, vid);
if (!skb_new)
goto out;
@ -1460,8 +1476,10 @@ bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv,
struct batadv_dat_entry *dat_entry = NULL;
bool dropped = false;
unsigned short vid;
u8 mac[ETH_ALEN];
__be32 ip_src;
__be32 ip_dst;
u64 u64_mac;
u8 *hw_src;
u8 *hw_dst;
u16 type;
@ -1493,11 +1511,17 @@ bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv,
* this frame would lead to doubled receive of an ARP reply.
*/
dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_src, vid);
if (dat_entry && batadv_compare_eth(hw_src, dat_entry->mac_addr)) {
batadv_dbg(BATADV_DBG_DAT, bat_priv, "Doubled ARP reply removed: ARP MSG = [src: %pM-%pI4 dst: %pM-%pI4]; dat_entry: %pM-%pI4\n",
hw_src, &ip_src, hw_dst, &ip_dst,
dat_entry->mac_addr, &dat_entry->ip);
dropped = true;
if (dat_entry) {
u64_mac = atomic64_read(&dat_entry->mac_addr);
u64_to_ether_addr(u64_mac, mac);
if (batadv_compare_eth(hw_src, mac)) {
batadv_dbg(BATADV_DBG_DAT, bat_priv,
"Doubled ARP reply removed: ARP MSG = [src: %pM-%pI4 dst: %pM-%pI4]; dat_entry: %pM-%pI4\n",
hw_src, &ip_src, hw_dst, &ip_dst,
mac, &dat_entry->ip);
dropped = true;
}
}
/* Update our internal cache with both the IP addresses the node got

View File

@ -492,6 +492,10 @@ int batadv_batman_skb_recv(struct sk_buff *skb, struct net_device *dev,
if (!skb)
goto err_put;
/* Merged fragments re-enter here with reused skb metadata. */
skb->dev = dev;
skb->skb_iif = dev->ifindex;
/* packet should hold at least type and version */
if (unlikely(!pskb_may_pull(skb, 2)))
goto err_free;

View File

@ -305,6 +305,8 @@ static netdev_tx_t batadv_interface_tx(struct sk_buff *skb,
if (batadv_compare_eth(ethhdr->h_dest, ectp_addr))
goto dropped;
batadv_skb_set_priority(skb, 0);
gw_mode = READ_ONCE(bat_priv->gw.mode);
if (is_multicast_ether_addr(ethhdr->h_dest)) {
/* if gw mode is off, broadcast every packet */
@ -338,6 +340,9 @@ static netdev_tx_t batadv_interface_tx(struct sk_buff *skb,
send:
if (do_bcast && !is_broadcast_ether_addr(ethhdr->h_dest)) {
/* WARNING batadv_mcast_forw_mode might add more headers
* in front of the skb. and might even reallocate the skb
*/
forw_mode = batadv_mcast_forw_mode(bat_priv, skb, vid,
&mcast_is_routable);
switch (forw_mode) {
@ -355,8 +360,6 @@ static netdev_tx_t batadv_interface_tx(struct sk_buff *skb,
}
}
batadv_skb_set_priority(skb, 0);
/* ethernet packet should be broadcasted */
if (do_bcast) {
primary_if = batadv_primary_if_get_selected(bat_priv);

View File

@ -1105,8 +1105,11 @@ static int batadv_mcast_forw_expand_head(struct batadv_priv *bat_priv,
return -EINVAL;
}
if (skb_headroom(skb) < hdr_size &&
pskb_expand_head(skb, hdr_size, 0, GFP_ATOMIC) < 0)
if (skb_cow(skb, hdr_size) < 0)
return -ENOMEM;
/* batadv_mcast_forw_scrape() + batadv_mcast_forw_packet() require linearized skb */
if (skb_linearize(skb) < 0)
return -ENOMEM;
return 0;

View File

@ -2176,7 +2176,7 @@ struct batadv_dat_entry {
__be32 ip;
/** @mac_addr: the MAC address associated to the stored IPv4 */
u8 mac_addr[ETH_ALEN];
atomic64_t mac_addr;
/** @vid: the vlan ID associated to this entry */
unsigned short vid;