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>
master
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; hard_iface->bat_iv.ogm_buff = NULL;
mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex); 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) static void batadv_iv_ogm_iface_update_mac(struct batadv_hard_iface *hard_iface)
@ -536,8 +538,10 @@ out:
* @if_incoming: interface where the packet was received * @if_incoming: interface where the packet was received
* @if_outgoing: interface for which the retransmission should be considered * @if_outgoing: interface for which the retransmission should be considered
* @own_packet: true if it is a self-generated ogm * @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, int packet_len, unsigned long send_time,
bool direct_link, bool direct_link,
struct batadv_hard_iface *if_incoming, 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); skb = netdev_alloc_skb_ip_align(NULL, skb_size);
if (!skb) if (!skb)
return; return false;
forw_packet_aggr = batadv_forw_packet_alloc(if_incoming, if_outgoing, forw_packet_aggr = batadv_forw_packet_alloc(if_incoming, if_outgoing,
queue_left, bat_priv, skb); queue_left, bat_priv, skb);
if (!forw_packet_aggr) { if (!forw_packet_aggr) {
kfree_skb(skb); kfree_skb(skb);
return; return false;
} }
forw_packet_aggr->skb->priority = TC_PRIO_CONTROL; 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_iv_send_outstanding_bat_ogm_packet);
batadv_forw_packet_ogmv1_queue(bat_priv, forw_packet_aggr, send_time); batadv_forw_packet_ogmv1_queue(bat_priv, forw_packet_aggr, send_time);
return true;
} }
/* aggregate a new packet into the existing ogm packet */ /* 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 * @if_outgoing: interface for which the retransmission should be considered
* @own_packet: true if it is a self-generated ogm * @own_packet: true if it is a self-generated ogm
* @send_time: timestamp (jiffies) when the packet is to be sent * @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, unsigned char *packet_buff,
int packet_len, int packet_len,
struct batadv_hard_iface *if_incoming, 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)) if (!own_packet && atomic_read(&bat_priv->aggregated_ogms))
send_time += max_aggregation_jiffies; send_time += max_aggregation_jiffies;
batadv_iv_ogm_aggregate_new(packet_buff, packet_len, return batadv_iv_ogm_aggregate_new(packet_buff, packet_len,
send_time, direct_link, send_time, direct_link,
if_incoming, if_outgoing, if_incoming, if_outgoing,
own_packet); own_packet);
} else { } else {
batadv_iv_ogm_aggregate(forw_packet_aggr, packet_buff, batadv_iv_ogm_aggregate(forw_packet_aggr, packet_buff,
packet_len, direct_link); packet_len, direct_link);
spin_unlock_bh(&bat_priv->forw_bat_list_lock); 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; u32 seqno;
u16 tvlv_len = 0; u16 tvlv_len = 0;
unsigned long send_time; unsigned long send_time;
bool reschedule = false;
bool scheduled;
int ret;
lockdep_assert_held(&hard_iface->bat_iv.ogm_buff_mutex); 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 * appended as it may alter the tt tvlv container
*/ */
batadv_tt_local_commit_changes(bat_priv); batadv_tt_local_commit_changes(bat_priv);
tvlv_len = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff, ret = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff,
ogm_buff_len, ogm_buff_len,
BATADV_OGM_HLEN); BATADV_OGM_HLEN);
if (ret < 0) {
reschedule = true;
goto out;
}
tvlv_len = ret;
} }
batadv_ogm_packet = (struct batadv_ogm_packet *)(*ogm_buff); 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 /* OGMs from secondary interfaces are only scheduled on their
* respective interfaces. * respective interfaces.
*/ */
batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, *ogm_buff_len, scheduled = batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, *ogm_buff_len,
hard_iface, hard_iface, 1, send_time); hard_iface, hard_iface, 1, send_time);
if (!scheduled)
reschedule = true;
goto out; 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)) if (!kref_get_unless_zero(&tmp_hard_iface->refcount))
continue; continue;
batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, scheduled = batadv_iv_ogm_queue_add(bat_priv, *ogm_buff,
*ogm_buff_len, hard_iface, *ogm_buff_len, hard_iface,
tmp_hard_iface, 1, send_time); tmp_hard_iface, 1, send_time);
batadv_hardif_put(tmp_hard_iface); batadv_hardif_put(tmp_hard_iface);
if (!scheduled && tmp_hard_iface == hard_iface)
reschedule = true;
} }
rcu_read_unlock(); rcu_read_unlock();
out: 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); 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); 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 * batadv_iv_orig_ifinfo_sum() - Get bcast_own sum for originator over interface
* @orig_node: originator which reproadcasted the OGMs directly * @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) 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 */ /* begin scheduling originator messages on that interface */
batadv_iv_ogm_schedule(hard_iface); 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 * 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 * @skb: the OGM to send
* @hard_iface: the interface to use to send the OGM * @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_hard_iface *hard_iface)
{ {
struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface);
if (hard_iface->if_status != BATADV_IF_ACTIVE) { if (hard_iface->if_status != BATADV_IF_ACTIVE) {
kfree_skb(skb); kfree_skb(skb);
return; 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 * 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 * @hard_iface: the interface with the aggregation queue to flush
* *
* Aggregates all OGMv2 packets currently in the aggregation queue into a * 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. * 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; unsigned int aggr_len = hard_iface->bat_v.aggr_len;
struct sk_buff *skb_aggr; 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); 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 * 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 * @skb: the OGM to queue
* @hard_iface: the interface to queue the OGM on * @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_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)) { 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; return;
} }
spin_lock_bh(&hard_iface->bat_v.aggr_list.lock); spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
if (!batadv_v_ogm_queue_left(skb, hard_iface)) 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); hard_iface->bat_v.aggr_len += batadv_v_ogm_len(skb);
__skb_queue_tail(&hard_iface->bat_v.aggr_list, 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_hard_iface *hard_iface;
struct batadv_ogm2_packet *ogm_packet; struct batadv_ogm2_packet *ogm_packet;
struct sk_buff *skb, *skb_tmp; struct sk_buff *skb, *skb_tmp;
unsigned char *ogm_buff; unsigned char **ogm_buff;
struct list_head *iter; struct list_head *iter;
int ogm_buff_len; int *ogm_buff_len;
u16 tvlv_len = 0; u16 tvlv_len;
int ret; int ret;
lockdep_assert_held(&bat_priv->bat_v.ogm_buff_mutex); 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) if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING)
goto out; goto out;
ogm_buff = bat_priv->bat_v.ogm_buff; ogm_buff = &bat_priv->bat_v.ogm_buff;
ogm_buff_len = bat_priv->bat_v.ogm_buff_len; ogm_buff_len = &bat_priv->bat_v.ogm_buff_len;
/* tt changes have to be committed before the tvlv data is /* tt changes have to be committed before the tvlv data is
* appended as it may alter the tt tvlv container * appended as it may alter the tt tvlv container
*/ */
batadv_tt_local_commit_changes(bat_priv); batadv_tt_local_commit_changes(bat_priv);
tvlv_len = batadv_tvlv_container_ogm_append(bat_priv, &ogm_buff, ret = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff,
&ogm_buff_len, ogm_buff_len,
BATADV_OGM2_HLEN); BATADV_OGM2_HLEN);
if (ret < 0)
goto reschedule;
bat_priv->bat_v.ogm_buff = ogm_buff; tvlv_len = ret;
bat_priv->bat_v.ogm_buff_len = ogm_buff_len;
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) if (!skb)
goto reschedule; goto reschedule;
skb_reserve(skb, ETH_HLEN); 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 = (struct batadv_ogm2_packet *)skb->data;
ogm_packet->seqno = htonl(atomic_read(&bat_priv->bat_v.ogm_seqno)); 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; 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); batadv_hardif_put(hard_iface);
} }
rcu_read_unlock(); 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_bat_v *batv;
struct batadv_hard_iface *hard_iface; struct batadv_hard_iface *hard_iface;
struct batadv_priv *bat_priv;
batv = container_of(work, struct batadv_hard_iface_bat_v, aggr_wq.work); batv = container_of(work, struct batadv_hard_iface_bat_v, aggr_wq.work);
hard_iface = container_of(batv, struct batadv_hard_iface, bat_v); 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); 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); spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
batadv_v_ogm_start_queue_timer(hard_iface); 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), if_outgoing->net_dev->name, ntohl(ogm_forward->throughput),
ogm_forward->ttl, if_incoming->net_dev->name); 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: out:
batadv_orig_ifinfo_put(orig_ifinfo); 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)); sizeof(local_claim_dest));
local_claim_dest.type = claimtype; 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, skb = arp_create(ARPOP_REPLY, ETH_P_ARP,
/* IP DST: 0.0.0.0 */ /* IP DST: 0.0.0.0 */
zeroip, zeroip,
primary_if->mesh_iface, mesh_iface,
/* IP SRC: 0.0.0.0 */ /* IP SRC: 0.0.0.0 */
zeroip, zeroip,
/* Ethernet DST: Broadcast */ /* 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->crc = BATADV_BLA_CRC_INIT;
entry->bat_priv = bat_priv; entry->bat_priv = bat_priv;
spin_lock_init(&entry->crc_lock); spin_lock_init(&entry->crc_lock);
atomic_set(&entry->request_sent, 0); entry->state = BATADV_BLA_BACKBONE_GW_SYNCED;
atomic_set(&entry->wait_periods, 0); entry->wait_periods = 0;
ether_addr_copy(entry->orig, orig); ether_addr_copy(entry->orig, orig);
INIT_WORK(&entry->report_work, batadv_bla_loopdetect_report); INIT_WORK(&entry->report_work, batadv_bla_loopdetect_report);
kref_init(&entry->refcount); 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); batadv_bla_send_announce(bat_priv, entry);
/* this will be decreased in the worker thread */ /* this will be decreased in the worker thread */
atomic_inc(&entry->request_sent); spin_lock_bh(&bat_priv->bla.num_requests_lock);
atomic_set(&entry->wait_periods, BATADV_BLA_WAIT_PERIODS); if (entry->state == BATADV_BLA_BACKBONE_GW_SYNCED) {
atomic_inc(&bat_priv->bla.num_requests); 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; 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); backbone_gw->vid, BATADV_CLAIM_TYPE_REQUEST);
/* no local broadcasts should be sent or received, for now. */ /* 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_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, /* if we have sent a request and the crc was OK,
* we can allow traffic again. * 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_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); 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 hlist_head *head;
struct batadv_hashtable *hash; struct batadv_hashtable *hash;
spinlock_t *list_lock; /* protects write access to the hash lists */ spinlock_t *list_lock; /* protects write access to the hash lists */
bool purged;
int i; int i;
hash = bat_priv->bla.backbone_hash; 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]; head = &hash->table[i];
list_lock = &hash->list_locks[i]; list_lock = &hash->list_locks[i];
spin_lock_bh(list_lock); do {
hlist_for_each_entry_safe(backbone_gw, node_tmp, purged = false;
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, spin_lock_bh(list_lock);
"%s(): backbone gw %pM timed out\n", hlist_for_each_entry_safe(backbone_gw, node_tmp,
__func__, backbone_gw->orig); 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: purge_now:
/* don't wait for the pending request anymore */ purged = true;
if (atomic_read(&backbone_gw->request_sent))
atomic_dec(&bat_priv->bla.num_requests);
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); backbone_gw->state = BATADV_BLA_BACKBONE_GW_STOPPED;
batadv_backbone_gw_put(backbone_gw); spin_unlock_bh(&bat_priv->bla.num_requests_lock);
}
spin_unlock_bh(list_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, batadv_bla_send_loopdetect(bat_priv,
backbone_gw); 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 * problems when we are not yet known as backbone gw
* in the backbone. * in the backbone.
* *
@ -1501,14 +1531,21 @@ static void batadv_bla_periodic_work(struct work_struct *work)
* some grace time. * some grace time.
*/ */
if (atomic_read(&backbone_gw->request_sent) == 0) spin_lock_bh(&bat_priv->bla.num_requests_lock);
continue; if (backbone_gw->state != BATADV_BLA_BACKBONE_GW_UNSYNCED)
goto unlock_next;
if (!atomic_dec_and_test(&backbone_gw->wait_periods)) if (backbone_gw->wait_periods > 0)
continue; 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_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(); rcu_read_unlock();
} }

View File

@ -305,10 +305,10 @@ free:
} }
/** /**
* 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 * @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) 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); atomic_set(&bat_priv->tt.ogm_append_cnt, 0);
#ifdef CONFIG_BATMAN_ADV_BLA #ifdef CONFIG_BATMAN_ADV_BLA
atomic_set(&bat_priv->bla.num_requests, 0); atomic_set(&bat_priv->bla.num_requests, 0);
spin_lock_init(&bat_priv->bla.num_requests_lock);
#endif #endif
atomic_set(&bat_priv->tp_num, 0); 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); orig_node = container_of(rcu, struct batadv_orig_node, rcu);
batadv_mcast_purge_orig(orig_node);
batadv_frag_purge_orig(orig_node, NULL); batadv_frag_purge_orig(orig_node, NULL);
kfree(orig_node->tt_buff); 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); spin_unlock_bh(&orig_node->vlan_list_lock);
batadv_mcast_purge_orig(orig_node);
call_rcu(&orig_node->rcu, batadv_orig_node_free_rcu); 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 * batadv_tp_list_find() - find a tp_vars object in the global list
* @bat_priv: the bat priv with all the mesh interface information * @bat_priv: the bat priv with all the mesh interface information
* @dst: the other endpoint MAC address to look for * @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 * Look for a tp_vars object matching dst as end_point and return it after
* having increment the refcounter. Return NULL is not found * 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 * 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, 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; 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)) if (!batadv_compare_eth(pos->other_end, dst))
continue; continue;
if (pos->role != role)
continue;
/* most of the time this function is invoked during the normal /* most of the time this function is invoked during the normal
* process..it makes sens to pay more when the session is * process..it makes sens to pay more when the session is
* finished and to speed the process up during the measurement * 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; 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 * batadv_tp_list_find_session() - find tp_vars session object in the global
* list * list
* @bat_priv: the bat priv with all the mesh interface information * @bat_priv: the bat priv with all the mesh interface information
* @dst: the other endpoint MAC address to look for * @dst: the other endpoint MAC address to look for
* @session: session identifier * @session: session identifier
* @role: role of the session
* *
* Look for a tp_vars object matching dst as end_point, session as tp meter * 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 * 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 * static struct batadv_tp_vars *
batadv_tp_list_find_session(struct batadv_priv *bat_priv, const u8 *dst, 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; 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) if (memcmp(pos->session, session, sizeof(pos->session)) != 0)
continue; continue;
if (pos->role != role)
continue;
/* most of the time this function is invoked during the normal /* most of the time this function is invoked during the normal
* process..it makes sense to pay more when the session is * process..it makes sense to pay more when the session is
* finished and to speed the process up during the measurement * 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, static void batadv_tp_sender_end(struct batadv_priv *bat_priv,
struct batadv_tp_vars *tp_vars) struct batadv_tp_vars *tp_vars)
{ {
enum batadv_tp_meter_reason reason;
u32 session_cookie; u32 session_cookie;
reason = atomic_read(&tp_vars->send_result);
batadv_dbg(BATADV_DBG_TP_METER, bat_priv, batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
"Test towards %pM finished..shutting down (reason=%d)\n", "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, batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
"Last timing stats: SRTT=%ums RTTVAR=%ums RTO=%ums\n", "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, session_cookie = batadv_tp_session_cookie(tp_vars->session,
tp_vars->icmp_uid); tp_vars->icmp_uid);
batadv_tp_batctl_notify(tp_vars->reason, batadv_tp_batctl_notify(reason,
tp_vars->other_end, tp_vars->other_end,
bat_priv, bat_priv,
tp_vars->start_time, 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, static void batadv_tp_sender_shutdown(struct batadv_tp_vars *tp_vars,
enum batadv_tp_meter_reason reason) enum batadv_tp_meter_reason reason)
{ {
if (atomic_xchg(&tp_vars->sending, 0) != 1) atomic_cmpxchg(&tp_vars->send_result, 0, reason);
return; }
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 /* most of the time this function is invoked while normal packet
* reception... * 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 */ /* timer ref will be dropped in batadv_tp_sender_cleanup */
return; 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_tp_vars *tp_vars = timer_container_of(tp_vars, t, timer);
struct batadv_priv *bat_priv = tp_vars->bat_priv; struct batadv_priv *bat_priv = tp_vars->bat_priv;
if (atomic_read(&tp_vars->sending) == 0) if (batadv_tp_sender_stopped(tp_vars))
return; return;
/* if the user waited long enough...shutdown the test */ /* 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 */ /* find the tp_vars */
tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig, tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
icmp->session); icmp->session, BATADV_TP_SENDER);
if (unlikely(!tp_vars)) if (unlikely(!tp_vars))
return; return;
if (unlikely(tp_vars->role != BATADV_TP_SENDER)) if (unlikely(batadv_tp_sender_stopped(tp_vars)))
goto out;
if (unlikely(atomic_read(&tp_vars->sending) == 0))
goto out; goto out;
/* old ACK? silently drop it.. */ /* old ACK? silently drop it.. */
@ -827,21 +864,21 @@ static int batadv_tp_send(void *arg)
if (unlikely(tp_vars->role != BATADV_TP_SENDER)) { if (unlikely(tp_vars->role != BATADV_TP_SENDER)) {
err = BATADV_TP_REASON_DST_UNREACHABLE; err = BATADV_TP_REASON_DST_UNREACHABLE;
tp_vars->reason = err; batadv_tp_sender_shutdown(tp_vars, err);
goto out; goto out;
} }
orig_node = batadv_orig_hash_find(bat_priv, tp_vars->other_end); orig_node = batadv_orig_hash_find(bat_priv, tp_vars->other_end);
if (unlikely(!orig_node)) { if (unlikely(!orig_node)) {
err = BATADV_TP_REASON_DST_UNREACHABLE; err = BATADV_TP_REASON_DST_UNREACHABLE;
tp_vars->reason = err; batadv_tp_sender_shutdown(tp_vars, err);
goto out; goto out;
} }
primary_if = batadv_primary_if_get_selected(bat_priv); primary_if = batadv_primary_if_get_selected(bat_priv);
if (unlikely(!primary_if)) { if (unlikely(!primary_if)) {
err = BATADV_TP_REASON_DST_UNREACHABLE; err = BATADV_TP_REASON_DST_UNREACHABLE;
tp_vars->reason = err; batadv_tp_sender_shutdown(tp_vars, err);
goto out; goto out;
} }
@ -860,7 +897,7 @@ static int batadv_tp_send(void *arg)
queue_delayed_work(batadv_event_workqueue, &tp_vars->finish_work, queue_delayed_work(batadv_event_workqueue, &tp_vars->finish_work,
msecs_to_jiffies(tp_vars->test_length)); 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))) { if (unlikely(!batadv_tp_avail(tp_vars, payload_len))) {
batadv_tp_wait_available(tp_vars, payload_len); batadv_tp_wait_available(tp_vars, payload_len);
continue; continue;
@ -883,8 +920,7 @@ static int batadv_tp_send(void *arg)
"Meter: %s() cannot send packets (%d)\n", "Meter: %s() cannot send packets (%d)\n",
__func__, err); __func__, err);
/* ensure nobody else tries to stop the thread now */ /* ensure nobody else tries to stop the thread now */
if (atomic_xchg(&tp_vars->sending, 0) == 1) batadv_tp_sender_shutdown(tp_vars, err);
tp_vars->reason = err;
break; break;
} }
@ -970,10 +1006,8 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
return; return;
} }
tp_vars = batadv_tp_list_find(bat_priv, dst); if (batadv_tp_list_active(bat_priv, dst)) {
if (tp_vars) {
spin_unlock_bh(&bat_priv->tp_list_lock); spin_unlock_bh(&bat_priv->tp_list_lock);
batadv_tp_vars_put(tp_vars);
batadv_dbg(BATADV_DBG_TP_METER, bat_priv, batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
"Meter: test to or from the same node already ongoing, aborting\n"); "Meter: test to or from the same node already ongoing, aborting\n");
batadv_tp_batctl_error_notify(BATADV_TP_REASON_ALREADY_ONGOING, 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); ether_addr_copy(tp_vars->other_end, dst);
kref_init(&tp_vars->refcount); kref_init(&tp_vars->refcount);
tp_vars->role = BATADV_TP_SENDER; 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)); memcpy(tp_vars->session, session_id, sizeof(session_id));
tp_vars->icmp_uid = icmp_uid; 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) if (!orig_node)
return; 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) { if (!tp_vars) {
batadv_dbg(BATADV_DBG_TP_METER, bat_priv, batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
"Meter: trying to interrupt an already over connection\n"); "Meter: trying to interrupt an already over connection\n");
goto out_put_orig_node; 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); batadv_tp_sender_shutdown(tp_vars, return_value);
out_put_tp_vars:
batadv_tp_vars_put(tp_vars); batadv_tp_vars_put(tp_vars);
out_put_orig_node: out_put_orig_node:
batadv_orig_node_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; goto out_unlock;
tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig, tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
icmp->session); icmp->session, BATADV_TP_RECEIVER);
if (tp_vars) if (tp_vars)
goto out_unlock; goto out_unlock;
@ -1432,7 +1462,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
} }
} else { } else {
tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig, tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
icmp->session); icmp->session, BATADV_TP_RECEIVER);
if (!tp_vars) { if (!tp_vars) {
batadv_dbg(BATADV_DBG_TP_METER, bat_priv, batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
"Unexpected packet from %pM!\n", "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; tp_vars->last_recv_time = jiffies;
/* if the packet is a duplicate, it may be the case that an ACK has been /* 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/byteorder/generic.h>
#include <linux/container_of.h> #include <linux/container_of.h>
#include <linux/errno.h>
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/gfp.h> #include <linux/gfp.h>
#include <linux/if_ether.h> #include <linux/if_ether.h>
#include <linux/kref.h> #include <linux/kref.h>
#include <linux/limits.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/lockdep.h> #include <linux/lockdep.h>
#include <linux/netdevice.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. * 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; struct batadv_tvlv_container *tvlv;
u16 tvlv_len = 0; size_t tvlv_len = 0;
lockdep_assert_held(&bat_priv->tvlv.container_list_lock); 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 * The ogm packet might be enlarged or shrunk depending on the current size
* and the size of the to-be-appended tvlv containers. * 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, unsigned char **packet_buff,
int *packet_buff_len, int packet_min_len) int *packet_buff_len, int packet_min_len)
{ {
struct batadv_tvlv_container *tvlv; struct batadv_tvlv_container *tvlv;
struct batadv_tvlv_hdr *tvlv_hdr; struct batadv_tvlv_hdr *tvlv_hdr;
u16 tvlv_value_len; size_t tvlv_value_len;
void *tvlv_value; void *tvlv_value;
int tvlv_len_ret;
bool ret; bool ret;
spin_lock_bh(&bat_priv->tvlv.container_list_lock); spin_lock_bh(&bat_priv->tvlv.container_list_lock);
tvlv_value_len = batadv_tvlv_container_list_size(bat_priv); 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, ret = batadv_tvlv_realloc_packet_buff(packet_buff, packet_buff_len,
packet_min_len, tvlv_value_len); packet_min_len, tvlv_value_len);
if (!ret) {
if (!ret) tvlv_len_ret = -ENOMEM;
goto end; goto end;
}
tvlv_len_ret = tvlv_value_len;
if (!tvlv_value_len) if (!tvlv_value_len)
goto end; goto end;
@ -344,7 +355,8 @@ u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
end: end:
spin_unlock_bh(&bat_priv->tvlv.container_list_lock); 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, void batadv_tvlv_container_register(struct batadv_priv *bat_priv,
u8 type, u8 version, u8 type, u8 version,
void *tvlv_value, u16 tvlv_value_len); 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, unsigned char **packet_buff,
int *packet_buff_len, int packet_min_len); int *packet_buff_len, int packet_min_len);
void batadv_tvlv_ogm_receive(struct batadv_priv *bat_priv, 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 */ /** @ogm_seqno: OGM sequence number - used to identify each OGM */
atomic_t ogm_seqno; 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 */ /** @ogm_buff_mutex: lock protecting ogm_buff and ogm_buff_len */
struct mutex ogm_buff_mutex; struct mutex ogm_buff_mutex;
}; };
@ -1023,6 +1026,12 @@ struct batadv_priv_bla {
/** @num_requests: number of bla requests in flight */ /** @num_requests: number of bla requests in flight */
atomic_t num_requests; 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 * @claim_hash: hash table containing mesh nodes this host has claimed
*/ */
@ -1320,15 +1329,15 @@ struct batadv_tp_vars {
/** @role: receiver/sender modi */ /** @role: receiver/sender modi */
enum batadv_tp_meter_role role; 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 */ /** @receiving: receiving binary semaphore: 1 if receiving, 0 is not */
atomic_t receiving; atomic_t receiving;
/** @reason: reason for a stopped session */
enum batadv_tp_meter_reason reason;
/** @finish_work: work item for the finishing procedure */ /** @finish_work: work item for the finishing procedure */
struct delayed_work finish_work; struct delayed_work finish_work;
@ -1669,6 +1678,27 @@ struct batadv_priv {
#ifdef CONFIG_BATMAN_ADV_BLA #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 * 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 * @wait_periods: grace time for bridge forward delays and bla group
* forming at bootup phase - no bcast traffic is formwared until it has * 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;
/** /** @state: sync state. Must only be access with num_requests_lock. */
* @request_sent: if this bool is set to true we are out of sync with enum batadv_bla_backbone_gw_state state;
* this backbone gateway - no bcast traffic is formwared until the
* situation was resolved
*/
atomic_t request_sent;
/** @crc: crc16 checksum over all claims */ /** @crc: crc16 checksum over all claims */
u16 crc; u16 crc;