mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 09:36:22 +02:00
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:
commit
50ffe0645d
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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_ */
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user