Here are some batman-adv bugfixes, all by Sven Eckelmann:

- gw: don't deselect gateway with active hardif
 
 - TTL fixes in bcast/mcast and fragmentation handling (4 patches)
 
 - BATMAN V: prevent OGM aggregation on disabled hardif
 
 - tp_meter: concurrency and range fixes, add cap to unacked list
   (4 patches)
 
 - tt: fix unchecked VLAN ID in tt for local add and roam (2 patches)
 
 - dat: check VLAN ID in the hash
 
 - tvlv: enforce 2-byte alignment
 
 - tvlv: avoid race of cifsnotfound handler state
 -----BEGIN PGP SIGNATURE-----
 
 iQJKBAABCgA0FiEE1ilQI7G+y+fdhnrfoSvjmEKSnqEFAmo06D4WHHN3QHNpbW9u
 d3VuZGVybGljaC5kZQAKCRChK+OYQpKeoS88EACP/9+bkLmZ1e+qgd5N6LAHa/ca
 bDN8N1hzEzNXVe2toG3TEyDfOdrZIoaHUTTMD7xWu9AD3d1jJIpZfokrQ9T5cnPP
 cuPmGDb1H+clDNsognTKUGxqfW9iwBLbNFOC+H7Nb0B6HTsJbkONIwrG3PN5Xl+P
 77CWQtnn7PYnxjTEzYVky3EHEmcYU7V6FYew8GFQfe8113DkWa9AT/35ToccxaEn
 PuzZXl89PK/pheWV4qMGIJFqFK5WV8fWM7BVFeJrIZM8i6OkOBbes94Tn6LVdhfQ
 M8TG6+nG3ca3fY7/PcFg/7lx91x98KWUmUj6m76jnXNlXLbuUl7CSM2ffaFvbeBD
 RzBoM9mBD4GON47HlHiu5ECqnsA49YbAPlM1gx6nfWVz2jf0WL1KNWasRXbZj9v/
 CyyDDELEdI7FudqjFxvfdQ57isobuWtajplEIBFNldAH24xhxZeM3HKvcGoJdKg7
 FP/JCzb2NkP+6STe4nLxzlbCFvYRUskUM3+U8LxqzUz+YnE4wiWhD/OXuOpRsobT
 o6JYZUaUBDx5xU1+PxbFHRewQWfIuWnhu/oj6ciiyjU1dOuNcld8n1pwbFnp441Z
 nMWzmiNVpdLPtYEAQT5uBAih8hmpNcZ8IQaM4netsnamHsLPb6uezSpcUK8cXB4x
 TlgHGL5AkP8kc0t7LQ==
 =K1KM
 -----END PGP SIGNATURE-----

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

Simon Wunderlich says:

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

- gw: don't deselect gateway with active hardif

- TTL fixes in bcast/mcast and fragmentation handling (4 patches)

- BATMAN V: prevent OGM aggregation on disabled hardif

- tp_meter: concurrency and range fixes, add cap to unacked list
  (4 patches)

- tt: fix unchecked VLAN ID in tt for local add and roam (2 patches)

- dat: check VLAN ID in the hash

- tvlv: enforce 2-byte alignment

- tvlv: avoid race of cifsnotfound handler state

* tag 'batadv-net-pullrequest-20260619' of https://git.open-mesh.org/batadv:
  batman-adv: tvlv: avoid race of cifsnotfound handler state
  batman-adv: tvlv: enforce 2-byte alignment
  batman-adv: dat: prevent false sharing between VLANs
  batman-adv: tt: track roam count per VID
  batman-adv: tt: don't merge change entries with different VIDs
  batman-adv: tp_meter: handle overlapping packets
  batman-adv: tp_meter: prevent parallel modifications of last_recv
  batman-adv: tp_meter: annotate last_recv_time access with READ/WRITE_ONCE
  batman-adv: tp_meter: restrict number of unacked list entries
  batman-adv: v: prevent OGM aggregation on disabled hardif
  batman-adv: frag: avoid underflow of TTL
  batman-adv: frag: ensure fragment is writable before modifying TTL
  batman-adv: fix (m|b)cast csum after decrementing TTL
  batman-adv: ensure bcast is writable before modifying TTL
  batman-adv: gw: don't deselect gateway with active hardif
====================

Link: https://patch.msgid.link/20260619070045.438101-1-sw@simonwunderlich.de
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-06-21 14:53:06 -07:00
commit 50ffe0645d
12 changed files with 270 additions and 79 deletions

View File

@ -316,14 +316,23 @@ batadv_iv_ogm_aggr_packet(int buff_pos, int packet_len,
const struct batadv_ogm_packet *ogm_packet)
{
int next_buff_pos = 0;
u16 tvlv_len;
/* check if there is enough space for the header */
next_buff_pos += buff_pos + sizeof(*ogm_packet);
if (next_buff_pos > packet_len)
return false;
tvlv_len = ntohs(ogm_packet->tvlv_len);
/* the fields of an aggregated OGM are accessed assuming (at least)
* 2-byte alignment, so a following OGM must start at an even offset.
*/
if (tvlv_len & 1)
return false;
/* check if there is enough space for the optional TVLV */
next_buff_pos += ntohs(ogm_packet->tvlv_len);
next_buff_pos += tvlv_len;
return next_buff_pos <= packet_len;
}

View File

@ -817,6 +817,7 @@ void batadv_v_hardif_init(struct batadv_hard_iface *hard_iface)
hard_iface->bat_v.aggr_len = 0;
skb_queue_head_init(&hard_iface->bat_v.aggr_list);
hard_iface->bat_v.aggr_list_enabled = false;
INIT_DELAYED_WORK(&hard_iface->bat_v.aggr_wq,
batadv_v_ogm_aggr_work);
/* make sure it doesn't run until interface gets enabled */

View File

@ -254,11 +254,18 @@ static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv,
}
spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
if (!hard_iface->bat_v.aggr_list_enabled) {
kfree_skb(skb);
goto unlock;
}
if (!batadv_v_ogm_queue_left(skb, 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);
unlock:
spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
}
@ -415,6 +422,10 @@ int batadv_v_ogm_iface_enable(struct batadv_hard_iface *hard_iface)
{
struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface);
spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
hard_iface->bat_v.aggr_list_enabled = true;
spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
enable_delayed_work(&hard_iface->bat_v.aggr_wq);
batadv_v_ogm_start_queue_timer(hard_iface);
@ -432,6 +443,7 @@ void batadv_v_ogm_iface_disable(struct batadv_hard_iface *hard_iface)
disable_delayed_work_sync(&hard_iface->bat_v.aggr_wq);
spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
hard_iface->bat_v.aggr_list_enabled = false;
batadv_v_ogm_aggr_list_free(hard_iface);
spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
}
@ -837,14 +849,23 @@ batadv_v_ogm_aggr_packet(int buff_pos, int packet_len,
const struct batadv_ogm2_packet *ogm2_packet)
{
int next_buff_pos = 0;
u16 tvlv_len;
/* check if there is enough space for the header */
next_buff_pos += buff_pos + sizeof(*ogm2_packet);
if (next_buff_pos > packet_len)
return false;
tvlv_len = ntohs(ogm2_packet->tvlv_len);
/* the fields of an aggregated OGMv2 are accessed assuming (at least)
* 2-byte alignment, so a following OGMv2 must start at an even offset.
*/
if (tvlv_len & 1)
return false;
/* check if there is enough space for the optional TVLV */
next_buff_pos += ntohs(ogm2_packet->tvlv_len);
next_buff_pos += tvlv_len;
return next_buff_pos <= packet_len;
}

View File

@ -215,10 +215,13 @@ static void batadv_dat_purge(struct work_struct *work)
*/
static bool batadv_compare_dat(const struct hlist_node *node, const void *data2)
{
const void *data1 = container_of(node, struct batadv_dat_entry,
hash_entry);
const struct batadv_dat_entry *entry1;
const struct batadv_dat_entry *entry2;
return memcmp(data1, data2, sizeof(__be32)) == 0;
entry1 = container_of(node, struct batadv_dat_entry, hash_entry);
entry2 = data2;
return entry1->ip == entry2->ip && entry1->vid == entry2->vid;
}
/**
@ -345,6 +348,9 @@ batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip,
if (dat_entry->ip != ip)
continue;
if (dat_entry->vid != vid)
continue;
if (!kref_get_unless_zero(&dat_entry->refcount))
continue;

View File

@ -386,6 +386,8 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb,
* @skb: skb to forward
* @recv_if: interface that the skb is received on
* @orig_node_src: originator that the skb is received from
* @rx_result: set to NET_RX_SUCCESS when the fragment was forwarded and
* NET_RX_DROP when it was dropped; only valid when true is returned
*
* Look up the next-hop of the fragments payload and check if the merged packet
* will exceed the MTU towards the next-hop. If so, the fragment is forwarded
@ -395,7 +397,8 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb,
*/
bool batadv_frag_skb_fwd(struct sk_buff *skb,
struct batadv_hard_iface *recv_if,
struct batadv_orig_node *orig_node_src)
struct batadv_orig_node *orig_node_src,
int *rx_result)
{
struct batadv_priv *bat_priv = netdev_priv(recv_if->mesh_iface);
struct batadv_neigh_node *neigh_node = NULL;
@ -414,12 +417,29 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb,
*/
total_size = ntohs(packet->total_size);
if (total_size > neigh_node->if_incoming->net_dev->mtu) {
if (packet->ttl < 2) {
kfree_skb(skb);
*rx_result = NET_RX_DROP;
ret = true;
goto out;
}
if (skb_cow(skb, ETH_HLEN) < 0) {
kfree_skb(skb);
*rx_result = NET_RX_DROP;
ret = true;
goto out;
}
packet = (struct batadv_frag_packet *)skb->data;
batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_FWD);
batadv_add_counter(bat_priv, BATADV_CNT_FRAG_FWD_BYTES,
skb->len + ETH_HLEN);
packet->ttl--;
batadv_send_unicast_skb(skb, neigh_node);
*rx_result = NET_RX_SUCCESS;
ret = true;
}

View File

@ -19,7 +19,8 @@ void batadv_frag_purge_orig(struct batadv_orig_node *orig,
bool (*check_cb)(struct batadv_frag_table_entry *));
bool batadv_frag_skb_fwd(struct sk_buff *skb,
struct batadv_hard_iface *recv_if,
struct batadv_orig_node *orig_node_src);
struct batadv_orig_node *orig_node_src,
int *rx_result);
bool batadv_frag_skb_buffer(struct sk_buff **skb,
struct batadv_orig_node *orig_node);
int batadv_frag_send_packet(struct sk_buff *skb,

View File

@ -814,30 +814,6 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
return ret;
}
/**
* batadv_hardif_cnt() - get number of interfaces enslaved to mesh interface
* @mesh_iface: mesh interface to check
*
* This function is only using RCU for locking - the result can therefore be
* off when another function is modifying the list at the same time. The
* caller can use the rtnl_lock to make sure that the count is accurate.
*
* Return: number of connected/enslaved hard interfaces
*/
static size_t batadv_hardif_cnt(struct net_device *mesh_iface)
{
struct batadv_hard_iface *hard_iface;
struct list_head *iter;
size_t count = 0;
rcu_read_lock();
netdev_for_each_lower_private_rcu(mesh_iface, hard_iface, iter)
count++;
rcu_read_unlock();
return count;
}
/**
* batadv_hardif_disable_interface() - Remove hard interface from mesh interface
* @hard_iface: hard interface to be removed
@ -878,8 +854,8 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
netdev_upper_dev_unlink(hard_iface->net_dev, hard_iface->mesh_iface);
batadv_hardif_recalc_extra_skbroom(hard_iface->mesh_iface);
/* nobody uses this interface anymore */
if (batadv_hardif_cnt(hard_iface->mesh_iface) <= 1)
/* nobody uses this mesh interface anymore */
if (list_empty(&hard_iface->mesh_iface->adj_list.lower))
batadv_gw_check_client_stop(bat_priv);
hard_iface->mesh_iface = NULL;

View File

@ -8,6 +8,7 @@
#include "main.h"
#include <linux/atomic.h>
#include <linux/build_bug.h>
#include <linux/byteorder/generic.h>
#include <linux/compiler.h>
#include <linux/errno.h>
@ -204,6 +205,59 @@ bool batadv_check_management_packet(struct sk_buff *skb,
return true;
}
/**
* batadv_skb_decrement_ttl() - decrement ttl in a batman-adv header, csum-safe
* @skb: the received packet with @skb->data pointing to the batman-adv header
*
* Supports the following packet types, all of which carry the TTL at offset 2:
*
* - batadv_ogm_packet
* - batadv_ogm2_packet
* - batadv_icmp_header
* - batadv_icmp_packet
* - batadv_icmp_tp_packet
* - batadv_icmp_packet_rr
* - batadv_unicast_packet
* - batadv_frag_packet
* - batadv_bcast_packet
* - batadv_mcast_packet
* - batadv_coded_packet
* - batadv_unicast_tvlv_packet
*
* Return: true if the packet may be forwarded (ttl decremented),
* false if it must be dropped (ttl would expire)
*/
static bool batadv_skb_decrement_ttl(struct sk_buff *skb)
{
static const size_t ttl_offset = 2;
u8 *ttl_pos;
BUILD_BUG_ON(offsetof(struct batadv_ogm_packet, ttl) != ttl_offset);
BUILD_BUG_ON(offsetof(struct batadv_ogm2_packet, ttl) != ttl_offset);
BUILD_BUG_ON(offsetof(struct batadv_icmp_header, ttl) != ttl_offset);
BUILD_BUG_ON(offsetof(struct batadv_icmp_packet, ttl) != ttl_offset);
BUILD_BUG_ON(offsetof(struct batadv_icmp_tp_packet, ttl) != ttl_offset);
BUILD_BUG_ON(offsetof(struct batadv_icmp_packet_rr, ttl) != ttl_offset);
BUILD_BUG_ON(offsetof(struct batadv_unicast_packet, ttl) != ttl_offset);
BUILD_BUG_ON(offsetof(struct batadv_frag_packet, ttl) != ttl_offset);
BUILD_BUG_ON(offsetof(struct batadv_bcast_packet, ttl) != ttl_offset);
BUILD_BUG_ON(offsetof(struct batadv_mcast_packet, ttl) != ttl_offset);
BUILD_BUG_ON(offsetof(struct batadv_coded_packet, ttl) != ttl_offset);
BUILD_BUG_ON(offsetof(struct batadv_unicast_tvlv_packet, ttl) != ttl_offset);
ttl_pos = skb->data + ttl_offset;
/* would expire on this hop -> drop, leave header + csum untouched */
if (*ttl_pos < 2)
return false;
skb_postpull_rcsum(skb, ttl_pos, 1);
(*ttl_pos)--;
skb_postpush_rcsum(skb, ttl_pos, 1);
return true;
}
/**
* batadv_recv_my_icmp_packet() - receive an icmp packet locally
* @bat_priv: the bat priv with all the mesh interface information
@ -1114,10 +1168,9 @@ int batadv_recv_frag_packet(struct sk_buff *skb,
/* Route the fragment if it is not for us and too big to be merged. */
if (!batadv_is_my_mac(bat_priv, frag_packet->dest) &&
batadv_frag_skb_fwd(skb, recv_if, orig_node_src)) {
batadv_frag_skb_fwd(skb, recv_if, orig_node_src, &ret)) {
/* skb was consumed */
skb = NULL;
ret = NET_RX_SUCCESS;
goto put_orig_node;
}
@ -1191,7 +1244,13 @@ int batadv_recv_bcast_packet(struct sk_buff *skb,
if (batadv_is_my_mac(bat_priv, bcast_packet->orig))
goto free_skb;
if (bcast_packet->ttl-- < 2)
/* create a copy of the skb, if needed, to modify it. */
if (skb_cow(skb, ETH_HLEN) < 0)
goto free_skb;
bcast_packet = (struct batadv_bcast_packet *)skb->data;
if (!batadv_skb_decrement_ttl(skb))
goto free_skb;
orig_node = batadv_orig_hash_find(bat_priv, bcast_packet->orig);
@ -1298,7 +1357,7 @@ int batadv_recv_mcast_packet(struct sk_buff *skb,
goto free_skb;
mcast_packet = (struct batadv_mcast_packet *)skb->data;
if (mcast_packet->ttl-- < 2)
if (!batadv_skb_decrement_ttl(skb))
goto free_skb;
tvlv_buff = (unsigned char *)(skb->data + hdr_size);
@ -1307,6 +1366,12 @@ int batadv_recv_mcast_packet(struct sk_buff *skb,
if (tvlv_buff_len > skb->len - hdr_size)
goto free_skb;
/* the fields of an multicast payload are accessed assuming (at least)
* 2-byte alignment, so a following packet must start at an even offset.
*/
if (tvlv_buff_len & 1)
goto free_skb;
ret = batadv_tvlv_containers_process(bat_priv, BATADV_MCAST, NULL, skb,
tvlv_buff, tvlv_buff_len);
if (ret >= 0) {

View File

@ -87,6 +87,11 @@
#define BATADV_TP_PLEN (BATADV_TP_PACKET_LEN - ETH_HLEN - \
sizeof(struct batadv_unicast_packet))
/**
* BATADV_TP_MAX_UNACKED - maximum number of packets a receiver didn't yet ack
*/
#define BATADV_TP_MAX_UNACKED 100
static u8 batadv_tp_prerandom[4096] __read_mostly;
/**
@ -1285,7 +1290,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)
bat_priv = tp_vars->common.bat_priv;
/* if there is recent activity rearm the timer */
if (!batadv_has_timed_out(tp_vars->last_recv_time,
if (!batadv_has_timed_out(READ_ONCE(tp_vars->last_recv_time),
BATADV_TP_RECV_TIMEOUT)) {
/* reset the receiver shutdown timer */
batadv_tp_reset_receiver_timer(tp_vars);
@ -1303,6 +1308,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)
list_for_each_entry_safe(un, safe, &tp_vars->common.unacked_list, list) {
list_del(&un->list);
kfree(un);
tp_vars->common.unacked_count--;
}
spin_unlock_bh(&tp_vars->common.unacked_lock);
@ -1386,7 +1392,8 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst,
/**
* batadv_tp_handle_out_of_order() - store an out of order packet
* @tp_vars: the private data of the current TP meter session
* @skb: the buffer containing the received packet
* @seqno: sequence number of new received packet
* @payload_len: length of the received packet
*
* Store the out of order packet in the unacked list for late processing. This
* packets are kept in this list so that they can be ACKed at once as soon as
@ -1395,28 +1402,24 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst,
* Return: true if the packed has been successfully processed, false otherwise
*/
static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars,
const struct sk_buff *skb)
u32 seqno, u32 payload_len)
__must_hold(&tp_vars->common.unacked_lock)
{
const struct batadv_icmp_tp_packet *icmp;
struct batadv_tp_unacked *un, *new;
u32 payload_len;
bool added = false;
new = kmalloc_obj(*new, GFP_ATOMIC);
if (unlikely(!new))
return false;
icmp = (struct batadv_icmp_tp_packet *)skb->data;
new->seqno = ntohl(icmp->seqno);
payload_len = skb->len - sizeof(struct batadv_unicast_packet);
new->seqno = seqno;
new->len = payload_len;
spin_lock_bh(&tp_vars->common.unacked_lock);
/* if the list is empty immediately attach this new object */
if (list_empty(&tp_vars->common.unacked_list)) {
list_add(&new->list, &tp_vars->common.unacked_list);
goto out;
tp_vars->common.unacked_count++;
return true;
}
/* otherwise loop over the list and either drop the packet because this
@ -1446,15 +1449,24 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars,
*/
list_add(&new->list, &un->list);
added = true;
tp_vars->common.unacked_count++;
break;
}
/* received packet with smallest seqno out of order; add it to front */
if (!added)
if (!added) {
list_add(&new->list, &tp_vars->common.unacked_list);
tp_vars->common.unacked_count++;
}
out:
spin_unlock_bh(&tp_vars->common.unacked_lock);
/* remove the last (biggest) unacked seqno when list is too large */
if (tp_vars->common.unacked_count > BATADV_TP_MAX_UNACKED) {
un = list_last_entry(&tp_vars->common.unacked_list,
struct batadv_tp_unacked, list);
list_del(&un->list);
kfree(un);
tp_vars->common.unacked_count--;
}
return true;
}
@ -1465,6 +1477,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars,
* @tp_vars: the private data of the current TP meter session
*/
static void batadv_tp_ack_unordered(struct batadv_tp_receiver *tp_vars)
__must_hold(&tp_vars->common.unacked_lock)
{
struct batadv_tp_unacked *un, *safe;
u32 to_ack;
@ -1472,7 +1485,6 @@ static void batadv_tp_ack_unordered(struct batadv_tp_receiver *tp_vars)
/* go through the unacked packet list and possibly ACK them as
* well
*/
spin_lock_bh(&tp_vars->common.unacked_lock);
list_for_each_entry_safe(un, safe, &tp_vars->common.unacked_list, list) {
/* the list is ordered, therefore it is possible to stop as soon
* there is a gap between the last acked seqno and the seqno of
@ -1488,8 +1500,8 @@ static void batadv_tp_ack_unordered(struct batadv_tp_receiver *tp_vars)
list_del(&un->list);
kfree(un);
tp_vars->common.unacked_count--;
}
spin_unlock_bh(&tp_vars->common.unacked_lock);
}
/**
@ -1512,7 +1524,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
tp_vars = batadv_tp_list_find_receiver_session(bat_priv, icmp->orig,
icmp->session);
if (tp_vars) {
tp_vars->last_recv_time = jiffies;
WRITE_ONCE(tp_vars->last_recv_time, jiffies);
goto out_unlock;
}
@ -1537,11 +1549,12 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
spin_lock_init(&tp_vars->common.unacked_lock);
INIT_LIST_HEAD(&tp_vars->common.unacked_list);
tp_vars->common.unacked_count = 0;
kref_get(&tp_vars->common.refcount);
timer_setup(&tp_vars->common.timer, batadv_tp_receiver_shutdown, 0);
tp_vars->last_recv_time = jiffies;
WRITE_ONCE(tp_vars->last_recv_time, jiffies);
kref_get(&tp_vars->common.refcount);
hlist_add_head_rcu(&tp_vars->common.list, &bat_priv->tp_receiver_list);
@ -1566,7 +1579,8 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
{
const struct batadv_icmp_tp_packet *icmp;
struct batadv_tp_receiver *tp_vars;
size_t packet_size;
u32 payload_len;
u32 to_ack;
u32 seqno;
icmp = (struct batadv_icmp_tp_packet *)skb->data;
@ -1592,40 +1606,48 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
goto out;
}
tp_vars->last_recv_time = jiffies;
WRITE_ONCE(tp_vars->last_recv_time, jiffies);
}
spin_lock_bh(&tp_vars->common.unacked_lock);
/* if the packet is a duplicate, it may be the case that an ACK has been
* lost. Resend the ACK
*/
if (batadv_seq_before(seqno, tp_vars->last_recv))
payload_len = skb->len - sizeof(struct batadv_unicast_packet);
to_ack = seqno + payload_len;
if (batadv_seq_before(to_ack, tp_vars->last_recv))
goto send_ack;
/* if the packet is out of order enqueue it */
if (ntohl(icmp->seqno) != tp_vars->last_recv) {
if (batadv_seq_before(tp_vars->last_recv, seqno)) {
/* exit immediately (and do not send any ACK) if the packet has
* not been enqueued correctly
*/
if (!batadv_tp_handle_out_of_order(tp_vars, skb))
if (!batadv_tp_handle_out_of_order(tp_vars, seqno, payload_len)) {
spin_unlock_bh(&tp_vars->common.unacked_lock);
goto out;
}
/* send a duplicate ACK */
goto send_ack;
}
/* if everything was fine count the ACKed bytes */
packet_size = skb->len - sizeof(struct batadv_unicast_packet);
tp_vars->last_recv += packet_size;
tp_vars->last_recv = to_ack;
/* check if this ordered message filled a gap.... */
batadv_tp_ack_unordered(tp_vars);
send_ack:
to_ack = tp_vars->last_recv;
spin_unlock_bh(&tp_vars->common.unacked_lock);
/* send the ACK. If the received packet was out of order, the ACK that
* is going to be sent is a duplicate (the sender will count them and
* possibly enter Fast Retransmit as soon as it has reached 3)
*/
batadv_tp_send_ack(bat_priv, icmp->orig, tp_vars->last_recv,
batadv_tp_send_ack(bat_priv, icmp->orig, to_ack,
icmp->timestamp, icmp->session, icmp->uid);
out:
batadv_tp_receiver_put(tp_vars);

View File

@ -447,6 +447,9 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv,
if (!batadv_compare_eth(entry->change.addr, common->addr))
continue;
if (entry->change.vid != tt_change_node->change.vid)
continue;
del_op_entry = entry->change.flags & BATADV_TT_CLIENT_DEL;
if (del_op_requested != del_op_entry) {
/* DEL+ADD in the same orig interval have no effect and
@ -3447,6 +3450,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv)
* batadv_tt_check_roam_count() - check if a client has roamed too frequently
* @bat_priv: the bat priv with all the mesh interface information
* @client: mac address of the roaming client
* @vid: VLAN identifier
*
* This function checks whether the client already reached the
* maximum number of possible roaming phases. In this case the ROAMING_ADV
@ -3454,7 +3458,7 @@ static void batadv_tt_roam_purge(struct batadv_priv *bat_priv)
*
* Return: true if the ROAMING_ADV can be sent, false otherwise
*/
static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client)
static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client, u16 vid)
{
struct batadv_tt_roam_node *tt_roam_node;
bool ret = false;
@ -3467,6 +3471,9 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client)
if (!batadv_compare_eth(tt_roam_node->addr, client))
continue;
if (tt_roam_node->vid != vid)
continue;
if (batadv_has_timed_out(tt_roam_node->first_time,
BATADV_ROAMING_MAX_TIME))
continue;
@ -3488,6 +3495,7 @@ static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client)
atomic_set(&tt_roam_node->counter,
BATADV_ROAMING_MAX_COUNT - 1);
ether_addr_copy(tt_roam_node->addr, client);
tt_roam_node->vid = vid;
list_add(&tt_roam_node->list, &bat_priv->tt.roam_list);
ret = true;
@ -3524,7 +3532,7 @@ static void batadv_send_roam_adv(struct batadv_priv *bat_priv, u8 *client,
/* before going on we have to check whether the client has
* already roamed to us too many times
*/
if (!batadv_tt_check_roam_count(bat_priv, client))
if (!batadv_tt_check_roam_count(bat_priv, client, vid))
goto out;
batadv_dbg(BATADV_DBG_TT, bat_priv,

View File

@ -411,7 +411,6 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv,
tvlv_handler->ogm_handler(bat_priv, orig_node,
BATADV_NO_FLAGS,
tvlv_value, tvlv_value_len);
tvlv_handler->flags |= BATADV_TVLV_HANDLER_OGM_CALLED;
break;
case BATADV_UNICAST_TVLV:
if (!skb)
@ -443,6 +442,48 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv,
return NET_RX_SUCCESS;
}
/**
* batadv_tvlv_containers_contain() - check if a tvlv buffer holds a container
* @tvlv_value: tvlv content
* @tvlv_value_len: tvlv content length
* @type: tvlv container type to look for
* @version: tvlv container version to look for
*
* Return: true if a container of the given type and version is present in the
* tvlv buffer, false otherwise.
*/
static bool batadv_tvlv_containers_contain(void *tvlv_value,
u16 tvlv_value_len, u8 type,
u8 version)
{
struct batadv_tvlv_hdr *tvlv_hdr;
u16 tvlv_value_cont_len;
while (tvlv_value_len >= sizeof(*tvlv_hdr)) {
tvlv_hdr = tvlv_value;
tvlv_value_cont_len = ntohs(tvlv_hdr->len);
tvlv_value = tvlv_hdr + 1;
tvlv_value_len -= sizeof(*tvlv_hdr);
if (tvlv_value_cont_len > tvlv_value_len)
break;
/* the next tvlv header is accessed assuming (at least) 2-byte
* alignment, so it must start at an even offset.
*/
if (tvlv_value_cont_len & 1)
break;
if (tvlv_hdr->type == type && tvlv_hdr->version == version)
return true;
tvlv_value = (u8 *)tvlv_value + tvlv_value_cont_len;
tvlv_value_len -= tvlv_value_cont_len;
}
return false;
}
/**
* batadv_tvlv_containers_process() - parse the given tvlv buffer to call the
* appropriate handlers
@ -462,7 +503,9 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
struct sk_buff *skb, void *tvlv_value,
u16 tvlv_value_len)
{
u16 tvlv_value_start_len = tvlv_value_len;
struct batadv_tvlv_handler *tvlv_handler;
void *tvlv_value_start = tvlv_value;
struct batadv_tvlv_hdr *tvlv_hdr;
u16 tvlv_value_cont_len;
u8 cifnotfound = BATADV_TVLV_HANDLER_OGM_CIFNOTFND;
@ -477,6 +520,12 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
if (tvlv_value_cont_len > tvlv_value_len)
break;
/* the next tvlv header is accessed assuming (at least) 2-byte
* alignment, so it must start at an even offset.
*/
if (tvlv_value_cont_len & 1)
break;
tvlv_handler = batadv_tvlv_handler_get(bat_priv,
tvlv_hdr->type,
tvlv_hdr->version);
@ -500,12 +549,20 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
if (!tvlv_handler->ogm_handler)
continue;
if ((tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) &&
!(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CALLED))
tvlv_handler->ogm_handler(bat_priv, orig_node,
cifnotfound, NULL, 0);
if (!(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND))
continue;
tvlv_handler->flags &= ~BATADV_TVLV_HANDLER_OGM_CALLED;
/* if the corresponding container was present then the handler
* was already called from the loop above
*/
if (batadv_tvlv_containers_contain(tvlv_value_start,
tvlv_value_start_len,
tvlv_handler->type,
tvlv_handler->version))
continue;
tvlv_handler->ogm_handler(bat_priv, orig_node,
cifnotfound, NULL, 0);
}
rcu_read_unlock();

View File

@ -145,6 +145,12 @@ struct batadv_hard_iface_bat_v {
/** @aggr_list: queue for to be aggregated OGM packets */
struct sk_buff_head aggr_list;
/**
* @aggr_list_enabled: aggr_list is active and new skbs can be
* enqueued. Protected by aggr_list.lock after initialization
*/
bool aggr_list_enabled:1;
/** @aggr_len: size of the OGM aggregate (excluding ethernet header) */
unsigned int aggr_len;
@ -1357,9 +1363,12 @@ struct batadv_tp_vars_common {
/** @unacked_list: list of unacked packets (meta-info only) */
struct list_head unacked_list;
/** @unacked_lock: protect unacked_list */
/** @unacked_lock: protect unacked_list + &batadv_tp_receiver.last_recv */
spinlock_t unacked_lock;
/** @unacked_count: number of unacked entries */
size_t unacked_count;
/** @refcount: number of context where the object is used */
struct kref refcount;
@ -1952,6 +1961,9 @@ struct batadv_tt_roam_node {
/** @addr: mac address of the client in the roaming phase */
u8 addr[ETH_ALEN];
/** @vid: VLAN identifier */
u16 vid;
/**
* @counter: number of allowed roaming events per client within a single
* OGM interval (changes are committed with each OGM)
@ -2282,13 +2294,6 @@ enum batadv_tvlv_handler_flags {
* will call this handler even if its type was not found (with no data)
*/
BATADV_TVLV_HANDLER_OGM_CIFNOTFND = BIT(1),
/**
* @BATADV_TVLV_HANDLER_OGM_CALLED: interval tvlv handling flag - the
* API marks a handler as being called, so it won't be called if the
* BATADV_TVLV_HANDLER_OGM_CIFNOTFND flag was set
*/
BATADV_TVLV_HANDLER_OGM_CALLED = BIT(2),
};
#endif /* _NET_BATMAN_ADV_TYPES_H_ */