Here are batman-adv bugfixes, all by by Sven Eckelmann.

- fix batadv_skb_is_frag() kernel-doc
 
  - BATMAN V: stop OGMv2 on disabled interface
 
  - BATMAN IV: abort OGM send on tvlv append failure
 
  - BATMAN IV: reject oversized TVLV packets
 
  - tp_meter: fix race condition in send error reporting
 
  - tp_meter: avoid role confusion in tp_list
 
  - mcast: fix use-after-free in orig_node RCU release
 
  - BATMAN IV: recover OGM scheduling after forward packet error
 
  - bla: fix report_work leak on backbone_gw purge
 
  - bla: avoid double decrement of bla.num_requests
 
  - bla: avoid NULL-ptr deref for claim via dropped interface
 -----BEGIN PGP SIGNATURE-----
 
 iQJKBAABCgA0FiEE1ilQI7G+y+fdhnrfoSvjmEKSnqEFAmoNoHkWHHN3QHNpbW9u
 d3VuZGVybGljaC5kZQAKCRChK+OYQpKeoaFZD/4yN7deqhkVKj039EVNxoOOVOSi
 T0kQCOG6h20UiJX6j2l6QuoXF6vwBGDPFZfwCMKSMWXFhbuhzl8L0oEvZ+p6goho
 FyPgAWkWRcopPnSiJTQcFXVVtkku7EkPO2MjMLUEkU8YEKGfnBSWHUBr9+Mw0OUV
 rmrkRQcl7/BpWX++fM0U/qUFfKi8VuIPgQubPyC602/iInseNvx6Ju0abRJeTCF6
 YM4IugyxKoHCwHbnR2jNwl1PfaI7FyLF/ZCvi/2NpAfwnjcykBlMYoEj46Qh3dTD
 05ZIAHlgHAfMDHuW87rema6aYT0nCpSwtBaM4YE2/vim4hxRot6AN/ho/4JFHzHW
 GHCuhw3NxADeUJZBefGy4QEtGPIP/Odz4WcVTr/29TnOTKEAG2rsI4mwLoM4ogp/
 Wa0tlZWUZNHEsjPHSkEWqnR+X/+J8Q9W/RUP85gthVpMKvHyMf7YC2JBXlJ2QvmF
 HzmCjYLv83X4pftutf6R/EAy8MedJTSCoCzJGitIDd5qW3EG3yYBgz6ep4DD/4Hp
 4qWP3gdUbcPnL5mw3SOWYXhOKFQnOs68h5QsSRdEp4LYj0FExC6EZagRZUU7fGOu
 W5r7vsN6j83sBuZd5rQRIs1XNSm5RlUAfJAsWawKLI96l2xSuIrtmJq3zP/kc4cH
 q4Lc88OyebVE5t8/yA==
 =0Yhf
 -----END PGP SIGNATURE-----

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

Simon Wunderlich says:

====================
Here are batman-adv bugfixes, all by by Sven Eckelmann.

 - fix batadv_skb_is_frag() kernel-doc

 - BATMAN V: stop OGMv2 on disabled interface

 - BATMAN IV: abort OGM send on tvlv append failure

 - BATMAN IV: reject oversized TVLV packets

 - tp_meter: fix race condition in send error reporting

 - tp_meter: avoid role confusion in tp_list

 - mcast: fix use-after-free in orig_node RCU release

 - BATMAN IV: recover OGM scheduling after forward packet error

 - bla: fix report_work leak on backbone_gw purge

 - bla: avoid double decrement of bla.num_requests

 - bla: avoid NULL-ptr deref for claim via dropped interface

* tag 'batadv-net-pullrequest-20260520' of https://git.open-mesh.org/batadv:
  batman-adv: bla: avoid NULL-ptr deref for claim via dropped interface
  batman-adv: bla: avoid double decrement of bla.num_requests
  batman-adv: bla: fix report_work leak on backbone_gw purge
  batman-adv: iv: recover OGM scheduling after forward packet error
  batman-adv: mcast: fix use-after-free in orig_node RCU release
  batman-adv: tp_meter: avoid role confusion in tp_list
  batman-adv: tp_meter: fix race condition in send error reporting
  batman-adv: tvlv: reject oversized TVLV packets
  batman-adv: tvlv: abort OGM send on tvlv append failure
  batman-adv: v: stop OGMv2 on disabled interface
  batman-adv: fix batadv_skb_is_frag() kernel-doc
====================

Link: https://patch.msgid.link/20260520115422.53552-1-sw@simonwunderlich.de
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni 2026-05-21 15:59:10 +02:00
commit 42734af663
10 changed files with 299 additions and 141 deletions

View File

@ -224,6 +224,8 @@ static void batadv_iv_ogm_iface_disable(struct batadv_hard_iface *hard_iface)
hard_iface->bat_iv.ogm_buff = NULL;
mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex);
cancel_delayed_work_sync(&hard_iface->bat_iv.reschedule_work);
}
static void batadv_iv_ogm_iface_update_mac(struct batadv_hard_iface *hard_iface)
@ -536,8 +538,10 @@ batadv_iv_ogm_can_aggregate(const struct batadv_ogm_packet *new_bat_ogm_packet,
* @if_incoming: interface where the packet was received
* @if_outgoing: interface for which the retransmission should be considered
* @own_packet: true if it is a self-generated ogm
*
* Return: whether forward packet was scheduled
*/
static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
static bool batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
int packet_len, unsigned long send_time,
bool direct_link,
struct batadv_hard_iface *if_incoming,
@ -561,13 +565,13 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
skb = netdev_alloc_skb_ip_align(NULL, skb_size);
if (!skb)
return;
return false;
forw_packet_aggr = batadv_forw_packet_alloc(if_incoming, if_outgoing,
queue_left, bat_priv, skb);
if (!forw_packet_aggr) {
kfree_skb(skb);
return;
return false;
}
forw_packet_aggr->skb->priority = TC_PRIO_CONTROL;
@ -590,6 +594,8 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
batadv_iv_send_outstanding_bat_ogm_packet);
batadv_forw_packet_ogmv1_queue(bat_priv, forw_packet_aggr, send_time);
return true;
}
/* aggregate a new packet into the existing ogm packet */
@ -617,8 +623,10 @@ static void batadv_iv_ogm_aggregate(struct batadv_forw_packet *forw_packet_aggr,
* @if_outgoing: interface for which the retransmission should be considered
* @own_packet: true if it is a self-generated ogm
* @send_time: timestamp (jiffies) when the packet is to be sent
*
* Return: whether forward packet was scheduled
*/
static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv,
static bool batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv,
unsigned char *packet_buff,
int packet_len,
struct batadv_hard_iface *if_incoming,
@ -670,14 +678,16 @@ static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv,
if (!own_packet && atomic_read(&bat_priv->aggregated_ogms))
send_time += max_aggregation_jiffies;
batadv_iv_ogm_aggregate_new(packet_buff, packet_len,
send_time, direct_link,
if_incoming, if_outgoing,
own_packet);
return batadv_iv_ogm_aggregate_new(packet_buff, packet_len,
send_time, direct_link,
if_incoming, if_outgoing,
own_packet);
} else {
batadv_iv_ogm_aggregate(forw_packet_aggr, packet_buff,
packet_len, direct_link);
spin_unlock_bh(&bat_priv->forw_bat_list_lock);
return true;
}
}
@ -790,6 +800,9 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
u32 seqno;
u16 tvlv_len = 0;
unsigned long send_time;
bool reschedule = false;
bool scheduled;
int ret;
lockdep_assert_held(&hard_iface->bat_iv.ogm_buff_mutex);
@ -813,9 +826,15 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
* appended as it may alter the tt tvlv container
*/
batadv_tt_local_commit_changes(bat_priv);
tvlv_len = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff,
ogm_buff_len,
BATADV_OGM_HLEN);
ret = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff,
ogm_buff_len,
BATADV_OGM_HLEN);
if (ret < 0) {
reschedule = true;
goto out;
}
tvlv_len = ret;
}
batadv_ogm_packet = (struct batadv_ogm_packet *)(*ogm_buff);
@ -834,8 +853,11 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
/* OGMs from secondary interfaces are only scheduled on their
* respective interfaces.
*/
batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, *ogm_buff_len,
hard_iface, hard_iface, 1, send_time);
scheduled = batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, *ogm_buff_len,
hard_iface, hard_iface, 1, send_time);
if (!scheduled)
reschedule = true;
goto out;
}
@ -847,15 +869,28 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
if (!kref_get_unless_zero(&tmp_hard_iface->refcount))
continue;
batadv_iv_ogm_queue_add(bat_priv, *ogm_buff,
*ogm_buff_len, hard_iface,
tmp_hard_iface, 1, send_time);
scheduled = batadv_iv_ogm_queue_add(bat_priv, *ogm_buff,
*ogm_buff_len, hard_iface,
tmp_hard_iface, 1, send_time);
batadv_hardif_put(tmp_hard_iface);
if (!scheduled && tmp_hard_iface == hard_iface)
reschedule = true;
}
rcu_read_unlock();
out:
if (reschedule) {
/* there was a failure scheduling the own forward packet.
* as result, the batadv_iv_send_outstanding_bat_ogm_packet()
* work item is no longer scheduled. it is therefore necessary
* to reschedule it manually
*/
queue_delayed_work(batadv_event_workqueue,
&hard_iface->bat_iv.reschedule_work,
msecs_to_jiffies(atomic_read(&bat_priv->orig_interval)));
}
batadv_hardif_put(primary_if);
}
@ -870,6 +905,17 @@ static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface)
mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex);
}
static void batadv_iv_ogm_reschedule(struct work_struct *work)
{
struct delayed_work *delayed_work = to_delayed_work(work);
struct batadv_hard_iface *hard_iface;
hard_iface = container_of(delayed_work,
struct batadv_hard_iface,
bat_iv.reschedule_work);
batadv_iv_ogm_schedule(hard_iface);
}
/**
* batadv_iv_orig_ifinfo_sum() - Get bcast_own sum for originator over interface
* @orig_node: originator which reproadcasted the OGMs directly
@ -2262,6 +2308,8 @@ batadv_iv_ogm_neigh_is_sob(struct batadv_neigh_node *neigh1,
static void batadv_iv_iface_enabled(struct batadv_hard_iface *hard_iface)
{
INIT_DELAYED_WORK(&hard_iface->bat_iv.reschedule_work, batadv_iv_ogm_reschedule);
/* begin scheduling originator messages on that interface */
batadv_iv_ogm_schedule(hard_iface);
}

View File

@ -113,14 +113,14 @@ static void batadv_v_ogm_start_timer(struct batadv_priv *bat_priv)
/**
* batadv_v_ogm_send_to_if() - send a batman ogm using a given interface
* @bat_priv: the bat priv with all the mesh interface information
* @skb: the OGM to send
* @hard_iface: the interface to use to send the OGM
*/
static void batadv_v_ogm_send_to_if(struct sk_buff *skb,
static void batadv_v_ogm_send_to_if(struct batadv_priv *bat_priv,
struct sk_buff *skb,
struct batadv_hard_iface *hard_iface)
{
struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface);
if (hard_iface->if_status != BATADV_IF_ACTIVE) {
kfree_skb(skb);
return;
@ -187,6 +187,7 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface)
/**
* batadv_v_ogm_aggr_send() - flush & send aggregation queue
* @bat_priv: the bat priv with all the mesh interface information
* @hard_iface: the interface with the aggregation queue to flush
*
* Aggregates all OGMv2 packets currently in the aggregation queue into a
@ -196,7 +197,8 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface)
*
* Caller needs to hold the hard_iface->bat_v.aggr_list.lock.
*/
static void batadv_v_ogm_aggr_send(struct batadv_hard_iface *hard_iface)
static void batadv_v_ogm_aggr_send(struct batadv_priv *bat_priv,
struct batadv_hard_iface *hard_iface)
{
unsigned int aggr_len = hard_iface->bat_v.aggr_len;
struct sk_buff *skb_aggr;
@ -226,27 +228,32 @@ static void batadv_v_ogm_aggr_send(struct batadv_hard_iface *hard_iface)
consume_skb(skb);
}
batadv_v_ogm_send_to_if(skb_aggr, hard_iface);
batadv_v_ogm_send_to_if(bat_priv, skb_aggr, hard_iface);
}
/**
* batadv_v_ogm_queue_on_if() - queue a batman ogm on a given interface
* @bat_priv: the bat priv with all the mesh interface information
* @skb: the OGM to queue
* @hard_iface: the interface to queue the OGM on
*/
static void batadv_v_ogm_queue_on_if(struct sk_buff *skb,
static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv,
struct sk_buff *skb,
struct batadv_hard_iface *hard_iface)
{
struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface);
if (hard_iface->mesh_iface != bat_priv->mesh_iface) {
kfree_skb(skb);
return;
}
if (!atomic_read(&bat_priv->aggregated_ogms)) {
batadv_v_ogm_send_to_if(skb, hard_iface);
batadv_v_ogm_send_to_if(bat_priv, skb, hard_iface);
return;
}
spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
if (!batadv_v_ogm_queue_left(skb, hard_iface))
batadv_v_ogm_aggr_send(hard_iface);
batadv_v_ogm_aggr_send(bat_priv, hard_iface);
hard_iface->bat_v.aggr_len += batadv_v_ogm_len(skb);
__skb_queue_tail(&hard_iface->bat_v.aggr_list, skb);
@ -262,10 +269,10 @@ static void batadv_v_ogm_send_meshif(struct batadv_priv *bat_priv)
struct batadv_hard_iface *hard_iface;
struct batadv_ogm2_packet *ogm_packet;
struct sk_buff *skb, *skb_tmp;
unsigned char *ogm_buff;
unsigned char **ogm_buff;
struct list_head *iter;
int ogm_buff_len;
u16 tvlv_len = 0;
int *ogm_buff_len;
u16 tvlv_len;
int ret;
lockdep_assert_held(&bat_priv->bat_v.ogm_buff_mutex);
@ -273,25 +280,27 @@ static void batadv_v_ogm_send_meshif(struct batadv_priv *bat_priv)
if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING)
goto out;
ogm_buff = bat_priv->bat_v.ogm_buff;
ogm_buff_len = bat_priv->bat_v.ogm_buff_len;
ogm_buff = &bat_priv->bat_v.ogm_buff;
ogm_buff_len = &bat_priv->bat_v.ogm_buff_len;
/* tt changes have to be committed before the tvlv data is
* appended as it may alter the tt tvlv container
*/
batadv_tt_local_commit_changes(bat_priv);
tvlv_len = batadv_tvlv_container_ogm_append(bat_priv, &ogm_buff,
&ogm_buff_len,
BATADV_OGM2_HLEN);
ret = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff,
ogm_buff_len,
BATADV_OGM2_HLEN);
if (ret < 0)
goto reschedule;
bat_priv->bat_v.ogm_buff = ogm_buff;
bat_priv->bat_v.ogm_buff_len = ogm_buff_len;
tvlv_len = ret;
skb = netdev_alloc_skb_ip_align(NULL, ETH_HLEN + ogm_buff_len);
skb = netdev_alloc_skb_ip_align(NULL, ETH_HLEN + *ogm_buff_len);
if (!skb)
goto reschedule;
skb_reserve(skb, ETH_HLEN);
skb_put_data(skb, ogm_buff, ogm_buff_len);
skb_put_data(skb, *ogm_buff, *ogm_buff_len);
ogm_packet = (struct batadv_ogm2_packet *)skb->data;
ogm_packet->seqno = htonl(atomic_read(&bat_priv->bat_v.ogm_seqno));
@ -343,7 +352,7 @@ static void batadv_v_ogm_send_meshif(struct batadv_priv *bat_priv)
break;
}
batadv_v_ogm_queue_on_if(skb_tmp, hard_iface);
batadv_v_ogm_queue_on_if(bat_priv, skb_tmp, hard_iface);
batadv_hardif_put(hard_iface);
}
rcu_read_unlock();
@ -383,12 +392,14 @@ void batadv_v_ogm_aggr_work(struct work_struct *work)
{
struct batadv_hard_iface_bat_v *batv;
struct batadv_hard_iface *hard_iface;
struct batadv_priv *bat_priv;
batv = container_of(work, struct batadv_hard_iface_bat_v, aggr_wq.work);
hard_iface = container_of(batv, struct batadv_hard_iface, bat_v);
bat_priv = netdev_priv(hard_iface->mesh_iface);
spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
batadv_v_ogm_aggr_send(hard_iface);
batadv_v_ogm_aggr_send(bat_priv, hard_iface);
spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
batadv_v_ogm_start_queue_timer(hard_iface);
@ -578,7 +589,7 @@ static void batadv_v_ogm_forward(struct batadv_priv *bat_priv,
if_outgoing->net_dev->name, ntohl(ogm_forward->throughput),
ogm_forward->ttl, if_incoming->net_dev->name);
batadv_v_ogm_queue_on_if(skb, if_outgoing);
batadv_v_ogm_queue_on_if(bat_priv, skb, if_outgoing);
out:
batadv_orig_ifinfo_put(orig_ifinfo);

View File

@ -356,12 +356,14 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, const u8 *mac,
sizeof(local_claim_dest));
local_claim_dest.type = claimtype;
mesh_iface = primary_if->mesh_iface;
mesh_iface = READ_ONCE(primary_if->mesh_iface);
if (!mesh_iface)
goto out;
skb = arp_create(ARPOP_REPLY, ETH_P_ARP,
/* IP DST: 0.0.0.0 */
zeroip,
primary_if->mesh_iface,
mesh_iface,
/* IP SRC: 0.0.0.0 */
zeroip,
/* Ethernet DST: Broadcast */
@ -514,8 +516,8 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, const u8 *orig,
entry->crc = BATADV_BLA_CRC_INIT;
entry->bat_priv = bat_priv;
spin_lock_init(&entry->crc_lock);
atomic_set(&entry->request_sent, 0);
atomic_set(&entry->wait_periods, 0);
entry->state = BATADV_BLA_BACKBONE_GW_SYNCED;
entry->wait_periods = 0;
ether_addr_copy(entry->orig, orig);
INIT_WORK(&entry->report_work, batadv_bla_loopdetect_report);
kref_init(&entry->refcount);
@ -544,9 +546,13 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, const u8 *orig,
batadv_bla_send_announce(bat_priv, entry);
/* this will be decreased in the worker thread */
atomic_inc(&entry->request_sent);
atomic_set(&entry->wait_periods, BATADV_BLA_WAIT_PERIODS);
atomic_inc(&bat_priv->bla.num_requests);
spin_lock_bh(&bat_priv->bla.num_requests_lock);
if (entry->state == BATADV_BLA_BACKBONE_GW_SYNCED) {
entry->state = BATADV_BLA_BACKBONE_GW_UNSYNCED;
entry->wait_periods = BATADV_BLA_WAIT_PERIODS;
atomic_inc(&bat_priv->bla.num_requests);
}
spin_unlock_bh(&bat_priv->bla.num_requests_lock);
}
return entry;
@ -649,10 +655,12 @@ static void batadv_bla_send_request(struct batadv_bla_backbone_gw *backbone_gw)
backbone_gw->vid, BATADV_CLAIM_TYPE_REQUEST);
/* no local broadcasts should be sent or received, for now. */
if (!atomic_read(&backbone_gw->request_sent)) {
spin_lock_bh(&backbone_gw->bat_priv->bla.num_requests_lock);
if (backbone_gw->state == BATADV_BLA_BACKBONE_GW_SYNCED) {
backbone_gw->state = BATADV_BLA_BACKBONE_GW_UNSYNCED;
atomic_inc(&backbone_gw->bat_priv->bla.num_requests);
atomic_set(&backbone_gw->request_sent, 1);
}
spin_unlock_bh(&backbone_gw->bat_priv->bla.num_requests_lock);
}
/**
@ -873,10 +881,12 @@ static bool batadv_handle_announce(struct batadv_priv *bat_priv, u8 *an_addr,
/* if we have sent a request and the crc was OK,
* we can allow traffic again.
*/
if (atomic_read(&backbone_gw->request_sent)) {
spin_lock_bh(&bat_priv->bla.num_requests_lock);
if (backbone_gw->state == BATADV_BLA_BACKBONE_GW_UNSYNCED) {
backbone_gw->state = BATADV_BLA_BACKBONE_GW_SYNCED;
atomic_dec(&backbone_gw->bat_priv->bla.num_requests);
atomic_set(&backbone_gw->request_sent, 0);
}
spin_unlock_bh(&bat_priv->bla.num_requests_lock);
}
batadv_backbone_gw_put(backbone_gw);
@ -1224,6 +1234,7 @@ static void batadv_bla_purge_backbone_gw(struct batadv_priv *bat_priv, int now)
struct hlist_head *head;
struct batadv_hashtable *hash;
spinlock_t *list_lock; /* protects write access to the hash lists */
bool purged;
int i;
hash = bat_priv->bla.backbone_hash;
@ -1234,30 +1245,49 @@ static void batadv_bla_purge_backbone_gw(struct batadv_priv *bat_priv, int now)
head = &hash->table[i];
list_lock = &hash->list_locks[i];
spin_lock_bh(list_lock);
hlist_for_each_entry_safe(backbone_gw, node_tmp,
head, hash_entry) {
if (now)
goto purge_now;
if (!batadv_has_timed_out(backbone_gw->lasttime,
BATADV_BLA_BACKBONE_TIMEOUT))
continue;
do {
purged = false;
batadv_dbg(BATADV_DBG_BLA, backbone_gw->bat_priv,
"%s(): backbone gw %pM timed out\n",
__func__, backbone_gw->orig);
spin_lock_bh(list_lock);
hlist_for_each_entry_safe(backbone_gw, node_tmp,
head, hash_entry) {
if (now)
goto purge_now;
if (!batadv_has_timed_out(backbone_gw->lasttime,
BATADV_BLA_BACKBONE_TIMEOUT))
continue;
batadv_dbg(BATADV_DBG_BLA, backbone_gw->bat_priv,
"%s(): backbone gw %pM timed out\n",
__func__, backbone_gw->orig);
purge_now:
/* don't wait for the pending request anymore */
if (atomic_read(&backbone_gw->request_sent))
atomic_dec(&bat_priv->bla.num_requests);
purged = true;
batadv_bla_del_backbone_claims(backbone_gw);
/* don't wait for the pending request anymore */
spin_lock_bh(&bat_priv->bla.num_requests_lock);
if (backbone_gw->state == BATADV_BLA_BACKBONE_GW_UNSYNCED)
atomic_dec(&bat_priv->bla.num_requests);
hlist_del_rcu(&backbone_gw->hash_entry);
batadv_backbone_gw_put(backbone_gw);
}
spin_unlock_bh(list_lock);
backbone_gw->state = BATADV_BLA_BACKBONE_GW_STOPPED;
spin_unlock_bh(&bat_priv->bla.num_requests_lock);
batadv_bla_del_backbone_claims(backbone_gw);
hlist_del_rcu(&backbone_gw->hash_entry);
break;
}
spin_unlock_bh(list_lock);
if (purged) {
/* reference for pending report_work */
if (cancel_work_sync(&backbone_gw->report_work))
batadv_backbone_gw_put(backbone_gw);
/* reference for hash_entry */
batadv_backbone_gw_put(backbone_gw);
}
} while (purged);
}
}
@ -1492,7 +1522,7 @@ static void batadv_bla_periodic_work(struct work_struct *work)
batadv_bla_send_loopdetect(bat_priv,
backbone_gw);
/* request_sent is only set after creation to avoid
/* state is only set to unsynced after creation to avoid
* problems when we are not yet known as backbone gw
* in the backbone.
*
@ -1501,14 +1531,21 @@ static void batadv_bla_periodic_work(struct work_struct *work)
* some grace time.
*/
if (atomic_read(&backbone_gw->request_sent) == 0)
continue;
spin_lock_bh(&bat_priv->bla.num_requests_lock);
if (backbone_gw->state != BATADV_BLA_BACKBONE_GW_UNSYNCED)
goto unlock_next;
if (!atomic_dec_and_test(&backbone_gw->wait_periods))
continue;
if (backbone_gw->wait_periods > 0)
backbone_gw->wait_periods--;
if (backbone_gw->wait_periods > 0)
goto unlock_next;
backbone_gw->state = BATADV_BLA_BACKBONE_GW_SYNCED;
atomic_dec(&backbone_gw->bat_priv->bla.num_requests);
atomic_set(&backbone_gw->request_sent, 0);
unlock_next:
spin_unlock_bh(&bat_priv->bla.num_requests_lock);
}
rcu_read_unlock();
}

View File

@ -305,10 +305,10 @@ batadv_frag_merge_packets(struct hlist_head *chain)
}
/**
* batadv_skb_is_frag() - check if newly merged skb is gain a unicast packet
* batadv_skb_is_frag() - check if newly merged skb contains unicast fragment
* @skb: newly merged skb
*
* Return: if newly skb is of type BATADV_UNICAST_FRAG
* Return: if newly merged skb is of type BATADV_UNICAST_FRAG
*/
static bool batadv_skb_is_frag(struct sk_buff *skb)
{

View File

@ -787,6 +787,7 @@ static int batadv_meshif_init_late(struct net_device *dev)
atomic_set(&bat_priv->tt.ogm_append_cnt, 0);
#ifdef CONFIG_BATMAN_ADV_BLA
atomic_set(&bat_priv->bla.num_requests, 0);
spin_lock_init(&bat_priv->bla.num_requests_lock);
#endif
atomic_set(&bat_priv->tp_num, 0);

View File

@ -835,8 +835,6 @@ static void batadv_orig_node_free_rcu(struct rcu_head *rcu)
orig_node = container_of(rcu, struct batadv_orig_node, rcu);
batadv_mcast_purge_orig(orig_node);
batadv_frag_purge_orig(orig_node, NULL);
kfree(orig_node->tt_buff);
@ -887,6 +885,8 @@ void batadv_orig_node_release(struct kref *ref)
}
spin_unlock_bh(&orig_node->vlan_list_lock);
batadv_mcast_purge_orig(orig_node);
call_rcu(&orig_node->rcu, batadv_orig_node_free_rcu);
}

View File

@ -255,6 +255,7 @@ static void batadv_tp_batctl_error_notify(enum batadv_tp_meter_reason reason,
* batadv_tp_list_find() - find a tp_vars object in the global list
* @bat_priv: the bat priv with all the mesh interface information
* @dst: the other endpoint MAC address to look for
* @role: role of the session
*
* Look for a tp_vars object matching dst as end_point and return it after
* having increment the refcounter. Return NULL is not found
@ -262,7 +263,8 @@ static void batadv_tp_batctl_error_notify(enum batadv_tp_meter_reason reason,
* Return: matching tp_vars or NULL when no tp_vars with @dst was found
*/
static struct batadv_tp_vars *batadv_tp_list_find(struct batadv_priv *bat_priv,
const u8 *dst)
const u8 *dst,
enum batadv_tp_meter_role role)
{
struct batadv_tp_vars *pos, *tp_vars = NULL;
@ -271,6 +273,9 @@ static struct batadv_tp_vars *batadv_tp_list_find(struct batadv_priv *bat_priv,
if (!batadv_compare_eth(pos->other_end, dst))
continue;
if (pos->role != role)
continue;
/* most of the time this function is invoked during the normal
* process..it makes sens to pay more when the session is
* finished and to speed the process up during the measurement
@ -286,12 +291,33 @@ static struct batadv_tp_vars *batadv_tp_list_find(struct batadv_priv *bat_priv,
return tp_vars;
}
/**
* batadv_tp_list_active() - check if session from/to destination is ongoing
* @bat_priv: the bat priv with all the mesh interface information
* @dst: the other endpoint MAC address to look for
*
* Return: if matching session with @dst was found
*/
static bool batadv_tp_list_active(struct batadv_priv *bat_priv, const u8 *dst)
__must_hold(&bat_priv->tp_list_lock)
{
struct batadv_tp_vars *tp_vars;
hlist_for_each_entry_rcu(tp_vars, &bat_priv->tp_list, list) {
if (batadv_compare_eth(tp_vars->other_end, dst))
return true;
}
return false;
}
/**
* batadv_tp_list_find_session() - find tp_vars session object in the global
* list
* @bat_priv: the bat priv with all the mesh interface information
* @dst: the other endpoint MAC address to look for
* @session: session identifier
* @role: role of the session
*
* Look for a tp_vars object matching dst as end_point, session as tp meter
* session and return it after having increment the refcounter. Return NULL
@ -301,7 +327,7 @@ static struct batadv_tp_vars *batadv_tp_list_find(struct batadv_priv *bat_priv,
*/
static struct batadv_tp_vars *
batadv_tp_list_find_session(struct batadv_priv *bat_priv, const u8 *dst,
const u8 *session)
const u8 *session, enum batadv_tp_meter_role role)
{
struct batadv_tp_vars *pos, *tp_vars = NULL;
@ -313,6 +339,9 @@ batadv_tp_list_find_session(struct batadv_priv *bat_priv, const u8 *dst,
if (memcmp(pos->session, session, sizeof(pos->session)) != 0)
continue;
if (pos->role != role)
continue;
/* most of the time this function is invoked during the normal
* process..it makes sense to pay more when the session is
* finished and to speed the process up during the measurement
@ -413,11 +442,14 @@ static void batadv_tp_sender_cleanup(struct batadv_tp_vars *tp_vars)
static void batadv_tp_sender_end(struct batadv_priv *bat_priv,
struct batadv_tp_vars *tp_vars)
{
enum batadv_tp_meter_reason reason;
u32 session_cookie;
reason = atomic_read(&tp_vars->send_result);
batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
"Test towards %pM finished..shutting down (reason=%d)\n",
tp_vars->other_end, tp_vars->reason);
tp_vars->other_end, reason);
batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
"Last timing stats: SRTT=%ums RTTVAR=%ums RTO=%ums\n",
@ -430,7 +462,7 @@ static void batadv_tp_sender_end(struct batadv_priv *bat_priv,
session_cookie = batadv_tp_session_cookie(tp_vars->session,
tp_vars->icmp_uid);
batadv_tp_batctl_notify(tp_vars->reason,
batadv_tp_batctl_notify(reason,
tp_vars->other_end,
bat_priv,
tp_vars->start_time,
@ -446,10 +478,18 @@ static void batadv_tp_sender_end(struct batadv_priv *bat_priv,
static void batadv_tp_sender_shutdown(struct batadv_tp_vars *tp_vars,
enum batadv_tp_meter_reason reason)
{
if (atomic_xchg(&tp_vars->sending, 0) != 1)
return;
atomic_cmpxchg(&tp_vars->send_result, 0, reason);
}
tp_vars->reason = reason;
/**
* batadv_tp_sender_stopped() - check if tp session was stopped with reason
* @tp_vars: the private data of the current TP meter session
*
* Return: whether stop reason was found
*/
static bool batadv_tp_sender_stopped(struct batadv_tp_vars *tp_vars)
{
return atomic_read(&tp_vars->send_result) != 0;
}
/**
@ -479,7 +519,7 @@ static void batadv_tp_reset_sender_timer(struct batadv_tp_vars *tp_vars)
/* most of the time this function is invoked while normal packet
* reception...
*/
if (unlikely(atomic_read(&tp_vars->sending) == 0))
if (unlikely(batadv_tp_sender_stopped(tp_vars)))
/* timer ref will be dropped in batadv_tp_sender_cleanup */
return;
@ -499,7 +539,7 @@ static void batadv_tp_sender_timeout(struct timer_list *t)
struct batadv_tp_vars *tp_vars = timer_container_of(tp_vars, t, timer);
struct batadv_priv *bat_priv = tp_vars->bat_priv;
if (atomic_read(&tp_vars->sending) == 0)
if (batadv_tp_sender_stopped(tp_vars))
return;
/* if the user waited long enough...shutdown the test */
@ -654,14 +694,11 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
/* find the tp_vars */
tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
icmp->session);
icmp->session, BATADV_TP_SENDER);
if (unlikely(!tp_vars))
return;
if (unlikely(tp_vars->role != BATADV_TP_SENDER))
goto out;
if (unlikely(atomic_read(&tp_vars->sending) == 0))
if (unlikely(batadv_tp_sender_stopped(tp_vars)))
goto out;
/* old ACK? silently drop it.. */
@ -827,21 +864,21 @@ static int batadv_tp_send(void *arg)
if (unlikely(tp_vars->role != BATADV_TP_SENDER)) {
err = BATADV_TP_REASON_DST_UNREACHABLE;
tp_vars->reason = err;
batadv_tp_sender_shutdown(tp_vars, err);
goto out;
}
orig_node = batadv_orig_hash_find(bat_priv, tp_vars->other_end);
if (unlikely(!orig_node)) {
err = BATADV_TP_REASON_DST_UNREACHABLE;
tp_vars->reason = err;
batadv_tp_sender_shutdown(tp_vars, err);
goto out;
}
primary_if = batadv_primary_if_get_selected(bat_priv);
if (unlikely(!primary_if)) {
err = BATADV_TP_REASON_DST_UNREACHABLE;
tp_vars->reason = err;
batadv_tp_sender_shutdown(tp_vars, err);
goto out;
}
@ -860,7 +897,7 @@ static int batadv_tp_send(void *arg)
queue_delayed_work(batadv_event_workqueue, &tp_vars->finish_work,
msecs_to_jiffies(tp_vars->test_length));
while (atomic_read(&tp_vars->sending) != 0) {
while (!batadv_tp_sender_stopped(tp_vars)) {
if (unlikely(!batadv_tp_avail(tp_vars, payload_len))) {
batadv_tp_wait_available(tp_vars, payload_len);
continue;
@ -883,8 +920,7 @@ static int batadv_tp_send(void *arg)
"Meter: %s() cannot send packets (%d)\n",
__func__, err);
/* ensure nobody else tries to stop the thread now */
if (atomic_xchg(&tp_vars->sending, 0) == 1)
tp_vars->reason = err;
batadv_tp_sender_shutdown(tp_vars, err);
break;
}
@ -970,10 +1006,8 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
return;
}
tp_vars = batadv_tp_list_find(bat_priv, dst);
if (tp_vars) {
if (batadv_tp_list_active(bat_priv, dst)) {
spin_unlock_bh(&bat_priv->tp_list_lock);
batadv_tp_vars_put(tp_vars);
batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
"Meter: test to or from the same node already ongoing, aborting\n");
batadv_tp_batctl_error_notify(BATADV_TP_REASON_ALREADY_ONGOING,
@ -1006,7 +1040,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
ether_addr_copy(tp_vars->other_end, dst);
kref_init(&tp_vars->refcount);
tp_vars->role = BATADV_TP_SENDER;
atomic_set(&tp_vars->sending, 1);
atomic_set(&tp_vars->send_result, 0);
memcpy(tp_vars->session, session_id, sizeof(session_id));
tp_vars->icmp_uid = icmp_uid;
@ -1094,18 +1128,14 @@ void batadv_tp_stop(struct batadv_priv *bat_priv, const u8 *dst,
if (!orig_node)
return;
tp_vars = batadv_tp_list_find(bat_priv, orig_node->orig);
tp_vars = batadv_tp_list_find(bat_priv, orig_node->orig, BATADV_TP_SENDER);
if (!tp_vars) {
batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
"Meter: trying to interrupt an already over connection\n");
goto out_put_orig_node;
}
if (unlikely(tp_vars->role != BATADV_TP_SENDER))
goto out_put_tp_vars;
batadv_tp_sender_shutdown(tp_vars, return_value);
out_put_tp_vars:
batadv_tp_vars_put(tp_vars);
out_put_orig_node:
batadv_orig_node_put(orig_node);
@ -1361,7 +1391,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
goto out_unlock;
tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
icmp->session);
icmp->session, BATADV_TP_RECEIVER);
if (tp_vars)
goto out_unlock;
@ -1432,7 +1462,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
}
} else {
tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
icmp->session);
icmp->session, BATADV_TP_RECEIVER);
if (!tp_vars) {
batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
"Unexpected packet from %pM!\n",
@ -1441,13 +1471,6 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
}
}
if (unlikely(tp_vars->role != BATADV_TP_RECEIVER)) {
batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
"Meter: dropping packet: not expected (role=%u)\n",
tp_vars->role);
goto out;
}
tp_vars->last_recv_time = jiffies;
/* if the packet is a duplicate, it may be the case that an ACK has been

View File

@ -8,10 +8,12 @@
#include <linux/byteorder/generic.h>
#include <linux/container_of.h>
#include <linux/errno.h>
#include <linux/etherdevice.h>
#include <linux/gfp.h>
#include <linux/if_ether.h>
#include <linux/kref.h>
#include <linux/limits.h>
#include <linux/list.h>
#include <linux/lockdep.h>
#include <linux/netdevice.h>
@ -159,10 +161,10 @@ batadv_tvlv_container_get(struct batadv_priv *bat_priv, u8 type, u8 version)
*
* Return: size of all currently registered tvlv containers in bytes.
*/
static u16 batadv_tvlv_container_list_size(struct batadv_priv *bat_priv)
static size_t batadv_tvlv_container_list_size(struct batadv_priv *bat_priv)
{
struct batadv_tvlv_container *tvlv;
u16 tvlv_len = 0;
size_t tvlv_len = 0;
lockdep_assert_held(&bat_priv->tvlv.container_list_lock);
@ -306,26 +308,35 @@ static bool batadv_tvlv_realloc_packet_buff(unsigned char **packet_buff,
* The ogm packet might be enlarged or shrunk depending on the current size
* and the size of the to-be-appended tvlv containers.
*
* Return: size of all appended tvlv containers in bytes.
* Return: size of all appended tvlv containers in bytes (max U16_MAX), negative
* if operation failed
*/
u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
int batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
unsigned char **packet_buff,
int *packet_buff_len, int packet_min_len)
{
struct batadv_tvlv_container *tvlv;
struct batadv_tvlv_hdr *tvlv_hdr;
u16 tvlv_value_len;
size_t tvlv_value_len;
void *tvlv_value;
int tvlv_len_ret;
bool ret;
spin_lock_bh(&bat_priv->tvlv.container_list_lock);
tvlv_value_len = batadv_tvlv_container_list_size(bat_priv);
if (tvlv_value_len > U16_MAX) {
tvlv_len_ret = -E2BIG;
goto end;
}
ret = batadv_tvlv_realloc_packet_buff(packet_buff, packet_buff_len,
packet_min_len, tvlv_value_len);
if (!ret)
if (!ret) {
tvlv_len_ret = -ENOMEM;
goto end;
}
tvlv_len_ret = tvlv_value_len;
if (!tvlv_value_len)
goto end;
@ -344,7 +355,8 @@ u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
end:
spin_unlock_bh(&bat_priv->tvlv.container_list_lock);
return tvlv_value_len;
return tvlv_len_ret;
}
/**

View File

@ -16,7 +16,7 @@
void batadv_tvlv_container_register(struct batadv_priv *bat_priv,
u8 type, u8 version,
void *tvlv_value, u16 tvlv_value_len);
u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
int batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
unsigned char **packet_buff,
int *packet_buff_len, int packet_min_len);
void batadv_tvlv_ogm_receive(struct batadv_priv *bat_priv,

View File

@ -83,6 +83,9 @@ struct batadv_hard_iface_bat_iv {
/** @ogm_seqno: OGM sequence number - used to identify each OGM */
atomic_t ogm_seqno;
/** @reschedule_work: recover OGM schedule after schedule error */
struct delayed_work reschedule_work;
/** @ogm_buff_mutex: lock protecting ogm_buff and ogm_buff_len */
struct mutex ogm_buff_mutex;
};
@ -1023,6 +1026,12 @@ struct batadv_priv_bla {
/** @num_requests: number of bla requests in flight */
atomic_t num_requests;
/**
* @num_requests_lock: locks update num_requests +
* batadv_backbone_gw::state + batadv_backbone_gw::wait_periods update
*/
spinlock_t num_requests_lock;
/**
* @claim_hash: hash table containing mesh nodes this host has claimed
*/
@ -1320,15 +1329,15 @@ struct batadv_tp_vars {
/** @role: receiver/sender modi */
enum batadv_tp_meter_role role;
/** @sending: sending binary semaphore: 1 if sending, 0 is not */
atomic_t sending;
/**
* @send_result: 0 when sending is ongoing and otherwise
* enum batadv_tp_meter_reason
*/
atomic_t send_result;
/** @receiving: receiving binary semaphore: 1 if receiving, 0 is not */
atomic_t receiving;
/** @reason: reason for a stopped session */
enum batadv_tp_meter_reason reason;
/** @finish_work: work item for the finishing procedure */
struct delayed_work finish_work;
@ -1669,6 +1678,27 @@ struct batadv_priv {
#ifdef CONFIG_BATMAN_ADV_BLA
enum batadv_bla_backbone_gw_state {
/**
* @BATADV_BLA_BACKBONE_GW_STOPPED: backbone gw is being removed
* and it must not longer work on requests
*/
BATADV_BLA_BACKBONE_GW_STOPPED,
/**
* @BATADV_BLA_BACKBONE_GW_UNSYNCED: backbone was detected out
* of sync and a request was send. No traffic is forwarded until the
* situation is resolved
*/
BATADV_BLA_BACKBONE_GW_UNSYNCED,
/**
* @BATADV_BLA_BACKBONE_GW_SYNCED: backbone is consider to be in
* sync. traffic can be forwarded
*/
BATADV_BLA_BACKBONE_GW_SYNCED,
};
/**
* struct batadv_bla_backbone_gw - batman-adv gateway bridged into the LAN
*/
@ -1694,16 +1724,12 @@ struct batadv_bla_backbone_gw {
/**
* @wait_periods: grace time for bridge forward delays and bla group
* forming at bootup phase - no bcast traffic is formwared until it has
* elapsed
* elapsed. Must only be access with num_requests_lock.
*/
atomic_t wait_periods;
u8 wait_periods;
/**
* @request_sent: if this bool is set to true we are out of sync with
* this backbone gateway - no bcast traffic is formwared until the
* situation was resolved
*/
atomic_t request_sent;
/** @state: sync state. Must only be access with num_requests_lock. */
enum batadv_bla_backbone_gw_state state;
/** @crc: crc16 checksum over all claims */
u16 crc;