batman-adv: add basic bridge loop avoidance code

This second version of the bridge loop avoidance for batman-adv
avoids loops between the mesh and a backbone (usually a LAN).

By connecting multiple batman-adv mesh nodes to the same ethernet
segment a loop can be created when the soft-interface is bridged
into that ethernet segment. A simple visualization of the loop
involving the most common case - a LAN as ethernet segment:

node1  <-- LAN  -->  node2
  |                   |
wifi   <-- mesh -->  wifi

Packets from the LAN (e.g. ARP broadcasts) will circle forever from
node1 or node2 over the mesh back into the LAN.

With this patch, batman recognizes backbone gateways, nodes which are
part of the mesh and backbone/LAN at the same time. Each backbone
gateway "claims" clients from within the mesh to handle them
exclusively. By restricting that only responsible backbone gateways
may handle their claimed clients traffic, loops are effectively
avoided.

Signed-off-by: Simon Wunderlich <siwu@hrz.tu-chemnitz.de>
Signed-off-by: Antonio Quartulli <ordex@autistici.org>
This commit is contained in:
Simon Wunderlich 2012-01-22 20:00:19 +01:00 committed by Antonio Quartulli
parent a7f6ee9493
commit 23721387c4
14 changed files with 1430 additions and 16 deletions

View File

@ -67,10 +67,11 @@ To deactivate an interface you have to write "none" into its
All mesh wide settings can be found in batman's own interface All mesh wide settings can be found in batman's own interface
folder: folder:
# ls /sys/class/net/bat0/mesh/ # ls /sys/class/net/bat0/mesh/
# aggregated_ogms fragmentation gw_sel_class vis_mode # aggregated_ogms fragmentation hop_penalty
# ap_isolation gw_bandwidth hop_penalty # ap_isolation gw_bandwidth log_level
# bonding gw_mode orig_interval # bonding gw_mode orig_interval
# bridge_loop_avoidance gw_sel_class vis_mode
There is a special folder for debugging information: There is a special folder for debugging information:
@ -202,12 +203,13 @@ abled during run time. Following log_levels are defined:
1 - Enable messages related to routing / flooding / broadcasting 1 - Enable messages related to routing / flooding / broadcasting
2 - Enable messages related to route added / changed / deleted 2 - Enable messages related to route added / changed / deleted
4 - Enable messages related to translation table operations 4 - Enable messages related to translation table operations
7 - Enable all messages 8 - Enable messages related to bridge loop avoidance
15 - enable all messages
The debug output can be changed at runtime using the file The debug output can be changed at runtime using the file
/sys/class/net/bat0/mesh/log_level. e.g. /sys/class/net/bat0/mesh/log_level. e.g.
# echo 2 > /sys/class/net/bat0/mesh/log_level # echo 6 > /sys/class/net/bat0/mesh/log_level
will enable debug messages for when routes change. will enable debug messages for when routes change.

View File

@ -4,7 +4,7 @@
config BATMAN_ADV config BATMAN_ADV
tristate "B.A.T.M.A.N. Advanced Meshing Protocol" tristate "B.A.T.M.A.N. Advanced Meshing Protocol"
depends on NET depends on NET && INET
select CRC16 select CRC16
default n default n
help help

View File

@ -23,6 +23,7 @@ batman-adv-y += bat_debugfs.o
batman-adv-y += bat_iv_ogm.o batman-adv-y += bat_iv_ogm.o
batman-adv-y += bat_sysfs.o batman-adv-y += bat_sysfs.o
batman-adv-y += bitarray.o batman-adv-y += bitarray.o
batman-adv-y += bridge_loop_avoidance.o
batman-adv-y += gateway_client.o batman-adv-y += gateway_client.o
batman-adv-y += gateway_common.o batman-adv-y += gateway_common.o
batman-adv-y += hard-interface.o batman-adv-y += hard-interface.o

View File

@ -398,7 +398,7 @@ BAT_ATTR_UINT(gw_sel_class, S_IRUGO | S_IWUSR, 1, TQ_MAX_VALUE,
static BAT_ATTR(gw_bandwidth, S_IRUGO | S_IWUSR, show_gw_bwidth, static BAT_ATTR(gw_bandwidth, S_IRUGO | S_IWUSR, show_gw_bwidth,
store_gw_bwidth); store_gw_bwidth);
#ifdef CONFIG_BATMAN_ADV_DEBUG #ifdef CONFIG_BATMAN_ADV_DEBUG
BAT_ATTR_UINT(log_level, S_IRUGO | S_IWUSR, 0, 7, NULL); BAT_ATTR_UINT(log_level, S_IRUGO | S_IWUSR, 0, 15, NULL);
#endif #endif
static struct bat_attribute *mesh_attrs[] = { static struct bat_attribute *mesh_attrs[] = {

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,37 @@
/*
* Copyright (C) 2011 B.A.T.M.A.N. contributors:
*
* 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
*
*/
#ifndef _NET_BATMAN_ADV_BLA_H_
#define _NET_BATMAN_ADV_BLA_H_
int bla_rx(struct bat_priv *bat_priv, struct sk_buff *skb, short vid);
int bla_tx(struct bat_priv *bat_priv, struct sk_buff *skb, short vid);
int bla_is_backbone_gw(struct sk_buff *skb,
struct orig_node *orig_node, int hdr_size);
void bla_update_orig_address(struct bat_priv *bat_priv,
struct hard_iface *primary_if,
struct hard_iface *oldif);
int bla_init(struct bat_priv *bat_priv);
void bla_free(struct bat_priv *bat_priv);
#define BLA_CRC_INIT 0
#endif /* ifndef _NET_BATMAN_ADV_BLA_H_ */

View File

@ -28,6 +28,7 @@
#include "bat_sysfs.h" #include "bat_sysfs.h"
#include "originator.h" #include "originator.h"
#include "hash.h" #include "hash.h"
#include "bridge_loop_avoidance.h"
#include <linux/if_arp.h> #include <linux/if_arp.h>
@ -107,7 +108,8 @@ out:
return hard_iface; return hard_iface;
} }
static void primary_if_update_addr(struct bat_priv *bat_priv) static void primary_if_update_addr(struct bat_priv *bat_priv,
struct hard_iface *oldif)
{ {
struct vis_packet *vis_packet; struct vis_packet *vis_packet;
struct hard_iface *primary_if; struct hard_iface *primary_if;
@ -122,6 +124,7 @@ static void primary_if_update_addr(struct bat_priv *bat_priv)
memcpy(vis_packet->sender_orig, memcpy(vis_packet->sender_orig,
primary_if->net_dev->dev_addr, ETH_ALEN); primary_if->net_dev->dev_addr, ETH_ALEN);
bla_update_orig_address(bat_priv, primary_if, oldif);
out: out:
if (primary_if) if (primary_if)
hardif_free_ref(primary_if); hardif_free_ref(primary_if);
@ -140,14 +143,15 @@ static void primary_if_select(struct bat_priv *bat_priv,
curr_hard_iface = rcu_dereference_protected(bat_priv->primary_if, 1); curr_hard_iface = rcu_dereference_protected(bat_priv->primary_if, 1);
rcu_assign_pointer(bat_priv->primary_if, new_hard_iface); rcu_assign_pointer(bat_priv->primary_if, new_hard_iface);
if (curr_hard_iface)
hardif_free_ref(curr_hard_iface);
if (!new_hard_iface) if (!new_hard_iface)
return; goto out;
bat_priv->bat_algo_ops->bat_ogm_init_primary(new_hard_iface); bat_priv->bat_algo_ops->bat_ogm_init_primary(new_hard_iface);
primary_if_update_addr(bat_priv); primary_if_update_addr(bat_priv, curr_hard_iface);
out:
if (curr_hard_iface)
hardif_free_ref(curr_hard_iface);
} }
static bool hardif_is_iface_up(const struct hard_iface *hard_iface) static bool hardif_is_iface_up(const struct hard_iface *hard_iface)
@ -531,7 +535,7 @@ static int hard_if_event(struct notifier_block *this,
goto hardif_put; goto hardif_put;
if (hard_iface == primary_if) if (hard_iface == primary_if)
primary_if_update_addr(bat_priv); primary_if_update_addr(bat_priv, NULL);
break; break;
default: default:
break; break;

View File

@ -30,6 +30,7 @@
#include "translation-table.h" #include "translation-table.h"
#include "hard-interface.h" #include "hard-interface.h"
#include "gateway_client.h" #include "gateway_client.h"
#include "bridge_loop_avoidance.h"
#include "vis.h" #include "vis.h"
#include "hash.h" #include "hash.h"
#include "bat_algo.h" #include "bat_algo.h"
@ -115,6 +116,9 @@ int mesh_init(struct net_device *soft_iface)
if (vis_init(bat_priv) < 1) if (vis_init(bat_priv) < 1)
goto err; goto err;
if (bla_init(bat_priv) < 1)
goto err;
atomic_set(&bat_priv->gw_reselect, 0); atomic_set(&bat_priv->gw_reselect, 0);
atomic_set(&bat_priv->mesh_state, MESH_ACTIVE); atomic_set(&bat_priv->mesh_state, MESH_ACTIVE);
goto end; goto end;
@ -142,6 +146,8 @@ void mesh_free(struct net_device *soft_iface)
tt_free(bat_priv); tt_free(bat_priv);
bla_free(bat_priv);
atomic_set(&bat_priv->mesh_state, MESH_INACTIVE); atomic_set(&bat_priv->mesh_state, MESH_INACTIVE);
} }

View File

@ -80,6 +80,9 @@
#define MAX_AGGREGATION_BYTES 512 #define MAX_AGGREGATION_BYTES 512
#define MAX_AGGREGATION_MS 100 #define MAX_AGGREGATION_MS 100
#define BLA_PERIOD_LENGTH 10000 /* 10 seconds */
#define BLA_BACKBONE_TIMEOUT (BLA_PERIOD_LENGTH * 3)
#define BLA_CLAIM_TIMEOUT (BLA_PERIOD_LENGTH * 10)
/* don't reset again within 30 seconds */ /* don't reset again within 30 seconds */
#define RESET_PROTECTION_MS 30000 #define RESET_PROTECTION_MS 30000
#define EXPECTED_SEQNO_RANGE 65536 #define EXPECTED_SEQNO_RANGE 65536
@ -117,7 +120,8 @@ enum dbg_level {
DBG_BATMAN = 1 << 0, DBG_BATMAN = 1 << 0,
DBG_ROUTES = 1 << 1, /* route added / changed / deleted */ DBG_ROUTES = 1 << 1, /* route added / changed / deleted */
DBG_TT = 1 << 2, /* translation table operations */ DBG_TT = 1 << 2, /* translation table operations */
DBG_ALL = 7 DBG_BLA = 1 << 3, /* bridge loop avoidance */
DBG_ALL = 15
}; };
/* Kernel headers */ /* Kernel headers */

View File

@ -28,6 +28,7 @@
#include "hard-interface.h" #include "hard-interface.h"
#include "unicast.h" #include "unicast.h"
#include "soft-interface.h" #include "soft-interface.h"
#include "bridge_loop_avoidance.h"
static void purge_orig(struct work_struct *work); static void purge_orig(struct work_struct *work);

View File

@ -90,6 +90,23 @@ enum tt_client_flags {
TT_CLIENT_PENDING = 1 << 10 TT_CLIENT_PENDING = 1 << 10
}; };
/* claim frame types for the bridge loop avoidance */
enum bla_claimframe {
CLAIM_TYPE_ADD = 0x00,
CLAIM_TYPE_DEL = 0x01,
CLAIM_TYPE_ANNOUNCE = 0x02,
CLAIM_TYPE_REQUEST = 0x03
};
/* the destination hardware field in the ARP frame is used to
* transport the claim type and the group id
*/
struct bla_claim_dst {
uint8_t magic[3]; /* FF:43:05 */
uint8_t type; /* bla_claimframe */
uint16_t group; /* group id */
} __packed;
struct batman_header { struct batman_header {
uint8_t packet_type; uint8_t packet_type;
uint8_t version; /* batman version field */ uint8_t version; /* batman version field */

View File

@ -29,6 +29,7 @@
#include "originator.h" #include "originator.h"
#include "vis.h" #include "vis.h"
#include "unicast.h" #include "unicast.h"
#include "bridge_loop_avoidance.h"
static int route_unicast_packet(struct sk_buff *skb, static int route_unicast_packet(struct sk_buff *skb,
struct hard_iface *recv_if); struct hard_iface *recv_if);
@ -1071,6 +1072,12 @@ int recv_bcast_packet(struct sk_buff *skb, struct hard_iface *recv_if)
/* rebroadcast packet */ /* rebroadcast packet */
add_bcast_packet_to_list(bat_priv, skb, 1); add_bcast_packet_to_list(bat_priv, skb, 1);
/* don't hand the broadcast up if it is from an originator
* from the same backbone.
*/
if (bla_is_backbone_gw(skb, orig_node, hdr_size))
goto out;
/* broadcast for me */ /* broadcast for me */
interface_rx(recv_if->soft_iface, skb, recv_if, hdr_size); interface_rx(recv_if->soft_iface, skb, recv_if, hdr_size);
ret = NET_RX_SUCCESS; ret = NET_RX_SUCCESS;

View File

@ -36,6 +36,7 @@
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/if_vlan.h> #include <linux/if_vlan.h>
#include "unicast.h" #include "unicast.h"
#include "bridge_loop_avoidance.h"
static int bat_get_settings(struct net_device *dev, struct ethtool_cmd *cmd); static int bat_get_settings(struct net_device *dev, struct ethtool_cmd *cmd);
@ -152,6 +153,9 @@ static int interface_tx(struct sk_buff *skb, struct net_device *soft_iface)
goto dropped; goto dropped;
} }
if (bla_tx(bat_priv, skb, vid))
goto dropped;
/* Register the client MAC in the transtable */ /* Register the client MAC in the transtable */
tt_local_add(soft_iface, ethhdr->h_source, skb->skb_iif); tt_local_add(soft_iface, ethhdr->h_source, skb->skb_iif);
@ -287,6 +291,12 @@ void interface_rx(struct net_device *soft_iface,
if (is_ap_isolated(bat_priv, ethhdr->h_source, ethhdr->h_dest)) if (is_ap_isolated(bat_priv, ethhdr->h_source, ethhdr->h_dest))
goto dropped; goto dropped;
/* Let the bridge loop avoidance check the packet. If will
* not handle it, we can safely push it up.
*/
if (bla_rx(bat_priv, skb, vid))
goto out;
netif_rx(skb); netif_rx(skb);
goto out; goto out;
@ -354,6 +364,7 @@ struct net_device *softif_create(const char *name)
atomic_set(&bat_priv->aggregated_ogms, 1); atomic_set(&bat_priv->aggregated_ogms, 1);
atomic_set(&bat_priv->bonding, 0); atomic_set(&bat_priv->bonding, 0);
atomic_set(&bat_priv->bridge_loop_avoidance, 0);
atomic_set(&bat_priv->ap_isolation, 0); atomic_set(&bat_priv->ap_isolation, 0);
atomic_set(&bat_priv->vis_mode, VIS_TYPE_CLIENT_UPDATE); atomic_set(&bat_priv->vis_mode, VIS_TYPE_CLIENT_UPDATE);
atomic_set(&bat_priv->gw_mode, GW_MODE_OFF); atomic_set(&bat_priv->gw_mode, GW_MODE_OFF);
@ -371,6 +382,7 @@ struct net_device *softif_create(const char *name)
atomic_set(&bat_priv->ttvn, 0); atomic_set(&bat_priv->ttvn, 0);
atomic_set(&bat_priv->tt_local_changes, 0); atomic_set(&bat_priv->tt_local_changes, 0);
atomic_set(&bat_priv->tt_ogm_append_cnt, 0); atomic_set(&bat_priv->tt_ogm_append_cnt, 0);
atomic_set(&bat_priv->bla_num_requests, 0);
bat_priv->tt_buff = NULL; bat_priv->tt_buff = NULL;
bat_priv->tt_buff_len = 0; bat_priv->tt_buff_len = 0;

View File

@ -148,6 +148,7 @@ struct bat_priv {
atomic_t bonding; /* boolean */ atomic_t bonding; /* boolean */
atomic_t fragmentation; /* boolean */ atomic_t fragmentation; /* boolean */
atomic_t ap_isolation; /* boolean */ atomic_t ap_isolation; /* boolean */
atomic_t bridge_loop_avoidance; /* boolean */
atomic_t vis_mode; /* VIS_TYPE_* */ atomic_t vis_mode; /* VIS_TYPE_* */
atomic_t gw_mode; /* GW_MODE_* */ atomic_t gw_mode; /* GW_MODE_* */
atomic_t gw_sel_class; /* uint */ atomic_t gw_sel_class; /* uint */
@ -161,6 +162,7 @@ struct bat_priv {
atomic_t ttvn; /* translation table version number */ atomic_t ttvn; /* translation table version number */
atomic_t tt_ogm_append_cnt; atomic_t tt_ogm_append_cnt;
atomic_t tt_local_changes; /* changes registered in a OGM interval */ atomic_t tt_local_changes; /* changes registered in a OGM interval */
atomic_t bla_num_requests; /* number of bla requests in flight */
/* The tt_poss_change flag is used to detect an ongoing roaming phase. /* The tt_poss_change flag is used to detect an ongoing roaming phase.
* If true, then I received a Roaming_adv and I have to inspect every * If true, then I received a Roaming_adv and I have to inspect every
* packet directed to me to check whether I am still the true * packet directed to me to check whether I am still the true
@ -179,6 +181,8 @@ struct bat_priv {
struct hashtable_t *orig_hash; struct hashtable_t *orig_hash;
struct hashtable_t *tt_local_hash; struct hashtable_t *tt_local_hash;
struct hashtable_t *tt_global_hash; struct hashtable_t *tt_global_hash;
struct hashtable_t *claim_hash;
struct hashtable_t *backbone_hash;
struct list_head tt_req_list; /* list of pending tt_requests */ struct list_head tt_req_list; /* list of pending tt_requests */
struct list_head tt_roam_list; struct list_head tt_roam_list;
struct hashtable_t *vis_hash; struct hashtable_t *vis_hash;
@ -199,6 +203,7 @@ struct bat_priv {
struct delayed_work tt_work; struct delayed_work tt_work;
struct delayed_work orig_work; struct delayed_work orig_work;
struct delayed_work vis_work; struct delayed_work vis_work;
struct delayed_work bla_work;
struct gw_node __rcu *curr_gw; /* rcu protected pointer */ struct gw_node __rcu *curr_gw; /* rcu protected pointer */
atomic_t gw_reselect; atomic_t gw_reselect;
struct hard_iface __rcu *primary_if; /* rcu protected pointer */ struct hard_iface __rcu *primary_if; /* rcu protected pointer */
@ -241,6 +246,28 @@ struct tt_global_entry {
unsigned long roam_at; /* time at which TT_GLOBAL_ROAM was set */ unsigned long roam_at; /* time at which TT_GLOBAL_ROAM was set */
}; };
struct backbone_gw {
uint8_t orig[ETH_ALEN];
short vid; /* used VLAN ID */
struct hlist_node hash_entry;
struct bat_priv *bat_priv;
unsigned long lasttime; /* last time we heard of this backbone gw */
atomic_t request_sent;
atomic_t refcount;
struct rcu_head rcu;
uint16_t crc; /* crc checksum over all claims */
};
struct claim {
uint8_t addr[ETH_ALEN];
short vid;
struct backbone_gw *backbone_gw;
unsigned long lasttime; /* last time we heard of claim (locals only) */
struct rcu_head rcu;
atomic_t refcount;
struct hlist_node hash_entry;
};
struct tt_change_node { struct tt_change_node {
struct list_head list; struct list_head list;
struct tt_change change; struct tt_change change;