Bug #252 » 0001-TODO-batman-adv-tt-reject-VLAN-TT-entries-before-rea.patch
| net/batman-adv/main.c | ||
|---|---|---|
|
spin_lock_init(&bat_priv->tt.roam_list_lock);
|
||
|
spin_lock_init(&bat_priv->tt.last_changeset_lock);
|
||
|
spin_lock_init(&bat_priv->tt.commit_lock);
|
||
|
spin_lock_init(&bat_priv->tt.reserve_lock);
|
||
|
spin_lock_init(&bat_priv->gw.list_lock);
|
||
|
#ifdef CONFIG_BATMAN_ADV_MCAST
|
||
|
spin_lock_init(&bat_priv->mcast.mla_lock);
|
||
| net/batman-adv/mesh-interface.c | ||
|---|---|---|
|
hlist_del_rcu(&vlan->list);
|
||
|
spin_unlock_bh(&vlan->bat_priv->meshif_vlan_list_lock);
|
||
|
batadv_tt_local_unreserve_vlan(vlan->bat_priv);
|
||
|
kfree_rcu(vlan, rcu);
|
||
|
}
|
||
| ... | ... | |
|
return -EEXIST;
|
||
|
}
|
||
|
/* Ignore the VLAN if its header no longer fits in a full table
|
||
|
* response. The room is reserved before the allocation to stop parallel
|
||
|
* additions from exceeding the maximum packet size together.
|
||
|
*/
|
||
|
if (!batadv_tt_local_reserve_vlan(bat_priv, vid)) {
|
||
|
spin_unlock_bh(&bat_priv->meshif_vlan_list_lock);
|
||
|
return -EMSGSIZE;
|
||
|
}
|
||
|
vlan = kzalloc_obj(*vlan, GFP_ATOMIC);
|
||
|
if (!vlan) {
|
||
|
spin_unlock_bh(&bat_priv->meshif_vlan_list_lock);
|
||
|
batadv_tt_local_unreserve_vlan(bat_priv);
|
||
|
return -ENOMEM;
|
||
|
}
|
||
| net/batman-adv/translation-table.c | ||
|---|---|---|
|
return tt_global_entry;
|
||
|
}
|
||
|
/**
|
||
|
* batadv_tt_len() - compute length in bytes of given number of tt changes
|
||
|
* @changes_num: number of tt changes
|
||
|
*
|
||
|
* Return: computed length in bytes.
|
||
|
*/
|
||
|
static int batadv_tt_len(int changes_num)
|
||
|
{
|
||
|
return changes_num * sizeof(struct batadv_tvlv_tt_change);
|
||
|
}
|
||
|
/**
|
||
|
* batadv_tt_local_transmit_size() - calculate the size of a full table response
|
||
|
* for a given number of VLANs and local TT entries
|
||
|
* @num_vlan: number of announced VLANs
|
||
|
* @num_entries: number of announced local TT entries
|
||
|
*
|
||
|
* Return: local translation table size in bytes.
|
||
|
*/
|
||
|
static int batadv_tt_local_transmit_size(u16 num_vlan, u16 num_entries)
|
||
|
{
|
||
|
int hdr_size;
|
||
|
/* header size of tvlv encapsulated tt response payload */
|
||
|
hdr_size = sizeof(struct batadv_unicast_tvlv_packet);
|
||
|
hdr_size += sizeof(struct batadv_tvlv_hdr);
|
||
|
hdr_size += sizeof(struct batadv_tvlv_tt_data);
|
||
|
hdr_size += num_vlan * sizeof(struct batadv_tvlv_tt_vlan_data);
|
||
|
return hdr_size + batadv_tt_len(num_entries);
|
||
|
}
|
||
|
/**
|
||
|
* batadv_tt_local_reserve() - reserve room in the transmittable local table
|
||
|
* @bat_priv: the bat priv with all the mesh interface information
|
||
|
* @num_vlan: number of VLANs to reserve
|
||
|
* @num_entries: number of local TT entries to reserve
|
||
|
* @table_size: stores the resulting worst case table size in bytes
|
||
|
*
|
||
|
* Add the requested number of VLANs and local TT entries to the reservation
|
||
|
* counters and check whether the local translation table would then still fit
|
||
|
* in a single full table response. The reservation is dropped again when it
|
||
|
* would not.
|
||
|
*
|
||
|
* The reservation has to happen before the related object is allocated. This
|
||
|
* way two parallel allocations cannot both observe enough room for themselves
|
||
|
* and end up with a local table which can no longer be transmitted.
|
||
|
*
|
||
|
* A granted reservation must be returned via batadv_tt_local_unreserve() when
|
||
|
* the related object is released or was never created.
|
||
|
*
|
||
|
* Return: true when the reservation was granted, false otherwise.
|
||
|
*/
|
||
|
static bool batadv_tt_local_reserve(struct batadv_priv *bat_priv, u16 num_vlan,
|
||
|
u16 num_entries, int *table_size)
|
||
|
{
|
||
|
int packet_size_max = READ_ONCE(bat_priv->packet_size_max);
|
||
|
scoped_guard(spinlock_bh, &bat_priv->tt.reserve_lock) {
|
||
|
bat_priv->tt.reserved_vlans += num_vlan;
|
||
|
bat_priv->tt.reserved_entries += num_entries;
|
||
|
*table_size =
|
||
|
batadv_tt_local_transmit_size(bat_priv->tt.reserved_vlans,
|
||
|
bat_priv->tt.reserved_entries);
|
||
|
if (*table_size <= packet_size_max)
|
||
|
return true;
|
||
|
bat_priv->tt.reserved_vlans -= num_vlan;
|
||
|
bat_priv->tt.reserved_entries -= num_entries;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
/**
|
||
|
* batadv_tt_local_unreserve() - return room in the transmittable local table
|
||
|
* @bat_priv: the bat priv with all the mesh interface information
|
||
|
* @num_vlan: number of VLANs to return
|
||
|
* @num_entries: number of local TT entries to return
|
||
|
*/
|
||
|
static void batadv_tt_local_unreserve(struct batadv_priv *bat_priv,
|
||
|
u16 num_vlan, u16 num_entries)
|
||
|
{
|
||
|
scoped_guard(spinlock_bh, &bat_priv->tt.reserve_lock) {
|
||
|
bat_priv->tt.reserved_vlans -= num_vlan;
|
||
|
bat_priv->tt.reserved_entries -= num_entries;
|
||
|
}
|
||
|
}
|
||
|
/**
|
||
|
* batadv_tt_local_reserve_entry() - reserve room for a new local TT entry
|
||
|
* @bat_priv: the bat priv with all the mesh interface information
|
||
|
* @addr: the mac address of the client to add
|
||
|
*
|
||
|
* Return: true when the reservation was granted, false otherwise.
|
||
|
*/
|
||
|
static bool batadv_tt_local_reserve_entry(struct batadv_priv *bat_priv,
|
||
|
const u8 *addr)
|
||
|
{
|
||
|
int table_size;
|
||
|
if (batadv_tt_local_reserve(bat_priv, 0, 1, &table_size))
|
||
|
return true;
|
||
|
net_ratelimited_function(batadv_info, bat_priv->mesh_iface,
|
||
|
"Local translation table size (%i) exceeds maximum packet size (%i); Ignoring new local tt entry: %pM\n",
|
||
|
table_size,
|
||
|
READ_ONCE(bat_priv->packet_size_max), addr);
|
||
|
return false;
|
||
|
}
|
||
|
/**
|
||
|
* batadv_tt_local_unreserve_entry() - return the room of a local TT entry
|
||
|
* @bat_priv: the bat priv with all the mesh interface information
|
||
|
*/
|
||
|
static void batadv_tt_local_unreserve_entry(struct batadv_priv *bat_priv)
|
||
|
{
|
||
|
batadv_tt_local_unreserve(bat_priv, 0, 1);
|
||
|
}
|
||
|
/**
|
||
|
* batadv_tt_local_reserve_vlan() - reserve room for a new VLAN
|
||
|
* @bat_priv: the bat priv with all the mesh interface information
|
||
|
* @vid: the VLAN identifier
|
||
|
*
|
||
|
* Each VLAN adds a per VLAN header to the full table response and therefore
|
||
|
* has to be reserved before batadv_meshif_create_vlan() allocates it.
|
||
|
*
|
||
|
* Return: true when the reservation was granted, false otherwise.
|
||
|
*/
|
||
|
bool batadv_tt_local_reserve_vlan(struct batadv_priv *bat_priv,
|
||
|
unsigned short vid)
|
||
|
{
|
||
|
int table_size;
|
||
|
if (batadv_tt_local_reserve(bat_priv, 1, 0, &table_size))
|
||
|
return true;
|
||
|
net_ratelimited_function(batadv_info, bat_priv->mesh_iface,
|
||
|
"Local translation table size (%i) exceeds maximum packet size (%i); Ignoring new VLAN: %d\n",
|
||
|
table_size, READ_ONCE(bat_priv->packet_size_max),
|
||
|
batadv_print_vid(vid));
|
||
|
return false;
|
||
|
}
|
||
|
/**
|
||
|
* batadv_tt_local_unreserve_vlan() - return the room of a VLAN
|
||
|
* @bat_priv: the bat priv with all the mesh interface information
|
||
|
*/
|
||
|
void batadv_tt_local_unreserve_vlan(struct batadv_priv *bat_priv)
|
||
|
{
|
||
|
batadv_tt_local_unreserve(bat_priv, 1, 0);
|
||
|
}
|
||
|
/**
|
||
|
* batadv_tt_local_entry_release() - release tt_local_entry from lists and queue
|
||
|
* for free after rcu grace period
|
||
| ... | ... | |
|
tt_local_entry = container_of(ref, struct batadv_tt_local_entry,
|
||
|
common.refcount);
|
||
|
batadv_tt_local_unreserve_entry(tt_local_entry->vlan->bat_priv);
|
||
|
batadv_meshif_vlan_put(tt_local_entry->vlan);
|
||
|
#if LINUX_VERSION_IS_GEQ(6, 12, 0) // UGLY_HACK_NEW
|
||
| ... | ... | |
|
spin_unlock_bh(&bat_priv->tt.changes_list_lock);
|
||
|
}
|
||
|
/**
|
||
|
* batadv_tt_len() - compute length in bytes of given number of tt changes
|
||
|
* @changes_num: number of tt changes
|
||
|
*
|
||
|
* Return: computed length in bytes.
|
||
|
*/
|
||
|
static int batadv_tt_len(int changes_num)
|
||
|
{
|
||
|
return changes_num * sizeof(struct batadv_tvlv_tt_change);
|
||
|
}
|
||
|
/**
|
||
|
* batadv_tt_entries() - compute the number of entries fitting in tt_len bytes
|
||
|
* @tt_len: available space
|
||
| ... | ... | |
|
struct batadv_meshif_vlan *vlan;
|
||
|
u16 tt_local_entries = 0;
|
||
|
u16 num_vlan = 0;
|
||
|
int hdr_size;
|
||
|
rcu_read_lock();
|
||
|
hlist_for_each_entry_rcu(vlan, &bat_priv->meshif_vlan_list, list) {
|
||
| ... | ... | |
|
}
|
||
|
rcu_read_unlock();
|
||
|
/* header size of tvlv encapsulated tt response payload */
|
||
|
hdr_size = sizeof(struct batadv_unicast_tvlv_packet);
|
||
|
hdr_size += sizeof(struct batadv_tvlv_hdr);
|
||
|
hdr_size += sizeof(struct batadv_tvlv_tt_data);
|
||
|
hdr_size += num_vlan * sizeof(struct batadv_tvlv_tt_vlan_data);
|
||
|
return hdr_size + batadv_tt_len(tt_local_entries);
|
||
|
return batadv_tt_local_transmit_size(num_vlan, tt_local_entries);
|
||
|
}
|
||
|
/**
|
||
| ... | ... | |
|
struct batadv_priv *bat_priv = netdev_priv(mesh_iface);
|
||
|
struct batadv_tt_local_entry *tt_local;
|
||
|
struct batadv_meshif_vlan *vlan;
|
||
|
int packet_size_max;
|
||
|
int table_size;
|
||
|
/* Ignore the client if we cannot send it in a full table response. */
|
||
|
table_size = batadv_tt_local_table_transmit_size(bat_priv);
|
||
|
table_size += batadv_tt_len(1);
|
||
|
packet_size_max = READ_ONCE(bat_priv->packet_size_max);
|
||
|
if (table_size > packet_size_max) {
|
||
|
net_ratelimited_function(batadv_info, mesh_iface,
|
||
|
"Local translation table size (%i) exceeds maximum packet size (%i); Ignoring new local tt entry: %pM\n",
|
||
|
table_size, packet_size_max, addr);
|
||
|
/* Ignore the client if we cannot send it in a full table response. The
|
||
|
* room is reserved before the allocation to stop parallel additions
|
||
|
* from exceeding the maximum packet size together.
|
||
|
*/
|
||
|
if (!batadv_tt_local_reserve_entry(bat_priv, addr))
|
||
|
return NULL;
|
||
|
}
|
||
|
tt_local = kmem_cache_alloc(batadv_tl_cache, GFP_ATOMIC);
|
||
|
if (!tt_local)
|
||
|
if (!tt_local) {
|
||
|
batadv_tt_local_unreserve_entry(bat_priv);
|
||
|
return NULL;
|
||
|
}
|
||
|
/* increase the refcounter of the related vlan */
|
||
|
vlan = batadv_meshif_vlan_get(bat_priv, vid);
|
||
| ... | ... | |
|
"adding TT local entry %pM to non-existent VLAN %d\n",
|
||
|
addr, batadv_print_vid(vid));
|
||
|
kmem_cache_free(batadv_tl_cache, tt_local);
|
||
|
batadv_tt_local_unreserve_entry(bat_priv);
|
||
|
return NULL;
|
||
|
}
|
||
| net/batman-adv/translation-table.h | ||
|---|---|---|
|
#include <linux/types.h>
|
||
|
int batadv_tt_init(struct batadv_priv *bat_priv);
|
||
|
bool batadv_tt_local_reserve_vlan(struct batadv_priv *bat_priv,
|
||
|
unsigned short vid);
|
||
|
void batadv_tt_local_unreserve_vlan(struct batadv_priv *bat_priv);
|
||
|
bool batadv_tt_local_add(struct net_device *mesh_iface, const u8 *addr,
|
||
|
unsigned short vid, int ifindex, u32 mark);
|
||
|
u16 batadv_tt_local_remove(struct batadv_priv *bat_priv,
|
||
| net/batman-adv/types.h | ||
|---|---|---|
|
*/
|
||
|
spinlock_t commit_lock;
|
||
|
/**
|
||
|
* @reserved_entries: number of local TT entries which are currently
|
||
|
* allocated or about to be allocated. Together with @reserved_vlans it
|
||
|
* describes the worst case size of a full table response.
|
||
|
*/
|
||
|
u16 reserved_entries;
|
||
|
/**
|
||
|
* @reserved_vlans: number of mesh interface VLANs which are currently
|
||
|
* allocated or about to be allocated. Together with @reserved_entries
|
||
|
* it describes the worst case size of a full table response.
|
||
|
*/
|
||
|
u16 reserved_vlans;
|
||
|
/**
|
||
|
* @reserve_lock: lock protecting reserved_entries & reserved_vlans
|
||
|
*/
|
||
|
spinlock_t reserve_lock;
|
||
|
/** @work: work queue callback item for translation table purging */
|
||
|
struct delayed_work work;
|
||
|
};
|
||
- « Previous
- 1
- 2
- Next »