summaryrefslogtreecommitdiffstats
path: root/net/batman-adv
diff options
context:
space:
mode:
Diffstat (limited to 'net/batman-adv')
-rw-r--r--net/batman-adv/Kconfig27
-rw-r--r--net/batman-adv/Makefile1
-rw-r--r--net/batman-adv/bat_debugfs.c19
-rw-r--r--net/batman-adv/bat_iv_ogm.c78
-rw-r--r--net/batman-adv/bat_sysfs.c14
-rw-r--r--net/batman-adv/bitarray.c118
-rw-r--r--net/batman-adv/bitarray.h26
-rw-r--r--net/batman-adv/bridge_loop_avoidance.c1580
-rw-r--r--net/batman-adv/bridge_loop_avoidance.h98
-rw-r--r--net/batman-adv/hard-interface.c51
-rw-r--r--net/batman-adv/icmp_socket.c4
-rw-r--r--net/batman-adv/main.c14
-rw-r--r--net/batman-adv/main.h11
-rw-r--r--net/batman-adv/originator.c3
-rw-r--r--net/batman-adv/packet.h49
-rw-r--r--net/batman-adv/routing.c39
-rw-r--r--net/batman-adv/routing.h1
-rw-r--r--net/batman-adv/send.c14
-rw-r--r--net/batman-adv/soft-interface.c500
-rw-r--r--net/batman-adv/soft-interface.h2
-rw-r--r--net/batman-adv/translation-table.c437
-rw-r--r--net/batman-adv/translation-table.h8
-rw-r--r--net/batman-adv/types.h89
-rw-r--r--net/batman-adv/vis.c8
24 files changed, 2335 insertions, 856 deletions
diff --git a/net/batman-adv/Kconfig b/net/batman-adv/Kconfig
index 2b68d068eaf3..53f5244e28f8 100644
--- a/net/batman-adv/Kconfig
+++ b/net/batman-adv/Kconfig
@@ -7,19 +7,28 @@ config BATMAN_ADV
depends on NET
select CRC16
default n
- ---help---
+ help
+ B.A.T.M.A.N. (better approach to mobile ad-hoc networking) is
+ a routing protocol for multi-hop ad-hoc mesh networks. The
+ networks may be wired or wireless. See
+ http://www.open-mesh.org/ for more information and user space
+ tools.
- B.A.T.M.A.N. (better approach to mobile ad-hoc networking) is
- a routing protocol for multi-hop ad-hoc mesh networks. The
- networks may be wired or wireless. See
- http://www.open-mesh.org/ for more information and user space
- tools.
+config BATMAN_ADV_BLA
+ bool "Bridge Loop Avoidance"
+ depends on BATMAN_ADV && INET
+ default y
+ help
+ This option enables BLA (Bridge Loop Avoidance), a mechanism
+ to avoid Ethernet frames looping when mesh nodes are connected
+ to both the same LAN and the same mesh. If you will never use
+ more than one mesh node in the same LAN, you can safely remove
+ this feature and save some space.
config BATMAN_ADV_DEBUG
bool "B.A.T.M.A.N. debugging"
- depends on BATMAN_ADV != n
- ---help---
-
+ depends on BATMAN_ADV
+ help
This is an option for use by developers; most people should
say N here. This enables compilation of support for
outputting debugging information to the kernel log. The
diff --git a/net/batman-adv/Makefile b/net/batman-adv/Makefile
index 4e392ebedb64..6d5c1940667d 100644
--- a/net/batman-adv/Makefile
+++ b/net/batman-adv/Makefile
@@ -23,6 +23,7 @@ batman-adv-y += bat_debugfs.o
batman-adv-y += bat_iv_ogm.o
batman-adv-y += bat_sysfs.o
batman-adv-y += bitarray.o
+batman-adv-$(CONFIG_BATMAN_ADV_BLA) += bridge_loop_avoidance.o
batman-adv-y += gateway_client.o
batman-adv-y += gateway_common.o
batman-adv-y += hard-interface.o
diff --git a/net/batman-adv/bat_debugfs.c b/net/batman-adv/bat_debugfs.c
index c3b0548b175d..916380c73ab7 100644
--- a/net/batman-adv/bat_debugfs.c
+++ b/net/batman-adv/bat_debugfs.c
@@ -32,6 +32,7 @@
#include "soft-interface.h"
#include "vis.h"
#include "icmp_socket.h"
+#include "bridge_loop_avoidance.h"
static struct dentry *bat_debugfs;
@@ -238,17 +239,19 @@ static int gateways_open(struct inode *inode, struct file *file)
return single_open(file, gw_client_seq_print_text, net_dev);
}
-static int softif_neigh_open(struct inode *inode, struct file *file)
+static int transtable_global_open(struct inode *inode, struct file *file)
{
struct net_device *net_dev = (struct net_device *)inode->i_private;
- return single_open(file, softif_neigh_seq_print_text, net_dev);
+ return single_open(file, tt_global_seq_print_text, net_dev);
}
-static int transtable_global_open(struct inode *inode, struct file *file)
+#ifdef CONFIG_BATMAN_ADV_BLA
+static int bla_claim_table_open(struct inode *inode, struct file *file)
{
struct net_device *net_dev = (struct net_device *)inode->i_private;
- return single_open(file, tt_global_seq_print_text, net_dev);
+ return single_open(file, bla_claim_table_seq_print_text, net_dev);
}
+#endif
static int transtable_local_open(struct inode *inode, struct file *file)
{
@@ -282,16 +285,20 @@ struct bat_debuginfo bat_debuginfo_##_name = { \
static BAT_DEBUGINFO(routing_algos, S_IRUGO, bat_algorithms_open);
static BAT_DEBUGINFO(originators, S_IRUGO, originators_open);
static BAT_DEBUGINFO(gateways, S_IRUGO, gateways_open);
-static BAT_DEBUGINFO(softif_neigh, S_IRUGO, softif_neigh_open);
static BAT_DEBUGINFO(transtable_global, S_IRUGO, transtable_global_open);
+#ifdef CONFIG_BATMAN_ADV_BLA
+static BAT_DEBUGINFO(bla_claim_table, S_IRUGO, bla_claim_table_open);
+#endif
static BAT_DEBUGINFO(transtable_local, S_IRUGO, transtable_local_open);
static BAT_DEBUGINFO(vis_data, S_IRUGO, vis_data_open);
static struct bat_debuginfo *mesh_debuginfos[] = {
&bat_debuginfo_originators,
&bat_debuginfo_gateways,
- &bat_debuginfo_softif_neigh,
&bat_debuginfo_transtable_global,
+#ifdef CONFIG_BATMAN_ADV_BLA
+ &bat_debuginfo_bla_claim_table,
+#endif
&bat_debuginfo_transtable_local,
&bat_debuginfo_vis_data,
NULL,
diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
index a6d5d63fb6ad..8b2db2e76c7e 100644
--- a/net/batman-adv/bat_iv_ogm.c
+++ b/net/batman-adv/bat_iv_ogm.c
@@ -30,24 +30,44 @@
#include "send.h"
#include "bat_algo.h"
-static void bat_iv_ogm_init(struct hard_iface *hard_iface)
+static int bat_iv_ogm_iface_enable(struct hard_iface *hard_iface)
{
struct batman_ogm_packet *batman_ogm_packet;
+ uint32_t random_seqno;
+ int res = -1;
- hard_iface->packet_len = BATMAN_OGM_LEN;
+ /* randomize initial seqno to avoid collision */
+ get_random_bytes(&random_seqno, sizeof(random_seqno));
+ atomic_set(&hard_iface->seqno, random_seqno);
+
+ hard_iface->packet_len = BATMAN_OGM_HLEN;
hard_iface->packet_buff = kmalloc(hard_iface->packet_len, GFP_ATOMIC);
+ if (!hard_iface->packet_buff)
+ goto out;
+
batman_ogm_packet = (struct batman_ogm_packet *)hard_iface->packet_buff;
- batman_ogm_packet->header.packet_type = BAT_OGM;
+ batman_ogm_packet->header.packet_type = BAT_IV_OGM;
batman_ogm_packet->header.version = COMPAT_VERSION;
batman_ogm_packet->header.ttl = 2;
batman_ogm_packet->flags = NO_FLAGS;
batman_ogm_packet->tq = TQ_MAX_VALUE;
batman_ogm_packet->tt_num_changes = 0;
batman_ogm_packet->ttvn = 0;
+
+ res = 0;
+
+out:
+ return res;
+}
+
+static void bat_iv_ogm_iface_disable(struct hard_iface *hard_iface)
+{
+ kfree(hard_iface->packet_buff);
+ hard_iface->packet_buff = NULL;
}
-static void bat_iv_ogm_init_primary(struct hard_iface *hard_iface)
+static void bat_iv_ogm_primary_iface_set(struct hard_iface *hard_iface)
{
struct batman_ogm_packet *batman_ogm_packet;
@@ -92,7 +112,7 @@ static uint8_t hop_penalty(uint8_t tq, const struct bat_priv *bat_priv)
static int bat_iv_ogm_aggr_packet(int buff_pos, int packet_len,
int tt_num_changes)
{
- int next_buff_pos = buff_pos + BATMAN_OGM_LEN + tt_len(tt_num_changes);
+ int next_buff_pos = buff_pos + BATMAN_OGM_HLEN + tt_len(tt_num_changes);
return (next_buff_pos <= packet_len) &&
(next_buff_pos <= MAX_AGGREGATION_BYTES);
@@ -132,7 +152,7 @@ static void bat_iv_ogm_send_to_if(struct forw_packet *forw_packet,
"Sending own" :
"Forwarding"));
bat_dbg(DBG_BATMAN, bat_priv,
- "%s %spacket (originator %pM, seqno %d, TQ %d, TTL %d, IDF %s, ttvn %d) on interface %s [%pM]\n",
+ "%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 " : ""),
batman_ogm_packet->orig,
ntohl(batman_ogm_packet->seqno),
@@ -142,7 +162,7 @@ static void bat_iv_ogm_send_to_if(struct forw_packet *forw_packet,
batman_ogm_packet->ttvn, hard_iface->net_dev->name,
hard_iface->net_dev->dev_addr);
- buff_pos += BATMAN_OGM_LEN +
+ buff_pos += BATMAN_OGM_HLEN +
tt_len(batman_ogm_packet->tt_num_changes);
packet_num++;
batman_ogm_packet = (struct batman_ogm_packet *)
@@ -191,7 +211,7 @@ static void bat_iv_ogm_emit(struct forw_packet *forw_packet)
/* FIXME: what about aggregated packets ? */
bat_dbg(DBG_BATMAN, bat_priv,
- "%s packet (originator %pM, seqno %d, TTL %d) on interface %s [%pM]\n",
+ "%s packet (originator %pM, seqno %u, TTL %d) on interface %s [%pM]\n",
(forw_packet->own ? "Sending own" : "Forwarding"),
batman_ogm_packet->orig,
ntohl(batman_ogm_packet->seqno),
@@ -335,10 +355,9 @@ static void bat_iv_ogm_aggregate_new(const unsigned char *packet_buff,
if ((atomic_read(&bat_priv->aggregated_ogms)) &&
(packet_len < MAX_AGGREGATION_BYTES))
forw_packet_aggr->skb = dev_alloc_skb(MAX_AGGREGATION_BYTES +
- sizeof(struct ethhdr));
+ ETH_HLEN);
else
- forw_packet_aggr->skb = dev_alloc_skb(packet_len +
- sizeof(struct ethhdr));
+ forw_packet_aggr->skb = dev_alloc_skb(packet_len + ETH_HLEN);
if (!forw_packet_aggr->skb) {
if (!own_packet)
@@ -346,7 +365,7 @@ static void bat_iv_ogm_aggregate_new(const unsigned char *packet_buff,
kfree(forw_packet_aggr);
goto out;
}
- skb_reserve(forw_packet_aggr->skb, sizeof(struct ethhdr));
+ skb_reserve(forw_packet_aggr->skb, ETH_HLEN);
INIT_HLIST_NODE(&forw_packet_aggr->list);
@@ -520,7 +539,7 @@ static void bat_iv_ogm_forward(struct orig_node *orig_node,
batman_ogm_packet->flags &= ~DIRECTLINK;
bat_iv_ogm_queue_add(bat_priv, (unsigned char *)batman_ogm_packet,
- BATMAN_OGM_LEN + tt_len(tt_num_changes),
+ BATMAN_OGM_HLEN + tt_len(tt_num_changes),
if_incoming, 0, bat_iv_ogm_fwd_send_time());
}
@@ -842,7 +861,8 @@ static int bat_iv_ogm_update_seqnos(const struct ethhdr *ethhdr,
seq_diff = batman_ogm_packet->seqno - orig_node->last_real_seqno;
/* signalize caller that the packet is to be dropped. */
- if (window_protected(bat_priv, seq_diff,
+ if (!hlist_empty(&orig_node->neigh_list) &&
+ window_protected(bat_priv, seq_diff,
&orig_node->batman_seqno_reset))
goto out;
@@ -850,9 +870,9 @@ static int bat_iv_ogm_update_seqnos(const struct ethhdr *ethhdr,
hlist_for_each_entry_rcu(tmp_neigh_node, node,
&orig_node->neigh_list, list) {
- is_duplicate |= get_bit_status(tmp_neigh_node->real_bits,
- orig_node->last_real_seqno,
- batman_ogm_packet->seqno);
+ is_duplicate |= bat_test_bit(tmp_neigh_node->real_bits,
+ orig_node->last_real_seqno,
+ batman_ogm_packet->seqno);
if (compare_eth(tmp_neigh_node->addr, ethhdr->h_source) &&
(tmp_neigh_node->if_incoming == if_incoming))
@@ -866,13 +886,14 @@ static int bat_iv_ogm_update_seqnos(const struct ethhdr *ethhdr,
seq_diff, set_mark);
tmp_neigh_node->real_packet_count =
- bit_packet_count(tmp_neigh_node->real_bits);
+ bitmap_weight(tmp_neigh_node->real_bits,
+ TQ_LOCAL_WINDOW_SIZE);
}
rcu_read_unlock();
if (need_update) {
bat_dbg(DBG_BATMAN, bat_priv,
- "updating last_seqno: old %d, new %d\n",
+ "updating last_seqno: old %u, new %u\n",
orig_node->last_real_seqno, batman_ogm_packet->seqno);
orig_node->last_real_seqno = batman_ogm_packet->seqno;
}
@@ -913,7 +934,7 @@ static void bat_iv_ogm_process(const struct ethhdr *ethhdr,
* packet in an aggregation. Here we expect that the padding
* is always zero (or not 0x01)
*/
- if (batman_ogm_packet->header.packet_type != BAT_OGM)
+ if (batman_ogm_packet->header.packet_type != BAT_IV_OGM)
return;
/* could be changed by schedule_own_packet() */
@@ -925,7 +946,7 @@ static void bat_iv_ogm_process(const struct ethhdr *ethhdr,
batman_ogm_packet->orig) ? 1 : 0);
bat_dbg(DBG_BATMAN, bat_priv,
- "Received BATMAN packet via NB: %pM, IF: %s [%pM] (from OG: %pM, via prev OG: %pM, seqno %d, ttvn %u, crc %u, changes %u, td %d, TTL %d, V %d, IDF %d)\n",
+ "Received BATMAN packet via NB: %pM, IF: %s [%pM] (from OG: %pM, via prev OG: %pM, seqno %u, ttvn %u, crc %u, changes %u, td %d, TTL %d, V %d, IDF %d)\n",
ethhdr->h_source, if_incoming->net_dev->name,
if_incoming->net_dev->dev_addr, batman_ogm_packet->orig,
batman_ogm_packet->prev_sender, batman_ogm_packet->seqno,
@@ -998,11 +1019,11 @@ static void bat_iv_ogm_process(const struct ethhdr *ethhdr,
spin_lock_bh(&orig_neigh_node->ogm_cnt_lock);
word = &(orig_neigh_node->bcast_own[offset]);
- bit_mark(word,
- if_incoming_seqno -
+ bat_set_bit(word,
+ if_incoming_seqno -
batman_ogm_packet->seqno - 2);
orig_neigh_node->bcast_own_sum[if_incoming->if_num] =
- bit_packet_count(word);
+ bitmap_weight(word, TQ_LOCAL_WINDOW_SIZE);
spin_unlock_bh(&orig_neigh_node->ogm_cnt_lock);
}
@@ -1152,12 +1173,12 @@ static void bat_iv_ogm_receive(struct hard_iface *if_incoming,
batman_ogm_packet->seqno = ntohl(batman_ogm_packet->seqno);
batman_ogm_packet->tt_crc = ntohs(batman_ogm_packet->tt_crc);
- tt_buff = packet_buff + buff_pos + BATMAN_OGM_LEN;
+ tt_buff = packet_buff + buff_pos + BATMAN_OGM_HLEN;
bat_iv_ogm_process(ethhdr, batman_ogm_packet,
tt_buff, if_incoming);
- buff_pos += BATMAN_OGM_LEN +
+ buff_pos += BATMAN_OGM_HLEN +
tt_len(batman_ogm_packet->tt_num_changes);
batman_ogm_packet = (struct batman_ogm_packet *)
@@ -1168,8 +1189,9 @@ static void bat_iv_ogm_receive(struct hard_iface *if_incoming,
static struct bat_algo_ops batman_iv __read_mostly = {
.name = "BATMAN IV",
- .bat_ogm_init = bat_iv_ogm_init,
- .bat_ogm_init_primary = bat_iv_ogm_init_primary,
+ .bat_iface_enable = bat_iv_ogm_iface_enable,
+ .bat_iface_disable = bat_iv_ogm_iface_disable,
+ .bat_primary_iface_set = bat_iv_ogm_primary_iface_set,
.bat_ogm_update_mac = bat_iv_ogm_update_mac,
.bat_ogm_schedule = bat_iv_ogm_schedule,
.bat_ogm_emit = bat_iv_ogm_emit,
diff --git a/net/batman-adv/bat_sysfs.c b/net/batman-adv/bat_sysfs.c
index 68ff759fc304..2c816883ca13 100644
--- a/net/batman-adv/bat_sysfs.c
+++ b/net/batman-adv/bat_sysfs.c
@@ -149,7 +149,7 @@ static int store_bool_attr(char *buff, size_t count,
atomic_read(attr) == 1 ? "enabled" : "disabled",
enabled == 1 ? "enabled" : "disabled");
- atomic_set(attr, (unsigned)enabled);
+ atomic_set(attr, (unsigned int)enabled);
return count;
}
@@ -268,7 +268,7 @@ static ssize_t store_vis_mode(struct kobject *kobj, struct attribute *attr,
"client" : "server", vis_mode_tmp == VIS_TYPE_CLIENT_UPDATE ?
"client" : "server");
- atomic_set(&bat_priv->vis_mode, (unsigned)vis_mode_tmp);
+ atomic_set(&bat_priv->vis_mode, (unsigned int)vis_mode_tmp);
return count;
}
@@ -354,7 +354,7 @@ static ssize_t store_gw_mode(struct kobject *kobj, struct attribute *attr,
curr_gw_mode_str, buff);
gw_deselect(bat_priv);
- atomic_set(&bat_priv->gw_mode, (unsigned)gw_mode_tmp);
+ atomic_set(&bat_priv->gw_mode, (unsigned int)gw_mode_tmp);
return count;
}
@@ -386,6 +386,9 @@ static ssize_t store_gw_bwidth(struct kobject *kobj, struct attribute *attr,
BAT_ATTR_BOOL(aggregated_ogms, S_IRUGO | S_IWUSR, NULL);
BAT_ATTR_BOOL(bonding, S_IRUGO | S_IWUSR, NULL);
+#ifdef CONFIG_BATMAN_ADV_BLA
+BAT_ATTR_BOOL(bridge_loop_avoidance, S_IRUGO | S_IWUSR, NULL);
+#endif
BAT_ATTR_BOOL(fragmentation, S_IRUGO | S_IWUSR, update_min_mtu);
BAT_ATTR_BOOL(ap_isolation, S_IRUGO | S_IWUSR, NULL);
static BAT_ATTR(vis_mode, S_IRUGO | S_IWUSR, show_vis_mode, store_vis_mode);
@@ -398,12 +401,15 @@ 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,
store_gw_bwidth);
#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
static struct bat_attribute *mesh_attrs[] = {
&bat_attr_aggregated_ogms,
&bat_attr_bonding,
+#ifdef CONFIG_BATMAN_ADV_BLA
+ &bat_attr_bridge_loop_avoidance,
+#endif
&bat_attr_fragmentation,
&bat_attr_ap_isolation,
&bat_attr_vis_mode,
diff --git a/net/batman-adv/bitarray.c b/net/batman-adv/bitarray.c
index 6d0aa216b232..07ae6e1b8aca 100644
--- a/net/batman-adv/bitarray.c
+++ b/net/batman-adv/bitarray.c
@@ -24,100 +24,13 @@
#include <linux/bitops.h>
-/* returns true if the corresponding bit in the given seq_bits indicates true
- * and curr_seqno is within range of last_seqno */
-int get_bit_status(const unsigned long *seq_bits, uint32_t last_seqno,
- uint32_t curr_seqno)
-{
- int32_t diff, word_offset, word_num;
-
- diff = last_seqno - curr_seqno;
- if (diff < 0 || diff >= TQ_LOCAL_WINDOW_SIZE) {
- return 0;
- } else {
- /* which word */
- word_num = (last_seqno - curr_seqno) / WORD_BIT_SIZE;
- /* which position in the selected word */
- word_offset = (last_seqno - curr_seqno) % WORD_BIT_SIZE;
-
- if (test_bit(word_offset, &seq_bits[word_num]))
- return 1;
- else
- return 0;
- }
-}
-
-/* turn corresponding bit on, so we can remember that we got the packet */
-void bit_mark(unsigned long *seq_bits, int32_t n)
-{
- int32_t word_offset, word_num;
-
- /* if too old, just drop it */
- if (n < 0 || n >= TQ_LOCAL_WINDOW_SIZE)
- return;
-
- /* which word */
- word_num = n / WORD_BIT_SIZE;
- /* which position in the selected word */
- word_offset = n % WORD_BIT_SIZE;
-
- set_bit(word_offset, &seq_bits[word_num]); /* turn the position on */
-}
-
/* shift the packet array by n places. */
-static void bit_shift(unsigned long *seq_bits, int32_t n)
+static void bat_bitmap_shift_left(unsigned long *seq_bits, int32_t n)
{
- int32_t word_offset, word_num;
- int32_t i;
-
if (n <= 0 || n >= TQ_LOCAL_WINDOW_SIZE)
return;
- word_offset = n % WORD_BIT_SIZE;/* shift how much inside each word */
- word_num = n / WORD_BIT_SIZE; /* shift over how much (full) words */
-
- for (i = NUM_WORDS - 1; i > word_num; i--) {
- /* going from old to new, so we don't overwrite the data we copy
- * from.
- *
- * left is high, right is low: FEDC BA98 7654 3210
- * ^^ ^^
- * vvvv
- * ^^^^ = from, vvvvv =to, we'd have word_num==1 and
- * word_offset==WORD_BIT_SIZE/2 ????? in this example.
- * (=24 bits)
- *
- * our desired output would be: 9876 5432 1000 0000
- * */
-
- seq_bits[i] =
- (seq_bits[i - word_num] << word_offset) +
- /* take the lower port from the left half, shift it left
- * to its final position */
- (seq_bits[i - word_num - 1] >>
- (WORD_BIT_SIZE-word_offset));
- /* and the upper part of the right half and shift it left to
- * its position */
- /* for our example that would be: word[0] = 9800 + 0076 =
- * 9876 */
- }
- /* now for our last word, i==word_num, we only have its "left" half.
- * that's the 1000 word in our example.*/
-
- seq_bits[i] = (seq_bits[i - word_num] << word_offset);
-
- /* pad the rest with 0, if there is anything */
- i--;
-
- for (; i >= 0; i--)
- seq_bits[i] = 0;
-}
-
-static void bit_reset_window(unsigned long *seq_bits)
-{
- int i;
- for (i = 0; i < NUM_WORDS; i++)
- seq_bits[i] = 0;
+ bitmap_shift_left(seq_bits, seq_bits, n, TQ_LOCAL_WINDOW_SIZE);
}
@@ -137,7 +50,7 @@ int bit_get_packet(void *priv, unsigned long *seq_bits,
if ((seq_num_diff <= 0) && (seq_num_diff > -TQ_LOCAL_WINDOW_SIZE)) {
if (set_mark)
- bit_mark(seq_bits, -seq_num_diff);
+ bat_set_bit(seq_bits, -seq_num_diff);
return 0;
}
@@ -145,10 +58,10 @@ int bit_get_packet(void *priv, unsigned long *seq_bits,
* set the mark if required */
if ((seq_num_diff > 0) && (seq_num_diff < TQ_LOCAL_WINDOW_SIZE)) {
- bit_shift(seq_bits, seq_num_diff);
+ bat_bitmap_shift_left(seq_bits, seq_num_diff);
if (set_mark)
- bit_mark(seq_bits, 0);
+ bat_set_bit(seq_bits, 0);
return 1;
}
@@ -159,9 +72,9 @@ int bit_get_packet(void *priv, unsigned long *seq_bits,
bat_dbg(DBG_BATMAN, bat_priv,
"We missed a lot of packets (%i) !\n",
seq_num_diff - 1);
- bit_reset_window(seq_bits);
+ bitmap_zero(seq_bits, TQ_LOCAL_WINDOW_SIZE);
if (set_mark)
- bit_mark(seq_bits, 0);
+ bat_set_bit(seq_bits, 0);
return 1;
}
@@ -176,9 +89,9 @@ int bit_get_packet(void *priv, unsigned long *seq_bits,
bat_dbg(DBG_BATMAN, bat_priv,
"Other host probably restarted!\n");
- bit_reset_window(seq_bits);
+ bitmap_zero(seq_bits, TQ_LOCAL_WINDOW_SIZE);
if (set_mark)
- bit_mark(seq_bits, 0);
+ bat_set_bit(seq_bits, 0);
return 1;
}
@@ -186,16 +99,3 @@ int bit_get_packet(void *priv, unsigned long *seq_bits,
/* never reached */
return 0;
}
-
-/* count the hamming weight, how many good packets did we receive? just count
- * the 1's.
- */
-int bit_packet_count(const unsigned long *seq_bits)
-{
- int i, hamming = 0;
-
- for (i = 0; i < NUM_WORDS; i++)
- hamming += hweight_long(seq_bits[i]);
-
- return hamming;
-}
diff --git a/net/batman-adv/bitarray.h b/net/batman-adv/bitarray.h
index c6135728a680..1835c15cda41 100644
--- a/net/batman-adv/bitarray.h
+++ b/net/batman-adv/bitarray.h
@@ -22,23 +22,33 @@
#ifndef _NET_BATMAN_ADV_BITARRAY_H_
#define _NET_BATMAN_ADV_BITARRAY_H_
-#define WORD_BIT_SIZE (sizeof(unsigned long) * 8)
-
/* returns true if the corresponding bit in the given seq_bits indicates true
* and curr_seqno is within range of last_seqno */
-int get_bit_status(const unsigned long *seq_bits, uint32_t last_seqno,
- uint32_t curr_seqno);
+static inline int bat_test_bit(const unsigned long *seq_bits,
+ uint32_t last_seqno, uint32_t curr_seqno)
+{
+ int32_t diff;
+
+ diff = last_seqno - curr_seqno;
+ if (diff < 0 || diff >= TQ_LOCAL_WINDOW_SIZE)
+ return 0;
+ else
+ return test_bit(diff, seq_bits);
+}
/* turn corresponding bit on, so we can remember that we got the packet */
-void bit_mark(unsigned long *seq_bits, int32_t n);
+static inline void bat_set_bit(unsigned long *seq_bits, int32_t n)
+{
+ /* if too old, just drop it */
+ if (n < 0 || n >= TQ_LOCAL_WINDOW_SIZE)
+ return;
+ set_bit(n, seq_bits); /* turn the position on */
+}
/* receive and process one packet, returns 1 if received seq_num is considered
* new, 0 if old */
int bit_get_packet(void *priv, unsigned long *seq_bits,
int32_t seq_num_diff, int set_mark);
-/* count the hamming weight, how many good packets did we receive? */
-int bit_packet_count(const unsigned long *seq_bits);
-
#endif /* _NET_BATMAN_ADV_BITARRAY_H_ */
diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
new file mode 100644
index 000000000000..ad394c6496cc
--- /dev/null
+++ b/net/batman-adv/bridge_loop_avoidance.c
@@ -0,0 +1,1580 @@
+/*
+ * 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
+ *
+ */
+
+#include "main.h"
+#include "hash.h"
+#include "hard-interface.h"
+#include "originator.h"
+#include "bridge_loop_avoidance.h"
+#include "translation-table.h"
+#include "send.h"
+
+#include <linux/etherdevice.h>
+#include <linux/crc16.h>
+#include <linux/if_arp.h>
+#include <net/arp.h>
+#include <linux/if_vlan.h>
+
+static const uint8_t announce_mac[4] = {0x43, 0x05, 0x43, 0x05};
+
+static void bla_periodic_work(struct work_struct *work);
+static void bla_send_announce(struct bat_priv *bat_priv,
+ struct backbone_gw *backbone_gw);
+
+/* return the index of the claim */
+static inline uint32_t choose_claim(const void *data, uint32_t size)
+{
+ const unsigned char *key = data;
+ uint32_t hash = 0;
+ size_t i;
+
+ for (i = 0; i < ETH_ALEN + sizeof(short); i++) {
+ hash += key[i];
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
+ }
+
+ hash += (hash << 3);
+ hash ^= (hash >> 11);
+ hash += (hash << 15);
+
+ return hash % size;
+}
+
+/* return the index of the backbone gateway */
+static inline uint32_t choose_backbone_gw(const void *data, uint32_t size)
+{
+ const unsigned char *key = data;
+ uint32_t hash = 0;
+ size_t i;
+
+ for (i = 0; i < ETH_ALEN + sizeof(short); i++) {
+ hash += key[i];
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
+ }
+
+ hash += (hash << 3);
+ hash ^= (hash >> 11);
+ hash += (hash << 15);
+
+ return hash % size;
+}
+
+
+/* compares address and vid of two backbone gws */
+static int compare_backbone_gw(const struct hlist_node *node, const void *data2)
+{
+ const void *data1 = container_of(node, struct backbone_gw,
+ hash_entry);
+
+ return (memcmp(data1, data2, ETH_ALEN + sizeof(short)) == 0 ? 1 : 0);
+}
+
+/* compares address and vid of two claims */
+static int compare_claim(const struct hlist_node *node, const void *data2)
+{
+ const void *data1 = container_of(node, struct claim,
+ hash_entry);
+
+ return (memcmp(data1, data2, ETH_ALEN + sizeof(short)) == 0 ? 1 : 0);
+}
+
+/* free a backbone gw */
+static void backbone_gw_free_ref(struct backbone_gw *backbone_gw)
+{
+ if (atomic_dec_and_test(&backbone_gw->refcount))
+ kfree_rcu(backbone_gw, rcu);
+}
+
+/* finally deinitialize the claim */
+static void claim_free_rcu(struct rcu_head *rcu)
+{
+ struct claim *claim;
+
+ claim = container_of(rcu, struct claim, rcu);
+
+ backbone_gw_free_ref(claim->backbone_gw);
+ kfree(claim);
+}
+
+/* free a claim, call claim_free_rcu if its the last reference */
+static void claim_free_ref(struct claim *claim)
+{
+ if (atomic_dec_and_test(&claim->refcount))
+ call_rcu(&claim->rcu, claim_free_rcu);
+}
+
+/**
+ * @bat_priv: the bat priv with all the soft interface information
+ * @data: search data (may be local/static data)
+ *
+ * looks for a claim in the hash, and returns it if found
+ * or NULL otherwise.
+ */
+static struct claim *claim_hash_find(struct bat_priv *bat_priv,
+ struct claim *data)
+{
+ struct hashtable_t *hash = bat_priv->claim_hash;
+ struct hlist_head *head;
+ struct hlist_node *node;
+ struct claim *claim;
+ struct claim *claim_tmp = NULL;
+ int index;
+
+ if (!hash)
+ return NULL;
+
+ index = choose_claim(data, hash->size);
+ head = &hash->table[index];
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(claim, node, head, hash_entry) {
+ if (!compare_claim(&claim->hash_entry, data))
+ continue;
+
+ if (!atomic_inc_not_zero(&claim->refcount))
+ continue;
+
+ claim_tmp = claim;
+ break;
+ }
+ rcu_read_unlock();
+
+ return claim_tmp;
+}
+
+/**
+ * @bat_priv: the bat priv with all the soft interface information
+ * @addr: the address of the originator
+ * @vid: the VLAN ID
+ *
+ * looks for a claim in the hash, and returns it if found
+ * or NULL otherwise.
+ */
+static struct backbone_gw *backbone_hash_find(struct bat_priv *bat_priv,
+ uint8_t *addr, short vid)
+{
+ struct hashtable_t *hash = bat_priv->backbone_hash;
+ struct hlist_head *head;
+ struct hlist_node *node;
+ struct backbone_gw search_entry, *backbone_gw;
+ struct backbone_gw *backbone_gw_tmp = NULL;
+ int index;
+
+ if (!hash)
+ return NULL;
+
+ memcpy(search_entry.orig, addr, ETH_ALEN);
+ search_entry.vid = vid;
+
+ index = choose_backbone_gw(&search_entry, hash->size);
+ head = &hash->table[index];
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(backbone_gw, node, head, hash_entry) {
+ if (!compare_backbone_gw(&backbone_gw->hash_entry,
+ &search_entry))
+ continue;
+
+ if (!atomic_inc_not_zero(&backbone_gw->refcount))
+ continue;
+
+ backbone_gw_tmp = backbone_gw;
+ break;
+ }
+ rcu_read_unlock();
+
+ return backbone_gw_tmp;
+}
+
+/* delete all claims for a backbone */
+static void bla_del_backbone_claims(struct backbone_gw *backbone_gw)
+{
+ struct hashtable_t *hash;
+ struct hlist_node *node, *node_tmp;
+ struct hlist_head *head;
+ struct claim *claim;
+ int i;
+ spinlock_t *list_lock; /* protects write access to the hash lists */
+
+ hash = backbone_gw->bat_priv->claim_hash;
+ if (!hash)
+ return;
+
+ for (i = 0; i < hash->size; i++) {
+ head = &hash->table[i];
+ list_lock = &hash->list_locks[i];
+
+ spin_lock_bh(list_lock);
+ hlist_for_each_entry_safe(claim, node, node_tmp,
+ head, hash_entry) {
+
+ if (claim->backbone_gw != backbone_gw)
+ continue;
+
+ claim_free_ref(claim);
+ hlist_del_rcu(node);
+ }
+ spin_unlock_bh(list_lock);
+ }
+
+ /* all claims gone, intialize CRC */
+ backbone_gw->crc = BLA_CRC_INIT;
+}
+
+/**
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig: the mac address to be announced within the claim
+ * @vid: the VLAN ID
+ * @claimtype: the type of the claim (CLAIM, UNCLAIM, ANNOUNCE, ...)
+ *
+ * sends a claim frame according to the provided info.
+ */
+static void bla_send_claim(struct bat_priv *bat_priv, uint8_t *mac,
+ short vid, int claimtype)
+{
+ struct sk_buff *skb;
+ struct ethhdr *ethhdr;
+ struct hard_iface *primary_if;
+ struct net_device *soft_iface;
+ uint8_t *hw_src;
+ struct bla_claim_dst local_claim_dest;
+ uint32_t zeroip = 0;
+
+ primary_if = primary_if_get_selected(bat_priv);
+ if (!primary_if)
+ return;
+
+ memcpy(&local_claim_dest, &bat_priv->claim_dest,
+ sizeof(local_claim_dest));
+ local_claim_dest.type = claimtype;
+
+ soft_iface = primary_if->soft_iface;
+
+ skb = arp_create(ARPOP_REPLY, ETH_P_ARP,
+ /* IP DST: 0.0.0.0 */
+ zeroip,
+ primary_if->soft_iface,
+ /* IP SRC: 0.0.0.0 */
+ zeroip,
+ /* Ethernet DST: Broadcast */
+ NULL,
+ /* Ethernet SRC/HW SRC: originator mac */
+ primary_if->net_dev->dev_addr,
+ /* HW DST: FF:43:05:XX:00:00
+ * with XX = claim type
+ * and YY:YY = group id
+ */
+ (uint8_t *)&local_claim_dest);
+
+ if (!skb)
+ goto out;
+
+ ethhdr = (struct ethhdr *)skb->data;
+ hw_src = (uint8_t *)ethhdr + ETH_HLEN + sizeof(struct arphdr);
+
+ /* now we pretend that the client would have sent this ... */
+ switch (claimtype) {
+ case CLAIM_TYPE_ADD:
+ /* normal claim frame
+ * set Ethernet SRC to the clients mac
+ */
+ memcpy(ethhdr->h_source, mac, ETH_ALEN);
+ bat_dbg(DBG_BLA, bat_priv,
+ "bla_send_claim(): CLAIM %pM on vid %d\n", mac, vid);
+ break;
+ case CLAIM_TYPE_DEL:
+ /* unclaim frame
+ * set HW SRC to the clients mac
+ */
+ memcpy(hw_src, mac, ETH_ALEN);
+ bat_dbg(DBG_BLA, bat_priv,
+ "bla_send_claim(): UNCLAIM %pM on vid %d\n", mac, vid);
+ break;
+ case CLAIM_TYPE_ANNOUNCE:
+ /* announcement frame
+ * set HW SRC to the special mac containg the crc
+ */
+ memcpy(hw_src, mac, ETH_ALEN);
+ bat_dbg(DBG_BLA, bat_priv,
+ "bla_send_claim(): ANNOUNCE of %pM on vid %d\n",
+ ethhdr->h_source, vid);
+ break;
+ case CLAIM_TYPE_REQUEST:
+ /* request frame
+ * set HW SRC to the special mac containg the crc
+ */
+ memcpy(hw_src, mac, ETH_ALEN);
+ memcpy(ethhdr->h_dest, mac, ETH_ALEN);
+ bat_dbg(DBG_BLA, bat_priv,
+ "bla_send_claim(): REQUEST of %pM to %pMon vid %d\n",
+ ethhdr->h_source, ethhdr->h_dest, vid);
+ break;
+
+ }
+
+ if (vid != -1)
+ skb = vlan_insert_tag(skb, vid);
+
+ skb_reset_mac_header(skb);
+ skb->protocol = eth_type_trans(skb, soft_iface);
+ bat_priv->stats.rx_packets++;
+ bat_priv->stats.rx_bytes += skb->len + ETH_HLEN;
+ soft_iface->last_rx = jiffies;
+
+ netif_rx(skb);
+out:
+ if (primary_if)
+ hardif_free_ref(primary_if);
+}
+
+/**
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig: the mac address of the originator
+ * @vid: the VLAN ID
+ *
+ * searches for the backbone gw or creates a new one if it could not
+ * be found.
+ */
+static struct backbone_gw *bla_get_backbone_gw(struct bat_priv *bat_priv,
+ uint8_t *orig, short vid)
+{
+ struct backbone_gw *entry;
+ struct orig_node *orig_node;
+ int hash_added;
+
+ entry = backbone_hash_find(bat_priv, orig, vid);
+
+ if (entry)
+ return entry;
+
+ bat_dbg(DBG_BLA, bat_priv,
+ "bla_get_backbone_gw(): not found (%pM, %d), creating new entry\n",
+ orig, vid);
+
+ entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
+ if (!entry)
+ return NULL;
+
+ entry->vid = vid;
+ entry->lasttime = jiffies;
+ entry->crc = BLA_CRC_INIT;
+ entry->bat_priv = bat_priv;
+ atomic_set(&entry->request_sent, 0);
+ memcpy(entry->orig, orig, ETH_ALEN);
+
+ /* one for the hash, one for returning */
+ atomic_set(&entry->refcount, 2);
+
+ hash_added = hash_add(bat_priv->backbone_hash, compare_backbone_gw,
+ choose_backbone_gw, entry, &entry->hash_entry);
+
+ if (unlikely(hash_added != 0)) {
+ /* hash failed, free the structure */
+ kfree(entry);
+ return NULL;
+ }
+
+ /* this is a gateway now, remove any tt entries */
+ orig_node = orig_hash_find(bat_priv, orig);
+ if (orig_node) {
+ tt_global_del_orig(bat_priv, orig_node,
+ "became a backbone gateway");
+ orig_node_free_ref(orig_node);
+ }
+ return entry;
+}
+
+/* update or add the own backbone gw to make sure we announce
+ * where we receive other backbone gws
+ */
+static void bla_update_own_backbone_gw(struct bat_priv *bat_priv,
+ struct hard_iface *primary_if,
+ short vid)
+{
+ struct backbone_gw *backbone_gw;
+
+ backbone_gw = bla_get_backbone_gw(bat_priv,
+ primary_if->net_dev->dev_addr, vid);
+ if (unlikely(!backbone_gw))
+ return;
+
+ backbone_gw->lasttime = jiffies;
+ backbone_gw_free_ref(backbone_gw);
+}
+
+/**
+ * @bat_priv: the bat priv with all the soft interface information
+ * @vid: the vid where the request came on
+ *
+ * Repeat all of our own claims, and finally send an ANNOUNCE frame
+ * to allow the requester another check if the CRC is correct now.
+ */
+static void bla_answer_request(struct bat_priv *bat_priv,
+ struct hard_iface *primary_if, short vid)
+{
+ struct hlist_node *node;
+ struct hlist_head *head;
+ struct hashtable_t *hash;
+ struct claim *claim;
+ struct backbone_gw *backbone_gw;
+ int i;
+
+ bat_dbg(DBG_BLA, bat_priv,
+ "bla_answer_request(): received a claim request, send all of our own claims again\n");
+
+ backbone_gw = backbone_hash_find(bat_priv,
+ primary_if->net_dev->dev_addr, vid);
+ if (!backbone_gw)
+ return;
+
+ hash = bat_priv->claim_hash;
+ for (i = 0; i < hash->size; i++) {
+ head = &hash->table[i];
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(claim, node, head, hash_entry) {
+ /* only own claims are interesting */
+ if (claim->backbone_gw != backbone_gw)
+ continue;
+
+ bla_send_claim(bat_priv, claim->addr, claim->vid,
+ CLAIM_TYPE_ADD);
+ }
+ rcu_read_unlock();
+ }
+
+ /* finally, send an announcement frame */
+ bla_send_announce(bat_priv, backbone_gw);
+ backbone_gw_free_ref(backbone_gw);
+}
+
+/**
+ * @backbone_gw: the backbone gateway from whom we are out of sync
+ *
+ * When the crc is wrong, ask the backbone gateway for a full table update.
+ * After the request, it will repeat all of his own claims and finally
+ * send an announcement claim with which we can check again.
+ */
+static void bla_send_request(struct backbone_gw *backbone_gw)
+{
+ /* first, remove all old entries */
+ bla_del_backbone_claims(backbone_gw);
+
+ bat_dbg(DBG_BLA, backbone_gw->bat_priv,
+ "Sending REQUEST to %pM\n",
+ backbone_gw->orig);
+
+ /* send request */
+ bla_send_claim(backbone_gw->bat_priv, backbone_gw->orig,
+ backbone_gw->vid, CLAIM_TYPE_REQUEST);
+
+ /* no local broadcasts should be sent or received, for now. */
+ if (!atomic_read(&backbone_gw->request_sent)) {
+ atomic_inc(&backbone_gw->bat_priv->bla_num_requests);
+ atomic_set(&backbone_gw->request_sent, 1);
+ }
+}
+
+/**
+ * @bat_priv: the bat priv with all the soft interface information
+ * @backbone_gw: our backbone gateway which should be announced
+ *
+ * This function sends an announcement. It is called from multiple
+ * places.
+ */
+static void bla_send_announce(struct bat_priv *bat_priv,
+ struct backbone_gw *backbone_gw)
+{
+ uint8_t mac[ETH_ALEN];
+ uint16_t crc;
+
+ memcpy(mac, announce_mac, 4);
+ crc = htons(backbone_gw->crc);
+ memcpy(&mac[4], (uint8_t *)&crc, 2);
+
+ bla_send_claim(bat_priv, mac, backbone_gw->vid, CLAIM_TYPE_ANNOUNCE);
+
+}
+
+/**
+ * @bat_priv: the bat priv with all the soft interface information
+ * @mac: the mac address of the claim
+ * @vid: the VLAN ID of the frame
+ * @backbone_gw: the backbone gateway which claims it
+ *
+ * Adds a claim in the claim hash.
+ */
+static void bla_add_claim(struct bat_priv *bat_priv, const uint8_t *mac,
+ const short vid, struct backbone_gw *backbone_gw)
+{
+ struct claim *claim;
+ struct claim search_claim;
+ int hash_added;
+
+ memcpy(search_claim.addr, mac, ETH_ALEN);
+ search_claim.vid = vid;
+ claim = claim_hash_find(bat_priv, &search_claim);
+
+ /* create a new claim entry if it does not exist yet. */
+ if (!claim) {
+ claim = kzalloc(sizeof(*claim), GFP_ATOMIC);
+ if (!claim)
+ return;
+
+ memcpy(claim->addr, mac, ETH_ALEN);
+ claim->vid = vid;
+ claim->lasttime = jiffies;
+ claim->backbone_gw = backbone_gw;
+
+ atomic_set(&claim->refcount, 2);
+ bat_dbg(DBG_BLA, bat_priv,
+ "bla_add_claim(): adding new entry %pM, vid %d to hash ...\n",
+ mac, vid);
+ hash_added = hash_add(bat_priv->claim_hash, compare_claim,
+ choose_claim, claim, &claim->hash_entry);
+
+ if (unlikely(hash_added != 0)) {
+ /* only local changes happened. */
+ kfree(claim);
+ return;
+ }
+ } else {
+ claim->lasttime = jiffies;
+ if (claim->backbone_gw == backbone_gw)
+ /* no need to register a new backbone */
+ goto claim_free_ref;
+
+ bat_dbg(DBG_BLA, bat_priv,
+ "bla_add_claim(): changing ownership for %pM, vid %d\n",
+ mac, vid);
+
+ claim->backbone_gw->crc ^=
+ crc16(0, claim->addr, ETH_ALEN);
+ backbone_gw_free_ref(claim->backbone_gw);
+
+ }
+ /* set (new) backbone gw */
+ atomic_inc(&backbone_gw->refcount);
+ claim->backbone_gw = backbone_gw;
+
+ backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
+ backbone_gw->lasttime = jiffies;
+
+claim_free_ref:
+ claim_free_ref(claim);
+}
+
+/* Delete a claim from the claim hash which has the
+ * given mac address and vid.
+ */
+static void bla_del_claim(struct bat_priv *bat_priv, const uint8_t *mac,
+ const short vid)
+{
+ struct claim search_claim, *claim;
+
+ memcpy(search_claim.addr, mac, ETH_ALEN);
+ search_claim.vid = vid;
+ claim = claim_hash_find(bat_priv, &search_claim);
+ if (!claim)
+ return;
+
+ bat_dbg(DBG_BLA, bat_priv, "bla_del_claim(): %pM, vid %d\n", mac, vid);
+
+ hash_remove(bat_priv->claim_hash, compare_claim, choose_claim, claim);
+ claim_free_ref(claim); /* reference from the hash is gone */
+
+ claim->backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
+
+ /* don't need the reference from hash_find() anymore */
+ claim_free_ref(claim);
+}
+
+/* check for ANNOUNCE frame, return 1 if handled */
+static int handle_announce(struct bat_priv *bat_priv,
+ uint8_t *an_addr, uint8_t *backbone_addr, short vid)
+{
+ struct backbone_gw *backbone_gw;
+ uint16_t crc;
+
+ if (memcmp(an_addr, announce_mac, 4) != 0)
+ return 0;
+
+ backbone_gw = bla_get_backbone_gw(bat_priv, backbone_addr, vid);
+
+ if (unlikely(!backbone_gw))
+ return 1;
+
+
+ /* handle as ANNOUNCE frame */
+ backbone_gw->lasttime = jiffies;
+ crc = ntohs(*((uint16_t *)(&an_addr[4])));
+
+ bat_dbg(DBG_BLA, bat_priv,
+ "handle_announce(): ANNOUNCE vid %d (sent by %pM)... CRC = %04x\n",
+ vid, backbone_gw->orig, crc);
+
+ if (backbone_gw->crc != crc) {
+ bat_dbg(DBG_BLA, backbone_gw->bat_priv,
+ "handle_announce(): CRC FAILED for %pM/%d (my = %04x, sent = %04x)\n",
+ backbone_gw->orig, backbone_gw->vid, backbone_gw->crc,
+ crc);
+
+ bla_send_request(backbone_gw);
+ } else {
+ /* if we have sent a request and the crc was OK,
+ * we can allow traffic again.
+ */
+ if (atomic_read(&backbone_gw->request_sent)) {
+ atomic_dec(&backbone_gw->bat_priv->bla_num_requests);
+ atomic_set(&backbone_gw->request_sent, 0);
+ }
+ }
+
+ backbone_gw_free_ref(backbone_gw);
+ return 1;
+}
+
+/* check for REQUEST frame, return 1 if handled */
+static int handle_request(struct bat_priv *bat_priv,
+ struct hard_iface *primary_if,
+ uint8_t *backbone_addr,
+ struct ethhdr *ethhdr, short vid)
+{
+ /* check for REQUEST frame */
+ if (!compare_eth(backbone_addr, ethhdr->h_dest))
+ return 0;
+
+ /* sanity check, this should not happen on a normal switch,
+ * we ignore it in this case.
+ */
+ if (!compare_eth(ethhdr->h_dest, primary_if->net_dev->dev_addr))
+ return 1;
+
+ bat_dbg(DBG_BLA, bat_priv,
+ "handle_request(): REQUEST vid %d (sent by %pM)...\n",
+ vid, ethhdr->h_source);
+
+ bla_answer_request(bat_priv, primary_if, vid);
+ return 1;
+}
+
+/* check for UNCLAIM frame, return 1 if handled */
+static int handle_unclaim(struct bat_priv *bat_priv,
+ struct hard_iface *primary_if,
+ uint8_t *backbone_addr,
+ uint8_t *claim_addr, short vid)
+{
+ struct backbone_gw *backbone_gw;
+
+ /* unclaim in any case if it is our own */
+ if (primary_if && compare_eth(backbone_addr,
+ primary_if->net_dev->dev_addr))
+ bla_send_claim(bat_priv, claim_addr, vid, CLAIM_TYPE_DEL);
+
+ backbone_gw = backbone_hash_find(bat_priv, backbone_addr, vid);
+
+ if (!backbone_gw)
+ return 1;
+
+ /* this must be an UNCLAIM frame */
+ bat_dbg(DBG_BLA, bat_priv,
+ "handle_unclaim(): UNCLAIM %pM on vid %d (sent by %pM)...\n",
+ claim_addr, vid, backbone_gw->orig);
+
+ bla_del_claim(bat_priv, claim_addr, vid);
+ backbone_gw_free_ref(backbone_gw);
+ return 1;
+}
+
+/* check for CLAIM frame, return 1 if handled */
+static int handle_claim(struct bat_priv *bat_priv,
+ struct hard_iface *primary_if, uint8_t *backbone_addr,
+ uint8_t *claim_addr, short vid)
+{
+ struct backbone_gw *backbone_gw;
+
+ /* register the gateway if not yet available, and add the claim. */
+
+ backbone_gw = bla_get_backbone_gw(bat_priv, backbone_addr, vid);
+
+ if (unlikely(!backbone_gw))
+ return 1;
+
+ /* this must be a CLAIM frame */
+ bla_add_claim(bat_priv, claim_addr, vid, backbone_gw);
+ if (compare_eth(backbone_addr, primary_if->net_dev->dev_addr))
+ bla_send_claim(bat_priv, claim_addr, vid, CLAIM_TYPE_ADD);
+
+ /* TODO: we could call something like tt_local_del() here. */
+
+ backbone_gw_free_ref(backbone_gw);
+ return 1;
+}
+
+/**
+ * @bat_priv: the bat priv with all the soft interface information
+ * @hw_src: the Hardware source in the ARP Header
+ * @hw_dst: the Hardware destination in the ARP Header
+ * @ethhdr: pointer to the Ethernet header of the claim frame
+ *
+ * checks if it is a claim packet and if its on the same group.
+ * This function also applies the group ID of the sender
+ * if it is in the same mesh.
+ *
+ * returns:
+ * 2 - if it is a claim packet and on the same group
+ * 1 - if is a claim packet from another group
+ * 0 - if it is not a claim packet
+ */
+static int check_claim_group(struct bat_priv *bat_priv,
+ struct hard_iface *primary_if,
+ uint8_t *hw_src, uint8_t *hw_dst,
+ struct ethhdr *ethhdr)
+{
+ uint8_t *backbone_addr;
+ struct orig_node *orig_node;
+ struct bla_claim_dst *bla_dst, *bla_dst_own;
+
+ bla_dst = (struct bla_claim_dst *)hw_dst;
+ bla_dst_own = &bat_priv->claim_dest;
+
+ /* check if it is a claim packet in general */
+ if (memcmp(bla_dst->magic, bla_dst_own->magic,
+ sizeof(bla_dst->magic)) != 0)
+ return 0;
+
+ /* if announcement packet, use the source,
+ * otherwise assume it is in the hw_src
+ */
+ switch (bla_dst->type) {
+ case CLAIM_TYPE_ADD:
+ backbone_addr = hw_src;
+ break;
+ case CLAIM_TYPE_REQUEST:
+ case CLAIM_TYPE_ANNOUNCE:
+ case CLAIM_TYPE_DEL:
+ backbone_addr = ethhdr->h_source;
+ break;
+ default:
+ return 0;
+ }
+
+ /* don't accept claim frames from ourselves */
+ if (compare_eth(backbone_addr, primary_if->net_dev->dev_addr))
+ return 0;
+
+ /* if its already the same group, it is fine. */
+ if (bla_dst->group == bla_dst_own->group)
+ return 2;
+
+ /* lets see if this originator is in our mesh */
+ orig_node = orig_hash_find(bat_priv, backbone_addr);
+
+ /* dont accept claims from gateways which are not in
+ * the same mesh or group.
+ */
+ if (!orig_node)
+ return 1;
+
+ /* if our mesh friends mac is bigger, use it for ourselves. */
+ if (ntohs(bla_dst->group) > ntohs(bla_dst_own->group)) {
+ bat_dbg(DBG_BLA, bat_priv,
+ "taking other backbones claim group: %04x\n",
+ ntohs(bla_dst->group));
+ bla_dst_own->group = bla_dst->group;
+ }
+
+ orig_node_free_ref(orig_node);
+
+ return 2;
+}
+
+
+/**
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: the frame to be checked
+ *
+ * Check if this is a claim frame, and process it accordingly.
+ *
+ * returns 1 if it was a claim frame, otherwise return 0 to
+ * tell the callee that it can use the frame on its own.
+ */
+static int bla_process_claim(struct bat_priv *bat_priv,
+ struct hard_iface *primary_if,
+ struct sk_buff *skb)
+{
+ struct ethhdr *ethhdr;
+ struct vlan_ethhdr *vhdr;
+ struct arphdr *arphdr;
+ uint8_t *hw_src, *hw_dst;
+ struct bla_claim_dst *bla_dst;
+ uint16_t proto;
+ int headlen;
+ short vid = -1;
+ int ret;
+
+ ethhdr = (struct ethhdr *)skb_mac_header(skb);
+
+ if (ntohs(ethhdr->h_proto) == ETH_P_8021Q) {
+ vhdr = (struct vlan_ethhdr *)ethhdr;
+ vid = ntohs(vhdr->h_vlan_TCI) & VLAN_VID_MASK;
+ proto = ntohs(vhdr->h_vlan_encapsulated_proto);
+ headlen = sizeof(*vhdr);
+ } else {
+ proto = ntohs(ethhdr->h_proto);
+ headlen = ETH_HLEN;
+ }
+
+ if (proto != ETH_P_ARP)
+ return 0; /* not a claim frame */
+
+ /* this must be a ARP frame. check if it is a claim. */
+
+ if (unlikely(!pskb_may_pull(skb, headlen + arp_hdr_len(skb->dev))))
+ return 0;
+
+ /* pskb_may_pull() may have modified the pointers, get ethhdr again */
+ ethhdr = (struct ethhdr *)skb_mac_header(skb);
+ arphdr = (struct arphdr *)((uint8_t *)ethhdr + headlen);
+
+ /* Check whether the ARP frame carries a valid
+ * IP information
+ */
+
+ if (arphdr->ar_hrd != htons(ARPHRD_ETHER))
+ return 0;
+ if (arphdr->ar_pro != htons(ETH_P_IP))
+ return 0;
+ if (arphdr->ar_hln != ETH_ALEN)
+ return 0;
+ if (arphdr->ar_pln != 4)
+ return 0;
+
+ hw_src = (uint8_t *)arphdr + sizeof(struct arphdr);
+ hw_dst = hw_src + ETH_ALEN + 4;
+ bla_dst = (struct bla_claim_dst *)hw_dst;
+
+ /* check if it is a claim frame. */
+ ret = check_claim_group(bat_priv, primary_if, hw_src, hw_dst, ethhdr);
+ if (ret == 1)
+ bat_dbg(DBG_BLA, bat_priv,
+ "bla_process_claim(): received a claim frame from another group. From: %pM on vid %d ...(hw_src %pM, hw_dst %pM)\n",
+ ethhdr->h_source, vid, hw_src, hw_dst);
+
+ if (ret < 2)
+ return ret;
+
+ /* become a backbone gw ourselves on this vlan if not happened yet */
+ bla_update_own_backbone_gw(bat_priv, primary_if, vid);
+
+ /* check for the different types of claim frames ... */
+ switch (bla_dst->type) {
+ case CLAIM_TYPE_ADD:
+ if (handle_claim(bat_priv, primary_if, hw_src,
+ ethhdr->h_source, vid))
+ return 1;
+ break;
+ case CLAIM_TYPE_DEL:
+ if (handle_unclaim(bat_priv, primary_if,
+ ethhdr->h_source, hw_src, vid))
+ return 1;
+ break;
+
+ case CLAIM_TYPE_ANNOUNCE:
+ if (handle_announce(bat_priv, hw_src, ethhdr->h_source, vid))
+ return 1;
+ break;
+ case CLAIM_TYPE_REQUEST:
+ if (handle_request(bat_priv, primary_if, hw_src, ethhdr, vid))
+ return 1;
+ break;
+ }
+
+ bat_dbg(DBG_BLA, bat_priv,
+ "bla_process_claim(): ERROR - this looks like a claim frame, but is useless. eth src %pM on vid %d ...(hw_src %pM, hw_dst %pM)\n",
+ ethhdr->h_source, vid, hw_src, hw_dst);
+ return 1;
+}
+
+/* Check when we last heard from other nodes, and remove them in case of
+ * a time out, or clean all backbone gws if now is set.
+ */
+static void bla_purge_backbone_gw(struct bat_priv *bat_priv, int now)
+{
+ struct backbone_gw *backbone_gw;
+ struct hlist_node *node, *node_tmp;
+ struct hlist_head *head;
+ struct hashtable_t *hash;
+ spinlock_t *list_lock; /* protects write access to the hash lists */
+ int i;
+
+ hash = bat_priv->backbone_hash;
+ if (!hash)
+ return;
+
+ for (i = 0; i < hash->size; i++) {
+ head = &hash->table[i];
+ list_lock = &hash->list_locks[i];
+
+ spin_lock_bh(list_lock);
+ hlist_for_each_entry_safe(backbone_gw, node, node_tmp,
+ head, hash_entry) {
+ if (now)
+ goto purge_now;
+ if (!has_timed_out(backbone_gw->lasttime,
+ BLA_BACKBONE_TIMEOUT))
+ continue;
+
+ bat_dbg(DBG_BLA, backbone_gw->bat_priv,
+ "bla_purge_backbone_gw(): backbone gw %pM timed out\n",
+ backbone_gw->orig);
+
+purge_now:
+ /* don't wait for the pending request anymore */
+ if (atomic_read(&backbone_gw->request_sent))
+ atomic_dec(&bat_priv->bla_num_requests);
+
+ bla_del_backbone_claims(backbone_gw);
+
+ hlist_del_rcu(node);
+ backbone_gw_free_ref(backbone_gw);
+ }
+ spin_unlock_bh(list_lock);
+ }
+}
+
+/**
+ * @bat_priv: the bat priv with all the soft interface information
+ * @primary_if: the selected primary interface, may be NULL if now is set
+ * @now: whether the whole hash shall be wiped now
+ *
+ * Check when we heard last time from our own claims, and remove them in case of
+ * a time out, or clean all claims if now is set
+ */
+static void bla_purge_claims(struct bat_priv *bat_priv,
+ struct hard_iface *primary_if, int now)
+{
+ struct claim *claim;
+ struct hlist_node *node;
+ struct hlist_head *head;
+ struct hashtable_t *hash;
+ int i;
+
+ hash = bat_priv->claim_hash;
+ if (!hash)
+ return;
+
+ for (i = 0; i < hash->size; i++) {
+ head = &hash->table[i];
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(claim, node, head, hash_entry) {
+ if (now)
+ goto purge_now;
+ if (!compare_eth(claim->backbone_gw->orig,
+ primary_if->net_dev->dev_addr))
+ continue;
+ if (!has_timed_out(claim->lasttime,
+ BLA_CLAIM_TIMEOUT))
+ continue;
+
+ bat_dbg(DBG_BLA, bat_priv,
+ "bla_purge_claims(): %pM, vid %d, time out\n",
+ claim->addr, claim->vid);
+
+purge_now:
+ handle_unclaim(bat_priv, primary_if,
+ claim->backbone_gw->orig,
+ claim->addr, claim->vid);
+ }
+ rcu_read_unlock();
+ }
+}
+
+/**
+ * @bat_priv: the bat priv with all the soft interface information
+ * @primary_if: the new selected primary_if
+ * @oldif: the old primary interface, may be NULL
+ *
+ * Update the backbone gateways when the own orig address changes.
+ *
+ */
+void bla_update_orig_address(struct bat_priv *bat_priv,
+ struct hard_iface *primary_if,
+ struct hard_iface *oldif)
+{
+ struct backbone_gw *backbone_gw;
+ struct hlist_node *node;
+ struct hlist_head *head;
+ struct hashtable_t *hash;
+ int i;
+
+ /* reset bridge loop avoidance group id */
+ bat_priv->claim_dest.group =
+ htons(crc16(0, primary_if->net_dev->dev_addr, ETH_ALEN));
+
+ if (!oldif) {
+ bla_purge_claims(bat_priv, NULL, 1);
+ bla_purge_backbone_gw(bat_priv, 1);
+ return;
+ }
+
+ hash = bat_priv->backbone_hash;
+ if (!hash)
+ return;
+
+ for (i = 0; i < hash->size; i++) {
+ head = &hash->table[i];
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(backbone_gw, node, head, hash_entry) {
+ /* own orig still holds the old value. */
+ if (!compare_eth(backbone_gw->orig,
+ oldif->net_dev->dev_addr))
+ continue;
+
+ memcpy(backbone_gw->orig,
+ primary_if->net_dev->dev_addr, ETH_ALEN);
+ /* send an announce frame so others will ask for our
+ * claims and update their tables.
+ */
+ bla_send_announce(bat_priv, backbone_gw);
+ }
+ rcu_read_unlock();
+ }
+}
+
+
+
+/* (re)start the timer */
+static void bla_start_timer(struct bat_priv *bat_priv)
+{
+ INIT_DELAYED_WORK(&bat_priv->bla_work, bla_periodic_work);
+ queue_delayed_work(bat_event_workqueue, &bat_priv->bla_work,
+ msecs_to_jiffies(BLA_PERIOD_LENGTH));
+}
+
+/* periodic work to do:
+ * * purge structures when they are too old
+ * * send announcements
+ */
+static void bla_periodic_work(struct work_struct *work)
+{
+ struct delayed_work *delayed_work =
+ container_of(work, struct delayed_work, work);
+ struct bat_priv *bat_priv =
+ container_of(delayed_work, struct bat_priv, bla_work);
+ struct hlist_node *node;
+ struct hlist_head *head;
+ struct backbone_gw *backbone_gw;
+ struct hashtable_t *hash;
+ struct hard_iface *primary_if;
+ int i;
+
+ primary_if = primary_if_get_selected(bat_priv);
+ if (!primary_if)
+ goto out;
+
+ bla_purge_claims(bat_priv, primary_if, 0);
+ bla_purge_backbone_gw(bat_priv, 0);
+
+ if (!atomic_read(&bat_priv->bridge_loop_avoidance))
+ goto out;
+
+ hash = bat_priv->backbone_hash;
+ if (!hash)
+ goto out;
+
+ for (i = 0; i < hash->size; i++) {
+ head = &hash->table[i];
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(backbone_gw, node, head, hash_entry) {
+ if (!compare_eth(backbone_gw->orig,
+ primary_if->net_dev->dev_addr))
+ continue;
+
+ backbone_gw->lasttime = jiffies;
+
+ bla_send_announce(bat_priv, backbone_gw);
+ }
+ rcu_read_unlock();
+ }
+out:
+ if (primary_if)
+ hardif_free_ref(primary_if);
+
+ bla_start_timer(bat_priv);
+}
+
+/* initialize all bla structures */
+int bla_init(struct bat_priv *bat_priv)
+{
+ int i;
+ uint8_t claim_dest[ETH_ALEN] = {0xff, 0x43, 0x05, 0x00, 0x00, 0x00};
+ struct hard_iface *primary_if;
+
+ bat_dbg(DBG_BLA, bat_priv, "bla hash registering\n");
+
+ /* setting claim destination address */
+ memcpy(&bat_priv->claim_dest.magic, claim_dest, 3);
+ bat_priv->claim_dest.type = 0;
+ primary_if = primary_if_get_selected(bat_priv);
+ if (primary_if) {
+ bat_priv->claim_dest.group =
+ htons(crc16(0, primary_if->net_dev->dev_addr,
+ ETH_ALEN));
+ hardif_free_ref(primary_if);
+ } else {
+ bat_priv->claim_dest.group = 0; /* will be set later */
+ }
+
+ /* initialize the duplicate list */
+ for (i = 0; i < DUPLIST_SIZE; i++)
+ bat_priv->bcast_duplist[i].entrytime =
+ jiffies - msecs_to_jiffies(DUPLIST_TIMEOUT);
+ bat_priv->bcast_duplist_curr = 0;
+
+ if (bat_priv->claim_hash)
+ return 1;
+
+ bat_priv->claim_hash = hash_new(128);
+ bat_priv->backbone_hash = hash_new(32);
+
+ if (!bat_priv->claim_hash || !bat_priv->backbone_hash)
+ return -1;
+
+ bat_dbg(DBG_BLA, bat_priv, "bla hashes initialized\n");
+
+ bla_start_timer(bat_priv);
+ return 1;
+}
+
+/**
+ * @bat_priv: the bat priv with all the soft interface information
+ * @bcast_packet: originator mac address
+ * @hdr_size: maximum length of the frame
+ *
+ * check if it is on our broadcast list. Another gateway might
+ * have sent the same packet because it is connected to the same backbone,
+ * so we have to remove this duplicate.
+ *
+ * This is performed by checking the CRC, which will tell us
+ * with a good chance that it is the same packet. If it is furthermore
+ * sent by another host, drop it. We allow equal packets from
+ * the same host however as this might be intended.
+ *
+ **/
+
+int bla_check_bcast_duplist(struct bat_priv *bat_priv,
+ struct bcast_packet *bcast_packet,
+ int hdr_size)
+{
+ int i, length, curr;
+ uint8_t *content;
+ uint16_t crc;
+ struct bcast_duplist_entry *entry;
+
+ length = hdr_size - sizeof(*bcast_packet);
+ content = (uint8_t *)bcast_packet;
+ content += sizeof(*bcast_packet);
+
+ /* calculate the crc ... */
+ crc = crc16(0, content, length);
+
+ for (i = 0 ; i < DUPLIST_SIZE; i++) {
+ curr = (bat_priv->bcast_duplist_curr + i) % DUPLIST_SIZE;
+ entry = &bat_priv->bcast_duplist[curr];
+
+ /* we can stop searching if the entry is too old ;
+ * later entries will be even older
+ */
+ if (has_timed_out(entry->entrytime, DUPLIST_TIMEOUT))
+ break;
+
+ if (entry->crc != crc)
+ continue;
+
+ if (compare_eth(entry->orig, bcast_packet->orig))
+ continue;
+
+ /* this entry seems to match: same crc, not too old,
+ * and from another gw. therefore return 1 to forbid it.
+ */
+ return 1;
+ }
+ /* not found, add a new entry (overwrite the oldest entry) */
+ curr = (bat_priv->bcast_duplist_curr + DUPLIST_SIZE - 1) % DUPLIST_SIZE;
+ entry = &bat_priv->bcast_duplist[curr];
+ entry->crc = crc;
+ entry->entrytime = jiffies;
+ memcpy(entry->orig, bcast_packet->orig, ETH_ALEN);
+ bat_priv->bcast_duplist_curr = curr;
+
+ /* allow it, its the first occurence. */
+ return 0;
+}
+
+
+
+/**
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig: originator mac address
+ *
+ * check if the originator is a gateway for any VLAN ID.
+ *
+ * returns 1 if it is found, 0 otherwise
+ *
+ */
+
+int bla_is_backbone_gw_orig(struct bat_priv *bat_priv, uint8_t *orig)
+{
+ struct hashtable_t *hash = bat_priv->backbone_hash;
+ struct hlist_head *head;
+ struct hlist_node *node;
+ struct backbone_gw *backbone_gw;
+ int i;
+
+ if (!atomic_read(&bat_priv->bridge_loop_avoidance))
+ return 0;
+
+ if (!hash)
+ return 0;
+
+ for (i = 0; i < hash->size; i++) {
+ head = &hash->table[i];
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(backbone_gw, node, head, hash_entry) {
+ if (compare_eth(backbone_gw->orig, orig)) {
+ rcu_read_unlock();
+ return 1;
+ }
+ }
+ rcu_read_unlock();
+ }
+
+ return 0;
+}
+
+
+/**
+ * @skb: the frame to be checked
+ * @orig_node: the orig_node of the frame
+ * @hdr_size: maximum length of the frame
+ *
+ * bla_is_backbone_gw inspects the skb for the VLAN ID and returns 1
+ * if the orig_node is also a gateway on the soft interface, otherwise it
+ * returns 0.
+ *
+ */
+int bla_is_backbone_gw(struct sk_buff *skb,
+ struct orig_node *orig_node, int hdr_size)
+{
+ struct ethhdr *ethhdr;
+ struct vlan_ethhdr *vhdr;
+ struct backbone_gw *backbone_gw;
+ short vid = -1;
+
+ if (!atomic_read(&orig_node->bat_priv->bridge_loop_avoidance))
+ return 0;
+
+ /* first, find out the vid. */
+ if (!pskb_may_pull(skb, hdr_size + ETH_HLEN))
+ return 0;
+
+ ethhdr = (struct ethhdr *)(((uint8_t *)skb->data) + hdr_size);
+
+ if (ntohs(ethhdr->h_proto) == ETH_P_8021Q) {
+ if (!pskb_may_pull(skb, hdr_size + sizeof(struct vlan_ethhdr)))
+ return 0;
+
+ vhdr = (struct vlan_ethhdr *)(((uint8_t *)skb->data) +
+ hdr_size);
+ vid = ntohs(vhdr->h_vlan_TCI) & VLAN_VID_MASK;
+ }
+
+ /* see if this originator is a backbone gw for this VLAN */
+
+ backbone_gw = backbone_hash_find(orig_node->bat_priv,
+ orig_node->orig, vid);
+ if (!backbone_gw)
+ return 0;
+
+ backbone_gw_free_ref(backbone_gw);
+ return 1;
+}
+
+/* free all bla structures (for softinterface free or module unload) */
+void bla_free(struct bat_priv *bat_priv)
+{
+ struct hard_iface *primary_if;
+
+ cancel_delayed_work_sync(&bat_priv->bla_work);
+ primary_if = primary_if_get_selected(bat_priv);
+
+ if (bat_priv->claim_hash) {
+ bla_purge_claims(bat_priv, primary_if, 1);
+ hash_destroy(bat_priv->claim_hash);
+ bat_priv->claim_hash = NULL;
+ }
+ if (bat_priv->backbone_hash) {
+ bla_purge_backbone_gw(bat_priv, 1);
+ hash_destroy(bat_priv->backbone_hash);
+ bat_priv->backbone_hash = NULL;
+ }
+ if (primary_if)
+ hardif_free_ref(primary_if);
+}
+
+/**
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: the frame to be checked
+ * @vid: the VLAN ID of the frame
+ *
+ * bla_rx avoidance checks if:
+ * * we have to race for a claim
+ * * if the frame is allowed on the LAN
+ *
+ * in these cases, the skb is further handled by this function and
+ * returns 1, otherwise it returns 0 and the caller shall further
+ * process the skb.
+ *
+ */
+int bla_rx(struct bat_priv *bat_priv, struct sk_buff *skb, short vid)
+{
+ struct ethhdr *ethhdr;
+ struct claim search_claim, *claim = NULL;
+ struct hard_iface *primary_if;
+ int ret;
+
+ ethhdr = (struct ethhdr *)skb_mac_header(skb);
+
+ primary_if = primary_if_get_selected(bat_priv);
+ if (!primary_if)
+ goto handled;
+
+ if (!atomic_read(&bat_priv->bridge_loop_avoidance))
+ goto allow;
+
+
+ if (unlikely(atomic_read(&bat_priv->bla_num_requests)))
+ /* don't allow broadcasts while requests are in flight */
+ if (is_multicast_ether_addr(ethhdr->h_dest))
+ goto handled;
+
+ memcpy(search_claim.addr, ethhdr->h_source, ETH_ALEN);
+ search_claim.vid = vid;
+ claim = claim_hash_find(bat_priv, &search_claim);
+
+ if (!claim) {
+ /* possible optimization: race for a claim */
+ /* No claim exists yet, claim it for us!
+ */
+ handle_claim(bat_priv, primary_if,
+ primary_if->net_dev->dev_addr,
+ ethhdr->h_source, vid);
+ goto allow;
+ }
+
+ /* if it is our own claim ... */
+ if (compare_eth(claim->backbone_gw->orig,
+ primary_if->net_dev->dev_addr)) {
+ /* ... allow it in any case */
+ claim->lasttime = jiffies;
+ goto allow;
+ }
+
+ /* if it is a broadcast ... */
+ if (is_multicast_ether_addr(ethhdr->h_dest)) {
+ /* ... drop it. the responsible gateway is in charge. */
+ goto handled;
+ } else {
+ /* seems the client considers us as its best gateway.
+ * send a claim and update the claim table
+ * immediately.
+ */
+ handle_claim(bat_priv, primary_if,
+ primary_if->net_dev->dev_addr,
+ ethhdr->h_source, vid);
+ goto allow;
+ }
+allow:
+ bla_update_own_backbone_gw(bat_priv, primary_if, vid);
+ ret = 0;
+ goto out;
+
+handled:
+ kfree_skb(skb);
+ ret = 1;
+
+out:
+ if (primary_if)
+ hardif_free_ref(primary_if);
+ if (claim)
+ claim_free_ref(claim);
+ return ret;
+}
+
+/**
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: the frame to be checked
+ * @vid: the VLAN ID of the frame
+ *
+ * bla_tx checks if:
+ * * a claim was received which has to be processed
+ * * the frame is allowed on the mesh
+ *
+ * in these cases, the skb is further handled by this function and
+ * returns 1, otherwise it returns 0 and the caller shall further
+ * process the skb.
+ *
+ */
+int bla_tx(struct bat_priv *bat_priv, struct sk_buff *skb, short vid)
+{
+ struct ethhdr *ethhdr;
+ struct claim search_claim, *claim = NULL;
+ struct hard_iface *primary_if;
+ int ret = 0;
+
+ primary_if = primary_if_get_selected(bat_priv);
+ if (!primary_if)
+ goto out;
+
+ if (!atomic_read(&bat_priv->bridge_loop_avoidance))
+ goto allow;
+
+ /* in VLAN case, the mac header might not be set. */
+ skb_reset_mac_header(skb);
+
+ if (bla_process_claim(bat_priv, primary_if, skb))
+ goto handled;
+
+ ethhdr = (struct ethhdr *)skb_mac_header(skb);
+
+ if (unlikely(atomic_read(&bat_priv->bla_num_requests)))
+ /* don't allow broadcasts while requests are in flight */
+ if (is_multicast_ether_addr(ethhdr->h_dest))
+ goto handled;
+
+ memcpy(search_claim.addr, ethhdr->h_source, ETH_ALEN);
+ search_claim.vid = vid;
+
+ claim = claim_hash_find(bat_priv, &search_claim);
+
+ /* if no claim exists, allow it. */
+ if (!claim)
+ goto allow;
+
+ /* check if we are responsible. */
+ if (compare_eth(claim->backbone_gw->orig,
+ primary_if->net_dev->dev_addr)) {
+ /* if yes, the client has roamed and we have
+ * to unclaim it.
+ */
+ handle_unclaim(bat_priv, primary_if,
+ primary_if->net_dev->dev_addr,
+ ethhdr->h_source, vid);
+ goto allow;
+ }
+
+ /* check if it is a multicast/broadcast frame */
+ if (is_multicast_ether_addr(ethhdr->h_dest)) {
+ /* drop it. the responsible gateway has forwarded it into
+ * the backbone network.
+ */
+ goto handled;
+ } else {
+ /* we must allow it. at least if we are
+ * responsible for the DESTINATION.
+ */
+ goto allow;
+ }
+allow:
+ bla_update_own_backbone_gw(bat_priv, primary_if, vid);
+ ret = 0;
+ goto out;
+handled:
+ ret = 1;
+out:
+ if (primary_if)
+ hardif_free_ref(primary_if);
+ if (claim)
+ claim_free_ref(claim);
+ return ret;
+}
+
+int bla_claim_table_seq_print_text(struct seq_file *seq, void *offset)
+{
+ struct net_device *net_dev = (struct net_device *)seq->private;
+ struct bat_priv *bat_priv = netdev_priv(net_dev);
+ struct hashtable_t *hash = bat_priv->claim_hash;
+ struct claim *claim;
+ struct hard_iface *primary_if;
+ struct hlist_node *node;
+ struct hlist_head *head;
+ uint32_t i;
+ bool is_own;
+ int ret = 0;
+
+ primary_if = primary_if_get_selected(bat_priv);
+ if (!primary_if) {
+ ret = seq_printf(seq,
+ "BATMAN mesh %s disabled - please specify interfaces to enable it\n",
+ net_dev->name);
+ goto out;
+ }
+
+ if (primary_if->if_status != IF_ACTIVE) {
+ ret = seq_printf(seq,
+ "BATMAN mesh %s disabled - primary interface not active\n",
+ net_dev->name);
+ goto out;
+ }
+
+ seq_printf(seq,
+ "Claims announced for the mesh %s (orig %pM, group id %04x)\n",
+ net_dev->name, primary_if->net_dev->dev_addr,
+ ntohs(bat_priv->claim_dest.group));
+ seq_printf(seq, " %-17s %-5s %-17s [o] (%-4s)\n",
+ "Client", "VID", "Originator", "CRC");
+ for (i = 0; i < hash->size; i++) {
+ head = &hash->table[i];
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(claim, node, head, hash_entry) {
+ is_own = compare_eth(claim->backbone_gw->orig,
+ primary_if->net_dev->dev_addr);
+ seq_printf(seq, " * %pM on % 5d by %pM [%c] (%04x)\n",
+ claim->addr, claim->vid,
+ claim->backbone_gw->orig,
+ (is_own ? 'x' : ' '),
+ claim->backbone_gw->crc);
+ }
+ rcu_read_unlock();
+ }
+out:
+ if (primary_if)
+ hardif_free_ref(primary_if);
+ return ret;
+}
diff --git a/net/batman-adv/bridge_loop_avoidance.h b/net/batman-adv/bridge_loop_avoidance.h
new file mode 100644
index 000000000000..4a8e4fc766bc
--- /dev/null
+++ b/net/batman-adv/bridge_loop_avoidance.h
@@ -0,0 +1,98 @@
+/*
+ * 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_
+
+#ifdef CONFIG_BATMAN_ADV_BLA
+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);
+int bla_claim_table_seq_print_text(struct seq_file *seq, void *offset);
+int bla_is_backbone_gw_orig(struct bat_priv *bat_priv, uint8_t *orig);
+int bla_check_bcast_duplist(struct bat_priv *bat_priv,
+ struct bcast_packet *bcast_packet, 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
+#else /* ifdef CONFIG_BATMAN_ADV_BLA */
+
+static inline int bla_rx(struct bat_priv *bat_priv, struct sk_buff *skb,
+ short vid)
+{
+ return 0;
+}
+
+static inline int bla_tx(struct bat_priv *bat_priv, struct sk_buff *skb,
+ short vid)
+{
+ return 0;
+}
+
+static inline int bla_is_backbone_gw(struct sk_buff *skb,
+ struct orig_node *orig_node,
+ int hdr_size)
+{
+ return 0;
+}
+
+static inline int bla_claim_table_seq_print_text(struct seq_file *seq,
+ void *offset)
+{
+ return 0;
+}
+
+static inline int bla_is_backbone_gw_orig(struct bat_priv *bat_priv,
+ uint8_t *orig)
+{
+ return 0;
+}
+
+static inline int bla_check_bcast_duplist(struct bat_priv *bat_priv,
+ struct bcast_packet *bcast_packet,
+ int hdr_size)
+{
+ return 0;
+}
+
+static inline void bla_update_orig_address(struct bat_priv *bat_priv,
+ struct hard_iface *primary_if,
+ struct hard_iface *oldif)
+{
+}
+
+static inline int bla_init(struct bat_priv *bat_priv)
+{
+ return 1;
+}
+
+static inline void bla_free(struct bat_priv *bat_priv)
+{
+}
+
+#endif /* ifdef CONFIG_BATMAN_ADV_BLA */
+
+#endif /* ifndef _NET_BATMAN_ADV_BLA_H_ */
diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c
index 377897701a85..47c79d724ba3 100644
--- a/net/batman-adv/hard-interface.c
+++ b/net/batman-adv/hard-interface.c
@@ -28,6 +28,7 @@
#include "bat_sysfs.h"
#include "originator.h"
#include "hash.h"
+#include "bridge_loop_avoidance.h"
#include <linux/if_arp.h>
@@ -107,7 +108,8 @@ out:
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 hard_iface *primary_if;
@@ -122,6 +124,7 @@ static void primary_if_update_addr(struct bat_priv *bat_priv)
memcpy(vis_packet->sender_orig,
primary_if->net_dev->dev_addr, ETH_ALEN);
+ bla_update_orig_address(bat_priv, primary_if, oldif);
out:
if (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);
rcu_assign_pointer(bat_priv->primary_if, new_hard_iface);
- if (curr_hard_iface)
- hardif_free_ref(curr_hard_iface);
-
if (!new_hard_iface)
- return;
+ goto out;
- bat_priv->bat_algo_ops->bat_ogm_init_primary(new_hard_iface);
- primary_if_update_addr(bat_priv);
+ bat_priv->bat_algo_ops->bat_primary_iface_set(new_hard_iface);
+ 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)
@@ -300,22 +304,17 @@ int hardif_enable_interface(struct hard_iface *hard_iface,
if (!softif_is_valid(soft_iface)) {
pr_err("Can't create batman mesh interface %s: already exists as regular interface\n",
soft_iface->name);
- dev_put(soft_iface);
ret = -EINVAL;
- goto err;
+ goto err_dev;
}
hard_iface->soft_iface = soft_iface;
bat_priv = netdev_priv(hard_iface->soft_iface);
- bat_priv->bat_algo_ops->bat_ogm_init(hard_iface);
-
- if (!hard_iface->packet_buff) {
- bat_err(hard_iface->soft_iface,
- "Can't add interface packet (%s): out of memory\n",
- hard_iface->net_dev->name);
+ ret = bat_priv->bat_algo_ops->bat_iface_enable(hard_iface);
+ if (ret < 0) {
ret = -ENOMEM;
- goto err;
+ goto err_dev;
}
hard_iface->if_num = bat_priv->num_ifaces;
@@ -328,7 +327,6 @@ int hardif_enable_interface(struct hard_iface *hard_iface,
hard_iface->batman_adv_ptype.dev = hard_iface->net_dev;
dev_add_pack(&hard_iface->batman_adv_ptype);
- atomic_set(&hard_iface->seqno, 1);
atomic_set(&hard_iface->frag_seqno, 1);
bat_info(hard_iface->soft_iface, "Adding interface: %s\n",
hard_iface->net_dev->name);
@@ -360,6 +358,8 @@ int hardif_enable_interface(struct hard_iface *hard_iface,
out:
return 0;
+err_dev:
+ dev_put(soft_iface);
err:
hardif_free_ref(hard_iface);
return ret;
@@ -394,8 +394,7 @@ void hardif_disable_interface(struct hard_iface *hard_iface)
hardif_free_ref(new_if);
}
- kfree(hard_iface->packet_buff);
- hard_iface->packet_buff = NULL;
+ bat_priv->bat_algo_ops->bat_iface_disable(hard_iface);
hard_iface->if_status = IF_NOT_IN_USE;
/* delete all references to this hard_iface */
@@ -447,6 +446,13 @@ static struct hard_iface *hardif_add_interface(struct net_device *net_dev)
check_known_mac_addr(hard_iface->net_dev);
list_add_tail_rcu(&hard_iface->list, &hardif_list);
+ /**
+ * This can't be called via a bat_priv callback because
+ * we have no bat_priv yet.
+ */
+ atomic_set(&hard_iface->seqno, 1);
+ hard_iface->packet_buff = NULL;
+
return hard_iface;
free_if:
@@ -531,7 +537,7 @@ static int hard_if_event(struct notifier_block *this,
goto hardif_put;
if (hard_iface == primary_if)
- primary_if_update_addr(bat_priv);
+ primary_if_update_addr(bat_priv, NULL);
break;
default:
break;
@@ -568,8 +574,7 @@ static int batman_skb_recv(struct sk_buff *skb, struct net_device *dev,
goto err_free;
/* expect a valid ethernet header here. */
- if (unlikely(skb->mac_len != sizeof(struct ethhdr) ||
- !skb_mac_header(skb)))
+ if (unlikely(skb->mac_len != ETH_HLEN || !skb_mac_header(skb)))
goto err_free;
if (!hard_iface->soft_iface)
@@ -598,7 +603,7 @@ static int batman_skb_recv(struct sk_buff *skb, struct net_device *dev,
switch (batman_ogm_packet->header.packet_type) {
/* batman originator packet */
- case BAT_OGM:
+ case BAT_IV_OGM:
ret = recv_bat_ogm_packet(skb, hard_iface);
break;
diff --git a/net/batman-adv/icmp_socket.c b/net/batman-adv/icmp_socket.c
index b87518edcef9..2e98a57f3407 100644
--- a/net/batman-adv/icmp_socket.c
+++ b/net/batman-adv/icmp_socket.c
@@ -175,13 +175,13 @@ static ssize_t bat_socket_write(struct file *file, const char __user *buff,
if (len >= sizeof(struct icmp_packet_rr))
packet_len = sizeof(struct icmp_packet_rr);
- skb = dev_alloc_skb(packet_len + sizeof(struct ethhdr));
+ skb = dev_alloc_skb(packet_len + ETH_HLEN);
if (!skb) {
len = -ENOMEM;
goto out;
}
- skb_reserve(skb, sizeof(struct ethhdr));
+ skb_reserve(skb, ETH_HLEN);
icmp_packet = (struct icmp_packet_rr *)skb_put(skb, packet_len);
if (copy_from_user(icmp_packet, buff, packet_len)) {
diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c
index 6d51caaf8cec..791327219531 100644
--- a/net/batman-adv/main.c
+++ b/net/batman-adv/main.c
@@ -30,6 +30,7 @@
#include "translation-table.h"
#include "hard-interface.h"
#include "gateway_client.h"
+#include "bridge_loop_avoidance.h"
#include "vis.h"
#include "hash.h"
#include "bat_algo.h"
@@ -96,13 +97,10 @@ int mesh_init(struct net_device *soft_iface)
spin_lock_init(&bat_priv->gw_list_lock);
spin_lock_init(&bat_priv->vis_hash_lock);
spin_lock_init(&bat_priv->vis_list_lock);
- spin_lock_init(&bat_priv->softif_neigh_lock);
- spin_lock_init(&bat_priv->softif_neigh_vid_lock);
INIT_HLIST_HEAD(&bat_priv->forw_bat_list);
INIT_HLIST_HEAD(&bat_priv->forw_bcast_list);
INIT_HLIST_HEAD(&bat_priv->gw_list);
- INIT_HLIST_HEAD(&bat_priv->softif_neigh_vids);
INIT_LIST_HEAD(&bat_priv->tt_changes_list);
INIT_LIST_HEAD(&bat_priv->tt_req_list);
INIT_LIST_HEAD(&bat_priv->tt_roam_list);
@@ -118,6 +116,9 @@ int mesh_init(struct net_device *soft_iface)
if (vis_init(bat_priv) < 1)
goto err;
+ if (bla_init(bat_priv) < 1)
+ goto err;
+
atomic_set(&bat_priv->gw_reselect, 0);
atomic_set(&bat_priv->mesh_state, MESH_ACTIVE);
goto end;
@@ -145,7 +146,7 @@ void mesh_free(struct net_device *soft_iface)
tt_free(bat_priv);
- softif_neigh_purge(bat_priv);
+ bla_free(bat_priv);
atomic_set(&bat_priv->mesh_state, MESH_INACTIVE);
}
@@ -207,8 +208,9 @@ int bat_algo_register(struct bat_algo_ops *bat_algo_ops)
}
/* all algorithms must implement all ops (for now) */
- if (!bat_algo_ops->bat_ogm_init ||
- !bat_algo_ops->bat_ogm_init_primary ||
+ if (!bat_algo_ops->bat_iface_enable ||
+ !bat_algo_ops->bat_iface_disable ||
+ !bat_algo_ops->bat_primary_iface_set ||
!bat_algo_ops->bat_ogm_update_mac ||
!bat_algo_ops->bat_ogm_schedule ||
!bat_algo_ops->bat_ogm_emit ||
diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h
index 94fa1c2393a6..d9832acf558d 100644
--- a/net/batman-adv/main.h
+++ b/net/batman-adv/main.h
@@ -65,7 +65,7 @@
#define NULL_IFINDEX 0 /* dummy ifindex used to avoid iface checks */
-#define NUM_WORDS (TQ_LOCAL_WINDOW_SIZE / WORD_BIT_SIZE)
+#define NUM_WORDS BITS_TO_LONGS(TQ_LOCAL_WINDOW_SIZE)
#define LOG_BUF_LEN 8192 /* has to be a power of 2 */
@@ -80,8 +80,12 @@
#define MAX_AGGREGATION_BYTES 512
#define MAX_AGGREGATION_MS 100
-#define SOFTIF_NEIGH_TIMEOUT 180000 /* 3 minutes */
+#define BLA_PERIOD_LENGTH 10000 /* 10 seconds */
+#define BLA_BACKBONE_TIMEOUT (BLA_PERIOD_LENGTH * 3)
+#define BLA_CLAIM_TIMEOUT (BLA_PERIOD_LENGTH * 10)
+#define DUPLIST_SIZE 16
+#define DUPLIST_TIMEOUT 500 /* 500 ms */
/* don't reset again within 30 seconds */
#define RESET_PROTECTION_MS 30000
#define EXPECTED_SEQNO_RANGE 65536
@@ -119,7 +123,8 @@ enum dbg_level {
DBG_BATMAN = 1 << 0,
DBG_ROUTES = 1 << 1, /* route added / changed / deleted */
DBG_TT = 1 << 2, /* translation table operations */
- DBG_ALL = 7
+ DBG_BLA = 1 << 3, /* bridge loop avoidance */
+ DBG_ALL = 15
};
/* Kernel headers */
diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c
index 43c0a4f1399e..ce4969885894 100644
--- a/net/batman-adv/originator.c
+++ b/net/batman-adv/originator.c
@@ -28,6 +28,7 @@
#include "hard-interface.h"
#include "unicast.h"
#include "soft-interface.h"
+#include "bridge_loop_avoidance.h"
static void purge_orig(struct work_struct *work);
@@ -375,8 +376,6 @@ static void _purge_orig(struct bat_priv *bat_priv)
gw_node_purge(bat_priv);
gw_election(bat_priv);
-
- softif_neigh_purge(bat_priv);
}
static void purge_orig(struct work_struct *work)
diff --git a/net/batman-adv/packet.h b/net/batman-adv/packet.h
index 441f3db1bd91..f54969c61a1e 100644
--- a/net/batman-adv/packet.h
+++ b/net/batman-adv/packet.h
@@ -25,7 +25,7 @@
#define ETH_P_BATMAN 0x4305 /* unofficial/not registered Ethertype */
enum bat_packettype {
- BAT_OGM = 0x01,
+ BAT_IV_OGM = 0x01,
BAT_ICMP = 0x02,
BAT_UNICAST = 0x03,
BAT_BCAST = 0x04,
@@ -38,7 +38,7 @@ enum bat_packettype {
/* this file is included by batctl which needs these defines */
#define COMPAT_VERSION 14
-enum batman_flags {
+enum batman_iv_flags {
PRIMARIES_FIRST_HOP = 1 << 4,
VIS_SERVER = 1 << 5,
DIRECTLINK = 1 << 6
@@ -90,6 +90,23 @@ enum tt_client_flags {
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 {
uint8_t packet_type;
uint8_t version; /* batman version field */
@@ -100,8 +117,8 @@ struct batman_ogm_packet {
struct batman_header header;
uint8_t flags; /* 0x40: DIRECTLINK flag, 0x20 VIS_SERVER flag... */
uint32_t seqno;
- uint8_t orig[6];
- uint8_t prev_sender[6];
+ uint8_t orig[ETH_ALEN];
+ uint8_t prev_sender[ETH_ALEN];
uint8_t gw_flags; /* flags related to gateway class */
uint8_t tq;
uint8_t tt_num_changes;
@@ -109,13 +126,13 @@ struct batman_ogm_packet {
uint16_t tt_crc;
} __packed;
-#define BATMAN_OGM_LEN sizeof(struct batman_ogm_packet)
+#define BATMAN_OGM_HLEN sizeof(struct batman_ogm_packet)
struct icmp_packet {
struct batman_header header;
uint8_t msg_type; /* see ICMP message types above */
- uint8_t dst[6];
- uint8_t orig[6];
+ uint8_t dst[ETH_ALEN];
+ uint8_t orig[ETH_ALEN];
uint16_t seqno;
uint8_t uid;
uint8_t reserved;
@@ -128,8 +145,8 @@ struct icmp_packet {
struct icmp_packet_rr {
struct batman_header header;
uint8_t msg_type; /* see ICMP message types above */
- uint8_t dst[6];
- uint8_t orig[6];
+ uint8_t dst[ETH_ALEN];
+ uint8_t orig[ETH_ALEN];
uint16_t seqno;
uint8_t uid;
uint8_t rr_cur;
@@ -139,16 +156,16 @@ struct icmp_packet_rr {
struct unicast_packet {
struct batman_header header;
uint8_t ttvn; /* destination translation table version number */
- uint8_t dest[6];
+ uint8_t dest[ETH_ALEN];
} __packed;
struct unicast_frag_packet {
struct batman_header header;
uint8_t ttvn; /* destination translation table version number */
- uint8_t dest[6];
+ uint8_t dest[ETH_ALEN];
uint8_t flags;
uint8_t align;
- uint8_t orig[6];
+ uint8_t orig[ETH_ALEN];
uint16_t seqno;
} __packed;
@@ -156,7 +173,7 @@ struct bcast_packet {
struct batman_header header;
uint8_t reserved;
uint32_t seqno;
- uint8_t orig[6];
+ uint8_t orig[ETH_ALEN];
} __packed;
struct vis_packet {
@@ -165,9 +182,9 @@ struct vis_packet {
uint32_t seqno; /* sequence number */
uint8_t entries; /* number of entries behind this struct */
uint8_t reserved;
- uint8_t vis_orig[6]; /* originator that announces its neighbors */
- uint8_t target_orig[6]; /* who should receive this packet */
- uint8_t sender_orig[6]; /* who sent or rebroadcasted this packet */
+ uint8_t vis_orig[ETH_ALEN]; /* originator reporting its neighbors */
+ uint8_t target_orig[ETH_ALEN]; /* who should receive this packet */
+ uint8_t sender_orig[ETH_ALEN]; /* who sent or forwarded this packet */
} __packed;
struct tt_query_packet {
diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
index 7f8e15899417..ff560863bc74 100644
--- a/net/batman-adv/routing.c
+++ b/net/batman-adv/routing.c
@@ -29,6 +29,10 @@
#include "originator.h"
#include "vis.h"
#include "unicast.h"
+#include "bridge_loop_avoidance.h"
+
+static int route_unicast_packet(struct sk_buff *skb,
+ struct hard_iface *recv_if);
void slide_own_bcast_window(struct hard_iface *hard_iface)
{
@@ -52,7 +56,7 @@ void slide_own_bcast_window(struct hard_iface *hard_iface)
bit_get_packet(bat_priv, word, 1, 0);
orig_node->bcast_own_sum[hard_iface->if_num] =
- bit_packet_count(word);
+ bitmap_weight(word, TQ_LOCAL_WINDOW_SIZE);
spin_unlock_bh(&orig_node->ogm_cnt_lock);
}
rcu_read_unlock();
@@ -250,7 +254,7 @@ int recv_bat_ogm_packet(struct sk_buff *skb, struct hard_iface *hard_iface)
struct ethhdr *ethhdr;
/* drop packet if it has not necessary minimum size */
- if (unlikely(!pskb_may_pull(skb, BATMAN_OGM_LEN)))
+ if (unlikely(!pskb_may_pull(skb, BATMAN_OGM_HLEN)))
return NET_RX_DROP;
ethhdr = (struct ethhdr *)skb_mac_header(skb);
@@ -309,7 +313,7 @@ static int recv_my_icmp_packet(struct bat_priv *bat_priv,
goto out;
/* create a copy of the skb, if needed, to modify it. */
- if (skb_cow(skb, sizeof(struct ethhdr)) < 0)
+ if (skb_cow(skb, ETH_HLEN) < 0)
goto out;
icmp_packet = (struct icmp_packet_rr *)skb->data;
@@ -364,7 +368,7 @@ static int recv_icmp_ttl_exceeded(struct bat_priv *bat_priv,
goto out;
/* create a copy of the skb, if needed, to modify it. */
- if (skb_cow(skb, sizeof(struct ethhdr)) < 0)
+ if (skb_cow(skb, ETH_HLEN) < 0)
goto out;
icmp_packet = (struct icmp_packet *)skb->data;
@@ -450,7 +454,7 @@ int recv_icmp_packet(struct sk_buff *skb, struct hard_iface *recv_if)
goto out;
/* create a copy of the skb, if needed, to modify it. */
- if (skb_cow(skb, sizeof(struct ethhdr)) < 0)
+ if (skb_cow(skb, ETH_HLEN) < 0)
goto out;
icmp_packet = (struct icmp_packet_rr *)skb->data;
@@ -669,6 +673,13 @@ int recv_roam_adv(struct sk_buff *skb, struct hard_iface *recv_if)
if (!is_my_mac(roam_adv_packet->dst))
return route_unicast_packet(skb, recv_if);
+ /* check if it is a backbone gateway. we don't accept
+ * roaming advertisement from it, as it has the same
+ * entries as we have.
+ */
+ if (bla_is_backbone_gw_orig(bat_priv, roam_adv_packet->src))
+ goto out;
+
orig_node = orig_hash_find(bat_priv, roam_adv_packet->src);
if (!orig_node)
goto out;
@@ -798,7 +809,7 @@ static int check_unicast_packet(struct sk_buff *skb, int hdr_size)
return 0;
}
-int route_unicast_packet(struct sk_buff *skb, struct hard_iface *recv_if)
+static int route_unicast_packet(struct sk_buff *skb, struct hard_iface *recv_if)
{
struct bat_priv *bat_priv = netdev_priv(recv_if->soft_iface);
struct orig_node *orig_node = NULL;
@@ -830,7 +841,7 @@ int route_unicast_packet(struct sk_buff *skb, struct hard_iface *recv_if)
goto out;
/* create a copy of the skb, if needed, to modify it. */
- if (skb_cow(skb, sizeof(struct ethhdr)) < 0)
+ if (skb_cow(skb, ETH_HLEN) < 0)
goto out;
unicast_packet = (struct unicast_packet *)skb->data;
@@ -1047,8 +1058,8 @@ int recv_bcast_packet(struct sk_buff *skb, struct hard_iface *recv_if)
spin_lock_bh(&orig_node->bcast_seqno_lock);
/* check whether the packet is a duplicate */
- if (get_bit_status(orig_node->bcast_bits, orig_node->last_bcast_seqno,
- ntohl(bcast_packet->seqno)))
+ if (bat_test_bit(orig_node->bcast_bits, orig_node->last_bcast_seqno,
+ ntohl(bcast_packet->seqno)))
goto spin_unlock;
seq_diff = ntohl(bcast_packet->seqno) - orig_node->last_bcast_seqno;
@@ -1065,9 +1076,19 @@ int recv_bcast_packet(struct sk_buff *skb, struct hard_iface *recv_if)
spin_unlock_bh(&orig_node->bcast_seqno_lock);
+ /* check whether this has been sent by another originator before */
+ if (bla_check_bcast_duplist(bat_priv, bcast_packet, hdr_size))
+ goto out;
+
/* rebroadcast packet */
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 */
interface_rx(recv_if->soft_iface, skb, recv_if, hdr_size);
ret = NET_RX_SUCCESS;
diff --git a/net/batman-adv/routing.h b/net/batman-adv/routing.h
index 92ac100d83da..3d729cb17113 100644
--- a/net/batman-adv/routing.h
+++ b/net/batman-adv/routing.h
@@ -25,7 +25,6 @@
void slide_own_bcast_window(struct hard_iface *hard_iface);
void update_route(struct bat_priv *bat_priv, struct orig_node *orig_node,
struct neigh_node *neigh_node);
-int route_unicast_packet(struct sk_buff *skb, struct hard_iface *recv_if);
int recv_icmp_packet(struct sk_buff *skb, struct hard_iface *recv_if);
int recv_unicast_packet(struct sk_buff *skb, struct hard_iface *recv_if);
int recv_ucast_frag_packet(struct sk_buff *skb, struct hard_iface *recv_if);
diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c
index af7a6741a685..7c66b6121fa6 100644
--- a/net/batman-adv/send.c
+++ b/net/batman-adv/send.c
@@ -51,7 +51,7 @@ int send_skb_packet(struct sk_buff *skb, struct hard_iface *hard_iface,
}
/* push to the ethernet header. */
- if (my_skb_head_push(skb, sizeof(*ethhdr)) < 0)
+ if (my_skb_head_push(skb, ETH_HLEN) < 0)
goto send_skb_err;
skb_reset_mac_header(skb);
@@ -87,7 +87,7 @@ static void realloc_packet_buffer(struct hard_iface *hard_iface,
/* keep old buffer if kmalloc should fail */
if (new_buff) {
memcpy(new_buff, hard_iface->packet_buff,
- BATMAN_OGM_LEN);
+ BATMAN_OGM_HLEN);
kfree(hard_iface->packet_buff);
hard_iface->packet_buff = new_buff;
@@ -101,13 +101,13 @@ static int prepare_packet_buffer(struct bat_priv *bat_priv,
{
int new_len;
- new_len = BATMAN_OGM_LEN +
+ new_len = BATMAN_OGM_HLEN +
tt_len((uint8_t)atomic_read(&bat_priv->tt_local_changes));
/* if we have too many changes for one packet don't send any
* and wait for the tt table request which will be fragmented */
if (new_len > hard_iface->soft_iface->mtu)
- new_len = BATMAN_OGM_LEN;
+ new_len = BATMAN_OGM_HLEN;
realloc_packet_buffer(hard_iface, new_len);
@@ -117,14 +117,14 @@ static int prepare_packet_buffer(struct bat_priv *bat_priv,
atomic_set(&bat_priv->tt_ogm_append_cnt, TT_OGM_APPEND_MAX);
return tt_changes_fill_buffer(bat_priv,
- hard_iface->packet_buff + BATMAN_OGM_LEN,
- hard_iface->packet_len - BATMAN_OGM_LEN);
+ hard_iface->packet_buff + BATMAN_OGM_HLEN,
+ hard_iface->packet_len - BATMAN_OGM_HLEN);
}
static int reset_packet_buffer(struct bat_priv *bat_priv,
struct hard_iface *hard_iface)
{
- realloc_packet_buffer(hard_iface, BATMAN_OGM_LEN);
+ realloc_packet_buffer(hard_iface, BATMAN_OGM_HLEN);
return 0;
}
diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c
index a5590f4193f1..6e2530b02043 100644
--- a/net/batman-adv/soft-interface.c
+++ b/net/batman-adv/soft-interface.c
@@ -36,6 +36,7 @@
#include <linux/etherdevice.h>
#include <linux/if_vlan.h>
#include "unicast.h"
+#include "bridge_loop_avoidance.h"
static int bat_get_settings(struct net_device *dev, struct ethtool_cmd *cmd);
@@ -73,439 +74,6 @@ int my_skb_head_push(struct sk_buff *skb, unsigned int len)
return 0;
}
-static void softif_neigh_free_ref(struct softif_neigh *softif_neigh)
-{
- if (atomic_dec_and_test(&softif_neigh->refcount))
- kfree_rcu(softif_neigh, rcu);
-}
-
-static void softif_neigh_vid_free_rcu(struct rcu_head *rcu)
-{
- struct softif_neigh_vid *softif_neigh_vid;
- struct softif_neigh *softif_neigh;
- struct hlist_node *node, *node_tmp;
- struct bat_priv *bat_priv;
-
- softif_neigh_vid = container_of(rcu, struct softif_neigh_vid, rcu);
- bat_priv = softif_neigh_vid->bat_priv;
-
- spin_lock_bh(&bat_priv->softif_neigh_lock);
- hlist_for_each_entry_safe(softif_neigh, node, node_tmp,
- &softif_neigh_vid->softif_neigh_list, list) {
- hlist_del_rcu(&softif_neigh->list);
- softif_neigh_free_ref(softif_neigh);
- }
- spin_unlock_bh(&bat_priv->softif_neigh_lock);
-
- kfree(softif_neigh_vid);
-}
-
-static void softif_neigh_vid_free_ref(struct softif_neigh_vid *softif_neigh_vid)
-{
- if (atomic_dec_and_test(&softif_neigh_vid->refcount))
- call_rcu(&softif_neigh_vid->rcu, softif_neigh_vid_free_rcu);
-}
-
-static struct softif_neigh_vid *softif_neigh_vid_get(struct bat_priv *bat_priv,
- short vid)
-{
- struct softif_neigh_vid *softif_neigh_vid;
- struct hlist_node *node;
-
- rcu_read_lock();
- hlist_for_each_entry_rcu(softif_neigh_vid, node,
- &bat_priv->softif_neigh_vids, list) {
- if (softif_neigh_vid->vid != vid)
- continue;
-
- if (!atomic_inc_not_zero(&softif_neigh_vid->refcount))
- continue;
-
- goto out;
- }
-
- softif_neigh_vid = kzalloc(sizeof(*softif_neigh_vid), GFP_ATOMIC);
- if (!softif_neigh_vid)
- goto out;
-
- softif_neigh_vid->vid = vid;
- softif_neigh_vid->bat_priv = bat_priv;
-
- /* initialize with 2 - caller decrements counter by one */
- atomic_set(&softif_neigh_vid->refcount, 2);
- INIT_HLIST_HEAD(&softif_neigh_vid->softif_neigh_list);
- INIT_HLIST_NODE(&softif_neigh_vid->list);
- spin_lock_bh(&bat_priv->softif_neigh_vid_lock);
- hlist_add_head_rcu(&softif_neigh_vid->list,
- &bat_priv->softif_neigh_vids);
- spin_unlock_bh(&bat_priv->softif_neigh_vid_lock);
-
-out:
- rcu_read_unlock();
- return softif_neigh_vid;
-}
-
-static struct softif_neigh *softif_neigh_get(struct bat_priv *bat_priv,
- const uint8_t *addr, short vid)
-{
- struct softif_neigh_vid *softif_neigh_vid;
- struct softif_neigh *softif_neigh = NULL;
- struct hlist_node *node;
-
- softif_neigh_vid = softif_neigh_vid_get(bat_priv, vid);
- if (!softif_neigh_vid)
- goto out;
-
- rcu_read_lock();
- hlist_for_each_entry_rcu(softif_neigh, node,
- &softif_neigh_vid->softif_neigh_list,
- list) {
- if (!compare_eth(softif_neigh->addr, addr))
- continue;
-
- if (!atomic_inc_not_zero(&softif_neigh->refcount))
- continue;
-
- softif_neigh->last_seen = jiffies;
- goto unlock;
- }
-
- softif_neigh = kzalloc(sizeof(*softif_neigh), GFP_ATOMIC);
- if (!softif_neigh)
- goto unlock;
-
- memcpy(softif_neigh->addr, addr, ETH_ALEN);
- softif_neigh->last_seen = jiffies;
- /* initialize with 2 - caller decrements counter by one */
- atomic_set(&softif_neigh->refcount, 2);
-
- INIT_HLIST_NODE(&softif_neigh->list);
- spin_lock_bh(&bat_priv->softif_neigh_lock);
- hlist_add_head_rcu(&softif_neigh->list,
- &softif_neigh_vid->softif_neigh_list);
- spin_unlock_bh(&bat_priv->softif_neigh_lock);
-
-unlock:
- rcu_read_unlock();
-out:
- if (softif_neigh_vid)
- softif_neigh_vid_free_ref(softif_neigh_vid);
- return softif_neigh;
-}
-
-static struct softif_neigh *softif_neigh_get_selected(
- struct softif_neigh_vid *softif_neigh_vid)
-{
- struct softif_neigh *softif_neigh;
-
- rcu_read_lock();
- softif_neigh = rcu_dereference(softif_neigh_vid->softif_neigh);
-
- if (softif_neigh && !atomic_inc_not_zero(&softif_neigh->refcount))
- softif_neigh = NULL;
-
- rcu_read_unlock();
- return softif_neigh;
-}
-
-static struct softif_neigh *softif_neigh_vid_get_selected(
- struct bat_priv *bat_priv,
- short vid)
-{
- struct softif_neigh_vid *softif_neigh_vid;
- struct softif_neigh *softif_neigh = NULL;
-
- softif_neigh_vid = softif_neigh_vid_get(bat_priv, vid);
- if (!softif_neigh_vid)
- goto out;
-
- softif_neigh = softif_neigh_get_selected(softif_neigh_vid);
-out:
- if (softif_neigh_vid)
- softif_neigh_vid_free_ref(softif_neigh_vid);
- return softif_neigh;
-}
-
-static void softif_neigh_vid_select(struct bat_priv *bat_priv,
- struct softif_neigh *new_neigh,
- short vid)
-{
- struct softif_neigh_vid *softif_neigh_vid;
- struct softif_neigh *curr_neigh;
-
- softif_neigh_vid = softif_neigh_vid_get(bat_priv, vid);
- if (!softif_neigh_vid)
- goto out;
-
- spin_lock_bh(&bat_priv->softif_neigh_lock);
-
- if (new_neigh && !atomic_inc_not_zero(&new_neigh->refcount))
- new_neigh = NULL;
-
- curr_neigh = rcu_dereference_protected(softif_neigh_vid->softif_neigh,
- 1);
- rcu_assign_pointer(softif_neigh_vid->softif_neigh, new_neigh);
-
- if ((curr_neigh) && (!new_neigh))
- bat_dbg(DBG_ROUTES, bat_priv,
- "Removing mesh exit point on vid: %d (prev: %pM).\n",
- vid, curr_neigh->addr);
- else if ((curr_neigh) && (new_neigh))
- bat_dbg(DBG_ROUTES, bat_priv,
- "Changing mesh exit point on vid: %d from %pM to %pM.\n",
- vid, curr_neigh->addr, new_neigh->addr);
- else if ((!curr_neigh) && (new_neigh))
- bat_dbg(DBG_ROUTES, bat_priv,
- "Setting mesh exit point on vid: %d to %pM.\n",
- vid, new_neigh->addr);
-
- if (curr_neigh)
- softif_neigh_free_ref(curr_neigh);
-
- spin_unlock_bh(&bat_priv->softif_neigh_lock);
-
-out:
- if (softif_neigh_vid)
- softif_neigh_vid_free_ref(softif_neigh_vid);
-}
-
-static void softif_neigh_vid_deselect(struct bat_priv *bat_priv,
- struct softif_neigh_vid *softif_neigh_vid)
-{
- struct softif_neigh *curr_neigh;
- struct softif_neigh *softif_neigh = NULL, *softif_neigh_tmp;
- struct hard_iface *primary_if = NULL;
- struct hlist_node *node;
-
- primary_if = primary_if_get_selected(bat_priv);
- if (!primary_if)
- goto out;
-
- /* find new softif_neigh immediately to avoid temporary loops */
- rcu_read_lock();
- curr_neigh = rcu_dereference(softif_neigh_vid->softif_neigh);
-
- hlist_for_each_entry_rcu(softif_neigh_tmp, node,
- &softif_neigh_vid->softif_neigh_list,
- list) {
- if (softif_neigh_tmp == curr_neigh)
- continue;
-
- /* we got a neighbor but its mac is 'bigger' than ours */
- if (memcmp(primary_if->net_dev->dev_addr,
- softif_neigh_tmp->addr, ETH_ALEN) < 0)
- continue;
-
- if (!atomic_inc_not_zero(&softif_neigh_tmp->refcount))
- continue;
-
- softif_neigh = softif_neigh_tmp;
- goto unlock;
- }
-
-unlock:
- rcu_read_unlock();
-out:
- softif_neigh_vid_select(bat_priv, softif_neigh, softif_neigh_vid->vid);
-
- if (primary_if)
- hardif_free_ref(primary_if);
- if (softif_neigh)
- softif_neigh_free_ref(softif_neigh);
-}
-
-int softif_neigh_seq_print_text(struct seq_file *seq, void *offset)
-{
- struct net_device *net_dev = (struct net_device *)seq->private;
- struct bat_priv *bat_priv = netdev_priv(net_dev);
- struct softif_neigh_vid *softif_neigh_vid;
- struct softif_neigh *softif_neigh;
- struct hard_iface *primary_if;
- struct hlist_node *node, *node_tmp;
- struct softif_neigh *curr_softif_neigh;
- int ret = 0, last_seen_secs, last_seen_msecs;
-
- primary_if = primary_if_get_selected(bat_priv);
- if (!primary_if) {
- ret = seq_printf(seq,
- "BATMAN mesh %s disabled - please specify interfaces to enable it\n",
- net_dev->name);
- goto out;
- }
-
- if (primary_if->if_status != IF_ACTIVE) {
- ret = seq_printf(seq,
- "BATMAN mesh %s disabled - primary interface not active\n",
- net_dev->name);
- goto out;
- }
-
- seq_printf(seq, "Softif neighbor list (%s)\n", net_dev->name);
-
- rcu_read_lock();
- hlist_for_each_entry_rcu(softif_neigh_vid, node,
- &bat_priv->softif_neigh_vids, list) {
- seq_printf(seq, " %-15s %s on vid: %d\n",
- "Originator", "last-seen", softif_neigh_vid->vid);
-
- curr_softif_neigh = softif_neigh_get_selected(softif_neigh_vid);
-
- hlist_for_each_entry_rcu(softif_neigh, node_tmp,
- &softif_neigh_vid->softif_neigh_list,
- list) {
- last_seen_secs = jiffies_to_msecs(jiffies -
- softif_neigh->last_seen) / 1000;
- last_seen_msecs = jiffies_to_msecs(jiffies -
- softif_neigh->last_seen) % 1000;
- seq_printf(seq, "%s %pM %3i.%03is\n",
- curr_softif_neigh == softif_neigh
- ? "=>" : " ", softif_neigh->addr,
- last_seen_secs, last_seen_msecs);
- }
-
- if (curr_softif_neigh)
- softif_neigh_free_ref(curr_softif_neigh);
-
- seq_printf(seq, "\n");
- }
- rcu_read_unlock();
-
-out:
- if (primary_if)
- hardif_free_ref(primary_if);
- return ret;
-}
-
-void softif_neigh_purge(struct bat_priv *bat_priv)
-{
- struct softif_neigh *softif_neigh, *curr_softif_neigh;
- struct softif_neigh_vid *softif_neigh_vid;
- struct hlist_node *node, *node_tmp, *node_tmp2;
- int do_deselect;
-
- rcu_read_lock();
- hlist_for_each_entry_rcu(softif_neigh_vid, node,
- &bat_priv->softif_neigh_vids, list) {
- if (!atomic_inc_not_zero(&softif_neigh_vid->refcount))
- continue;
-
- curr_softif_neigh = softif_neigh_get_selected(softif_neigh_vid);
- do_deselect = 0;
-
- spin_lock_bh(&bat_priv->softif_neigh_lock);
- hlist_for_each_entry_safe(softif_neigh, node_tmp, node_tmp2,
- &softif_neigh_vid->softif_neigh_list,
- list) {
- if ((!has_timed_out(softif_neigh->last_seen,
- SOFTIF_NEIGH_TIMEOUT)) &&
- (atomic_read(&bat_priv->mesh_state) == MESH_ACTIVE))
- continue;
-
- if (curr_softif_neigh == softif_neigh) {
- bat_dbg(DBG_ROUTES, bat_priv,
- "Current mesh exit point on vid: %d '%pM' vanished.\n",
- softif_neigh_vid->vid,
- softif_neigh->addr);
- do_deselect = 1;
- }
-
- hlist_del_rcu(&softif_neigh->list);
- softif_neigh_free_ref(softif_neigh);
- }
- spin_unlock_bh(&bat_priv->softif_neigh_lock);
-
- /* soft_neigh_vid_deselect() needs to acquire the
- * softif_neigh_lock */
- if (do_deselect)
- softif_neigh_vid_deselect(bat_priv, softif_neigh_vid);
-
- if (curr_softif_neigh)
- softif_neigh_free_ref(curr_softif_neigh);
-
- softif_neigh_vid_free_ref(softif_neigh_vid);
- }
- rcu_read_unlock();
-
- spin_lock_bh(&bat_priv->softif_neigh_vid_lock);
- hlist_for_each_entry_safe(softif_neigh_vid, node, node_tmp,
- &bat_priv->softif_neigh_vids, list) {
- if (!hlist_empty(&softif_neigh_vid->softif_neigh_list))
- continue;
-
- hlist_del_rcu(&softif_neigh_vid->list);
- softif_neigh_vid_free_ref(softif_neigh_vid);
- }
- spin_unlock_bh(&bat_priv->softif_neigh_vid_lock);
-
-}
-
-static void softif_batman_recv(struct sk_buff *skb, struct net_device *dev,
- short vid)
-{
- struct bat_priv *bat_priv = netdev_priv(dev);
- struct ethhdr *ethhdr = (struct ethhdr *)skb->data;
- struct batman_ogm_packet *batman_ogm_packet;
- struct softif_neigh *softif_neigh = NULL;
- struct hard_iface *primary_if = NULL;
- struct softif_neigh *curr_softif_neigh = NULL;
-
- if (ntohs(ethhdr->h_proto) == ETH_P_8021Q)
- batman_ogm_packet = (struct batman_ogm_packet *)
- (skb->data + ETH_HLEN + VLAN_HLEN);
- else
- batman_ogm_packet = (struct batman_ogm_packet *)
- (skb->data + ETH_HLEN);
-
- if (batman_ogm_packet->header.version != COMPAT_VERSION)
- goto out;
-
- if (batman_ogm_packet->header.packet_type != BAT_OGM)
- goto out;
-
- if (!(batman_ogm_packet->flags & PRIMARIES_FIRST_HOP))
- goto out;
-
- if (is_my_mac(batman_ogm_packet->orig))
- goto out;
-
- softif_neigh = softif_neigh_get(bat_priv, batman_ogm_packet->orig, vid);
- if (!softif_neigh)
- goto out;
-
- curr_softif_neigh = softif_neigh_vid_get_selected(bat_priv, vid);
- if (curr_softif_neigh == softif_neigh)
- goto out;
-
- primary_if = primary_if_get_selected(bat_priv);
- if (!primary_if)
- goto out;
-
- /* we got a neighbor but its mac is 'bigger' than ours */
- if (memcmp(primary_if->net_dev->dev_addr,
- softif_neigh->addr, ETH_ALEN) < 0)
- goto out;
-
- /* close own batX device and use softif_neigh as exit node */
- if (!curr_softif_neigh) {
- softif_neigh_vid_select(bat_priv, softif_neigh, vid);
- goto out;
- }
-
- /* switch to new 'smallest neighbor' */
- if (memcmp(softif_neigh->addr, curr_softif_neigh->addr, ETH_ALEN) < 0)
- softif_neigh_vid_select(bat_priv, softif_neigh, vid);
-
-out:
- kfree_skb(skb);
- if (softif_neigh)
- softif_neigh_free_ref(softif_neigh);
- if (curr_softif_neigh)
- softif_neigh_free_ref(curr_softif_neigh);
- if (primary_if)
- hardif_free_ref(primary_if);
- return;
-}
-
static int interface_open(struct net_device *dev)
{
netif_start_queue(dev);
@@ -562,10 +130,11 @@ static int interface_tx(struct sk_buff *skb, struct net_device *soft_iface)
struct hard_iface *primary_if = NULL;
struct bcast_packet *bcast_packet;
struct vlan_ethhdr *vhdr;
- struct softif_neigh *curr_softif_neigh = NULL;
+ static const uint8_t stp_addr[ETH_ALEN] = {0x01, 0x80, 0xC2, 0x00, 0x00,
+ 0x00};
unsigned int header_len = 0;
int data_len = skb->len, ret;
- short vid = -1;
+ short vid __maybe_unused = -1;
bool do_bcast = false;
if (atomic_read(&bat_priv->mesh_state) != MESH_ACTIVE)
@@ -583,21 +152,21 @@ static int interface_tx(struct sk_buff *skb, struct net_device *soft_iface)
/* fall through */
case ETH_P_BATMAN:
- softif_batman_recv(skb, soft_iface, vid);
- goto end;
+ goto dropped;
}
- /**
- * if we have a another chosen mesh exit node in range
- * it will transport the packets to the mesh
- */
- curr_softif_neigh = softif_neigh_vid_get_selected(bat_priv, vid);
- if (curr_softif_neigh)
+ if (bla_tx(bat_priv, skb, vid))
goto dropped;
/* Register the client MAC in the transtable */
tt_local_add(soft_iface, ethhdr->h_source, skb->skb_iif);
+ /* don't accept stp packets. STP does not help in meshes.
+ * better use the bridge loop avoidance ...
+ */
+ if (compare_eth(ethhdr->h_dest, stp_addr))
+ goto dropped;
+
if (is_multicast_ether_addr(ethhdr->h_dest)) {
do_bcast = true;
@@ -675,8 +244,6 @@ dropped:
dropped_freed:
bat_priv->stats.tx_dropped++;
end:
- if (curr_softif_neigh)
- softif_neigh_free_ref(curr_softif_neigh);
if (primary_if)
hardif_free_ref(primary_if);
return NETDEV_TX_OK;
@@ -687,12 +254,9 @@ void interface_rx(struct net_device *soft_iface,
int hdr_size)
{
struct bat_priv *bat_priv = netdev_priv(soft_iface);
- struct unicast_packet *unicast_packet;
struct ethhdr *ethhdr;
struct vlan_ethhdr *vhdr;
- struct softif_neigh *curr_softif_neigh = NULL;
- short vid = -1;
- int ret;
+ short vid __maybe_unused = -1;
/* check if enough space is available for pulling, and pull */
if (!pskb_may_pull(skb, hdr_size))
@@ -716,30 +280,6 @@ void interface_rx(struct net_device *soft_iface,
goto dropped;
}
- /**
- * if we have a another chosen mesh exit node in range
- * it will transport the packets to the non-mesh network
- */
- curr_softif_neigh = softif_neigh_vid_get_selected(bat_priv, vid);
- if (curr_softif_neigh) {
- skb_push(skb, hdr_size);
- unicast_packet = (struct unicast_packet *)skb->data;
-
- if ((unicast_packet->header.packet_type != BAT_UNICAST) &&
- (unicast_packet->header.packet_type != BAT_UNICAST_FRAG))
- goto dropped;
-
- skb_reset_mac_header(skb);
-
- memcpy(unicast_packet->dest,
- curr_softif_neigh->addr, ETH_ALEN);
- ret = route_unicast_packet(skb, recv_if);
- if (ret == NET_RX_DROP)
- goto dropped;
-
- goto out;
- }
-
/* skb->dev & skb->pkt_type are set here */
if (unlikely(!pskb_may_pull(skb, ETH_HLEN)))
goto dropped;
@@ -752,21 +292,25 @@ void interface_rx(struct net_device *soft_iface,
/* skb->ip_summed = CHECKSUM_UNNECESSARY;*/
bat_priv->stats.rx_packets++;
- bat_priv->stats.rx_bytes += skb->len + sizeof(struct ethhdr);
+ bat_priv->stats.rx_bytes += skb->len + ETH_HLEN;
soft_iface->last_rx = jiffies;
if (is_ap_isolated(bat_priv, ethhdr->h_source, ethhdr->h_dest))
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);
goto out;
dropped:
kfree_skb(skb);
out:
- if (curr_softif_neigh)
- softif_neigh_free_ref(curr_softif_neigh);
return;
}
@@ -828,13 +372,14 @@ struct net_device *softif_create(const char *name)
atomic_set(&bat_priv->aggregated_ogms, 1);
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->vis_mode, VIS_TYPE_CLIENT_UPDATE);
atomic_set(&bat_priv->gw_mode, GW_MODE_OFF);
atomic_set(&bat_priv->gw_sel_class, 20);
atomic_set(&bat_priv->gw_bandwidth, 41);
atomic_set(&bat_priv->orig_interval, 1000);
- atomic_set(&bat_priv->hop_penalty, 10);
+ atomic_set(&bat_priv->hop_penalty, 30);
atomic_set(&bat_priv->log_level, 0);
atomic_set(&bat_priv->fragmentation, 1);
atomic_set(&bat_priv->bcast_queue_left, BCAST_QUEUE_LEN);
@@ -845,6 +390,7 @@ struct net_device *softif_create(const char *name)
atomic_set(&bat_priv->ttvn, 0);
atomic_set(&bat_priv->tt_local_changes, 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_len = 0;
diff --git a/net/batman-adv/soft-interface.h b/net/batman-adv/soft-interface.h
index 756eab5b8dd4..020300673884 100644
--- a/net/batman-adv/soft-interface.h
+++ b/net/batman-adv/soft-interface.h
@@ -23,8 +23,6 @@
#define _NET_BATMAN_ADV_SOFT_INTERFACE_H_
int my_skb_head_push(struct sk_buff *skb, unsigned int len);
-int softif_neigh_seq_print_text(struct seq_file *seq, void *offset);
-void softif_neigh_purge(struct bat_priv *bat_priv);
void interface_rx(struct net_device *soft_iface,
struct sk_buff *skb, struct hard_iface *recv_if,
int hdr_size);
diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
index 1f8692127840..a38d315d3cd6 100644
--- a/net/batman-adv/translation-table.c
+++ b/net/batman-adv/translation-table.c
@@ -27,13 +27,14 @@
#include "hash.h"
#include "originator.h"
#include "routing.h"
+#include "bridge_loop_avoidance.h"
#include <linux/crc16.h>
-static void _tt_global_del(struct bat_priv *bat_priv,
- struct tt_global_entry *tt_global_entry,
- const char *message);
+static void send_roam_adv(struct bat_priv *bat_priv, uint8_t *client,
+ struct orig_node *orig_node);
static void tt_purge(struct work_struct *work);
+static void tt_global_del_orig_list(struct tt_global_entry *tt_global_entry);
/* returns 1 if they are the same mac addr */
static int compare_tt(const struct hlist_node *node, const void *data2)
@@ -123,17 +124,31 @@ static void tt_global_entry_free_rcu(struct rcu_head *rcu)
tt_global_entry = container_of(tt_common_entry, struct tt_global_entry,
common);
- if (tt_global_entry->orig_node)
- orig_node_free_ref(tt_global_entry->orig_node);
-
kfree(tt_global_entry);
}
static void tt_global_entry_free_ref(struct tt_global_entry *tt_global_entry)
{
- if (atomic_dec_and_test(&tt_global_entry->common.refcount))
+ if (atomic_dec_and_test(&tt_global_entry->common.refcount)) {
+ tt_global_del_orig_list(tt_global_entry);
call_rcu(&tt_global_entry->common.rcu,
tt_global_entry_free_rcu);
+ }
+}
+
+static void tt_orig_list_entry_free_rcu(struct rcu_head *rcu)
+{
+ struct tt_orig_list_entry *orig_entry;
+
+ orig_entry = container_of(rcu, struct tt_orig_list_entry, rcu);
+ atomic_dec(&orig_entry->orig_node->tt_size);
+ orig_node_free_ref(orig_entry->orig_node);
+ kfree(orig_entry);
+}
+
+static void tt_orig_list_entry_free_ref(struct tt_orig_list_entry *orig_entry)
+{
+ call_rcu(&orig_entry->rcu, tt_orig_list_entry_free_rcu);
}
static void tt_local_event(struct bat_priv *bat_priv, const uint8_t *addr,
@@ -182,6 +197,9 @@ void tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
struct bat_priv *bat_priv = netdev_priv(soft_iface);
struct tt_local_entry *tt_local_entry = NULL;
struct tt_global_entry *tt_global_entry = NULL;
+ struct hlist_head *head;
+ struct hlist_node *node;
+ struct tt_orig_list_entry *orig_entry;
int hash_added;
tt_local_entry = tt_local_hash_find(bat_priv, addr);
@@ -232,14 +250,21 @@ void tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
/* Check whether it is a roaming! */
if (tt_global_entry) {
- /* This node is probably going to update its tt table */
- tt_global_entry->orig_node->tt_poss_change = true;
- /* The global entry has to be marked as ROAMING and has to be
- * kept for consistency purpose */
+ /* These node are probably going to update their tt table */
+ head = &tt_global_entry->orig_list;
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(orig_entry, node, head, list) {
+ orig_entry->orig_node->tt_poss_change = true;
+
+ send_roam_adv(bat_priv, tt_global_entry->common.addr,
+ orig_entry->orig_node);
+ }
+ rcu_read_unlock();
+ /* The global entry has to be marked as ROAMING and
+ * has to be kept for consistency purpose
+ */
tt_global_entry->common.flags |= TT_CLIENT_ROAM;
tt_global_entry->roam_at = jiffies;
- send_roam_adv(bat_priv, tt_global_entry->common.addr,
- tt_global_entry->orig_node);
}
out:
if (tt_local_entry)
@@ -490,33 +515,76 @@ static void tt_changes_list_free(struct bat_priv *bat_priv)
spin_unlock_bh(&bat_priv->tt_changes_list_lock);
}
+/* find out if an orig_node is already in the list of a tt_global_entry.
+ * returns 1 if found, 0 otherwise
+ */
+static bool tt_global_entry_has_orig(const struct tt_global_entry *entry,
+ const struct orig_node *orig_node)
+{
+ struct tt_orig_list_entry *tmp_orig_entry;
+ const struct hlist_head *head;
+ struct hlist_node *node;
+ bool found = false;
+
+ rcu_read_lock();
+ head = &entry->orig_list;
+ hlist_for_each_entry_rcu(tmp_orig_entry, node, head, list) {
+ if (tmp_orig_entry->orig_node == orig_node) {
+ found = true;
+ break;
+ }
+ }
+ rcu_read_unlock();
+ return found;
+}
+
+static void tt_global_add_orig_entry(struct tt_global_entry *tt_global_entry,
+ struct orig_node *orig_node,
+ int ttvn)
+{
+ struct tt_orig_list_entry *orig_entry;
+
+ orig_entry = kzalloc(sizeof(*orig_entry), GFP_ATOMIC);
+ if (!orig_entry)
+ return;
+
+ INIT_HLIST_NODE(&orig_entry->list);
+ atomic_inc(&orig_node->refcount);
+ atomic_inc(&orig_node->tt_size);
+ orig_entry->orig_node = orig_node;
+ orig_entry->ttvn = ttvn;
+
+ spin_lock_bh(&tt_global_entry->list_lock);
+ hlist_add_head_rcu(&orig_entry->list,
+ &tt_global_entry->orig_list);
+ spin_unlock_bh(&tt_global_entry->list_lock);
+}
+
/* caller must hold orig_node refcount */
int tt_global_add(struct bat_priv *bat_priv, struct orig_node *orig_node,
const unsigned char *tt_addr, uint8_t ttvn, bool roaming,
bool wifi)
{
- struct tt_global_entry *tt_global_entry;
- struct orig_node *orig_node_tmp;
+ struct tt_global_entry *tt_global_entry = NULL;
int ret = 0;
int hash_added;
tt_global_entry = tt_global_hash_find(bat_priv, tt_addr);
if (!tt_global_entry) {
- tt_global_entry =
- kmalloc(sizeof(*tt_global_entry),
- GFP_ATOMIC);
+ tt_global_entry = kzalloc(sizeof(*tt_global_entry),
+ GFP_ATOMIC);
if (!tt_global_entry)
goto out;
memcpy(tt_global_entry->common.addr, tt_addr, ETH_ALEN);
+
tt_global_entry->common.flags = NO_FLAGS;
- atomic_set(&tt_global_entry->common.refcount, 2);
- /* Assign the new orig_node */
- atomic_inc(&orig_node->refcount);
- tt_global_entry->orig_node = orig_node;
- tt_global_entry->ttvn = ttvn;
tt_global_entry->roam_at = 0;
+ atomic_set(&tt_global_entry->common.refcount, 2);
+
+ INIT_HLIST_HEAD(&tt_global_entry->orig_list);
+ spin_lock_init(&tt_global_entry->list_lock);
hash_added = hash_add(bat_priv->tt_global_hash, compare_tt,
choose_orig, &tt_global_entry->common,
@@ -527,19 +595,27 @@ int tt_global_add(struct bat_priv *bat_priv, struct orig_node *orig_node,
tt_global_entry_free_ref(tt_global_entry);
goto out_remove;
}
- atomic_inc(&orig_node->tt_size);
+
+ tt_global_add_orig_entry(tt_global_entry, orig_node, ttvn);
} else {
- if (tt_global_entry->orig_node != orig_node) {
- atomic_dec(&tt_global_entry->orig_node->tt_size);
- orig_node_tmp = tt_global_entry->orig_node;
- atomic_inc(&orig_node->refcount);
- tt_global_entry->orig_node = orig_node;
- orig_node_free_ref(orig_node_tmp);
- atomic_inc(&orig_node->tt_size);
+ /* there is already a global entry, use this one. */
+
+ /* If there is the TT_CLIENT_ROAM flag set, there is only one
+ * originator left in the list and we previously received a
+ * delete + roaming change for this originator.
+ *
+ * We should first delete the old originator before adding the
+ * new one.
+ */
+ if (tt_global_entry->common.flags & TT_CLIENT_ROAM) {
+ tt_global_del_orig_list(tt_global_entry);
+ tt_global_entry->common.flags &= ~TT_CLIENT_ROAM;
+ tt_global_entry->roam_at = 0;
}
- tt_global_entry->common.flags = NO_FLAGS;
- tt_global_entry->ttvn = ttvn;
- tt_global_entry->roam_at = 0;
+
+ if (!tt_global_entry_has_orig(tt_global_entry, orig_node))
+ tt_global_add_orig_entry(tt_global_entry, orig_node,
+ ttvn);
}
if (wifi)
@@ -560,6 +636,34 @@ out:
return ret;
}
+/* print all orig nodes who announce the address for this global entry.
+ * it is assumed that the caller holds rcu_read_lock();
+ */
+static void tt_global_print_entry(struct tt_global_entry *tt_global_entry,
+ struct seq_file *seq)
+{
+ struct hlist_head *head;
+ struct hlist_node *node;
+ struct tt_orig_list_entry *orig_entry;
+ struct tt_common_entry *tt_common_entry;
+ uint16_t flags;
+ uint8_t last_ttvn;
+
+ tt_common_entry = &tt_global_entry->common;
+
+ head = &tt_global_entry->orig_list;
+
+ hlist_for_each_entry_rcu(orig_entry, node, head, list) {
+ flags = tt_common_entry->flags;
+ last_ttvn = atomic_read(&orig_entry->orig_node->last_ttvn);
+ seq_printf(seq, " * %pM (%3u) via %pM (%3u) [%c%c]\n",
+ tt_global_entry->common.addr, orig_entry->ttvn,
+ orig_entry->orig_node->orig, last_ttvn,
+ (flags & TT_CLIENT_ROAM ? 'R' : '.'),
+ (flags & TT_CLIENT_WIFI ? 'W' : '.'));
+ }
+}
+
int tt_global_seq_print_text(struct seq_file *seq, void *offset)
{
struct net_device *net_dev = (struct net_device *)seq->private;
@@ -603,18 +707,7 @@ int tt_global_seq_print_text(struct seq_file *seq, void *offset)
tt_global_entry = container_of(tt_common_entry,
struct tt_global_entry,
common);
- seq_printf(seq,
- " * %pM (%3u) via %pM (%3u) [%c%c]\n",
- tt_global_entry->common.addr,
- tt_global_entry->ttvn,
- tt_global_entry->orig_node->orig,
- (uint8_t) atomic_read(
- &tt_global_entry->orig_node->
- last_ttvn),
- (tt_global_entry->common.flags &
- TT_CLIENT_ROAM ? 'R' : '.'),
- (tt_global_entry->common.flags &
- TT_CLIENT_WIFI ? 'W' : '.'));
+ tt_global_print_entry(tt_global_entry, seq);
}
rcu_read_unlock();
}
@@ -624,59 +717,150 @@ out:
return ret;
}
-static void _tt_global_del(struct bat_priv *bat_priv,
- struct tt_global_entry *tt_global_entry,
- const char *message)
+/* deletes the orig list of a tt_global_entry */
+static void tt_global_del_orig_list(struct tt_global_entry *tt_global_entry)
{
- if (!tt_global_entry)
- goto out;
+ struct hlist_head *head;
+ struct hlist_node *node, *safe;
+ struct tt_orig_list_entry *orig_entry;
- bat_dbg(DBG_TT, bat_priv,
- "Deleting global tt entry %pM (via %pM): %s\n",
- tt_global_entry->common.addr, tt_global_entry->orig_node->orig,
- message);
+ spin_lock_bh(&tt_global_entry->list_lock);
+ head = &tt_global_entry->orig_list;
+ hlist_for_each_entry_safe(orig_entry, node, safe, head, list) {
+ hlist_del_rcu(node);
+ tt_orig_list_entry_free_ref(orig_entry);
+ }
+ spin_unlock_bh(&tt_global_entry->list_lock);
- atomic_dec(&tt_global_entry->orig_node->tt_size);
+}
+
+static void tt_global_del_orig_entry(struct bat_priv *bat_priv,
+ struct tt_global_entry *tt_global_entry,
+ struct orig_node *orig_node,
+ const char *message)
+{
+ struct hlist_head *head;
+ struct hlist_node *node, *safe;
+ struct tt_orig_list_entry *orig_entry;
+
+ spin_lock_bh(&tt_global_entry->list_lock);
+ head = &tt_global_entry->orig_list;
+ hlist_for_each_entry_safe(orig_entry, node, safe, head, list) {
+ if (orig_entry->orig_node == orig_node) {
+ bat_dbg(DBG_TT, bat_priv,
+ "Deleting %pM from global tt entry %pM: %s\n",
+ orig_node->orig, tt_global_entry->common.addr,
+ message);
+ hlist_del_rcu(node);
+ tt_orig_list_entry_free_ref(orig_entry);
+ }
+ }
+ spin_unlock_bh(&tt_global_entry->list_lock);
+}
+
+static void tt_global_del_struct(struct bat_priv *bat_priv,
+ struct tt_global_entry *tt_global_entry,
+ const char *message)
+{
+ bat_dbg(DBG_TT, bat_priv,
+ "Deleting global tt entry %pM: %s\n",
+ tt_global_entry->common.addr, message);
hash_remove(bat_priv->tt_global_hash, compare_tt, choose_orig,
tt_global_entry->common.addr);
-out:
- if (tt_global_entry)
- tt_global_entry_free_ref(tt_global_entry);
+ tt_global_entry_free_ref(tt_global_entry);
+
}
-void tt_global_del(struct bat_priv *bat_priv,
- struct orig_node *orig_node, const unsigned char *addr,
- const char *message, bool roaming)
+/* If the client is to be deleted, we check if it is the last origantor entry
+ * within tt_global entry. If yes, we set the TT_CLIENT_ROAM flag and the timer,
+ * otherwise we simply remove the originator scheduled for deletion.
+ */
+static void tt_global_del_roaming(struct bat_priv *bat_priv,
+ struct tt_global_entry *tt_global_entry,
+ struct orig_node *orig_node,
+ const char *message)
+{
+ bool last_entry = true;
+ struct hlist_head *head;
+ struct hlist_node *node;
+ struct tt_orig_list_entry *orig_entry;
+
+ /* no local entry exists, case 1:
+ * Check if this is the last one or if other entries exist.
+ */
+
+ rcu_read_lock();
+ head = &tt_global_entry->orig_list;
+ hlist_for_each_entry_rcu(orig_entry, node, head, list) {
+ if (orig_entry->orig_node != orig_node) {
+ last_entry = false;
+ break;
+ }
+ }
+ rcu_read_unlock();
+
+ if (last_entry) {
+ /* its the last one, mark for roaming. */
+ tt_global_entry->common.flags |= TT_CLIENT_ROAM;
+ tt_global_entry->roam_at = jiffies;
+ } else
+ /* there is another entry, we can simply delete this
+ * one and can still use the other one.
+ */
+ tt_global_del_orig_entry(bat_priv, tt_global_entry,
+ orig_node, message);
+}
+
+
+
+static void tt_global_del(struct bat_priv *bat_priv,
+ struct orig_node *orig_node,
+ const unsigned char *addr,
+ const char *message, bool roaming)
{
struct tt_global_entry *tt_global_entry = NULL;
struct tt_local_entry *tt_local_entry = NULL;
tt_global_entry = tt_global_hash_find(bat_priv, addr);
- if (!tt_global_entry || tt_global_entry->orig_node != orig_node)
+ if (!tt_global_entry)
goto out;
- if (!roaming)
- goto out_del;
+ if (!roaming) {
+ tt_global_del_orig_entry(bat_priv, tt_global_entry, orig_node,
+ message);
+
+ if (hlist_empty(&tt_global_entry->orig_list))
+ tt_global_del_struct(bat_priv, tt_global_entry,
+ message);
+
+ goto out;
+ }
/* if we are deleting a global entry due to a roam
* event, there are two possibilities:
- * 1) the client roamed from node A to node B => we mark
+ * 1) the client roamed from node A to node B => if there
+ * is only one originator left for this client, we mark
* it with TT_CLIENT_ROAM, we start a timer and we
* wait for node B to claim it. In case of timeout
* the entry is purged.
+ *
+ * If there are other originators left, we directly delete
+ * the originator.
* 2) the client roamed to us => we can directly delete
* the global entry, since it is useless now. */
+
tt_local_entry = tt_local_hash_find(bat_priv,
tt_global_entry->common.addr);
- if (!tt_local_entry) {
- tt_global_entry->common.flags |= TT_CLIENT_ROAM;
- tt_global_entry->roam_at = jiffies;
- goto out;
- }
+ if (tt_local_entry) {
+ /* local entry exists, case 2: client roamed to us. */
+ tt_global_del_orig_list(tt_global_entry);
+ tt_global_del_struct(bat_priv, tt_global_entry, message);
+ } else
+ /* no local entry exists, case 1: check for roaming */
+ tt_global_del_roaming(bat_priv, tt_global_entry, orig_node,
+ message);
-out_del:
- _tt_global_del(bat_priv, tt_global_entry, message);
out:
if (tt_global_entry)
@@ -709,11 +893,14 @@ void tt_global_del_orig(struct bat_priv *bat_priv,
tt_global_entry = container_of(tt_common_entry,
struct tt_global_entry,
common);
- if (tt_global_entry->orig_node == orig_node) {
+
+ tt_global_del_orig_entry(bat_priv, tt_global_entry,
+ orig_node, message);
+
+ if (hlist_empty(&tt_global_entry->orig_list)) {
bat_dbg(DBG_TT, bat_priv,
- "Deleting global tt entry %pM (via %pM): %s\n",
+ "Deleting global tt entry %pM: %s\n",
tt_global_entry->common.addr,
- tt_global_entry->orig_node->orig,
message);
hlist_del_rcu(node);
tt_global_entry_free_ref(tt_global_entry);
@@ -754,7 +941,7 @@ static void tt_global_roam_purge(struct bat_priv *bat_priv)
bat_dbg(DBG_TT, bat_priv,
"Deleting global tt entry (%pM): Roaming timeout\n",
tt_global_entry->common.addr);
- atomic_dec(&tt_global_entry->orig_node->tt_size);
+
hlist_del_rcu(node);
tt_global_entry_free_ref(tt_global_entry);
}
@@ -817,6 +1004,11 @@ struct orig_node *transtable_search(struct bat_priv *bat_priv,
struct tt_local_entry *tt_local_entry = NULL;
struct tt_global_entry *tt_global_entry = NULL;
struct orig_node *orig_node = NULL;
+ struct neigh_node *router = NULL;
+ struct hlist_head *head;
+ struct hlist_node *node;
+ struct tt_orig_list_entry *orig_entry;
+ int best_tq;
if (src && atomic_read(&bat_priv->ap_isolation)) {
tt_local_entry = tt_local_hash_find(bat_priv, src);
@@ -833,11 +1025,25 @@ struct orig_node *transtable_search(struct bat_priv *bat_priv,
if (tt_local_entry && _is_ap_isolated(tt_local_entry, tt_global_entry))
goto out;
- if (!atomic_inc_not_zero(&tt_global_entry->orig_node->refcount))
- goto out;
+ best_tq = 0;
- orig_node = tt_global_entry->orig_node;
+ rcu_read_lock();
+ head = &tt_global_entry->orig_list;
+ hlist_for_each_entry_rcu(orig_entry, node, head, list) {
+ router = orig_node_get_router(orig_entry->orig_node);
+ if (!router)
+ continue;
+ if (router->tq_avg > best_tq) {
+ orig_node = orig_entry->orig_node;
+ best_tq = router->tq_avg;
+ }
+ neigh_node_free_ref(router);
+ }
+ /* found anything? */
+ if (orig_node && !atomic_inc_not_zero(&orig_node->refcount))
+ orig_node = NULL;
+ rcu_read_unlock();
out:
if (tt_global_entry)
tt_global_entry_free_ref(tt_global_entry);
@@ -848,7 +1054,8 @@ out:
}
/* Calculates the checksum of the local table of a given orig_node */
-uint16_t tt_global_crc(struct bat_priv *bat_priv, struct orig_node *orig_node)
+static uint16_t tt_global_crc(struct bat_priv *bat_priv,
+ struct orig_node *orig_node)
{
uint16_t total = 0, total_one;
struct hashtable_t *hash = bat_priv->tt_global_hash;
@@ -868,20 +1075,26 @@ uint16_t tt_global_crc(struct bat_priv *bat_priv, struct orig_node *orig_node)
tt_global_entry = container_of(tt_common_entry,
struct tt_global_entry,
common);
- if (compare_eth(tt_global_entry->orig_node,
- orig_node)) {
- /* Roaming clients are in the global table for
- * consistency only. They don't have to be
- * taken into account while computing the
- * global crc */
- if (tt_common_entry->flags & TT_CLIENT_ROAM)
- continue;
- total_one = 0;
- for (j = 0; j < ETH_ALEN; j++)
- total_one = crc16_byte(total_one,
- tt_common_entry->addr[j]);
- total ^= total_one;
- }
+ /* Roaming clients are in the global table for
+ * consistency only. They don't have to be
+ * taken into account while computing the
+ * global crc
+ */
+ if (tt_global_entry->common.flags & TT_CLIENT_ROAM)
+ continue;
+
+ /* find out if this global entry is announced by this
+ * originator
+ */
+ if (!tt_global_entry_has_orig(tt_global_entry,
+ orig_node))
+ continue;
+
+ total_one = 0;
+ for (j = 0; j < ETH_ALEN; j++)
+ total_one = crc16_byte(total_one,
+ tt_global_entry->common.addr[j]);
+ total ^= total_one;
}
rcu_read_unlock();
}
@@ -936,8 +1149,10 @@ static void tt_req_list_free(struct bat_priv *bat_priv)
spin_unlock_bh(&bat_priv->tt_req_list_lock);
}
-void tt_save_orig_buffer(struct bat_priv *bat_priv, struct orig_node *orig_node,
- const unsigned char *tt_buff, uint8_t tt_num_changes)
+static void tt_save_orig_buffer(struct bat_priv *bat_priv,
+ struct orig_node *orig_node,
+ const unsigned char *tt_buff,
+ uint8_t tt_num_changes)
{
uint16_t tt_buff_len = tt_len(tt_num_changes);
@@ -1020,7 +1235,7 @@ static int tt_global_valid_entry(const void *entry_ptr, const void *data_ptr)
tt_global_entry = container_of(tt_common_entry, struct tt_global_entry,
common);
- return (tt_global_entry->orig_node == orig_node);
+ return tt_global_entry_has_orig(tt_global_entry, orig_node);
}
static struct sk_buff *tt_response_fill_table(uint16_t tt_len, uint8_t ttvn,
@@ -1124,7 +1339,7 @@ static int send_tt_request(struct bat_priv *bat_priv,
memcpy(tt_request->dst, dst_orig_node->orig, ETH_ALEN);
tt_request->header.ttl = TTL;
tt_request->ttvn = ttvn;
- tt_request->tt_data = tt_crc;
+ tt_request->tt_data = htons(tt_crc);
tt_request->flags = TT_REQUEST;
if (full_table)
@@ -1401,10 +1616,15 @@ out:
bool send_tt_response(struct bat_priv *bat_priv,
struct tt_query_packet *tt_request)
{
- if (is_my_mac(tt_request->dst))
+ if (is_my_mac(tt_request->dst)) {
+ /* don't answer backbone gws! */
+ if (bla_is_backbone_gw_orig(bat_priv, tt_request->src))
+ return true;
+
return send_my_tt_response(bat_priv, tt_request);
- else
+ } else {
return send_other_tt_response(bat_priv, tt_request);
+ }
}
static void _tt_update_changes(struct bat_priv *bat_priv,
@@ -1508,6 +1728,10 @@ void handle_tt_response(struct bat_priv *bat_priv,
tt_response->src, tt_response->ttvn, tt_response->tt_data,
(tt_response->flags & TT_FULL_TABLE ? 'F' : '.'));
+ /* we should have never asked a backbone gw */
+ if (bla_is_backbone_gw_orig(bat_priv, tt_response->src))
+ goto out;
+
orig_node = orig_hash_find(bat_priv, tt_response->src);
if (!orig_node)
goto out;
@@ -1627,8 +1851,8 @@ unlock:
return ret;
}
-void send_roam_adv(struct bat_priv *bat_priv, uint8_t *client,
- struct orig_node *orig_node)
+static void send_roam_adv(struct bat_priv *bat_priv, uint8_t *client,
+ struct orig_node *orig_node)
{
struct neigh_node *neigh_node = NULL;
struct sk_buff *skb = NULL;
@@ -1796,6 +2020,8 @@ void tt_commit_changes(struct bat_priv *bat_priv)
/* Increment the TTVN only once per OGM interval */
atomic_inc(&bat_priv->ttvn);
+ bat_dbg(DBG_TT, bat_priv, "Local changes committed, updating to ttvn %u\n",
+ (uint8_t)atomic_read(&bat_priv->ttvn));
bat_priv->tt_poss_change = false;
}
@@ -1836,6 +2062,10 @@ void tt_update_orig(struct bat_priv *bat_priv, struct orig_node *orig_node,
uint8_t orig_ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn);
bool full_table = true;
+ /* don't care about a backbone gateways updates. */
+ if (bla_is_backbone_gw_orig(bat_priv, orig_node->orig))
+ return;
+
/* orig table not initialised AND first diff is in the OGM OR the ttvn
* increased by one -> we can apply the attached changes */
if ((!orig_node->tt_initialised && ttvn == 1) ||
@@ -1873,6 +2103,7 @@ void tt_update_orig(struct bat_priv *bat_priv, struct orig_node *orig_node,
} else {
/* if we missed more than one change or our tables are not
* in sync anymore -> request fresh tt data */
+
if (!orig_node->tt_initialised || ttvn != orig_ttvn ||
orig_node->tt_crc != tt_crc) {
request_table:
diff --git a/net/batman-adv/translation-table.h b/net/batman-adv/translation-table.h
index c753633b1da1..bfebe26edd8e 100644
--- a/net/batman-adv/translation-table.h
+++ b/net/batman-adv/translation-table.h
@@ -39,23 +39,15 @@ int tt_global_add(struct bat_priv *bat_priv, struct orig_node *orig_node,
int tt_global_seq_print_text(struct seq_file *seq, void *offset);
void tt_global_del_orig(struct bat_priv *bat_priv,
struct orig_node *orig_node, const char *message);
-void tt_global_del(struct bat_priv *bat_priv,
- struct orig_node *orig_node, const unsigned char *addr,
- const char *message, bool roaming);
struct orig_node *transtable_search(struct bat_priv *bat_priv,
const uint8_t *src, const uint8_t *addr);
-void tt_save_orig_buffer(struct bat_priv *bat_priv, struct orig_node *orig_node,
- const unsigned char *tt_buff, uint8_t tt_num_changes);
uint16_t tt_local_crc(struct bat_priv *bat_priv);
-uint16_t tt_global_crc(struct bat_priv *bat_priv, struct orig_node *orig_node);
void tt_free(struct bat_priv *bat_priv);
bool send_tt_response(struct bat_priv *bat_priv,
struct tt_query_packet *tt_request);
bool is_my_client(struct bat_priv *bat_priv, const uint8_t *addr);
void handle_tt_response(struct bat_priv *bat_priv,
struct tt_query_packet *tt_response);
-void send_roam_adv(struct bat_priv *bat_priv, uint8_t *client,
- struct orig_node *orig_node);
void tt_commit_changes(struct bat_priv *bat_priv);
bool is_ap_isolated(struct bat_priv *bat_priv, uint8_t *src, uint8_t *dst);
void tt_update_orig(struct bat_priv *bat_priv, struct orig_node *orig_node,
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index 302efb523475..2f4848b776a7 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -27,7 +27,7 @@
#include "packet.h"
#include "bitarray.h"
-#define BAT_HEADER_LEN (sizeof(struct ethhdr) + \
+#define BAT_HEADER_LEN (ETH_HLEN + \
((sizeof(struct unicast_packet) > sizeof(struct bcast_packet) ? \
sizeof(struct unicast_packet) : \
sizeof(struct bcast_packet))))
@@ -90,7 +90,7 @@ struct orig_node {
bool tt_poss_change;
uint32_t last_real_seqno;
uint8_t last_ttl;
- unsigned long bcast_bits[NUM_WORDS];
+ DECLARE_BITMAP(bcast_bits, TQ_LOCAL_WINDOW_SIZE);
uint32_t last_bcast_seqno;
struct hlist_head neigh_list;
struct list_head frag_list;
@@ -132,7 +132,7 @@ struct neigh_node {
uint8_t last_ttl;
struct list_head bonding_list;
unsigned long last_valid;
- unsigned long real_bits[NUM_WORDS];
+ DECLARE_BITMAP(real_bits, TQ_LOCAL_WINDOW_SIZE);
atomic_t refcount;
struct rcu_head rcu;
struct orig_node *orig_node;
@@ -140,6 +140,13 @@ struct neigh_node {
spinlock_t tq_lock; /* protects: tq_recv, tq_index */
};
+#ifdef CONFIG_BATMAN_ADV_BLA
+struct bcast_duplist_entry {
+ uint8_t orig[ETH_ALEN];
+ uint16_t crc;
+ unsigned long entrytime;
+};
+#endif
struct bat_priv {
atomic_t mesh_state;
@@ -148,6 +155,7 @@ struct bat_priv {
atomic_t bonding; /* boolean */
atomic_t fragmentation; /* boolean */
atomic_t ap_isolation; /* boolean */
+ atomic_t bridge_loop_avoidance; /* boolean */
atomic_t vis_mode; /* VIS_TYPE_* */
atomic_t gw_mode; /* GW_MODE_* */
atomic_t gw_sel_class; /* uint */
@@ -161,6 +169,7 @@ struct bat_priv {
atomic_t ttvn; /* translation table version number */
atomic_t tt_ogm_append_cnt;
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.
* 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
@@ -174,15 +183,23 @@ struct bat_priv {
struct hlist_head forw_bat_list;
struct hlist_head forw_bcast_list;
struct hlist_head gw_list;
- struct hlist_head softif_neigh_vids;
struct list_head tt_changes_list; /* tracks changes in a OGM int */
struct list_head vis_send_list;
struct hashtable_t *orig_hash;
struct hashtable_t *tt_local_hash;
struct hashtable_t *tt_global_hash;
+#ifdef CONFIG_BATMAN_ADV_BLA
+ struct hashtable_t *claim_hash;
+ struct hashtable_t *backbone_hash;
+#endif
struct list_head tt_req_list; /* list of pending tt_requests */
struct list_head tt_roam_list;
struct hashtable_t *vis_hash;
+#ifdef CONFIG_BATMAN_ADV_BLA
+ struct bcast_duplist_entry bcast_duplist[DUPLIST_SIZE];
+ int bcast_duplist_curr;
+ struct bla_claim_dst claim_dest;
+#endif
spinlock_t forw_bat_list_lock; /* protects forw_bat_list */
spinlock_t forw_bcast_list_lock; /* protects */
spinlock_t tt_changes_list_lock; /* protects tt_changes */
@@ -191,8 +208,6 @@ struct bat_priv {
spinlock_t gw_list_lock; /* protects gw_list and curr_gw */
spinlock_t vis_hash_lock; /* protects vis_hash */
spinlock_t vis_list_lock; /* protects vis_info::recv_list */
- spinlock_t softif_neigh_lock; /* protects soft-interface neigh list */
- spinlock_t softif_neigh_vid_lock; /* protects soft-interface vid list */
atomic_t num_local_tt;
/* Checksum of the local table, recomputed before sending a new OGM */
atomic_t tt_crc;
@@ -202,6 +217,7 @@ struct bat_priv {
struct delayed_work tt_work;
struct delayed_work orig_work;
struct delayed_work vis_work;
+ struct delayed_work bla_work;
struct gw_node __rcu *curr_gw; /* rcu protected pointer */
atomic_t gw_reselect;
struct hard_iface __rcu *primary_if; /* rcu protected pointer */
@@ -239,10 +255,41 @@ struct tt_local_entry {
struct tt_global_entry {
struct tt_common_entry common;
+ struct hlist_head orig_list;
+ spinlock_t list_lock; /* protects the list */
+ unsigned long roam_at; /* time at which TT_GLOBAL_ROAM was set */
+};
+
+struct tt_orig_list_entry {
struct orig_node *orig_node;
uint8_t ttvn;
- unsigned long roam_at; /* time at which TT_GLOBAL_ROAM was set */
+ struct rcu_head rcu;
+ struct hlist_node list;
+};
+
+#ifdef CONFIG_BATMAN_ADV_BLA
+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;
};
+#endif
struct tt_change_node {
struct list_head list;
@@ -327,31 +374,15 @@ struct recvlist_node {
uint8_t mac[ETH_ALEN];
};
-struct softif_neigh_vid {
- struct hlist_node list;
- struct bat_priv *bat_priv;
- short vid;
- atomic_t refcount;
- struct softif_neigh __rcu *softif_neigh;
- struct rcu_head rcu;
- struct hlist_head softif_neigh_list;
-};
-
-struct softif_neigh {
- struct hlist_node list;
- uint8_t addr[ETH_ALEN];
- unsigned long last_seen;
- atomic_t refcount;
- struct rcu_head rcu;
-};
-
struct bat_algo_ops {
struct hlist_node list;
char *name;
- /* init OGM when hard-interface is enabled */
- void (*bat_ogm_init)(struct hard_iface *hard_iface);
- /* init primary OGM when primary interface is selected */
- void (*bat_ogm_init_primary)(struct hard_iface *hard_iface);
+ /* init routing info when hard-interface is enabled */
+ int (*bat_iface_enable)(struct hard_iface *hard_iface);
+ /* de-init routing info when hard-interface is disabled */
+ void (*bat_iface_disable)(struct hard_iface *hard_iface);
+ /* called when primary interface is selected / changed */
+ void (*bat_primary_iface_set)(struct hard_iface *hard_iface);
/* init mac addresses of the OGM belonging to this hard-interface */
void (*bat_ogm_update_mac)(struct hard_iface *hard_iface);
/* prepare a new outgoing OGM for the send queue */
diff --git a/net/batman-adv/vis.c b/net/batman-adv/vis.c
index c4a5b8cafada..cec216fb77c7 100644
--- a/net/batman-adv/vis.c
+++ b/net/batman-adv/vis.c
@@ -434,12 +434,12 @@ static struct vis_info *add_packet(struct bat_priv *bat_priv,
return NULL;
info->skb_packet = dev_alloc_skb(sizeof(*packet) + vis_info_len +
- sizeof(struct ethhdr));
+ ETH_HLEN);
if (!info->skb_packet) {
kfree(info);
return NULL;
}
- skb_reserve(info->skb_packet, sizeof(struct ethhdr));
+ skb_reserve(info->skb_packet, ETH_HLEN);
packet = (struct vis_packet *)skb_put(info->skb_packet, sizeof(*packet)
+ vis_info_len);
@@ -894,11 +894,11 @@ int vis_init(struct bat_priv *bat_priv)
bat_priv->my_vis_info->skb_packet = dev_alloc_skb(sizeof(*packet) +
MAX_VIS_PACKET_SIZE +
- sizeof(struct ethhdr));
+ ETH_HLEN);
if (!bat_priv->my_vis_info->skb_packet)
goto free_info;
- skb_reserve(bat_priv->my_vis_info->skb_packet, sizeof(struct ethhdr));
+ skb_reserve(bat_priv->my_vis_info->skb_packet, ETH_HLEN);
packet = (struct vis_packet *)skb_put(bat_priv->my_vis_info->skb_packet,
sizeof(*packet));