mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-25 03:55:09 +08:00
b67bfe0d42
I'm not sure why, but the hlist for each entry iterators were conceived list_for_each_entry(pos, head, member) The hlist ones were greedy and wanted an extra parameter: hlist_for_each_entry(tpos, pos, head, member) Why did they need an extra pos parameter? I'm not quite sure. Not only they don't really need it, it also prevents the iterator from looking exactly like the list iterator, which is unfortunate. Besides the semantic patch, there was some manual work required: - Fix up the actual hlist iterators in linux/list.h - Fix up the declaration of other iterators based on the hlist ones. - A very small amount of places were using the 'node' parameter, this was modified to use 'obj->member' instead. - Coccinelle didn't handle the hlist_for_each_entry_safe iterator properly, so those had to be fixed up manually. The semantic patch which is mostly the work of Peter Senna Tschudin is here: @@ iterator name hlist_for_each_entry, hlist_for_each_entry_continue, hlist_for_each_entry_from, hlist_for_each_entry_rcu, hlist_for_each_entry_rcu_bh, hlist_for_each_entry_continue_rcu_bh, for_each_busy_worker, ax25_uid_for_each, ax25_for_each, inet_bind_bucket_for_each, sctp_for_each_hentry, sk_for_each, sk_for_each_rcu, sk_for_each_from, sk_for_each_safe, sk_for_each_bound, hlist_for_each_entry_safe, hlist_for_each_entry_continue_rcu, nr_neigh_for_each, nr_neigh_for_each_safe, nr_node_for_each, nr_node_for_each_safe, for_each_gfn_indirect_valid_sp, for_each_gfn_sp, for_each_host; type T; expression a,c,d,e; identifier b; statement S; @@ -T b; <+... when != b ( hlist_for_each_entry(a, - b, c, d) S | hlist_for_each_entry_continue(a, - b, c) S | hlist_for_each_entry_from(a, - b, c) S | hlist_for_each_entry_rcu(a, - b, c, d) S | hlist_for_each_entry_rcu_bh(a, - b, c, d) S | hlist_for_each_entry_continue_rcu_bh(a, - b, c) S | for_each_busy_worker(a, c, - b, d) S | ax25_uid_for_each(a, - b, c) S | ax25_for_each(a, - b, c) S | inet_bind_bucket_for_each(a, - b, c) S | sctp_for_each_hentry(a, - b, c) S | sk_for_each(a, - b, c) S | sk_for_each_rcu(a, - b, c) S | sk_for_each_from -(a, b) +(a) S + sk_for_each_from(a) S | sk_for_each_safe(a, - b, c, d) S | sk_for_each_bound(a, - b, c) S | hlist_for_each_entry_safe(a, - b, c, d, e) S | hlist_for_each_entry_continue_rcu(a, - b, c) S | nr_neigh_for_each(a, - b, c) S | nr_neigh_for_each_safe(a, - b, c, d) S | nr_node_for_each(a, - b, c) S | nr_node_for_each_safe(a, - b, c, d) S | - for_each_gfn_sp(a, c, d, b) S + for_each_gfn_sp(a, c, d) S | - for_each_gfn_indirect_valid_sp(a, c, d, b) S + for_each_gfn_indirect_valid_sp(a, c, d) S | for_each_host(a, - b, c) S | for_each_host_safe(a, - b, c, d) S | for_each_mesh_entry(a, - b, c, d) S ) ...+> [akpm@linux-foundation.org: drop bogus change from net/ipv4/raw.c] [akpm@linux-foundation.org: drop bogus hunk from net/ipv6/raw.c] [akpm@linux-foundation.org: checkpatch fixes] [akpm@linux-foundation.org: fix warnings] [akpm@linux-foudnation.org: redo intrusive kvm changes] Tested-by: Peter Senna Tschudin <peter.senna@gmail.com> Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com> Signed-off-by: Sasha Levin <sasha.levin@oracle.com> Cc: Wu Fengguang <fengguang.wu@intel.com> Cc: Marcelo Tosatti <mtosatti@redhat.com> Cc: Gleb Natapov <gleb@redhat.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
1340 lines
41 KiB
C
1340 lines
41 KiB
C
/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors:
|
|
*
|
|
* Marek Lindner, Simon Wunderlich
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of version 2 of the GNU General Public
|
|
* License as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
* 02110-1301, USA
|
|
*/
|
|
|
|
#include "main.h"
|
|
#include "translation-table.h"
|
|
#include "ring_buffer.h"
|
|
#include "originator.h"
|
|
#include "routing.h"
|
|
#include "gateway_common.h"
|
|
#include "gateway_client.h"
|
|
#include "hard-interface.h"
|
|
#include "send.h"
|
|
#include "bat_algo.h"
|
|
|
|
static struct batadv_neigh_node *
|
|
batadv_iv_ogm_neigh_new(struct batadv_hard_iface *hard_iface,
|
|
const uint8_t *neigh_addr,
|
|
struct batadv_orig_node *orig_node,
|
|
struct batadv_orig_node *orig_neigh, __be32 seqno)
|
|
{
|
|
struct batadv_neigh_node *neigh_node;
|
|
|
|
neigh_node = batadv_neigh_node_new(hard_iface, neigh_addr,
|
|
ntohl(seqno));
|
|
if (!neigh_node)
|
|
goto out;
|
|
|
|
INIT_LIST_HEAD(&neigh_node->bonding_list);
|
|
|
|
neigh_node->orig_node = orig_neigh;
|
|
neigh_node->if_incoming = hard_iface;
|
|
|
|
spin_lock_bh(&orig_node->neigh_list_lock);
|
|
hlist_add_head_rcu(&neigh_node->list, &orig_node->neigh_list);
|
|
spin_unlock_bh(&orig_node->neigh_list_lock);
|
|
|
|
out:
|
|
return neigh_node;
|
|
}
|
|
|
|
static int batadv_iv_ogm_iface_enable(struct batadv_hard_iface *hard_iface)
|
|
{
|
|
struct batadv_ogm_packet *batadv_ogm_packet;
|
|
unsigned char *ogm_buff;
|
|
uint32_t random_seqno;
|
|
int res = -ENOMEM;
|
|
|
|
/* randomize initial seqno to avoid collision */
|
|
get_random_bytes(&random_seqno, sizeof(random_seqno));
|
|
atomic_set(&hard_iface->bat_iv.ogm_seqno, random_seqno);
|
|
|
|
hard_iface->bat_iv.ogm_buff_len = BATADV_OGM_HLEN;
|
|
ogm_buff = kmalloc(hard_iface->bat_iv.ogm_buff_len, GFP_ATOMIC);
|
|
if (!ogm_buff)
|
|
goto out;
|
|
|
|
hard_iface->bat_iv.ogm_buff = ogm_buff;
|
|
|
|
batadv_ogm_packet = (struct batadv_ogm_packet *)ogm_buff;
|
|
batadv_ogm_packet->header.packet_type = BATADV_IV_OGM;
|
|
batadv_ogm_packet->header.version = BATADV_COMPAT_VERSION;
|
|
batadv_ogm_packet->header.ttl = 2;
|
|
batadv_ogm_packet->flags = BATADV_NO_FLAGS;
|
|
batadv_ogm_packet->tq = BATADV_TQ_MAX_VALUE;
|
|
batadv_ogm_packet->tt_num_changes = 0;
|
|
batadv_ogm_packet->ttvn = 0;
|
|
|
|
res = 0;
|
|
|
|
out:
|
|
return res;
|
|
}
|
|
|
|
static void batadv_iv_ogm_iface_disable(struct batadv_hard_iface *hard_iface)
|
|
{
|
|
kfree(hard_iface->bat_iv.ogm_buff);
|
|
hard_iface->bat_iv.ogm_buff = NULL;
|
|
}
|
|
|
|
static void batadv_iv_ogm_iface_update_mac(struct batadv_hard_iface *hard_iface)
|
|
{
|
|
struct batadv_ogm_packet *batadv_ogm_packet;
|
|
unsigned char *ogm_buff = hard_iface->bat_iv.ogm_buff;
|
|
|
|
batadv_ogm_packet = (struct batadv_ogm_packet *)ogm_buff;
|
|
memcpy(batadv_ogm_packet->orig,
|
|
hard_iface->net_dev->dev_addr, ETH_ALEN);
|
|
memcpy(batadv_ogm_packet->prev_sender,
|
|
hard_iface->net_dev->dev_addr, ETH_ALEN);
|
|
}
|
|
|
|
static void
|
|
batadv_iv_ogm_primary_iface_set(struct batadv_hard_iface *hard_iface)
|
|
{
|
|
struct batadv_ogm_packet *batadv_ogm_packet;
|
|
unsigned char *ogm_buff = hard_iface->bat_iv.ogm_buff;
|
|
|
|
batadv_ogm_packet = (struct batadv_ogm_packet *)ogm_buff;
|
|
batadv_ogm_packet->flags = BATADV_PRIMARIES_FIRST_HOP;
|
|
batadv_ogm_packet->header.ttl = BATADV_TTL;
|
|
}
|
|
|
|
/* when do we schedule our own ogm to be sent */
|
|
static unsigned long
|
|
batadv_iv_ogm_emit_send_time(const struct batadv_priv *bat_priv)
|
|
{
|
|
unsigned int msecs;
|
|
|
|
msecs = atomic_read(&bat_priv->orig_interval) - BATADV_JITTER;
|
|
msecs += prandom_u32() % (2 * BATADV_JITTER);
|
|
|
|
return jiffies + msecs_to_jiffies(msecs);
|
|
}
|
|
|
|
/* when do we schedule a ogm packet to be sent */
|
|
static unsigned long batadv_iv_ogm_fwd_send_time(void)
|
|
{
|
|
return jiffies + msecs_to_jiffies(prandom_u32() % (BATADV_JITTER / 2));
|
|
}
|
|
|
|
/* apply hop penalty for a normal link */
|
|
static uint8_t batadv_hop_penalty(uint8_t tq,
|
|
const struct batadv_priv *bat_priv)
|
|
{
|
|
int hop_penalty = atomic_read(&bat_priv->hop_penalty);
|
|
int new_tq;
|
|
|
|
new_tq = tq * (BATADV_TQ_MAX_VALUE - hop_penalty);
|
|
new_tq /= BATADV_TQ_MAX_VALUE;
|
|
|
|
return new_tq;
|
|
}
|
|
|
|
/* is there another aggregated packet here? */
|
|
static int batadv_iv_ogm_aggr_packet(int buff_pos, int packet_len,
|
|
int tt_num_changes)
|
|
{
|
|
int next_buff_pos = 0;
|
|
|
|
next_buff_pos += buff_pos + BATADV_OGM_HLEN;
|
|
next_buff_pos += batadv_tt_len(tt_num_changes);
|
|
|
|
return (next_buff_pos <= packet_len) &&
|
|
(next_buff_pos <= BATADV_MAX_AGGREGATION_BYTES);
|
|
}
|
|
|
|
/* send a batman ogm to a given interface */
|
|
static void batadv_iv_ogm_send_to_if(struct batadv_forw_packet *forw_packet,
|
|
struct batadv_hard_iface *hard_iface)
|
|
{
|
|
struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
|
|
char *fwd_str;
|
|
uint8_t packet_num;
|
|
int16_t buff_pos;
|
|
struct batadv_ogm_packet *batadv_ogm_packet;
|
|
struct sk_buff *skb;
|
|
uint8_t *packet_pos;
|
|
|
|
if (hard_iface->if_status != BATADV_IF_ACTIVE)
|
|
return;
|
|
|
|
packet_num = 0;
|
|
buff_pos = 0;
|
|
packet_pos = forw_packet->skb->data;
|
|
batadv_ogm_packet = (struct batadv_ogm_packet *)packet_pos;
|
|
|
|
/* adjust all flags and log packets */
|
|
while (batadv_iv_ogm_aggr_packet(buff_pos, forw_packet->packet_len,
|
|
batadv_ogm_packet->tt_num_changes)) {
|
|
/* we might have aggregated direct link packets with an
|
|
* ordinary base packet
|
|
*/
|
|
if (forw_packet->direct_link_flags & BIT(packet_num) &&
|
|
forw_packet->if_incoming == hard_iface)
|
|
batadv_ogm_packet->flags |= BATADV_DIRECTLINK;
|
|
else
|
|
batadv_ogm_packet->flags &= ~BATADV_DIRECTLINK;
|
|
|
|
if (packet_num > 0 || !forw_packet->own)
|
|
fwd_str = "Forwarding";
|
|
else
|
|
fwd_str = "Sending own";
|
|
|
|
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
|
"%s %spacket (originator %pM, seqno %u, TQ %d, TTL %d, IDF %s, ttvn %d) on interface %s [%pM]\n",
|
|
fwd_str, (packet_num > 0 ? "aggregated " : ""),
|
|
batadv_ogm_packet->orig,
|
|
ntohl(batadv_ogm_packet->seqno),
|
|
batadv_ogm_packet->tq, batadv_ogm_packet->header.ttl,
|
|
(batadv_ogm_packet->flags & BATADV_DIRECTLINK ?
|
|
"on" : "off"),
|
|
batadv_ogm_packet->ttvn, hard_iface->net_dev->name,
|
|
hard_iface->net_dev->dev_addr);
|
|
|
|
buff_pos += BATADV_OGM_HLEN;
|
|
buff_pos += batadv_tt_len(batadv_ogm_packet->tt_num_changes);
|
|
packet_num++;
|
|
packet_pos = forw_packet->skb->data + buff_pos;
|
|
batadv_ogm_packet = (struct batadv_ogm_packet *)packet_pos;
|
|
}
|
|
|
|
/* create clone because function is called more than once */
|
|
skb = skb_clone(forw_packet->skb, GFP_ATOMIC);
|
|
if (skb) {
|
|
batadv_inc_counter(bat_priv, BATADV_CNT_MGMT_TX);
|
|
batadv_add_counter(bat_priv, BATADV_CNT_MGMT_TX_BYTES,
|
|
skb->len + ETH_HLEN);
|
|
batadv_send_skb_packet(skb, hard_iface, batadv_broadcast_addr);
|
|
}
|
|
}
|
|
|
|
/* send a batman ogm packet */
|
|
static void batadv_iv_ogm_emit(struct batadv_forw_packet *forw_packet)
|
|
{
|
|
struct batadv_hard_iface *hard_iface;
|
|
struct net_device *soft_iface;
|
|
struct batadv_priv *bat_priv;
|
|
struct batadv_hard_iface *primary_if = NULL;
|
|
struct batadv_ogm_packet *batadv_ogm_packet;
|
|
unsigned char directlink;
|
|
uint8_t *packet_pos;
|
|
|
|
packet_pos = forw_packet->skb->data;
|
|
batadv_ogm_packet = (struct batadv_ogm_packet *)packet_pos;
|
|
directlink = (batadv_ogm_packet->flags & BATADV_DIRECTLINK ? 1 : 0);
|
|
|
|
if (!forw_packet->if_incoming) {
|
|
pr_err("Error - can't forward packet: incoming iface not specified\n");
|
|
goto out;
|
|
}
|
|
|
|
soft_iface = forw_packet->if_incoming->soft_iface;
|
|
bat_priv = netdev_priv(soft_iface);
|
|
|
|
if (forw_packet->if_incoming->if_status != BATADV_IF_ACTIVE)
|
|
goto out;
|
|
|
|
primary_if = batadv_primary_if_get_selected(bat_priv);
|
|
if (!primary_if)
|
|
goto out;
|
|
|
|
/* multihomed peer assumed
|
|
* non-primary OGMs are only broadcasted on their interface
|
|
*/
|
|
if ((directlink && (batadv_ogm_packet->header.ttl == 1)) ||
|
|
(forw_packet->own && (forw_packet->if_incoming != primary_if))) {
|
|
/* FIXME: what about aggregated packets ? */
|
|
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
|
"%s packet (originator %pM, seqno %u, TTL %d) on interface %s [%pM]\n",
|
|
(forw_packet->own ? "Sending own" : "Forwarding"),
|
|
batadv_ogm_packet->orig,
|
|
ntohl(batadv_ogm_packet->seqno),
|
|
batadv_ogm_packet->header.ttl,
|
|
forw_packet->if_incoming->net_dev->name,
|
|
forw_packet->if_incoming->net_dev->dev_addr);
|
|
|
|
/* skb is only used once and than forw_packet is free'd */
|
|
batadv_send_skb_packet(forw_packet->skb,
|
|
forw_packet->if_incoming,
|
|
batadv_broadcast_addr);
|
|
forw_packet->skb = NULL;
|
|
|
|
goto out;
|
|
}
|
|
|
|
/* broadcast on every interface */
|
|
rcu_read_lock();
|
|
list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
|
|
if (hard_iface->soft_iface != soft_iface)
|
|
continue;
|
|
|
|
batadv_iv_ogm_send_to_if(forw_packet, hard_iface);
|
|
}
|
|
rcu_read_unlock();
|
|
|
|
out:
|
|
if (primary_if)
|
|
batadv_hardif_free_ref(primary_if);
|
|
}
|
|
|
|
/* return true if new_packet can be aggregated with forw_packet */
|
|
static bool
|
|
batadv_iv_ogm_can_aggregate(const struct batadv_ogm_packet *new_bat_ogm_packet,
|
|
struct batadv_priv *bat_priv,
|
|
int packet_len, unsigned long send_time,
|
|
bool directlink,
|
|
const struct batadv_hard_iface *if_incoming,
|
|
const struct batadv_forw_packet *forw_packet)
|
|
{
|
|
struct batadv_ogm_packet *batadv_ogm_packet;
|
|
int aggregated_bytes = forw_packet->packet_len + packet_len;
|
|
struct batadv_hard_iface *primary_if = NULL;
|
|
bool res = false;
|
|
unsigned long aggregation_end_time;
|
|
|
|
batadv_ogm_packet = (struct batadv_ogm_packet *)forw_packet->skb->data;
|
|
aggregation_end_time = send_time;
|
|
aggregation_end_time += msecs_to_jiffies(BATADV_MAX_AGGREGATION_MS);
|
|
|
|
/* we can aggregate the current packet to this aggregated packet
|
|
* if:
|
|
*
|
|
* - the send time is within our MAX_AGGREGATION_MS time
|
|
* - the resulting packet wont be bigger than
|
|
* MAX_AGGREGATION_BYTES
|
|
*/
|
|
if (time_before(send_time, forw_packet->send_time) &&
|
|
time_after_eq(aggregation_end_time, forw_packet->send_time) &&
|
|
(aggregated_bytes <= BATADV_MAX_AGGREGATION_BYTES)) {
|
|
/* check aggregation compatibility
|
|
* -> direct link packets are broadcasted on
|
|
* their interface only
|
|
* -> aggregate packet if the current packet is
|
|
* a "global" packet as well as the base
|
|
* packet
|
|
*/
|
|
primary_if = batadv_primary_if_get_selected(bat_priv);
|
|
if (!primary_if)
|
|
goto out;
|
|
|
|
/* packets without direct link flag and high TTL
|
|
* are flooded through the net
|
|
*/
|
|
if ((!directlink) &&
|
|
(!(batadv_ogm_packet->flags & BATADV_DIRECTLINK)) &&
|
|
(batadv_ogm_packet->header.ttl != 1) &&
|
|
|
|
/* own packets originating non-primary
|
|
* interfaces leave only that interface
|
|
*/
|
|
((!forw_packet->own) ||
|
|
(forw_packet->if_incoming == primary_if))) {
|
|
res = true;
|
|
goto out;
|
|
}
|
|
|
|
/* if the incoming packet is sent via this one
|
|
* interface only - we still can aggregate
|
|
*/
|
|
if ((directlink) &&
|
|
(new_bat_ogm_packet->header.ttl == 1) &&
|
|
(forw_packet->if_incoming == if_incoming) &&
|
|
|
|
/* packets from direct neighbors or
|
|
* own secondary interface packets
|
|
* (= secondary interface packets in general)
|
|
*/
|
|
(batadv_ogm_packet->flags & BATADV_DIRECTLINK ||
|
|
(forw_packet->own &&
|
|
forw_packet->if_incoming != primary_if))) {
|
|
res = true;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
out:
|
|
if (primary_if)
|
|
batadv_hardif_free_ref(primary_if);
|
|
return res;
|
|
}
|
|
|
|
/* create a new aggregated packet and add this packet to it */
|
|
static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
|
|
int packet_len, unsigned long send_time,
|
|
bool direct_link,
|
|
struct batadv_hard_iface *if_incoming,
|
|
int own_packet)
|
|
{
|
|
struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
|
|
struct batadv_forw_packet *forw_packet_aggr;
|
|
unsigned char *skb_buff;
|
|
unsigned int skb_size;
|
|
|
|
if (!atomic_inc_not_zero(&if_incoming->refcount))
|
|
return;
|
|
|
|
/* own packet should always be scheduled */
|
|
if (!own_packet) {
|
|
if (!batadv_atomic_dec_not_zero(&bat_priv->batman_queue_left)) {
|
|
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
|
"batman packet queue full\n");
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
forw_packet_aggr = kmalloc(sizeof(*forw_packet_aggr), GFP_ATOMIC);
|
|
if (!forw_packet_aggr) {
|
|
if (!own_packet)
|
|
atomic_inc(&bat_priv->batman_queue_left);
|
|
goto out;
|
|
}
|
|
|
|
if ((atomic_read(&bat_priv->aggregated_ogms)) &&
|
|
(packet_len < BATADV_MAX_AGGREGATION_BYTES))
|
|
skb_size = BATADV_MAX_AGGREGATION_BYTES;
|
|
else
|
|
skb_size = packet_len;
|
|
|
|
skb_size += ETH_HLEN + NET_IP_ALIGN;
|
|
|
|
forw_packet_aggr->skb = dev_alloc_skb(skb_size);
|
|
if (!forw_packet_aggr->skb) {
|
|
if (!own_packet)
|
|
atomic_inc(&bat_priv->batman_queue_left);
|
|
kfree(forw_packet_aggr);
|
|
goto out;
|
|
}
|
|
skb_reserve(forw_packet_aggr->skb, ETH_HLEN + NET_IP_ALIGN);
|
|
|
|
INIT_HLIST_NODE(&forw_packet_aggr->list);
|
|
|
|
skb_buff = skb_put(forw_packet_aggr->skb, packet_len);
|
|
forw_packet_aggr->packet_len = packet_len;
|
|
memcpy(skb_buff, packet_buff, packet_len);
|
|
|
|
forw_packet_aggr->own = own_packet;
|
|
forw_packet_aggr->if_incoming = if_incoming;
|
|
forw_packet_aggr->num_packets = 0;
|
|
forw_packet_aggr->direct_link_flags = BATADV_NO_FLAGS;
|
|
forw_packet_aggr->send_time = send_time;
|
|
|
|
/* save packet direct link flag status */
|
|
if (direct_link)
|
|
forw_packet_aggr->direct_link_flags |= 1;
|
|
|
|
/* add new packet to packet list */
|
|
spin_lock_bh(&bat_priv->forw_bat_list_lock);
|
|
hlist_add_head(&forw_packet_aggr->list, &bat_priv->forw_bat_list);
|
|
spin_unlock_bh(&bat_priv->forw_bat_list_lock);
|
|
|
|
/* start timer for this packet */
|
|
INIT_DELAYED_WORK(&forw_packet_aggr->delayed_work,
|
|
batadv_send_outstanding_bat_ogm_packet);
|
|
queue_delayed_work(batadv_event_workqueue,
|
|
&forw_packet_aggr->delayed_work,
|
|
send_time - jiffies);
|
|
|
|
return;
|
|
out:
|
|
batadv_hardif_free_ref(if_incoming);
|
|
}
|
|
|
|
/* aggregate a new packet into the existing ogm packet */
|
|
static void batadv_iv_ogm_aggregate(struct batadv_forw_packet *forw_packet_aggr,
|
|
const unsigned char *packet_buff,
|
|
int packet_len, bool direct_link)
|
|
{
|
|
unsigned char *skb_buff;
|
|
unsigned long new_direct_link_flag;
|
|
|
|
skb_buff = skb_put(forw_packet_aggr->skb, packet_len);
|
|
memcpy(skb_buff, packet_buff, packet_len);
|
|
forw_packet_aggr->packet_len += packet_len;
|
|
forw_packet_aggr->num_packets++;
|
|
|
|
/* save packet direct link flag status */
|
|
if (direct_link) {
|
|
new_direct_link_flag = BIT(forw_packet_aggr->num_packets);
|
|
forw_packet_aggr->direct_link_flags |= new_direct_link_flag;
|
|
}
|
|
}
|
|
|
|
static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv,
|
|
unsigned char *packet_buff,
|
|
int packet_len,
|
|
struct batadv_hard_iface *if_incoming,
|
|
int own_packet, unsigned long send_time)
|
|
{
|
|
/* _aggr -> pointer to the packet we want to aggregate with
|
|
* _pos -> pointer to the position in the queue
|
|
*/
|
|
struct batadv_forw_packet *forw_packet_aggr = NULL;
|
|
struct batadv_forw_packet *forw_packet_pos = NULL;
|
|
struct batadv_ogm_packet *batadv_ogm_packet;
|
|
bool direct_link;
|
|
unsigned long max_aggregation_jiffies;
|
|
|
|
batadv_ogm_packet = (struct batadv_ogm_packet *)packet_buff;
|
|
direct_link = batadv_ogm_packet->flags & BATADV_DIRECTLINK ? 1 : 0;
|
|
max_aggregation_jiffies = msecs_to_jiffies(BATADV_MAX_AGGREGATION_MS);
|
|
|
|
/* find position for the packet in the forward queue */
|
|
spin_lock_bh(&bat_priv->forw_bat_list_lock);
|
|
/* own packets are not to be aggregated */
|
|
if ((atomic_read(&bat_priv->aggregated_ogms)) && (!own_packet)) {
|
|
hlist_for_each_entry(forw_packet_pos,
|
|
&bat_priv->forw_bat_list, list) {
|
|
if (batadv_iv_ogm_can_aggregate(batadv_ogm_packet,
|
|
bat_priv, packet_len,
|
|
send_time, direct_link,
|
|
if_incoming,
|
|
forw_packet_pos)) {
|
|
forw_packet_aggr = forw_packet_pos;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* nothing to aggregate with - either aggregation disabled or no
|
|
* suitable aggregation packet found
|
|
*/
|
|
if (!forw_packet_aggr) {
|
|
/* the following section can run without the lock */
|
|
spin_unlock_bh(&bat_priv->forw_bat_list_lock);
|
|
|
|
/* if we could not aggregate this packet with one of the others
|
|
* we hold it back for a while, so that it might be aggregated
|
|
* later on
|
|
*/
|
|
if (!own_packet && atomic_read(&bat_priv->aggregated_ogms))
|
|
send_time += max_aggregation_jiffies;
|
|
|
|
batadv_iv_ogm_aggregate_new(packet_buff, packet_len,
|
|
send_time, direct_link,
|
|
if_incoming, own_packet);
|
|
} else {
|
|
batadv_iv_ogm_aggregate(forw_packet_aggr, packet_buff,
|
|
packet_len, direct_link);
|
|
spin_unlock_bh(&bat_priv->forw_bat_list_lock);
|
|
}
|
|
}
|
|
|
|
static void batadv_iv_ogm_forward(struct batadv_orig_node *orig_node,
|
|
const struct ethhdr *ethhdr,
|
|
struct batadv_ogm_packet *batadv_ogm_packet,
|
|
bool is_single_hop_neigh,
|
|
bool is_from_best_next_hop,
|
|
struct batadv_hard_iface *if_incoming)
|
|
{
|
|
struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
|
|
uint8_t tt_num_changes;
|
|
|
|
if (batadv_ogm_packet->header.ttl <= 1) {
|
|
batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "ttl exceeded\n");
|
|
return;
|
|
}
|
|
|
|
if (!is_from_best_next_hop) {
|
|
/* Mark the forwarded packet when it is not coming from our
|
|
* best next hop. We still need to forward the packet for our
|
|
* neighbor link quality detection to work in case the packet
|
|
* originated from a single hop neighbor. Otherwise we can
|
|
* simply drop the ogm.
|
|
*/
|
|
if (is_single_hop_neigh)
|
|
batadv_ogm_packet->flags |= BATADV_NOT_BEST_NEXT_HOP;
|
|
else
|
|
return;
|
|
}
|
|
|
|
tt_num_changes = batadv_ogm_packet->tt_num_changes;
|
|
|
|
batadv_ogm_packet->header.ttl--;
|
|
memcpy(batadv_ogm_packet->prev_sender, ethhdr->h_source, ETH_ALEN);
|
|
|
|
/* apply hop penalty */
|
|
batadv_ogm_packet->tq = batadv_hop_penalty(batadv_ogm_packet->tq,
|
|
bat_priv);
|
|
|
|
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
|
"Forwarding packet: tq: %i, ttl: %i\n",
|
|
batadv_ogm_packet->tq, batadv_ogm_packet->header.ttl);
|
|
|
|
/* switch of primaries first hop flag when forwarding */
|
|
batadv_ogm_packet->flags &= ~BATADV_PRIMARIES_FIRST_HOP;
|
|
if (is_single_hop_neigh)
|
|
batadv_ogm_packet->flags |= BATADV_DIRECTLINK;
|
|
else
|
|
batadv_ogm_packet->flags &= ~BATADV_DIRECTLINK;
|
|
|
|
batadv_iv_ogm_queue_add(bat_priv, (unsigned char *)batadv_ogm_packet,
|
|
BATADV_OGM_HLEN + batadv_tt_len(tt_num_changes),
|
|
if_incoming, 0, batadv_iv_ogm_fwd_send_time());
|
|
}
|
|
|
|
static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface)
|
|
{
|
|
struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
|
|
unsigned char **ogm_buff = &hard_iface->bat_iv.ogm_buff;
|
|
struct batadv_ogm_packet *batadv_ogm_packet;
|
|
struct batadv_hard_iface *primary_if;
|
|
int *ogm_buff_len = &hard_iface->bat_iv.ogm_buff_len;
|
|
int vis_server, tt_num_changes = 0;
|
|
uint32_t seqno;
|
|
uint8_t bandwidth;
|
|
|
|
vis_server = atomic_read(&bat_priv->vis_mode);
|
|
primary_if = batadv_primary_if_get_selected(bat_priv);
|
|
|
|
if (hard_iface == primary_if)
|
|
tt_num_changes = batadv_tt_append_diff(bat_priv, ogm_buff,
|
|
ogm_buff_len,
|
|
BATADV_OGM_HLEN);
|
|
|
|
batadv_ogm_packet = (struct batadv_ogm_packet *)(*ogm_buff);
|
|
|
|
/* change sequence number to network order */
|
|
seqno = (uint32_t)atomic_read(&hard_iface->bat_iv.ogm_seqno);
|
|
batadv_ogm_packet->seqno = htonl(seqno);
|
|
atomic_inc(&hard_iface->bat_iv.ogm_seqno);
|
|
|
|
batadv_ogm_packet->ttvn = atomic_read(&bat_priv->tt.vn);
|
|
batadv_ogm_packet->tt_crc = htons(bat_priv->tt.local_crc);
|
|
if (tt_num_changes >= 0)
|
|
batadv_ogm_packet->tt_num_changes = tt_num_changes;
|
|
|
|
if (vis_server == BATADV_VIS_TYPE_SERVER_SYNC)
|
|
batadv_ogm_packet->flags |= BATADV_VIS_SERVER;
|
|
else
|
|
batadv_ogm_packet->flags &= ~BATADV_VIS_SERVER;
|
|
|
|
if (hard_iface == primary_if &&
|
|
atomic_read(&bat_priv->gw_mode) == BATADV_GW_MODE_SERVER) {
|
|
bandwidth = (uint8_t)atomic_read(&bat_priv->gw_bandwidth);
|
|
batadv_ogm_packet->gw_flags = bandwidth;
|
|
} else {
|
|
batadv_ogm_packet->gw_flags = BATADV_NO_FLAGS;
|
|
}
|
|
|
|
batadv_slide_own_bcast_window(hard_iface);
|
|
batadv_iv_ogm_queue_add(bat_priv, hard_iface->bat_iv.ogm_buff,
|
|
hard_iface->bat_iv.ogm_buff_len, hard_iface, 1,
|
|
batadv_iv_ogm_emit_send_time(bat_priv));
|
|
|
|
if (primary_if)
|
|
batadv_hardif_free_ref(primary_if);
|
|
}
|
|
|
|
static void
|
|
batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv,
|
|
struct batadv_orig_node *orig_node,
|
|
const struct ethhdr *ethhdr,
|
|
const struct batadv_ogm_packet *batadv_ogm_packet,
|
|
struct batadv_hard_iface *if_incoming,
|
|
const unsigned char *tt_buff,
|
|
int is_duplicate)
|
|
{
|
|
struct batadv_neigh_node *neigh_node = NULL, *tmp_neigh_node = NULL;
|
|
struct batadv_neigh_node *router = NULL;
|
|
struct batadv_orig_node *orig_node_tmp;
|
|
int if_num;
|
|
uint8_t sum_orig, sum_neigh;
|
|
uint8_t *neigh_addr;
|
|
uint8_t tq_avg;
|
|
|
|
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
|
"update_originator(): Searching and updating originator entry of received packet\n");
|
|
|
|
rcu_read_lock();
|
|
hlist_for_each_entry_rcu(tmp_neigh_node,
|
|
&orig_node->neigh_list, list) {
|
|
neigh_addr = tmp_neigh_node->addr;
|
|
if (batadv_compare_eth(neigh_addr, ethhdr->h_source) &&
|
|
tmp_neigh_node->if_incoming == if_incoming &&
|
|
atomic_inc_not_zero(&tmp_neigh_node->refcount)) {
|
|
if (neigh_node)
|
|
batadv_neigh_node_free_ref(neigh_node);
|
|
neigh_node = tmp_neigh_node;
|
|
continue;
|
|
}
|
|
|
|
if (is_duplicate)
|
|
continue;
|
|
|
|
spin_lock_bh(&tmp_neigh_node->lq_update_lock);
|
|
batadv_ring_buffer_set(tmp_neigh_node->tq_recv,
|
|
&tmp_neigh_node->tq_index, 0);
|
|
tq_avg = batadv_ring_buffer_avg(tmp_neigh_node->tq_recv);
|
|
tmp_neigh_node->tq_avg = tq_avg;
|
|
spin_unlock_bh(&tmp_neigh_node->lq_update_lock);
|
|
}
|
|
|
|
if (!neigh_node) {
|
|
struct batadv_orig_node *orig_tmp;
|
|
|
|
orig_tmp = batadv_get_orig_node(bat_priv, ethhdr->h_source);
|
|
if (!orig_tmp)
|
|
goto unlock;
|
|
|
|
neigh_node = batadv_iv_ogm_neigh_new(if_incoming,
|
|
ethhdr->h_source,
|
|
orig_node, orig_tmp,
|
|
batadv_ogm_packet->seqno);
|
|
|
|
batadv_orig_node_free_ref(orig_tmp);
|
|
if (!neigh_node)
|
|
goto unlock;
|
|
} else
|
|
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
|
"Updating existing last-hop neighbor of originator\n");
|
|
|
|
rcu_read_unlock();
|
|
|
|
orig_node->flags = batadv_ogm_packet->flags;
|
|
neigh_node->last_seen = jiffies;
|
|
|
|
spin_lock_bh(&neigh_node->lq_update_lock);
|
|
batadv_ring_buffer_set(neigh_node->tq_recv,
|
|
&neigh_node->tq_index,
|
|
batadv_ogm_packet->tq);
|
|
neigh_node->tq_avg = batadv_ring_buffer_avg(neigh_node->tq_recv);
|
|
spin_unlock_bh(&neigh_node->lq_update_lock);
|
|
|
|
if (!is_duplicate) {
|
|
orig_node->last_ttl = batadv_ogm_packet->header.ttl;
|
|
neigh_node->last_ttl = batadv_ogm_packet->header.ttl;
|
|
}
|
|
|
|
batadv_bonding_candidate_add(orig_node, neigh_node);
|
|
|
|
/* if this neighbor already is our next hop there is nothing
|
|
* to change
|
|
*/
|
|
router = batadv_orig_node_get_router(orig_node);
|
|
if (router == neigh_node)
|
|
goto update_tt;
|
|
|
|
/* if this neighbor does not offer a better TQ we won't consider it */
|
|
if (router && (router->tq_avg > neigh_node->tq_avg))
|
|
goto update_tt;
|
|
|
|
/* if the TQ is the same and the link not more symmetric we
|
|
* won't consider it either
|
|
*/
|
|
if (router && (neigh_node->tq_avg == router->tq_avg)) {
|
|
orig_node_tmp = router->orig_node;
|
|
spin_lock_bh(&orig_node_tmp->ogm_cnt_lock);
|
|
if_num = router->if_incoming->if_num;
|
|
sum_orig = orig_node_tmp->bcast_own_sum[if_num];
|
|
spin_unlock_bh(&orig_node_tmp->ogm_cnt_lock);
|
|
|
|
orig_node_tmp = neigh_node->orig_node;
|
|
spin_lock_bh(&orig_node_tmp->ogm_cnt_lock);
|
|
if_num = neigh_node->if_incoming->if_num;
|
|
sum_neigh = orig_node_tmp->bcast_own_sum[if_num];
|
|
spin_unlock_bh(&orig_node_tmp->ogm_cnt_lock);
|
|
|
|
if (sum_orig >= sum_neigh)
|
|
goto update_tt;
|
|
}
|
|
|
|
batadv_update_route(bat_priv, orig_node, neigh_node);
|
|
|
|
update_tt:
|
|
/* I have to check for transtable changes only if the OGM has been
|
|
* sent through a primary interface
|
|
*/
|
|
if (((batadv_ogm_packet->orig != ethhdr->h_source) &&
|
|
(batadv_ogm_packet->header.ttl > 2)) ||
|
|
(batadv_ogm_packet->flags & BATADV_PRIMARIES_FIRST_HOP))
|
|
batadv_tt_update_orig(bat_priv, orig_node, tt_buff,
|
|
batadv_ogm_packet->tt_num_changes,
|
|
batadv_ogm_packet->ttvn,
|
|
ntohs(batadv_ogm_packet->tt_crc));
|
|
|
|
if (orig_node->gw_flags != batadv_ogm_packet->gw_flags)
|
|
batadv_gw_node_update(bat_priv, orig_node,
|
|
batadv_ogm_packet->gw_flags);
|
|
|
|
orig_node->gw_flags = batadv_ogm_packet->gw_flags;
|
|
|
|
/* restart gateway selection if fast or late switching was enabled */
|
|
if ((orig_node->gw_flags) &&
|
|
(atomic_read(&bat_priv->gw_mode) == BATADV_GW_MODE_CLIENT) &&
|
|
(atomic_read(&bat_priv->gw_sel_class) > 2))
|
|
batadv_gw_check_election(bat_priv, orig_node);
|
|
|
|
goto out;
|
|
|
|
unlock:
|
|
rcu_read_unlock();
|
|
out:
|
|
if (neigh_node)
|
|
batadv_neigh_node_free_ref(neigh_node);
|
|
if (router)
|
|
batadv_neigh_node_free_ref(router);
|
|
}
|
|
|
|
static int batadv_iv_ogm_calc_tq(struct batadv_orig_node *orig_node,
|
|
struct batadv_orig_node *orig_neigh_node,
|
|
struct batadv_ogm_packet *batadv_ogm_packet,
|
|
struct batadv_hard_iface *if_incoming)
|
|
{
|
|
struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
|
|
struct batadv_neigh_node *neigh_node = NULL, *tmp_neigh_node;
|
|
uint8_t total_count;
|
|
uint8_t orig_eq_count, neigh_rq_count, neigh_rq_inv, tq_own;
|
|
unsigned int neigh_rq_inv_cube, neigh_rq_max_cube;
|
|
int tq_asym_penalty, inv_asym_penalty, ret = 0;
|
|
unsigned int combined_tq;
|
|
|
|
/* find corresponding one hop neighbor */
|
|
rcu_read_lock();
|
|
hlist_for_each_entry_rcu(tmp_neigh_node,
|
|
&orig_neigh_node->neigh_list, list) {
|
|
if (!batadv_compare_eth(tmp_neigh_node->addr,
|
|
orig_neigh_node->orig))
|
|
continue;
|
|
|
|
if (tmp_neigh_node->if_incoming != if_incoming)
|
|
continue;
|
|
|
|
if (!atomic_inc_not_zero(&tmp_neigh_node->refcount))
|
|
continue;
|
|
|
|
neigh_node = tmp_neigh_node;
|
|
break;
|
|
}
|
|
rcu_read_unlock();
|
|
|
|
if (!neigh_node)
|
|
neigh_node = batadv_iv_ogm_neigh_new(if_incoming,
|
|
orig_neigh_node->orig,
|
|
orig_neigh_node,
|
|
orig_neigh_node,
|
|
batadv_ogm_packet->seqno);
|
|
|
|
if (!neigh_node)
|
|
goto out;
|
|
|
|
/* if orig_node is direct neighbor update neigh_node last_seen */
|
|
if (orig_node == orig_neigh_node)
|
|
neigh_node->last_seen = jiffies;
|
|
|
|
orig_node->last_seen = jiffies;
|
|
|
|
/* find packet count of corresponding one hop neighbor */
|
|
spin_lock_bh(&orig_node->ogm_cnt_lock);
|
|
orig_eq_count = orig_neigh_node->bcast_own_sum[if_incoming->if_num];
|
|
neigh_rq_count = neigh_node->real_packet_count;
|
|
spin_unlock_bh(&orig_node->ogm_cnt_lock);
|
|
|
|
/* pay attention to not get a value bigger than 100 % */
|
|
if (orig_eq_count > neigh_rq_count)
|
|
total_count = neigh_rq_count;
|
|
else
|
|
total_count = orig_eq_count;
|
|
|
|
/* if we have too few packets (too less data) we set tq_own to zero
|
|
* if we receive too few packets it is not considered bidirectional
|
|
*/
|
|
if (total_count < BATADV_TQ_LOCAL_BIDRECT_SEND_MINIMUM ||
|
|
neigh_rq_count < BATADV_TQ_LOCAL_BIDRECT_RECV_MINIMUM)
|
|
tq_own = 0;
|
|
else
|
|
/* neigh_node->real_packet_count is never zero as we
|
|
* only purge old information when getting new
|
|
* information
|
|
*/
|
|
tq_own = (BATADV_TQ_MAX_VALUE * total_count) / neigh_rq_count;
|
|
|
|
/* 1 - ((1-x) ** 3), normalized to TQ_MAX_VALUE this does
|
|
* affect the nearly-symmetric links only a little, but
|
|
* punishes asymmetric links more. This will give a value
|
|
* between 0 and TQ_MAX_VALUE
|
|
*/
|
|
neigh_rq_inv = BATADV_TQ_LOCAL_WINDOW_SIZE - neigh_rq_count;
|
|
neigh_rq_inv_cube = neigh_rq_inv * neigh_rq_inv * neigh_rq_inv;
|
|
neigh_rq_max_cube = BATADV_TQ_LOCAL_WINDOW_SIZE *
|
|
BATADV_TQ_LOCAL_WINDOW_SIZE *
|
|
BATADV_TQ_LOCAL_WINDOW_SIZE;
|
|
inv_asym_penalty = BATADV_TQ_MAX_VALUE * neigh_rq_inv_cube;
|
|
inv_asym_penalty /= neigh_rq_max_cube;
|
|
tq_asym_penalty = BATADV_TQ_MAX_VALUE - inv_asym_penalty;
|
|
|
|
combined_tq = batadv_ogm_packet->tq * tq_own * tq_asym_penalty;
|
|
combined_tq /= BATADV_TQ_MAX_VALUE * BATADV_TQ_MAX_VALUE;
|
|
batadv_ogm_packet->tq = combined_tq;
|
|
|
|
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
|
"bidirectional: orig = %-15pM neigh = %-15pM => own_bcast = %2i, real recv = %2i, local tq: %3i, asym_penalty: %3i, total tq: %3i\n",
|
|
orig_node->orig, orig_neigh_node->orig, total_count,
|
|
neigh_rq_count, tq_own,
|
|
tq_asym_penalty, batadv_ogm_packet->tq);
|
|
|
|
/* if link has the minimum required transmission quality
|
|
* consider it bidirectional
|
|
*/
|
|
if (batadv_ogm_packet->tq >= BATADV_TQ_TOTAL_BIDRECT_LIMIT)
|
|
ret = 1;
|
|
|
|
out:
|
|
if (neigh_node)
|
|
batadv_neigh_node_free_ref(neigh_node);
|
|
return ret;
|
|
}
|
|
|
|
/* processes a batman packet for all interfaces, adjusts the sequence number and
|
|
* finds out whether it is a duplicate.
|
|
* returns:
|
|
* 1 the packet is a duplicate
|
|
* 0 the packet has not yet been received
|
|
* -1 the packet is old and has been received while the seqno window
|
|
* was protected. Caller should drop it.
|
|
*/
|
|
static int
|
|
batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr,
|
|
const struct batadv_ogm_packet *batadv_ogm_packet,
|
|
const struct batadv_hard_iface *if_incoming)
|
|
{
|
|
struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
|
|
struct batadv_orig_node *orig_node;
|
|
struct batadv_neigh_node *tmp_neigh_node;
|
|
int is_duplicate = 0;
|
|
int32_t seq_diff;
|
|
int need_update = 0;
|
|
int set_mark, ret = -1;
|
|
uint32_t seqno = ntohl(batadv_ogm_packet->seqno);
|
|
uint8_t *neigh_addr;
|
|
uint8_t packet_count;
|
|
|
|
orig_node = batadv_get_orig_node(bat_priv, batadv_ogm_packet->orig);
|
|
if (!orig_node)
|
|
return 0;
|
|
|
|
spin_lock_bh(&orig_node->ogm_cnt_lock);
|
|
seq_diff = seqno - orig_node->last_real_seqno;
|
|
|
|
/* signalize caller that the packet is to be dropped. */
|
|
if (!hlist_empty(&orig_node->neigh_list) &&
|
|
batadv_window_protected(bat_priv, seq_diff,
|
|
&orig_node->batman_seqno_reset))
|
|
goto out;
|
|
|
|
rcu_read_lock();
|
|
hlist_for_each_entry_rcu(tmp_neigh_node,
|
|
&orig_node->neigh_list, list) {
|
|
is_duplicate |= batadv_test_bit(tmp_neigh_node->real_bits,
|
|
orig_node->last_real_seqno,
|
|
seqno);
|
|
|
|
neigh_addr = tmp_neigh_node->addr;
|
|
if (batadv_compare_eth(neigh_addr, ethhdr->h_source) &&
|
|
tmp_neigh_node->if_incoming == if_incoming)
|
|
set_mark = 1;
|
|
else
|
|
set_mark = 0;
|
|
|
|
/* if the window moved, set the update flag. */
|
|
need_update |= batadv_bit_get_packet(bat_priv,
|
|
tmp_neigh_node->real_bits,
|
|
seq_diff, set_mark);
|
|
|
|
packet_count = bitmap_weight(tmp_neigh_node->real_bits,
|
|
BATADV_TQ_LOCAL_WINDOW_SIZE);
|
|
tmp_neigh_node->real_packet_count = packet_count;
|
|
}
|
|
rcu_read_unlock();
|
|
|
|
if (need_update) {
|
|
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
|
"updating last_seqno: old %u, new %u\n",
|
|
orig_node->last_real_seqno, seqno);
|
|
orig_node->last_real_seqno = seqno;
|
|
}
|
|
|
|
ret = is_duplicate;
|
|
|
|
out:
|
|
spin_unlock_bh(&orig_node->ogm_cnt_lock);
|
|
batadv_orig_node_free_ref(orig_node);
|
|
return ret;
|
|
}
|
|
|
|
static void batadv_iv_ogm_process(const struct ethhdr *ethhdr,
|
|
struct batadv_ogm_packet *batadv_ogm_packet,
|
|
const unsigned char *tt_buff,
|
|
struct batadv_hard_iface *if_incoming)
|
|
{
|
|
struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
|
|
struct batadv_hard_iface *hard_iface;
|
|
struct batadv_orig_node *orig_neigh_node, *orig_node;
|
|
struct batadv_neigh_node *router = NULL, *router_router = NULL;
|
|
struct batadv_neigh_node *orig_neigh_router = NULL;
|
|
int has_directlink_flag;
|
|
int is_my_addr = 0, is_my_orig = 0, is_my_oldorig = 0;
|
|
int is_broadcast = 0, is_bidirect;
|
|
bool is_single_hop_neigh = false;
|
|
bool is_from_best_next_hop = false;
|
|
int is_duplicate, sameseq, simlar_ttl;
|
|
uint32_t if_incoming_seqno;
|
|
uint8_t *prev_sender;
|
|
|
|
/* Silently drop when the batman packet is actually not a
|
|
* correct packet.
|
|
*
|
|
* This might happen if a packet is padded (e.g. Ethernet has a
|
|
* minimum frame length of 64 byte) and the aggregation interprets
|
|
* it as an additional length.
|
|
*
|
|
* TODO: A more sane solution would be to have a bit in the
|
|
* batadv_ogm_packet to detect whether the packet is the last
|
|
* packet in an aggregation. Here we expect that the padding
|
|
* is always zero (or not 0x01)
|
|
*/
|
|
if (batadv_ogm_packet->header.packet_type != BATADV_IV_OGM)
|
|
return;
|
|
|
|
/* could be changed by schedule_own_packet() */
|
|
if_incoming_seqno = atomic_read(&if_incoming->bat_iv.ogm_seqno);
|
|
|
|
if (batadv_ogm_packet->flags & BATADV_DIRECTLINK)
|
|
has_directlink_flag = 1;
|
|
else
|
|
has_directlink_flag = 0;
|
|
|
|
if (batadv_compare_eth(ethhdr->h_source, batadv_ogm_packet->orig))
|
|
is_single_hop_neigh = true;
|
|
|
|
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
|
"Received BATMAN packet via NB: %pM, IF: %s [%pM] (from OG: %pM, via prev OG: %pM, seqno %u, ttvn %u, crc %#.4x, changes %u, tq %d, TTL %d, V %d, IDF %d)\n",
|
|
ethhdr->h_source, if_incoming->net_dev->name,
|
|
if_incoming->net_dev->dev_addr, batadv_ogm_packet->orig,
|
|
batadv_ogm_packet->prev_sender,
|
|
ntohl(batadv_ogm_packet->seqno), batadv_ogm_packet->ttvn,
|
|
ntohs(batadv_ogm_packet->tt_crc),
|
|
batadv_ogm_packet->tt_num_changes, batadv_ogm_packet->tq,
|
|
batadv_ogm_packet->header.ttl,
|
|
batadv_ogm_packet->header.version, has_directlink_flag);
|
|
|
|
rcu_read_lock();
|
|
list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
|
|
if (hard_iface->if_status != BATADV_IF_ACTIVE)
|
|
continue;
|
|
|
|
if (hard_iface->soft_iface != if_incoming->soft_iface)
|
|
continue;
|
|
|
|
if (batadv_compare_eth(ethhdr->h_source,
|
|
hard_iface->net_dev->dev_addr))
|
|
is_my_addr = 1;
|
|
|
|
if (batadv_compare_eth(batadv_ogm_packet->orig,
|
|
hard_iface->net_dev->dev_addr))
|
|
is_my_orig = 1;
|
|
|
|
if (batadv_compare_eth(batadv_ogm_packet->prev_sender,
|
|
hard_iface->net_dev->dev_addr))
|
|
is_my_oldorig = 1;
|
|
|
|
if (is_broadcast_ether_addr(ethhdr->h_source))
|
|
is_broadcast = 1;
|
|
}
|
|
rcu_read_unlock();
|
|
|
|
if (batadv_ogm_packet->header.version != BATADV_COMPAT_VERSION) {
|
|
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
|
"Drop packet: incompatible batman version (%i)\n",
|
|
batadv_ogm_packet->header.version);
|
|
return;
|
|
}
|
|
|
|
if (is_my_addr) {
|
|
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
|
"Drop packet: received my own broadcast (sender: %pM)\n",
|
|
ethhdr->h_source);
|
|
return;
|
|
}
|
|
|
|
if (is_broadcast) {
|
|
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
|
"Drop packet: ignoring all packets with broadcast source addr (sender: %pM)\n",
|
|
ethhdr->h_source);
|
|
return;
|
|
}
|
|
|
|
if (is_my_orig) {
|
|
unsigned long *word;
|
|
int offset;
|
|
int32_t bit_pos;
|
|
int16_t if_num;
|
|
uint8_t *weight;
|
|
|
|
orig_neigh_node = batadv_get_orig_node(bat_priv,
|
|
ethhdr->h_source);
|
|
if (!orig_neigh_node)
|
|
return;
|
|
|
|
/* neighbor has to indicate direct link and it has to
|
|
* come via the corresponding interface
|
|
* save packet seqno for bidirectional check
|
|
*/
|
|
if (has_directlink_flag &&
|
|
batadv_compare_eth(if_incoming->net_dev->dev_addr,
|
|
batadv_ogm_packet->orig)) {
|
|
if_num = if_incoming->if_num;
|
|
offset = if_num * BATADV_NUM_WORDS;
|
|
|
|
spin_lock_bh(&orig_neigh_node->ogm_cnt_lock);
|
|
word = &(orig_neigh_node->bcast_own[offset]);
|
|
bit_pos = if_incoming_seqno - 2;
|
|
bit_pos -= ntohl(batadv_ogm_packet->seqno);
|
|
batadv_set_bit(word, bit_pos);
|
|
weight = &orig_neigh_node->bcast_own_sum[if_num];
|
|
*weight = bitmap_weight(word,
|
|
BATADV_TQ_LOCAL_WINDOW_SIZE);
|
|
spin_unlock_bh(&orig_neigh_node->ogm_cnt_lock);
|
|
}
|
|
|
|
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
|
"Drop packet: originator packet from myself (via neighbor)\n");
|
|
batadv_orig_node_free_ref(orig_neigh_node);
|
|
return;
|
|
}
|
|
|
|
if (is_my_oldorig) {
|
|
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
|
"Drop packet: ignoring all rebroadcast echos (sender: %pM)\n",
|
|
ethhdr->h_source);
|
|
return;
|
|
}
|
|
|
|
if (batadv_ogm_packet->flags & BATADV_NOT_BEST_NEXT_HOP) {
|
|
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
|
"Drop packet: ignoring all packets not forwarded from the best next hop (sender: %pM)\n",
|
|
ethhdr->h_source);
|
|
return;
|
|
}
|
|
|
|
orig_node = batadv_get_orig_node(bat_priv, batadv_ogm_packet->orig);
|
|
if (!orig_node)
|
|
return;
|
|
|
|
is_duplicate = batadv_iv_ogm_update_seqnos(ethhdr, batadv_ogm_packet,
|
|
if_incoming);
|
|
|
|
if (is_duplicate == -1) {
|
|
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
|
"Drop packet: packet within seqno protection time (sender: %pM)\n",
|
|
ethhdr->h_source);
|
|
goto out;
|
|
}
|
|
|
|
if (batadv_ogm_packet->tq == 0) {
|
|
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
|
"Drop packet: originator packet with tq equal 0\n");
|
|
goto out;
|
|
}
|
|
|
|
router = batadv_orig_node_get_router(orig_node);
|
|
if (router)
|
|
router_router = batadv_orig_node_get_router(router->orig_node);
|
|
|
|
if ((router && router->tq_avg != 0) &&
|
|
(batadv_compare_eth(router->addr, ethhdr->h_source)))
|
|
is_from_best_next_hop = true;
|
|
|
|
prev_sender = batadv_ogm_packet->prev_sender;
|
|
/* avoid temporary routing loops */
|
|
if (router && router_router &&
|
|
(batadv_compare_eth(router->addr, prev_sender)) &&
|
|
!(batadv_compare_eth(batadv_ogm_packet->orig, prev_sender)) &&
|
|
(batadv_compare_eth(router->addr, router_router->addr))) {
|
|
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
|
"Drop packet: ignoring all rebroadcast packets that may make me loop (sender: %pM)\n",
|
|
ethhdr->h_source);
|
|
goto out;
|
|
}
|
|
|
|
/* if sender is a direct neighbor the sender mac equals
|
|
* originator mac
|
|
*/
|
|
if (is_single_hop_neigh)
|
|
orig_neigh_node = orig_node;
|
|
else
|
|
orig_neigh_node = batadv_get_orig_node(bat_priv,
|
|
ethhdr->h_source);
|
|
|
|
if (!orig_neigh_node)
|
|
goto out;
|
|
|
|
orig_neigh_router = batadv_orig_node_get_router(orig_neigh_node);
|
|
|
|
/* drop packet if sender is not a direct neighbor and if we
|
|
* don't route towards it
|
|
*/
|
|
if (!is_single_hop_neigh && (!orig_neigh_router)) {
|
|
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
|
"Drop packet: OGM via unknown neighbor!\n");
|
|
goto out_neigh;
|
|
}
|
|
|
|
is_bidirect = batadv_iv_ogm_calc_tq(orig_node, orig_neigh_node,
|
|
batadv_ogm_packet, if_incoming);
|
|
|
|
batadv_bonding_save_primary(orig_node, orig_neigh_node,
|
|
batadv_ogm_packet);
|
|
|
|
/* update ranking if it is not a duplicate or has the same
|
|
* seqno and similar ttl as the non-duplicate
|
|
*/
|
|
sameseq = orig_node->last_real_seqno == ntohl(batadv_ogm_packet->seqno);
|
|
simlar_ttl = orig_node->last_ttl - 3 <= batadv_ogm_packet->header.ttl;
|
|
if (is_bidirect && (!is_duplicate || (sameseq && simlar_ttl)))
|
|
batadv_iv_ogm_orig_update(bat_priv, orig_node, ethhdr,
|
|
batadv_ogm_packet, if_incoming,
|
|
tt_buff, is_duplicate);
|
|
|
|
/* is single hop (direct) neighbor */
|
|
if (is_single_hop_neigh) {
|
|
/* mark direct link on incoming interface */
|
|
batadv_iv_ogm_forward(orig_node, ethhdr, batadv_ogm_packet,
|
|
is_single_hop_neigh,
|
|
is_from_best_next_hop, if_incoming);
|
|
|
|
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
|
"Forwarding packet: rebroadcast neighbor packet with direct link flag\n");
|
|
goto out_neigh;
|
|
}
|
|
|
|
/* multihop originator */
|
|
if (!is_bidirect) {
|
|
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
|
"Drop packet: not received via bidirectional link\n");
|
|
goto out_neigh;
|
|
}
|
|
|
|
if (is_duplicate) {
|
|
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
|
"Drop packet: duplicate packet received\n");
|
|
goto out_neigh;
|
|
}
|
|
|
|
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
|
"Forwarding packet: rebroadcast originator packet\n");
|
|
batadv_iv_ogm_forward(orig_node, ethhdr, batadv_ogm_packet,
|
|
is_single_hop_neigh, is_from_best_next_hop,
|
|
if_incoming);
|
|
|
|
out_neigh:
|
|
if ((orig_neigh_node) && (!is_single_hop_neigh))
|
|
batadv_orig_node_free_ref(orig_neigh_node);
|
|
out:
|
|
if (router)
|
|
batadv_neigh_node_free_ref(router);
|
|
if (router_router)
|
|
batadv_neigh_node_free_ref(router_router);
|
|
if (orig_neigh_router)
|
|
batadv_neigh_node_free_ref(orig_neigh_router);
|
|
|
|
batadv_orig_node_free_ref(orig_node);
|
|
}
|
|
|
|
static int batadv_iv_ogm_receive(struct sk_buff *skb,
|
|
struct batadv_hard_iface *if_incoming)
|
|
{
|
|
struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
|
|
struct batadv_ogm_packet *batadv_ogm_packet;
|
|
struct ethhdr *ethhdr;
|
|
int buff_pos = 0, packet_len;
|
|
unsigned char *tt_buff, *packet_buff;
|
|
bool ret;
|
|
uint8_t *packet_pos;
|
|
|
|
ret = batadv_check_management_packet(skb, if_incoming, BATADV_OGM_HLEN);
|
|
if (!ret)
|
|
return NET_RX_DROP;
|
|
|
|
/* did we receive a B.A.T.M.A.N. IV OGM packet on an interface
|
|
* that does not have B.A.T.M.A.N. IV enabled ?
|
|
*/
|
|
if (bat_priv->bat_algo_ops->bat_ogm_emit != batadv_iv_ogm_emit)
|
|
return NET_RX_DROP;
|
|
|
|
batadv_inc_counter(bat_priv, BATADV_CNT_MGMT_RX);
|
|
batadv_add_counter(bat_priv, BATADV_CNT_MGMT_RX_BYTES,
|
|
skb->len + ETH_HLEN);
|
|
|
|
packet_len = skb_headlen(skb);
|
|
ethhdr = (struct ethhdr *)skb_mac_header(skb);
|
|
packet_buff = skb->data;
|
|
batadv_ogm_packet = (struct batadv_ogm_packet *)packet_buff;
|
|
|
|
/* unpack the aggregated packets and process them one by one */
|
|
do {
|
|
tt_buff = packet_buff + buff_pos + BATADV_OGM_HLEN;
|
|
|
|
batadv_iv_ogm_process(ethhdr, batadv_ogm_packet, tt_buff,
|
|
if_incoming);
|
|
|
|
buff_pos += BATADV_OGM_HLEN;
|
|
buff_pos += batadv_tt_len(batadv_ogm_packet->tt_num_changes);
|
|
|
|
packet_pos = packet_buff + buff_pos;
|
|
batadv_ogm_packet = (struct batadv_ogm_packet *)packet_pos;
|
|
} while (batadv_iv_ogm_aggr_packet(buff_pos, packet_len,
|
|
batadv_ogm_packet->tt_num_changes));
|
|
|
|
kfree_skb(skb);
|
|
return NET_RX_SUCCESS;
|
|
}
|
|
|
|
static struct batadv_algo_ops batadv_batman_iv __read_mostly = {
|
|
.name = "BATMAN_IV",
|
|
.bat_iface_enable = batadv_iv_ogm_iface_enable,
|
|
.bat_iface_disable = batadv_iv_ogm_iface_disable,
|
|
.bat_iface_update_mac = batadv_iv_ogm_iface_update_mac,
|
|
.bat_primary_iface_set = batadv_iv_ogm_primary_iface_set,
|
|
.bat_ogm_schedule = batadv_iv_ogm_schedule,
|
|
.bat_ogm_emit = batadv_iv_ogm_emit,
|
|
};
|
|
|
|
int __init batadv_iv_init(void)
|
|
{
|
|
int ret;
|
|
|
|
/* batman originator packet */
|
|
ret = batadv_recv_handler_register(BATADV_IV_OGM,
|
|
batadv_iv_ogm_receive);
|
|
if (ret < 0)
|
|
goto out;
|
|
|
|
ret = batadv_algo_register(&batadv_batman_iv);
|
|
if (ret < 0)
|
|
goto handler_unregister;
|
|
|
|
goto out;
|
|
|
|
handler_unregister:
|
|
batadv_recv_handler_unregister(BATADV_IV_OGM);
|
|
out:
|
|
return ret;
|
|
}
|