The following commit has been merged in the merge/master branch: commit 94c4c96db406fec45fcbba7cdab2aab06a6d6208 Merge: cbf3c88b7a54dc0e3098a8f7fc8db2333af68eb3 3c26295d112d709a104561aaad70ac9a327c4fed Author: Antonio Quartulli ordex@autistici.org Date: Sat Oct 27 16:09:38 2012 +0200
Merge remote-tracking branch 'pkg/master' into merge/master
Conflicts: net/batman-adv/Makefile net/batman-adv/Makefile.kbuild net/batman-adv/README net/batman-adv/README.external net/batman-adv/compat.c net/batman-adv/compat.h net/batman-adv/gen-compat-autoconf.sh
diff --combined Documentation/networking/batman-adv.txt index a173d2a,c1d8204..c1d8204 --- a/Documentation/networking/batman-adv.txt +++ b/Documentation/networking/batman-adv.txt @@@ -203,7 -203,8 +203,8 @@@ abled during run time. Following log_l 2 - Enable messages related to route added / changed / deleted 4 - Enable messages related to translation table operations 8 - Enable messages related to bridge loop avoidance - 15 - enable all messages + 16 - Enable messaged related to DAT, ARP snooping and parsing + 31 - Enable all messages
The debug output can be changed at runtime using the file /sys/class/net/bat0/mesh/log_level. e.g. diff --combined net/batman-adv/Makefile index 8676d2b,e45e3b4..e45e3b4 --- a/net/batman-adv/Makefile +++ b/net/batman-adv/Makefile @@@ -23,6 -23,7 +23,7 @@@ batman-adv-y += bat_iv_ogm. batman-adv-y += bitarray.o batman-adv-$(CONFIG_BATMAN_ADV_BLA) += bridge_loop_avoidance.o batman-adv-y += debugfs.o + batman-adv-$(CONFIG_BATMAN_ADV_DAT) += distributed-arp-table.o batman-adv-y += gateway_client.o batman-adv-y += gateway_common.o batman-adv-y += hard-interface.o diff --combined net/batman-adv/bat_iv_ogm.c index b02b75d,75403a4..75403a4 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@@ -57,20 -57,22 +57,22 @@@ out static int batadv_iv_ogm_iface_enable(struct batadv_hard_iface *hard_iface) { struct batadv_ogm_packet *batadv_ogm_packet; + unsigned char *ogm_buff; uint32_t random_seqno; int res = -ENOMEM;
/* randomize initial seqno to avoid collision */ get_random_bytes(&random_seqno, sizeof(random_seqno)); - atomic_set(&hard_iface->seqno, random_seqno); + atomic_set(&hard_iface->bat_iv.ogm_seqno, random_seqno);
- hard_iface->packet_len = BATADV_OGM_HLEN; - hard_iface->packet_buff = kmalloc(hard_iface->packet_len, GFP_ATOMIC); - - if (!hard_iface->packet_buff) + hard_iface->bat_iv.ogm_buff_len = BATADV_OGM_HLEN; + ogm_buff = kmalloc(hard_iface->bat_iv.ogm_buff_len, GFP_ATOMIC); + if (!ogm_buff) goto out;
- batadv_ogm_packet = (struct batadv_ogm_packet *)hard_iface->packet_buff; + hard_iface->bat_iv.ogm_buff = ogm_buff; + + batadv_ogm_packet = (struct batadv_ogm_packet *)ogm_buff; batadv_ogm_packet->header.packet_type = BATADV_IV_OGM; batadv_ogm_packet->header.version = BATADV_COMPAT_VERSION; batadv_ogm_packet->header.ttl = 2; @@@ -87,15 -89,16 +89,16 @@@ out
static void batadv_iv_ogm_iface_disable(struct batadv_hard_iface *hard_iface) { - kfree(hard_iface->packet_buff); - hard_iface->packet_buff = NULL; + kfree(hard_iface->bat_iv.ogm_buff); + hard_iface->bat_iv.ogm_buff = NULL; }
static void batadv_iv_ogm_iface_update_mac(struct batadv_hard_iface *hard_iface) { struct batadv_ogm_packet *batadv_ogm_packet; + unsigned char *ogm_buff = hard_iface->bat_iv.ogm_buff;
- batadv_ogm_packet = (struct batadv_ogm_packet *)hard_iface->packet_buff; + batadv_ogm_packet = (struct batadv_ogm_packet *)ogm_buff; memcpy(batadv_ogm_packet->orig, hard_iface->net_dev->dev_addr, ETH_ALEN); memcpy(batadv_ogm_packet->prev_sender, @@@ -106,8 -109,9 +109,9 @@@ static voi batadv_iv_ogm_primary_iface_set(struct batadv_hard_iface *hard_iface) { struct batadv_ogm_packet *batadv_ogm_packet; + unsigned char *ogm_buff = hard_iface->bat_iv.ogm_buff;
- batadv_ogm_packet = (struct batadv_ogm_packet *)hard_iface->packet_buff; + batadv_ogm_packet = (struct batadv_ogm_packet *)ogm_buff; batadv_ogm_packet->flags = BATADV_PRIMARIES_FIRST_HOP; batadv_ogm_packet->header.ttl = BATADV_TTL; } @@@ -590,8 -594,10 +594,10 @@@ static void batadv_iv_ogm_forward(struc static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface) { struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); + unsigned char **ogm_buff = &hard_iface->bat_iv.ogm_buff; struct batadv_ogm_packet *batadv_ogm_packet; struct batadv_hard_iface *primary_if; + int *ogm_buff_len = &hard_iface->bat_iv.ogm_buff_len; int vis_server, tt_num_changes = 0; uint32_t seqno; uint8_t bandwidth; @@@ -600,17 -606,16 +606,16 @@@ primary_if = batadv_primary_if_get_selected(bat_priv);
if (hard_iface == primary_if) - tt_num_changes = batadv_tt_append_diff(bat_priv, - &hard_iface->packet_buff, - &hard_iface->packet_len, + tt_num_changes = batadv_tt_append_diff(bat_priv, ogm_buff, + ogm_buff_len, BATADV_OGM_HLEN);
- batadv_ogm_packet = (struct batadv_ogm_packet *)hard_iface->packet_buff; + batadv_ogm_packet = (struct batadv_ogm_packet *)(*ogm_buff);
/* change sequence number to network order */ - seqno = (uint32_t)atomic_read(&hard_iface->seqno); + seqno = (uint32_t)atomic_read(&hard_iface->bat_iv.ogm_seqno); batadv_ogm_packet->seqno = htonl(seqno); - atomic_inc(&hard_iface->seqno); + atomic_inc(&hard_iface->bat_iv.ogm_seqno);
batadv_ogm_packet->ttvn = atomic_read(&bat_priv->tt.vn); batadv_ogm_packet->tt_crc = htons(bat_priv->tt.local_crc); @@@ -631,8 -636,8 +636,8 @@@ }
batadv_slide_own_bcast_window(hard_iface); - batadv_iv_ogm_queue_add(bat_priv, hard_iface->packet_buff, - hard_iface->packet_len, hard_iface, 1, + batadv_iv_ogm_queue_add(bat_priv, hard_iface->bat_iv.ogm_buff, + hard_iface->bat_iv.ogm_buff_len, hard_iface, 1, batadv_iv_ogm_emit_send_time(bat_priv));
if (primary_if) @@@ -1015,7 -1020,7 +1020,7 @@@ static void batadv_iv_ogm_process(cons return;
/* could be changed by schedule_own_packet() */ - if_incoming_seqno = atomic_read(&if_incoming->seqno); + if_incoming_seqno = atomic_read(&if_incoming->bat_iv.ogm_seqno);
if (batadv_ogm_packet->flags & BATADV_DIRECTLINK) has_directlink_flag = 1; diff --combined net/batman-adv/bitarray.c index aea174c,5453b17..5453b17 --- a/net/batman-adv/bitarray.c +++ b/net/batman-adv/bitarray.c @@@ -79,20 -79,17 +79,17 @@@ int batadv_bit_get_packet(void *priv, u * or the old packet got delayed somewhere in the network. The * packet should be dropped without calling this function if the * seqno window is protected. + * + * seq_num_diff <= -BATADV_TQ_LOCAL_WINDOW_SIZE + * or + * seq_num_diff >= BATADV_EXPECTED_SEQNO_RANGE */ - if (seq_num_diff <= -BATADV_TQ_LOCAL_WINDOW_SIZE || - seq_num_diff >= BATADV_EXPECTED_SEQNO_RANGE) { + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Other host probably restarted!\n");
- batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "Other host probably restarted!\n"); - - bitmap_zero(seq_bits, BATADV_TQ_LOCAL_WINDOW_SIZE); - if (set_mark) - batadv_set_bit(seq_bits, 0); - - return 1; - } + bitmap_zero(seq_bits, BATADV_TQ_LOCAL_WINDOW_SIZE); + if (set_mark) + batadv_set_bit(seq_bits, 0);
- /* never reached */ - return 0; + return 1; } diff --combined net/batman-adv/bridge_loop_avoidance.c index fd8d5af,a05b1b4..a05b1b4 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@@ -40,15 -40,11 +40,11 @@@ static void batadv_bla_send_announce(st /* return the index of the claim */ static inline uint32_t batadv_choose_claim(const void *data, uint32_t size) { - const unsigned char *key = data; + struct batadv_claim *claim = (struct batadv_claim *)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); - } + batadv_hash_bytes(&hash, &claim->addr, sizeof(claim->addr)); + batadv_hash_bytes(&hash, &claim->vid, sizeof(claim->vid));
hash += (hash << 3); hash ^= (hash >> 11); @@@ -61,15 -57,11 +57,11 @@@ static inline uint32_t batadv_choose_backbone_gw(const void *data, uint32_t size) { - const unsigned char *key = data; + struct batadv_claim *claim = (struct batadv_claim *)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); - } + batadv_hash_bytes(&hash, &claim->addr, sizeof(claim->addr)); + batadv_hash_bytes(&hash, &claim->vid, sizeof(claim->vid));
hash += (hash << 3); hash ^= (hash >> 11); @@@ -85,8 -77,15 +77,15 @@@ static int batadv_compare_backbone_gw(c { const void *data1 = container_of(node, struct batadv_backbone_gw, hash_entry); + const struct batadv_backbone_gw *gw1 = data1, *gw2 = data2; + + if (memcmp(gw1->orig, gw2->orig, ETH_ALEN)) + return 0; + + if (gw1->vid != gw2->vid) + return 0;
- return (memcmp(data1, data2, ETH_ALEN + sizeof(short)) == 0 ? 1 : 0); + return 1; }
/* compares address and vid of two claims */ @@@ -95,8 -94,15 +94,15 @@@ static int batadv_compare_claim(const s { const void *data1 = container_of(node, struct batadv_claim, hash_entry); + const struct batadv_claim *cl1 = data1, *cl2 = data2; + + if (memcmp(cl1->addr, cl2->addr, ETH_ALEN)) + return 0; + + if (cl1->vid != cl2->vid) + return 0;
- return (memcmp(data1, data2, ETH_ALEN + sizeof(short)) == 0 ? 1 : 0); + return 1; }
/* free a backbone gw */ @@@ -362,7 -368,7 +368,7 @@@ out */ static struct batadv_backbone_gw * batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, uint8_t *orig, - short vid) + short vid, bool own_backbone) { struct batadv_backbone_gw *entry; struct batadv_orig_node *orig_node; @@@ -386,6 -392,7 +392,7 @@@ entry->crc = BATADV_BLA_CRC_INIT; entry->bat_priv = bat_priv; atomic_set(&entry->request_sent, 0); + atomic_set(&entry->wait_periods, 0); memcpy(entry->orig, orig, ETH_ALEN);
/* one for the hash, one for returning */ @@@ -409,6 -416,16 +416,16 @@@ "became a backbone gateway"); batadv_orig_node_free_ref(orig_node); } + + if (own_backbone) { + batadv_bla_send_announce(bat_priv, entry); + + /* this will be decreased in the worker thread */ + atomic_inc(&entry->request_sent); + atomic_set(&entry->wait_periods, BATADV_BLA_WAIT_PERIODS); + atomic_inc(&bat_priv->bla.num_requests); + } + return entry; }
@@@ -424,7 -441,7 +441,7 @@@ batadv_bla_update_own_backbone_gw(struc
backbone_gw = batadv_bla_get_backbone_gw(bat_priv, primary_if->net_dev->dev_addr, - vid); + vid, true); if (unlikely(!backbone_gw)) return;
@@@ -632,7 -649,8 +649,8 @@@ static int batadv_handle_announce(struc if (memcmp(an_addr, batadv_announce_mac, 4) != 0) return 0;
- backbone_gw = batadv_bla_get_backbone_gw(bat_priv, backbone_addr, vid); + backbone_gw = batadv_bla_get_backbone_gw(bat_priv, backbone_addr, vid, + false);
if (unlikely(!backbone_gw)) return 1; @@@ -730,7 -748,8 +748,8 @@@ static int batadv_handle_claim(struct b
/* register the gateway if not yet available, and add the claim. */
- backbone_gw = batadv_bla_get_backbone_gw(bat_priv, backbone_addr, vid); + backbone_gw = batadv_bla_get_backbone_gw(bat_priv, backbone_addr, vid, + false);
if (unlikely(!backbone_gw)) return 1; @@@ -1140,6 -1159,24 +1159,24 @@@ static void batadv_bla_periodic_work(st backbone_gw->lasttime = jiffies;
batadv_bla_send_announce(bat_priv, backbone_gw); + + /* request_sent is only set after creation to avoid + * problems when we are not yet known as backbone gw + * in the backbone. + * + * We can reset this now after we waited some periods + * to give bridge forward delays and bla group forming + * some grace time. + */ + + if (atomic_read(&backbone_gw->request_sent) == 0) + continue; + + if (!atomic_dec_and_test(&backbone_gw->wait_periods)) + continue; + + atomic_dec(&backbone_gw->bat_priv->bla.num_requests); + atomic_set(&backbone_gw->request_sent, 0); } rcu_read_unlock(); } @@@ -1585,23 -1622,11 +1622,11 @@@ int batadv_bla_claim_table_seq_print_te struct hlist_head *head; uint32_t i; bool is_own; - int ret = 0; uint8_t *primary_addr;
- primary_if = batadv_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 != BATADV_IF_ACTIVE) { - ret = seq_printf(seq, - "BATMAN mesh %s disabled - primary interface not active\n", - net_dev->name); + primary_if = batadv_seq_print_text_primary_if_get(seq); + if (!primary_if) goto out; - }
primary_addr = primary_if->net_dev->dev_addr; seq_printf(seq, @@@ -1628,7 -1653,7 +1653,7 @@@ out: if (primary_if) batadv_hardif_free_ref(primary_if); - return ret; + return 0; }
int batadv_bla_backbone_table_seq_print_text(struct seq_file *seq, void *offset) @@@ -1643,23 -1668,11 +1668,11 @@@ int secs, msecs; uint32_t i; bool is_own; - int ret = 0; uint8_t *primary_addr;
- primary_if = batadv_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 != BATADV_IF_ACTIVE) { - ret = seq_printf(seq, - "BATMAN mesh %s disabled - primary interface not active\n", - net_dev->name); + primary_if = batadv_seq_print_text_primary_if_get(seq); + if (!primary_if) goto out; - }
primary_addr = primary_if->net_dev->dev_addr; seq_printf(seq, @@@ -1693,5 -1706,5 +1706,5 @@@ out: if (primary_if) batadv_hardif_free_ref(primary_if); - return ret; + return 0; } diff --combined net/batman-adv/debugfs.c index 391d4fb,ae79b19..ae79b19 --- a/net/batman-adv/debugfs.c +++ b/net/batman-adv/debugfs.c @@@ -31,6 -31,7 +31,7 @@@ #include "vis.h" #include "icmp_socket.h" #include "bridge_loop_avoidance.h" + #include "distributed-arp-table.h"
static struct dentry *batadv_debugfs;
@@@ -99,15 -100,17 +100,17 @@@ int batadv_debug_log(struct batadv_pri
static int batadv_log_open(struct inode *inode, struct file *file) { + if (!try_module_get(THIS_MODULE)) + return -EBUSY; + nonseekable_open(inode, file); file->private_data = inode->i_private; - batadv_inc_module_count(); return 0; }
static int batadv_log_release(struct inode *inode, struct file *file) { - batadv_dec_module_count(); + module_put(THIS_MODULE); return 0; }
@@@ -242,6 -245,16 +245,16 @@@ static int batadv_algorithms_open(struc return single_open(file, batadv_algo_seq_print_text, NULL); }
+ /** + * batadv_compat_open - Prepare file handler for printing of the compat version + * @inode: inode which was opened + * @file: file handle to be initialized + */ + static int batadv_compat_open(struct inode *inode, struct file *file) + { + return single_open(file, batadv_compat_seq_print_text, NULL); + } + static int batadv_originators_open(struct inode *inode, struct file *file) { struct net_device *net_dev = (struct net_device *)inode->i_private; @@@ -278,6 -291,19 +291,19 @@@ static int batadv_bla_backbone_table_op
#endif
+ #ifdef CONFIG_BATMAN_ADV_DAT + /** + * batadv_dat_cache_open - Prepare file handler for reads from dat_chache + * @inode: inode which was opened + * @file: file handle to be initialized + */ + static int batadv_dat_cache_open(struct inode *inode, struct file *file) + { + struct net_device *net_dev = (struct net_device *)inode->i_private; + return single_open(file, batadv_dat_cache_seq_print_text, net_dev); + } + #endif + static int batadv_transtable_local_open(struct inode *inode, struct file *file) { struct net_device *net_dev = (struct net_device *)inode->i_private; @@@ -307,7 -333,19 +333,19 @@@ struct batadv_debuginfo batadv_debuginf } \ };
+ /* the following attributes are general and therefore they will be directly + * placed in the BATADV_DEBUGFS_SUBDIR subdirectory of debugfs + */ static BATADV_DEBUGINFO(routing_algos, S_IRUGO, batadv_algorithms_open); + static BATADV_DEBUGINFO(compat_version, S_IRUGO, batadv_compat_open); + + static struct batadv_debuginfo *batadv_general_debuginfos[] = { + &batadv_debuginfo_routing_algos, + &batadv_debuginfo_compat_version, + NULL, + }; + + /* The following attributes are per soft interface */ static BATADV_DEBUGINFO(originators, S_IRUGO, batadv_originators_open); static BATADV_DEBUGINFO(gateways, S_IRUGO, batadv_gateways_open); static BATADV_DEBUGINFO(transtable_global, S_IRUGO, @@@ -317,6 -355,9 +355,9 @@@ static BATADV_DEBUGINFO(bla_claim_table static BATADV_DEBUGINFO(bla_backbone_table, S_IRUGO, batadv_bla_backbone_table_open); #endif + #ifdef CONFIG_BATMAN_ADV_DAT + static BATADV_DEBUGINFO(dat_cache, S_IRUGO, batadv_dat_cache_open); + #endif static BATADV_DEBUGINFO(transtable_local, S_IRUGO, batadv_transtable_local_open); static BATADV_DEBUGINFO(vis_data, S_IRUGO, batadv_vis_data_open); @@@ -329,6 -370,9 +370,9 @@@ static struct batadv_debuginfo *batadv_ &batadv_debuginfo_bla_claim_table, &batadv_debuginfo_bla_backbone_table, #endif + #ifdef CONFIG_BATMAN_ADV_DAT + &batadv_debuginfo_dat_cache, + #endif &batadv_debuginfo_transtable_local, &batadv_debuginfo_vis_data, NULL, @@@ -336,7 -380,7 +380,7 @@@
void batadv_debugfs_init(void) { - struct batadv_debuginfo *bat_debug; + struct batadv_debuginfo **bat_debug; struct dentry *file;
batadv_debugfs = debugfs_create_dir(BATADV_DEBUGFS_SUBDIR, NULL); @@@ -344,17 -388,23 +388,23 @@@ batadv_debugfs = NULL;
if (!batadv_debugfs) - goto out; + goto err;
- bat_debug = &batadv_debuginfo_routing_algos; - file = debugfs_create_file(bat_debug->attr.name, - S_IFREG | bat_debug->attr.mode, - batadv_debugfs, NULL, &bat_debug->fops); - if (!file) - pr_err("Can't add debugfs file: %s\n", bat_debug->attr.name); + for (bat_debug = batadv_general_debuginfos; *bat_debug; ++bat_debug) { + file = debugfs_create_file(((*bat_debug)->attr).name, + S_IFREG | ((*bat_debug)->attr).mode, + batadv_debugfs, NULL, + &(*bat_debug)->fops); + if (!file) { + pr_err("Can't add general debugfs file: %s\n", + ((*bat_debug)->attr).name); + goto err; + } + }
- out: return; + err: + debugfs_remove_recursive(batadv_debugfs); }
void batadv_debugfs_destroy(void) diff --combined net/batman-adv/distributed-arp-table.c index 0000000,c933615..c933615 mode 000000,100644..100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@@ -1,0 -1,1073 +1,1073 @@@ + /* Copyright (C) 2011-2012 B.A.T.M.A.N. contributors: + * + * Antonio Quartulli + * + * 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 <linux/if_ether.h> + #include <linux/if_arp.h> + #include <net/arp.h> + + #include "main.h" + #include "hash.h" + #include "distributed-arp-table.h" + #include "hard-interface.h" + #include "originator.h" + #include "send.h" + #include "types.h" + #include "translation-table.h" + #include "unicast.h" + + static void batadv_dat_purge(struct work_struct *work); + + /** + * batadv_dat_start_timer - initialise the DAT periodic worker + * @bat_priv: the bat priv with all the soft interface information + */ + static void batadv_dat_start_timer(struct batadv_priv *bat_priv) + { + INIT_DELAYED_WORK(&bat_priv->dat.work, batadv_dat_purge); + queue_delayed_work(batadv_event_workqueue, &bat_priv->dat.work, + msecs_to_jiffies(10000)); + } + + /** + * batadv_dat_entry_free_ref - decrements the dat_entry refcounter and possibly + * free it + * @dat_entry: the oentry to free + */ + static void batadv_dat_entry_free_ref(struct batadv_dat_entry *dat_entry) + { + if (atomic_dec_and_test(&dat_entry->refcount)) + kfree_rcu(dat_entry, rcu); + } + + /** + * batadv_dat_to_purge - checks whether a dat_entry has to be purged or not + * @dat_entry: the entry to check + * + * Returns true if the entry has to be purged now, false otherwise + */ + static bool batadv_dat_to_purge(struct batadv_dat_entry *dat_entry) + { + return batadv_has_timed_out(dat_entry->last_update, + BATADV_DAT_ENTRY_TIMEOUT); + } + + /** + * __batadv_dat_purge - delete entries from the DAT local storage + * @bat_priv: the bat priv with all the soft interface information + * @to_purge: function in charge to decide whether an entry has to be purged or + * not. This function takes the dat_entry as argument and has to + * returns a boolean value: true is the entry has to be deleted, + * false otherwise + * + * Loops over each entry in the DAT local storage and delete it if and only if + * the to_purge function passed as argument returns true + */ + static void __batadv_dat_purge(struct batadv_priv *bat_priv, + bool (*to_purge)(struct batadv_dat_entry *)) + { + spinlock_t *list_lock; /* protects write access to the hash lists */ + struct batadv_dat_entry *dat_entry; + struct hlist_node *node, *node_tmp; + struct hlist_head *head; + uint32_t i; + + if (!bat_priv->dat.hash) + return; + + for (i = 0; i < bat_priv->dat.hash->size; i++) { + head = &bat_priv->dat.hash->table[i]; + list_lock = &bat_priv->dat.hash->list_locks[i]; + + spin_lock_bh(list_lock); + hlist_for_each_entry_safe(dat_entry, node, node_tmp, head, + hash_entry) { + /* if an helper function has been passed as parameter, + * ask it if the entry has to be purged or not + */ + if (to_purge && !to_purge(dat_entry)) + continue; + + hlist_del_rcu(node); + batadv_dat_entry_free_ref(dat_entry); + } + spin_unlock_bh(list_lock); + } + } + + /** + * batadv_dat_purge - periodic task that deletes old entries from the local DAT + * hash table + * @work: kernel work struct + */ + static void batadv_dat_purge(struct work_struct *work) + { + struct delayed_work *delayed_work; + struct batadv_priv_dat *priv_dat; + struct batadv_priv *bat_priv; + + delayed_work = container_of(work, struct delayed_work, work); + priv_dat = container_of(delayed_work, struct batadv_priv_dat, work); + bat_priv = container_of(priv_dat, struct batadv_priv, dat); + + __batadv_dat_purge(bat_priv, batadv_dat_to_purge); + batadv_dat_start_timer(bat_priv); + } + + /** + * batadv_compare_dat - comparing function used in the local DAT hash table + * @node: node in the local table + * @data2: second object to compare the node to + * + * Returns 1 if the two entry are the same, 0 otherwise + */ + static int batadv_compare_dat(const struct hlist_node *node, const void *data2) + { + const void *data1 = container_of(node, struct batadv_dat_entry, + hash_entry); + + return (memcmp(data1, data2, sizeof(__be32)) == 0 ? 1 : 0); + } + + /** + * batadv_arp_hw_src - extract the hw_src field from an ARP packet + * @skb: ARP packet + * @hdr_size: size of the possible header before the ARP packet + * + * Returns the value of the hw_src field in the ARP packet + */ + static uint8_t *batadv_arp_hw_src(struct sk_buff *skb, int hdr_size) + { + uint8_t *addr; + + addr = (uint8_t *)(skb->data + hdr_size); + addr += ETH_HLEN + sizeof(struct arphdr); + + return addr; + } + + /** + * batadv_arp_ip_src - extract the ip_src field from an ARP packet + * @skb: ARP packet + * @hdr_size: size of the possible header before the ARP packet + * + * Returns the value of the ip_src field in the ARP packet + */ + static __be32 batadv_arp_ip_src(struct sk_buff *skb, int hdr_size) + { + return *(__be32 *)(batadv_arp_hw_src(skb, hdr_size) + ETH_ALEN); + } + + /** + * batadv_arp_hw_dst - extract the hw_dst field from an ARP packet + * @skb: ARP packet + * @hdr_size: size of the possible header before the ARP packet + * + * Returns the value of the hw_dst field in the ARP packet + */ + static uint8_t *batadv_arp_hw_dst(struct sk_buff *skb, int hdr_size) + { + return batadv_arp_hw_src(skb, hdr_size) + ETH_ALEN + 4; + } + + /** + * batadv_arp_ip_dst - extract the ip_dst field from an ARP packet + * @skb: ARP packet + * @hdr_size: size of the possible header before the ARP packet + * + * Returns the value of the ip_dst field in the ARP packet + */ + static __be32 batadv_arp_ip_dst(struct sk_buff *skb, int hdr_size) + { + return *(__be32 *)(batadv_arp_hw_src(skb, hdr_size) + ETH_ALEN * 2 + 4); + } + + /** + * batadv_hash_dat - compute the hash value for an IP address + * @data: data to hash + * @size: size of the hash table + * + * Returns the selected index in the hash table for the given data + */ + static uint32_t batadv_hash_dat(const void *data, uint32_t size) + { + const unsigned char *key = data; + uint32_t hash = 0; + size_t i; + + for (i = 0; i < 4; i++) { + hash += key[i]; + hash += (hash << 10); + hash ^= (hash >> 6); + } + + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + + return hash % size; + } + + /** + * batadv_dat_entry_hash_find - looks for a given dat_entry in the local hash + * table + * @bat_priv: the bat priv with all the soft interface information + * @ip: search key + * + * Returns the dat_entry if found, NULL otherwise + */ + static struct batadv_dat_entry * + batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip) + { + struct hlist_head *head; + struct hlist_node *node; + struct batadv_dat_entry *dat_entry, *dat_entry_tmp = NULL; + struct batadv_hashtable *hash = bat_priv->dat.hash; + uint32_t index; + + if (!hash) + return NULL; + + index = batadv_hash_dat(&ip, hash->size); + head = &hash->table[index]; + + rcu_read_lock(); + hlist_for_each_entry_rcu(dat_entry, node, head, hash_entry) { + if (dat_entry->ip != ip) + continue; + + if (!atomic_inc_not_zero(&dat_entry->refcount)) + continue; + + dat_entry_tmp = dat_entry; + break; + } + rcu_read_unlock(); + + return dat_entry_tmp; + } + + /** + * batadv_dat_entry_add - add a new dat entry or update it if already exists + * @bat_priv: the bat priv with all the soft interface information + * @ip: ipv4 to add/edit + * @mac_addr: mac address to assign to the given ipv4 + */ + static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip, + uint8_t *mac_addr) + { + struct batadv_dat_entry *dat_entry; + int hash_added; + + dat_entry = batadv_dat_entry_hash_find(bat_priv, ip); + /* if this entry is already known, just update it */ + if (dat_entry) { + if (!batadv_compare_eth(dat_entry->mac_addr, mac_addr)) + memcpy(dat_entry->mac_addr, mac_addr, ETH_ALEN); + dat_entry->last_update = jiffies; + batadv_dbg(BATADV_DBG_DAT, bat_priv, + "Entry updated: %pI4 %pM\n", &dat_entry->ip, + dat_entry->mac_addr); + goto out; + } + + dat_entry = kmalloc(sizeof(*dat_entry), GFP_ATOMIC); + if (!dat_entry) + goto out; + + dat_entry->ip = ip; + memcpy(dat_entry->mac_addr, mac_addr, ETH_ALEN); + dat_entry->last_update = jiffies; + atomic_set(&dat_entry->refcount, 2); + + hash_added = batadv_hash_add(bat_priv->dat.hash, batadv_compare_dat, + batadv_hash_dat, &dat_entry->ip, + &dat_entry->hash_entry); + + if (unlikely(hash_added != 0)) { + /* remove the reference for the hash */ + batadv_dat_entry_free_ref(dat_entry); + goto out; + } + + batadv_dbg(BATADV_DBG_DAT, bat_priv, "New entry added: %pI4 %pM\n", + &dat_entry->ip, dat_entry->mac_addr); + + out: + if (dat_entry) + batadv_dat_entry_free_ref(dat_entry); + } + + #ifdef CONFIG_BATMAN_ADV_DEBUG + + /** + * batadv_dbg_arp - print a debug message containing all the ARP packet details + * @bat_priv: the bat priv with all the soft interface information + * @skb: ARP packet + * @type: ARP type + * @hdr_size: size of the possible header before the ARP packet + * @msg: message to print together with the debugging information + */ + static void batadv_dbg_arp(struct batadv_priv *bat_priv, struct sk_buff *skb, + uint16_t type, int hdr_size, char *msg) + { + struct batadv_unicast_4addr_packet *unicast_4addr_packet; + struct batadv_bcast_packet *bcast_pkt; + uint8_t *orig_addr; + __be32 ip_src, ip_dst; + + if (msg) + batadv_dbg(BATADV_DBG_DAT, bat_priv, "%s\n", msg); + + ip_src = batadv_arp_ip_src(skb, hdr_size); + ip_dst = batadv_arp_ip_dst(skb, hdr_size); + batadv_dbg(BATADV_DBG_DAT, bat_priv, + "ARP MSG = [src: %pM-%pI4 dst: %pM-%pI4]\n", + batadv_arp_hw_src(skb, hdr_size), &ip_src, + batadv_arp_hw_dst(skb, hdr_size), &ip_dst); + + if (hdr_size == 0) + return; + + /* if the ARP packet is encapsulated in a batman packet, let's print + * some debug messages + */ + unicast_4addr_packet = (struct batadv_unicast_4addr_packet *)skb->data; + + switch (unicast_4addr_packet->u.header.packet_type) { + case BATADV_UNICAST: + batadv_dbg(BATADV_DBG_DAT, bat_priv, + "* encapsulated within a UNICAST packet\n"); + break; + case BATADV_UNICAST_4ADDR: + batadv_dbg(BATADV_DBG_DAT, bat_priv, + "* encapsulated within a UNICAST_4ADDR packet (src: %pM)\n", + unicast_4addr_packet->src); + switch (unicast_4addr_packet->subtype) { + case BATADV_P_DAT_DHT_PUT: + batadv_dbg(BATADV_DBG_DAT, bat_priv, "* type: DAT_DHT_PUT\n"); + break; + case BATADV_P_DAT_DHT_GET: + batadv_dbg(BATADV_DBG_DAT, bat_priv, "* type: DAT_DHT_GET\n"); + break; + case BATADV_P_DAT_CACHE_REPLY: + batadv_dbg(BATADV_DBG_DAT, bat_priv, + "* type: DAT_CACHE_REPLY\n"); + break; + case BATADV_P_DATA: + batadv_dbg(BATADV_DBG_DAT, bat_priv, "* type: DATA\n"); + break; + default: + batadv_dbg(BATADV_DBG_DAT, bat_priv, "* type: Unknown (%u)!\n", + unicast_4addr_packet->u.header.packet_type); + } + break; + case BATADV_BCAST: + bcast_pkt = (struct batadv_bcast_packet *)unicast_4addr_packet; + orig_addr = bcast_pkt->orig; + batadv_dbg(BATADV_DBG_DAT, bat_priv, + "* encapsulated within a BCAST packet (src: %pM)\n", + orig_addr); + break; + default: + batadv_dbg(BATADV_DBG_DAT, bat_priv, + "* encapsulated within an unknown packet type (0x%x)\n", + unicast_4addr_packet->u.header.packet_type); + } + } + + #else + + static void batadv_dbg_arp(struct batadv_priv *bat_priv, struct sk_buff *skb, + uint16_t type, int hdr_size, char *msg) + { + } + + #endif /* CONFIG_BATMAN_ADV_DEBUG */ + + /** + * batadv_is_orig_node_eligible - check whether a node can be a DHT candidate + * @res: the array with the already selected candidates + * @select: number of already selected candidates + * @tmp_max: address of the currently evaluated node + * @max: current round max address + * @last_max: address of the last selected candidate + * @candidate: orig_node under evaluation + * @max_orig_node: last selected candidate + * + * Returns true if the node has been elected as next candidate or false othrwise + */ + static bool batadv_is_orig_node_eligible(struct batadv_dat_candidate *res, + int select, batadv_dat_addr_t tmp_max, + batadv_dat_addr_t max, + batadv_dat_addr_t last_max, + struct batadv_orig_node *candidate, + struct batadv_orig_node *max_orig_node) + { + bool ret = false; + int j; + + /* Check if this node has already been selected... */ + for (j = 0; j < select; j++) + if (res[j].orig_node == candidate) + break; + /* ..and possibly skip it */ + if (j < select) + goto out; + /* sanity check: has it already been selected? This should not happen */ + if (tmp_max > last_max) + goto out; + /* check if during this iteration an originator with a closer dht + * address has already been found + */ + if (tmp_max < max) + goto out; + /* this is an hash collision with the temporary selected node. Choose + * the one with the lowest address + */ + if ((tmp_max == max) && + (batadv_compare_eth(candidate->orig, max_orig_node->orig) > 0)) + goto out; + + ret = true; + out: + return ret; + } + + /** + * batadv_choose_next_candidate - select the next DHT candidate + * @bat_priv: the bat priv with all the soft interface information + * @cands: candidates array + * @select: number of candidates already present in the array + * @ip_key: key to look up in the DHT + * @last_max: pointer where the address of the selected candidate will be saved + */ + static void batadv_choose_next_candidate(struct batadv_priv *bat_priv, + struct batadv_dat_candidate *cands, + int select, batadv_dat_addr_t ip_key, + batadv_dat_addr_t *last_max) + { + batadv_dat_addr_t max = 0, tmp_max = 0; + struct batadv_orig_node *orig_node, *max_orig_node = NULL; + struct batadv_hashtable *hash = bat_priv->orig_hash; + struct hlist_node *node; + struct hlist_head *head; + int i; + + /* if no node is eligible as candidate, leave the candidate type as + * NOT_FOUND + */ + cands[select].type = BATADV_DAT_CANDIDATE_NOT_FOUND; + + /* iterate over the originator list and find the node with closest + * dat_address which has not been selected yet + */ + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + + rcu_read_lock(); + hlist_for_each_entry_rcu(orig_node, node, head, hash_entry) { + /* the dht space is a ring and addresses are unsigned */ + tmp_max = BATADV_DAT_ADDR_MAX - orig_node->dat_addr + + ip_key; + + if (!batadv_is_orig_node_eligible(cands, select, + tmp_max, max, + *last_max, orig_node, + max_orig_node)) + continue; + + if (!atomic_inc_not_zero(&orig_node->refcount)) + continue; + + max = tmp_max; + if (max_orig_node) + batadv_orig_node_free_ref(max_orig_node); + max_orig_node = orig_node; + } + rcu_read_unlock(); + } + if (max_orig_node) { + cands[select].type = BATADV_DAT_CANDIDATE_ORIG; + cands[select].orig_node = max_orig_node; + batadv_dbg(BATADV_DBG_DAT, bat_priv, + "dat_select_candidates() %d: selected %pM addr=%u dist=%u\n", + select, max_orig_node->orig, max_orig_node->dat_addr, + max); + } + *last_max = max; + } + + /** + * batadv_dat_select_candidates - selects the nodes which the DHT message has to + * be sent to + * @bat_priv: the bat priv with all the soft interface information + * @ip_dst: ipv4 to look up in the DHT + * + * An originator O is selected if and only if its DHT_ID value is one of three + * closest values (from the LEFT, with wrap around if needed) then the hash + * value of the key. ip_dst is the key. + * + * Returns the candidate array of size BATADV_DAT_CANDIDATE_NUM + */ + static struct batadv_dat_candidate * + batadv_dat_select_candidates(struct batadv_priv *bat_priv, __be32 ip_dst) + { + int select; + batadv_dat_addr_t last_max = BATADV_DAT_ADDR_MAX, ip_key; + struct batadv_dat_candidate *res; + + if (!bat_priv->orig_hash) + return NULL; + + res = kmalloc(BATADV_DAT_CANDIDATES_NUM * sizeof(*res), GFP_ATOMIC); + if (!res) + return NULL; + + ip_key = (batadv_dat_addr_t)batadv_hash_dat(&ip_dst, + BATADV_DAT_ADDR_MAX); + + batadv_dbg(BATADV_DBG_DAT, bat_priv, + "dat_select_candidates(): IP=%pI4 hash(IP)=%u\n", &ip_dst, + ip_key); + + for (select = 0; select < BATADV_DAT_CANDIDATES_NUM; select++) + batadv_choose_next_candidate(bat_priv, res, select, ip_key, + &last_max); + + return res; + } + + /** + * batadv_dat_send_data - send a payload to the selected candidates + * @bat_priv: the bat priv with all the soft interface information + * @skb: payload to send + * @ip: the DHT key + * @packet_subtype: unicast4addr packet subtype to use + * + * In this function the skb is copied by means of pskb_copy() and is sent as + * unicast packet to each of the selected candidates + * + * Returns true if the packet is sent to at least one candidate, false otherwise + */ + static bool batadv_dat_send_data(struct batadv_priv *bat_priv, + struct sk_buff *skb, __be32 ip, + int packet_subtype) + { + int i; + bool ret = false; + int send_status; + struct batadv_neigh_node *neigh_node = NULL; + struct sk_buff *tmp_skb; + struct batadv_dat_candidate *cand; + + cand = batadv_dat_select_candidates(bat_priv, ip); + if (!cand) + goto out; + + batadv_dbg(BATADV_DBG_DAT, bat_priv, "DHT_SEND for %pI4\n", &ip); + + for (i = 0; i < BATADV_DAT_CANDIDATES_NUM; i++) { + if (cand[i].type == BATADV_DAT_CANDIDATE_NOT_FOUND) + continue; + + neigh_node = batadv_orig_node_get_router(cand[i].orig_node); + if (!neigh_node) + goto free_orig; + + tmp_skb = pskb_copy(skb, GFP_ATOMIC); + if (!batadv_unicast_4addr_prepare_skb(bat_priv, tmp_skb, + cand[i].orig_node, + packet_subtype)) { + kfree_skb(tmp_skb); + goto free_neigh; + } + + send_status = batadv_send_skb_packet(tmp_skb, + neigh_node->if_incoming, + neigh_node->addr); + if (send_status == NET_XMIT_SUCCESS) { + /* count the sent packet */ + switch (packet_subtype) { + case BATADV_P_DAT_DHT_GET: + batadv_inc_counter(bat_priv, + BATADV_CNT_DAT_GET_TX); + break; + case BATADV_P_DAT_DHT_PUT: + batadv_inc_counter(bat_priv, + BATADV_CNT_DAT_PUT_TX); + break; + } + + /* packet sent to a candidate: return true */ + ret = true; + } + free_neigh: + batadv_neigh_node_free_ref(neigh_node); + free_orig: + batadv_orig_node_free_ref(cand[i].orig_node); + } + + out: + kfree(cand); + return ret; + } + + /** + * batadv_dat_hash_free - free the local DAT hash table + * @bat_priv: the bat priv with all the soft interface information + */ + static void batadv_dat_hash_free(struct batadv_priv *bat_priv) + { + __batadv_dat_purge(bat_priv, NULL); + + batadv_hash_destroy(bat_priv->dat.hash); + + bat_priv->dat.hash = NULL; + } + + /** + * batadv_dat_init - initialise the DAT internals + * @bat_priv: the bat priv with all the soft interface information + */ + int batadv_dat_init(struct batadv_priv *bat_priv) + { + if (bat_priv->dat.hash) + return 0; + + bat_priv->dat.hash = batadv_hash_new(1024); + + if (!bat_priv->dat.hash) + return -ENOMEM; + + batadv_dat_start_timer(bat_priv); + + return 0; + } + + /** + * batadv_dat_free - free the DAT internals + * @bat_priv: the bat priv with all the soft interface information + */ + void batadv_dat_free(struct batadv_priv *bat_priv) + { + cancel_delayed_work_sync(&bat_priv->dat.work); + + batadv_dat_hash_free(bat_priv); + } + + /** + * batadv_dat_cache_seq_print_text - print the local DAT hash table + * @seq: seq file to print on + * @offset: not used + */ + int batadv_dat_cache_seq_print_text(struct seq_file *seq, void *offset) + { + struct net_device *net_dev = (struct net_device *)seq->private; + struct batadv_priv *bat_priv = netdev_priv(net_dev); + struct batadv_hashtable *hash = bat_priv->dat.hash; + struct batadv_dat_entry *dat_entry; + struct batadv_hard_iface *primary_if; + struct hlist_node *node; + struct hlist_head *head; + unsigned long last_seen_jiffies; + int last_seen_msecs, last_seen_secs, last_seen_mins; + uint32_t i; + + primary_if = batadv_seq_print_text_primary_if_get(seq); + if (!primary_if) + goto out; + + seq_printf(seq, "Distributed ARP Table (%s):\n", net_dev->name); + seq_printf(seq, " %-7s %-13s %5s\n", "IPv4", "MAC", + "last-seen"); + + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + + rcu_read_lock(); + hlist_for_each_entry_rcu(dat_entry, node, head, hash_entry) { + last_seen_jiffies = jiffies - dat_entry->last_update; + last_seen_msecs = jiffies_to_msecs(last_seen_jiffies); + last_seen_mins = last_seen_msecs / 60000; + last_seen_msecs = last_seen_msecs % 60000; + last_seen_secs = last_seen_msecs / 1000; + + seq_printf(seq, " * %15pI4 %14pM %6i:%02i\n", + &dat_entry->ip, dat_entry->mac_addr, + last_seen_mins, last_seen_secs); + } + rcu_read_unlock(); + } + + out: + if (primary_if) + batadv_hardif_free_ref(primary_if); + return 0; + } + + /** + * batadv_arp_get_type - parse an ARP packet and gets the type + * @bat_priv: the bat priv with all the soft interface information + * @skb: packet to analyse + * @hdr_size: size of the possible header before the ARP packet in the skb + * + * Returns the ARP type if the skb contains a valid ARP packet, 0 otherwise + */ + static uint16_t batadv_arp_get_type(struct batadv_priv *bat_priv, + struct sk_buff *skb, int hdr_size) + { + struct arphdr *arphdr; + struct ethhdr *ethhdr; + __be32 ip_src, ip_dst; + uint16_t type = 0; + + /* pull the ethernet header */ + if (unlikely(!pskb_may_pull(skb, hdr_size + ETH_HLEN))) + goto out; + + ethhdr = (struct ethhdr *)(skb->data + hdr_size); + + if (ethhdr->h_proto != htons(ETH_P_ARP)) + goto out; + + /* pull the ARP payload */ + if (unlikely(!pskb_may_pull(skb, hdr_size + ETH_HLEN + + arp_hdr_len(skb->dev)))) + goto out; + + arphdr = (struct arphdr *)(skb->data + hdr_size + ETH_HLEN); + + /* Check whether the ARP packet carries a valid + * IP information + */ + if (arphdr->ar_hrd != htons(ARPHRD_ETHER)) + goto out; + + if (arphdr->ar_pro != htons(ETH_P_IP)) + goto out; + + if (arphdr->ar_hln != ETH_ALEN) + goto out; + + if (arphdr->ar_pln != 4) + goto out; + + /* Check for bad reply/request. If the ARP message is not sane, DAT + * will simply ignore it + */ + ip_src = batadv_arp_ip_src(skb, hdr_size); + ip_dst = batadv_arp_ip_dst(skb, hdr_size); + if (ipv4_is_loopback(ip_src) || ipv4_is_multicast(ip_src) || + ipv4_is_loopback(ip_dst) || ipv4_is_multicast(ip_dst)) + goto out; + + type = ntohs(arphdr->ar_op); + out: + return type; + } + + /** + * batadv_dat_snoop_outgoing_arp_request - snoop the ARP request and try to + * answer using DAT + * @bat_priv: the bat priv with all the soft interface information + * @skb: packet to check + * + * Returns true if the message has been sent to the dht candidates, false + * otherwise. In case of true the message has to be enqueued to permit the + * fallback + */ + bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv, + struct sk_buff *skb) + { + uint16_t type = 0; + __be32 ip_dst, ip_src; + uint8_t *hw_src; + bool ret = false; + struct batadv_dat_entry *dat_entry = NULL; + struct sk_buff *skb_new; + struct batadv_hard_iface *primary_if = NULL; + + if (!atomic_read(&bat_priv->distributed_arp_table)) + goto out; + + type = batadv_arp_get_type(bat_priv, skb, 0); + /* If the node gets an ARP_REQUEST it has to send a DHT_GET unicast + * message to the selected DHT candidates + */ + if (type != ARPOP_REQUEST) + goto out; + + batadv_dbg_arp(bat_priv, skb, type, 0, "Parsing outgoing ARP REQUEST"); + + ip_src = batadv_arp_ip_src(skb, 0); + hw_src = batadv_arp_hw_src(skb, 0); + ip_dst = batadv_arp_ip_dst(skb, 0); + + batadv_dat_entry_add(bat_priv, ip_src, hw_src); + + dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst); + if (dat_entry) { + primary_if = batadv_primary_if_get_selected(bat_priv); + if (!primary_if) + goto out; + + skb_new = arp_create(ARPOP_REPLY, ETH_P_ARP, ip_src, + primary_if->soft_iface, ip_dst, hw_src, + dat_entry->mac_addr, hw_src); + if (!skb_new) + goto out; + + skb_reset_mac_header(skb_new); + skb_new->protocol = eth_type_trans(skb_new, + primary_if->soft_iface); + bat_priv->stats.rx_packets++; + bat_priv->stats.rx_bytes += skb->len + ETH_HLEN; + primary_if->soft_iface->last_rx = jiffies; + + netif_rx(skb_new); + batadv_dbg(BATADV_DBG_DAT, bat_priv, "ARP request replied locally\n"); + ret = true; + } else { + /* Send the request on the DHT */ + ret = batadv_dat_send_data(bat_priv, skb, ip_dst, + BATADV_P_DAT_DHT_GET); + } + out: + if (dat_entry) + batadv_dat_entry_free_ref(dat_entry); + if (primary_if) + batadv_hardif_free_ref(primary_if); + return ret; + } + + /** + * batadv_dat_snoop_incoming_arp_request - snoop the ARP request and try to + * answer using the local DAT storage + * @bat_priv: the bat priv with all the soft interface information + * @skb: packet to check + * @hdr_size: size of the encapsulation header + * + * Returns true if the request has been answered, false otherwise + */ + bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv, + struct sk_buff *skb, int hdr_size) + { + uint16_t type; + __be32 ip_src, ip_dst; + uint8_t *hw_src; + struct sk_buff *skb_new; + struct batadv_hard_iface *primary_if = NULL; + struct batadv_dat_entry *dat_entry = NULL; + bool ret = false; + int err; + + if (!atomic_read(&bat_priv->distributed_arp_table)) + goto out; + + type = batadv_arp_get_type(bat_priv, skb, hdr_size); + if (type != ARPOP_REQUEST) + goto out; + + hw_src = batadv_arp_hw_src(skb, hdr_size); + ip_src = batadv_arp_ip_src(skb, hdr_size); + ip_dst = batadv_arp_ip_dst(skb, hdr_size); + + batadv_dbg_arp(bat_priv, skb, type, hdr_size, + "Parsing incoming ARP REQUEST"); + + batadv_dat_entry_add(bat_priv, ip_src, hw_src); + + dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst); + if (!dat_entry) + goto out; + + primary_if = batadv_primary_if_get_selected(bat_priv); + if (!primary_if) + goto out; + + skb_new = arp_create(ARPOP_REPLY, ETH_P_ARP, ip_src, + primary_if->soft_iface, ip_dst, hw_src, + dat_entry->mac_addr, hw_src); + + if (!skb_new) + goto out; + + /* to preserve backwards compatibility, here the node has to answer + * using the same packet type it received for the request. This is due + * to that if a node is not using the 4addr packet format it may not + * support it. + */ + if (hdr_size == sizeof(struct batadv_unicast_4addr_packet)) + err = batadv_unicast_4addr_send_skb(bat_priv, skb_new, + BATADV_P_DAT_CACHE_REPLY); + else + err = batadv_unicast_send_skb(bat_priv, skb_new); + + if (!err) { + batadv_inc_counter(bat_priv, BATADV_CNT_DAT_CACHED_REPLY_TX); + ret = true; + } + out: + if (dat_entry) + batadv_dat_entry_free_ref(dat_entry); + if (primary_if) + batadv_hardif_free_ref(primary_if); + if (ret) + kfree_skb(skb); + return ret; + } + + /** + * batadv_dat_snoop_outgoing_arp_reply - snoop the ARP reply and fill the DHT + * @bat_priv: the bat priv with all the soft interface information + * @skb: packet to check + */ + void batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv, + struct sk_buff *skb) + { + uint16_t type; + __be32 ip_src, ip_dst; + uint8_t *hw_src, *hw_dst; + + if (!atomic_read(&bat_priv->distributed_arp_table)) + return; + + type = batadv_arp_get_type(bat_priv, skb, 0); + if (type != ARPOP_REPLY) + return; + + batadv_dbg_arp(bat_priv, skb, type, 0, "Parsing outgoing ARP REPLY"); + + hw_src = batadv_arp_hw_src(skb, 0); + ip_src = batadv_arp_ip_src(skb, 0); + hw_dst = batadv_arp_hw_dst(skb, 0); + ip_dst = batadv_arp_ip_dst(skb, 0); + + batadv_dat_entry_add(bat_priv, ip_src, hw_src); + batadv_dat_entry_add(bat_priv, ip_dst, hw_dst); + + /* Send the ARP reply to the candidates for both the IP addresses that + * the node got within the ARP reply + */ + batadv_dat_send_data(bat_priv, skb, ip_src, BATADV_P_DAT_DHT_PUT); + batadv_dat_send_data(bat_priv, skb, ip_dst, BATADV_P_DAT_DHT_PUT); + } + /** + * batadv_dat_snoop_incoming_arp_reply - snoop the ARP reply and fill the local + * DAT storage only + * @bat_priv: the bat priv with all the soft interface information + * @skb: packet to check + * @hdr_size: siaze of the encapsulation header + */ + bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv, + struct sk_buff *skb, int hdr_size) + { + uint16_t type; + __be32 ip_src, ip_dst; + uint8_t *hw_src, *hw_dst; + bool ret = false; + + if (!atomic_read(&bat_priv->distributed_arp_table)) + goto out; + + type = batadv_arp_get_type(bat_priv, skb, hdr_size); + if (type != ARPOP_REPLY) + goto out; + + batadv_dbg_arp(bat_priv, skb, type, hdr_size, + "Parsing incoming ARP REPLY"); + + hw_src = batadv_arp_hw_src(skb, hdr_size); + ip_src = batadv_arp_ip_src(skb, hdr_size); + hw_dst = batadv_arp_hw_dst(skb, hdr_size); + ip_dst = batadv_arp_ip_dst(skb, hdr_size); + + /* Update our internal cache with both the IP addresses the node got + * within the ARP reply + */ + batadv_dat_entry_add(bat_priv, ip_src, hw_src); + batadv_dat_entry_add(bat_priv, ip_dst, hw_dst); + + /* if this REPLY is directed to a client of mine, let's deliver the + * packet to the interface + */ + ret = !batadv_is_my_client(bat_priv, hw_dst); + out: + /* if ret == false -> packet has to be delivered to the interface */ + return ret; + } + + /** + * batadv_dat_drop_broadcast_packet - check if an ARP request has to be dropped + * (because the node has already got the reply via DAT) or not + * @bat_priv: the bat priv with all the soft interface information + * @forw_packet: the broadcast packet + * + * Returns true if the node can drop the packet, false otherwise + */ + bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv, + struct batadv_forw_packet *forw_packet) + { + uint16_t type; + __be32 ip_dst; + struct batadv_dat_entry *dat_entry = NULL; + bool ret = false; + const size_t bcast_len = sizeof(struct batadv_bcast_packet); + + if (!atomic_read(&bat_priv->distributed_arp_table)) + goto out; + + /* If this packet is an ARP_REQUEST and the node already has the + * information that it is going to ask, then the packet can be dropped + */ + if (forw_packet->num_packets) + goto out; + + type = batadv_arp_get_type(bat_priv, forw_packet->skb, bcast_len); + if (type != ARPOP_REQUEST) + goto out; + + ip_dst = batadv_arp_ip_dst(forw_packet->skb, bcast_len); + dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst); + /* check if the node already got this entry */ + if (!dat_entry) { + batadv_dbg(BATADV_DBG_DAT, bat_priv, + "ARP Request for %pI4: fallback\n", &ip_dst); + goto out; + } + + batadv_dbg(BATADV_DBG_DAT, bat_priv, + "ARP Request for %pI4: fallback prevented\n", &ip_dst); + ret = true; + + out: + if (dat_entry) + batadv_dat_entry_free_ref(dat_entry); + return ret; + } + + void batadv_dat_switch(struct net_device *net_dev) + { + struct batadv_priv *bat_priv = netdev_priv(net_dev); + + if (atomic_read(&bat_priv->distributed_arp_table)) + batadv_dat_init(bat_priv); + else + batadv_dat_free(bat_priv); + } diff --combined net/batman-adv/distributed-arp-table.h index 0000000,63a13a5..63a13a5 mode 000000,100644..100644 --- a/net/batman-adv/distributed-arp-table.h +++ b/net/batman-adv/distributed-arp-table.h @@@ -1,0 -1,168 +1,168 @@@ + /* Copyright (C) 2011-2012 B.A.T.M.A.N. contributors: + * + * Antonio Quartulli + * + * 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_ARP_H_ + #define _NET_BATMAN_ADV_ARP_H_ + + #ifdef CONFIG_BATMAN_ADV_DAT + + #include "types.h" + #include "originator.h" + + #include <linux/if_arp.h> + + #define BATADV_DAT_ADDR_MAX ((batadv_dat_addr_t)~(batadv_dat_addr_t)0) + + bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv, + struct sk_buff *skb); + bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv, + struct sk_buff *skb, int hdr_size); + void batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv, + struct sk_buff *skb); + bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv, + struct sk_buff *skb, int hdr_size); + bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv, + struct batadv_forw_packet *forw_packet); + + /** + * batadv_dat_init_orig_node_addr - assign a DAT address to the orig_node + * @orig_node: the node to assign the DAT address to + */ + static inline void + batadv_dat_init_orig_node_addr(struct batadv_orig_node *orig_node) + { + uint32_t addr; + + addr = batadv_choose_orig(orig_node->orig, BATADV_DAT_ADDR_MAX); + orig_node->dat_addr = (batadv_dat_addr_t)addr; + } + + /** + * batadv_dat_init_own_addr - assign a DAT address to the node itself + * @bat_priv: the bat priv with all the soft interface information + * @primary_if: a pointer to the primary interface + */ + static inline void + batadv_dat_init_own_addr(struct batadv_priv *bat_priv, + struct batadv_hard_iface *primary_if) + { + uint32_t addr; + + addr = batadv_choose_orig(primary_if->net_dev->dev_addr, + BATADV_DAT_ADDR_MAX); + + bat_priv->dat.addr = (batadv_dat_addr_t)addr; + } + + int batadv_dat_init(struct batadv_priv *bat_priv); + void batadv_dat_free(struct batadv_priv *bat_priv); + int batadv_dat_cache_seq_print_text(struct seq_file *seq, void *offset); + void batadv_dat_switch(struct net_device *net_dev); + + /** + * batadv_dat_inc_counter - increment the correct DAT packet counter + * @bat_priv: the bat priv with all the soft interface information + * @subtype: the 4addr subtype of the packet to be counted + * + * Updates the ethtool statistics for the received packet if it is a DAT subtype + */ + static inline void batadv_dat_inc_counter(struct batadv_priv *bat_priv, + uint8_t subtype) + { + switch (subtype) { + case BATADV_P_DAT_DHT_GET: + batadv_inc_counter(bat_priv, + BATADV_CNT_DAT_GET_RX); + break; + case BATADV_P_DAT_DHT_PUT: + batadv_inc_counter(bat_priv, + BATADV_CNT_DAT_PUT_RX); + break; + } + } + + #else + + static inline bool + batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv, + struct sk_buff *skb) + { + return false; + } + + static inline bool + batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv, + struct sk_buff *skb, int hdr_size) + { + return false; + } + + static inline bool + batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv, + struct sk_buff *skb) + { + return false; + } + + static inline bool + batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv, + struct sk_buff *skb, int hdr_size) + { + return false; + } + + static inline bool + batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv, + struct batadv_forw_packet *forw_packet) + { + return false; + } + + static inline void + batadv_dat_init_orig_node_addr(struct batadv_orig_node *orig_node) + { + } + + static inline void batadv_dat_init_own_addr(struct batadv_priv *bat_priv, + struct batadv_hard_iface *iface) + { + } + + static inline void batadv_arp_change_timeout(struct net_device *soft_iface, + const char *name) + { + } + + static inline int batadv_dat_init(struct batadv_priv *bat_priv) + { + return 0; + } + + static inline void batadv_dat_free(struct batadv_priv *bat_priv) + { + } + + static inline void batadv_dat_inc_counter(struct batadv_priv *bat_priv, + uint8_t subtype) + { + } + + #endif /* CONFIG_BATMAN_ADV_DAT */ + + #endif /* _NET_BATMAN_ADV_ARP_H_ */ diff --combined net/batman-adv/gateway_client.c index 15d67ab,dd07c7e..dd07c7e --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c @@@ -477,22 -477,11 +477,11 @@@ int batadv_gw_client_seq_print_text(str struct batadv_hard_iface *primary_if; struct batadv_gw_node *gw_node; struct hlist_node *node; - int gw_count = 0, ret = 0; + int gw_count = 0;
- primary_if = batadv_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); + primary_if = batadv_seq_print_text_primary_if_get(seq); + if (!primary_if) goto out; - } - - if (primary_if->if_status != BATADV_IF_ACTIVE) { - ret = seq_printf(seq, - "BATMAN mesh %s disabled - primary interface not active\n", - net_dev->name); - goto out; - }
seq_printf(seq, " %-12s (%s/%i) %17s [%10s]: gw_class ... [B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s)]\n", @@@ -519,7 -508,7 +508,7 @@@ out: if (primary_if) batadv_hardif_free_ref(primary_if); - return ret; + return 0; }
static bool batadv_is_type_dhcprequest(struct sk_buff *skb, int header_len) diff --combined net/batman-adv/hard-interface.c index d112fd6,365ed74..365ed74 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@@ -18,6 -18,7 +18,7 @@@ */
#include "main.h" + #include "distributed-arp-table.h" #include "hard-interface.h" #include "soft-interface.h" #include "send.h" @@@ -58,6 -59,45 +59,45 @@@ out return hard_iface; }
+ /** + * batadv_is_on_batman_iface - check if a device is a batman iface descendant + * @net_dev: the device to check + * + * If the user creates any virtual device on top of a batman-adv interface, it + * is important to prevent this new interface to be used to create a new mesh + * network (this behaviour would lead to a batman-over-batman configuration). + * This function recursively checks all the fathers of the device passed as + * argument looking for a batman-adv soft interface. + * + * Returns true if the device is descendant of a batman-adv mesh interface (or + * if it is a batman-adv interface itself), false otherwise + */ + static bool batadv_is_on_batman_iface(const struct net_device *net_dev) + { + struct net_device *parent_dev; + bool ret; + + /* check if this is a batman-adv mesh interface */ + if (batadv_softif_is_valid(net_dev)) + return true; + + /* no more parents..stop recursion */ + if (net_dev->iflink == net_dev->ifindex) + return false; + + /* recurse over the parent device */ + parent_dev = dev_get_by_index(&init_net, net_dev->iflink); + /* if we got a NULL parent_dev there is something broken.. */ + if (WARN(!parent_dev, "Cannot find parent device")) + return false; + + ret = batadv_is_on_batman_iface(parent_dev); + + if (parent_dev) + dev_put(parent_dev); + return ret; + } + static int batadv_is_valid_iface(const struct net_device *net_dev) { if (net_dev->flags & IFF_LOOPBACK) @@@ -70,7 -110,7 +110,7 @@@ return 0;
/* no batman over batman */ - if (batadv_softif_is_valid(net_dev)) + if (batadv_is_on_batman_iface(net_dev)) return 0;
return 1; @@@ -109,6 -149,8 +149,8 @@@ static void batadv_primary_if_update_ad if (!primary_if) goto out;
+ batadv_dat_init_own_addr(bat_priv, primary_if); + skb = bat_priv->vis.my_info->skb_packet; vis_packet = (struct batadv_vis_packet *)skb->data; memcpy(vis_packet->vis_orig, primary_if->net_dev->dev_addr, ETH_ALEN); @@@ -450,8 -492,8 +492,8 @@@ batadv_hardif_add_interface(struct net_ /* 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; + atomic_set(&hard_iface->bat_iv.ogm_seqno, 1); + hard_iface->bat_iv.ogm_buff = NULL;
return hard_iface;
diff --combined net/batman-adv/hash.h index 977de9c,f173427..f173427 --- a/net/batman-adv/hash.h +++ b/net/batman-adv/hash.h @@@ -82,6 -82,24 +82,24 @@@ static inline void batadv_hash_delete(s }
/** + * batadv_hash_bytes - hash some bytes and add them to the previous hash + * @hash: previous hash value + * @data: data to be hashed + * @size: number of bytes to be hashed + */ + static inline void batadv_hash_bytes(uint32_t *hash, void *data, uint32_t size) + { + const unsigned char *key = data; + int i; + + for (i = 0; i < size; i++) { + *hash += key[i]; + *hash += (*hash << 10); + *hash ^= (*hash >> 6); + } + } + + /** * batadv_hash_add - adds data to the hashtable * @hash: storage hash table * @compare: callback to determine if 2 hash elements are identical diff --combined net/batman-adv/icmp_socket.c index bde3cf747,5874c0e..5874c0e --- a/net/batman-adv/icmp_socket.c +++ b/net/batman-adv/icmp_socket.c @@@ -42,12 -42,16 +42,16 @@@ static int batadv_socket_open(struct in unsigned int i; struct batadv_socket_client *socket_client;
+ if (!try_module_get(THIS_MODULE)) + return -EBUSY; + nonseekable_open(inode, file);
socket_client = kmalloc(sizeof(*socket_client), GFP_KERNEL); - - if (!socket_client) + if (!socket_client) { + module_put(THIS_MODULE); return -ENOMEM; + }
for (i = 0; i < ARRAY_SIZE(batadv_socket_client_hash); i++) { if (!batadv_socket_client_hash[i]) { @@@ -59,6 -63,7 +63,7 @@@ if (i == ARRAY_SIZE(batadv_socket_client_hash)) { pr_err("Error - can't add another packet client: maximum number of clients reached\n"); kfree(socket_client); + module_put(THIS_MODULE); return -EXFULL; }
@@@ -71,7 -76,6 +76,6 @@@
file->private_data = socket_client;
- batadv_inc_module_count(); return 0; }
@@@ -96,7 -100,7 +100,7 @@@ static int batadv_socket_release(struc spin_unlock_bh(&socket_client->lock);
kfree(socket_client); - batadv_dec_module_count(); + module_put(THIS_MODULE);
return 0; } diff --combined net/batman-adv/main.c index b4aa470,253e240..253e240 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@@ -17,6 -17,8 +17,8 @@@ * 02110-1301, USA */
+ #include <linux/crc32c.h> + #include <linux/highmem.h> #include "main.h" #include "sysfs.h" #include "debugfs.h" @@@ -29,6 -31,7 +31,7 @@@ #include "hard-interface.h" #include "gateway_client.h" #include "bridge_loop_avoidance.h" + #include "distributed-arp-table.h" #include "vis.h" #include "hash.h" #include "bat_algo.h" @@@ -128,6 -131,10 +131,10 @@@ int batadv_mesh_init(struct net_device if (ret < 0) goto err;
+ ret = batadv_dat_init(bat_priv); + if (ret < 0) + goto err; + atomic_set(&bat_priv->gw.reselect, 0); atomic_set(&bat_priv->mesh_state, BATADV_MESH_ACTIVE);
@@@ -155,21 -162,13 +162,13 @@@ void batadv_mesh_free(struct net_devic
batadv_bla_free(bat_priv);
+ batadv_dat_free(bat_priv); + free_percpu(bat_priv->bat_counters);
atomic_set(&bat_priv->mesh_state, BATADV_MESH_INACTIVE); }
- void batadv_inc_module_count(void) - { - try_module_get(THIS_MODULE); - } - - void batadv_dec_module_count(void) - { - module_put(THIS_MODULE); - } - int batadv_is_my_mac(const uint8_t *addr) { const struct batadv_hard_iface *hard_iface; @@@ -188,6 -187,42 +187,42 @@@ return 0; }
+ /** + * batadv_seq_print_text_primary_if_get - called from debugfs table printing + * function that requires the primary interface + * @seq: debugfs table seq_file struct + * + * Returns primary interface if found or NULL otherwise. + */ + struct batadv_hard_iface * + batadv_seq_print_text_primary_if_get(struct seq_file *seq) + { + struct net_device *net_dev = (struct net_device *)seq->private; + struct batadv_priv *bat_priv = netdev_priv(net_dev); + struct batadv_hard_iface *primary_if; + + primary_if = batadv_primary_if_get_selected(bat_priv); + + if (!primary_if) { + seq_printf(seq, + "BATMAN mesh %s disabled - please specify interfaces to enable it\n", + net_dev->name); + goto out; + } + + if (primary_if->if_status == BATADV_IF_ACTIVE) + goto out; + + seq_printf(seq, + "BATMAN mesh %s disabled - primary interface not active\n", + net_dev->name); + batadv_hardif_free_ref(primary_if); + primary_if = NULL; + + out: + return primary_if; + } + static int batadv_recv_unhandled_packet(struct sk_buff *skb, struct batadv_hard_iface *recv_if) { @@@ -274,6 -309,8 +309,8 @@@ static void batadv_recv_handler_init(vo
/* batman icmp packet */ batadv_rx_handler[BATADV_ICMP] = batadv_recv_icmp_packet; + /* unicast with 4 addresses packet */ + batadv_rx_handler[BATADV_UNICAST_4ADDR] = batadv_recv_unicast_packet; /* unicast packet */ batadv_rx_handler[BATADV_UNICAST] = batadv_recv_unicast_packet; /* fragmented unicast packet */ @@@ -385,6 -422,50 +422,50 @@@ int batadv_algo_seq_print_text(struct s return 0; }
+ /** + * batadv_compat_seq_print_text - print the compatibility version + * @seq: debugfs table seq_file struct + * @offset: not used + */ + int batadv_compat_seq_print_text(struct seq_file *seq, void *offset) + { + seq_printf(seq, "%d\n", BATADV_COMPAT_VERSION); + + return 0; + } + + /** + * batadv_skb_crc32 - calculate CRC32 of the whole packet and skip bytes in + * the header + * @skb: skb pointing to fragmented socket buffers + * @payload_ptr: Pointer to position inside the head buffer of the skb + * marking the start of the data to be CRC'ed + * + * payload_ptr must always point to an address in the skb head buffer and not to + * a fragment. + */ + __be32 batadv_skb_crc32(struct sk_buff *skb, u8 *payload_ptr) + { + u32 crc = 0; + unsigned int from; + unsigned int to = skb->len; + struct skb_seq_state st; + const u8 *data; + unsigned int len; + unsigned int consumed = 0; + + from = (unsigned int)(payload_ptr - skb->data); + + skb_prepare_seq_read(skb, from, to, &st); + while ((len = skb_seq_read(consumed, &data, &st)) != 0) { + crc = crc32c(crc, data, len); + consumed += len; + } + skb_abort_seq_read(&st); + + return htonl(crc); + } + static int batadv_param_set_ra(const char *val, const struct kernel_param *kp) { struct batadv_algo_ops *bat_algo_ops; diff --combined net/batman-adv/main.h index d57b746,0000000..0213cb5 mode 100644,000000..100644 --- a/net/batman-adv/main.h +++ b/net/batman-adv/main.h @@@ -1,281 -1,0 +1,302 @@@ +/* Copyright (C) 2007-2012 B.A.T.M.A.N. contributors: + * + * Marek Lindner, Simon Wunderlich + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _NET_BATMAN_ADV_MAIN_H_ +#define _NET_BATMAN_ADV_MAIN_H_ + +#define BATADV_DRIVER_AUTHOR "Marek Lindner lindner_marek@yahoo.de, " \ + "Simon Wunderlich siwu@hrz.tu-chemnitz.de" +#define BATADV_DRIVER_DESC "B.A.T.M.A.N. advanced" +#define BATADV_DRIVER_DEVICE "batman-adv" + +#ifndef BATADV_SOURCE_VERSION +#define BATADV_SOURCE_VERSION "2012.4.0" +#endif + +/* B.A.T.M.A.N. parameters */ + +#define BATADV_TQ_MAX_VALUE 255 +#define BATADV_JITTER 20 + +/* Time To Live of broadcast messages */ +#define BATADV_TTL 50 + +/* purge originators after time in seconds if no valid packet comes in + * -> TODO: check influence on BATADV_TQ_LOCAL_WINDOW_SIZE + */ +#define BATADV_PURGE_TIMEOUT 200000 /* 200 seconds */ +#define BATADV_TT_LOCAL_TIMEOUT 3600000 /* in milliseconds */ +#define BATADV_TT_CLIENT_ROAM_TIMEOUT 600000 /* in milliseconds */ +#define BATADV_TT_CLIENT_TEMP_TIMEOUT 600000 /* in milliseconds */ ++#define BATADV_DAT_ENTRY_TIMEOUT (5*60000) /* 5 mins in milliseconds */ +/* sliding packet range of received originator messages in sequence numbers + * (should be a multiple of our word size) + */ +#define BATADV_TQ_LOCAL_WINDOW_SIZE 64 +/* milliseconds we have to keep pending tt_req */ +#define BATADV_TT_REQUEST_TIMEOUT 3000 + +#define BATADV_TQ_GLOBAL_WINDOW_SIZE 5 +#define BATADV_TQ_LOCAL_BIDRECT_SEND_MINIMUM 1 +#define BATADV_TQ_LOCAL_BIDRECT_RECV_MINIMUM 1 +#define BATADV_TQ_TOTAL_BIDRECT_LIMIT 1 + +/* number of OGMs sent with the last tt diff */ +#define BATADV_TT_OGM_APPEND_MAX 3 + +/* Time in which a client can roam at most ROAMING_MAX_COUNT times in + * milliseconds + */ +#define BATADV_ROAMING_MAX_TIME 20000 +#define BATADV_ROAMING_MAX_COUNT 5 + +#define BATADV_NO_FLAGS 0 + +#define BATADV_NULL_IFINDEX 0 /* dummy ifindex used to avoid iface checks */ + +#define BATADV_NUM_WORDS BITS_TO_LONGS(BATADV_TQ_LOCAL_WINDOW_SIZE) + +#define BATADV_LOG_BUF_LEN 8192 /* has to be a power of 2 */ + ++/* msecs after which an ARP_REQUEST is sent in broadcast as fallback */ ++#define ARP_REQ_DELAY 250 ++/* numbers of originator to contact for any PUT/GET DHT operation */ ++#define BATADV_DAT_CANDIDATES_NUM 3 ++ +#define BATADV_VIS_INTERVAL 5000 /* 5 seconds */ + +/* how much worse secondary interfaces may be to be considered as bonding + * candidates + */ +#define BATADV_BONDING_TQ_THRESHOLD 50 + +/* should not be bigger than 512 bytes or change the size of + * forw_packet->direct_link_flags + */ +#define BATADV_MAX_AGGREGATION_BYTES 512 +#define BATADV_MAX_AGGREGATION_MS 100 + +#define BATADV_BLA_PERIOD_LENGTH 10000 /* 10 seconds */ +#define BATADV_BLA_BACKBONE_TIMEOUT (BATADV_BLA_PERIOD_LENGTH * 3) +#define BATADV_BLA_CLAIM_TIMEOUT (BATADV_BLA_PERIOD_LENGTH * 10) ++#define BATADV_BLA_WAIT_PERIODS 3 + +#define BATADV_DUPLIST_SIZE 16 +#define BATADV_DUPLIST_TIMEOUT 500 /* 500 ms */ +/* don't reset again within 30 seconds */ +#define BATADV_RESET_PROTECTION_MS 30000 +#define BATADV_EXPECTED_SEQNO_RANGE 65536 + +enum batadv_mesh_state { + BATADV_MESH_INACTIVE, + BATADV_MESH_ACTIVE, + BATADV_MESH_DEACTIVATING, +}; + +#define BATADV_BCAST_QUEUE_LEN 256 +#define BATADV_BATMAN_QUEUE_LEN 256 + +enum batadv_uev_action { + BATADV_UEV_ADD = 0, + BATADV_UEV_DEL, + BATADV_UEV_CHANGE, +}; + +enum batadv_uev_type { + BATADV_UEV_GW = 0, +}; + +#define BATADV_GW_THRESHOLD 50 + ++#define BATADV_DAT_CANDIDATE_NOT_FOUND 0 ++#define BATADV_DAT_CANDIDATE_ORIG 1 ++ +/* Debug Messages */ +#ifdef pr_fmt +#undef pr_fmt +#endif +/* Append 'batman-adv: ' before kernel messages */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +/* Kernel headers */ + +#include <linux/mutex.h> /* mutex */ +#include <linux/module.h> /* needed by all modules */ +#include <linux/netdevice.h> /* netdevice */ +#include <linux/etherdevice.h> /* ethernet address classification */ +#include <linux/if_ether.h> /* ethernet header */ +#include <linux/poll.h> /* poll_table */ +#include <linux/kthread.h> /* kernel threads */ +#include <linux/pkt_sched.h> /* schedule types */ +#include <linux/workqueue.h> /* workqueue */ +#include <linux/percpu.h> +#include <linux/slab.h> +#include <net/sock.h> /* struct sock */ +#include <linux/jiffies.h> +#include <linux/seq_file.h> +#include "types.h" + +extern char batadv_routing_algo[]; +extern struct list_head batadv_hardif_list; + +extern unsigned char batadv_broadcast_addr[]; +extern struct workqueue_struct *batadv_event_workqueue; + +int batadv_mesh_init(struct net_device *soft_iface); +void batadv_mesh_free(struct net_device *soft_iface); - void batadv_inc_module_count(void); - void batadv_dec_module_count(void); +int batadv_is_my_mac(const uint8_t *addr); ++struct batadv_hard_iface * ++batadv_seq_print_text_primary_if_get(struct seq_file *seq); +int batadv_batman_skb_recv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *ptype, + struct net_device *orig_dev); +int +batadv_recv_handler_register(uint8_t packet_type, + int (*recv_handler)(struct sk_buff *, + struct batadv_hard_iface *)); +void batadv_recv_handler_unregister(uint8_t packet_type); +int batadv_algo_register(struct batadv_algo_ops *bat_algo_ops); +int batadv_algo_select(struct batadv_priv *bat_priv, char *name); +int batadv_algo_seq_print_text(struct seq_file *seq, void *offset); - - /* all messages related to routing / flooding / broadcasting / etc */ ++int batadv_compat_seq_print_text(struct seq_file *seq, void *offset); ++__be32 batadv_skb_crc32(struct sk_buff *skb, u8 *payload_ptr); ++ ++/** ++ * enum batadv_dbg_level - available log levels ++ * @BATADV_DBG_BATMAN: OGM and TQ computations related messages ++ * @BATADV_DBG_ROUTES: route added / changed / deleted ++ * @BATADV_DBG_TT: translation table messages ++ * @BATADV_DBG_BLA: bridge loop avoidance messages ++ * @BATADV_DBG_DAT: ARP snooping and DAT related messages ++ * @BATADV_DBG_ALL: the union of all the above log levels ++ */ +enum batadv_dbg_level { + BATADV_DBG_BATMAN = BIT(0), - BATADV_DBG_ROUTES = BIT(1), /* route added / changed / deleted */ - BATADV_DBG_TT = BIT(2), /* translation table operations */ - BATADV_DBG_BLA = BIT(3), /* bridge loop avoidance */ - BATADV_DBG_ALL = 15, ++ BATADV_DBG_ROUTES = BIT(1), ++ BATADV_DBG_TT = BIT(2), ++ BATADV_DBG_BLA = BIT(3), ++ BATADV_DBG_DAT = BIT(4), ++ BATADV_DBG_ALL = 31, +}; + +#ifdef CONFIG_BATMAN_ADV_DEBUG +int batadv_debug_log(struct batadv_priv *bat_priv, const char *fmt, ...) +__printf(2, 3); + +#define batadv_dbg(type, bat_priv, fmt, arg...) \ + do { \ + if (atomic_read(&bat_priv->log_level) & type) \ + batadv_debug_log(bat_priv, fmt, ## arg);\ + } \ + while (0) +#else /* !CONFIG_BATMAN_ADV_DEBUG */ +__printf(3, 4) +static inline void batadv_dbg(int type __always_unused, + struct batadv_priv *bat_priv __always_unused, + const char *fmt __always_unused, ...) +{ +} +#endif + +#define batadv_info(net_dev, fmt, arg...) \ + do { \ + struct net_device *_netdev = (net_dev); \ + struct batadv_priv *_batpriv = netdev_priv(_netdev); \ + batadv_dbg(BATADV_DBG_ALL, _batpriv, fmt, ## arg); \ + pr_info("%s: " fmt, _netdev->name, ## arg); \ + } while (0) +#define batadv_err(net_dev, fmt, arg...) \ + do { \ + struct net_device *_netdev = (net_dev); \ + struct batadv_priv *_batpriv = netdev_priv(_netdev); \ + batadv_dbg(BATADV_DBG_ALL, _batpriv, fmt, ## arg); \ + pr_err("%s: " fmt, _netdev->name, ## arg); \ + } while (0) + +/* returns 1 if they are the same ethernet addr + * + * note: can't use compare_ether_addr() as it requires aligned memory + */ +static inline int batadv_compare_eth(const void *data1, const void *data2) +{ + return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0); +} + +/** + * has_timed_out - compares current time (jiffies) and timestamp + timeout + * @timestamp: base value to compare with (in jiffies) + * @timeout: added to base value before comparing (in milliseconds) + * + * Returns true if current time is after timestamp + timeout + */ +static inline bool batadv_has_timed_out(unsigned long timestamp, + unsigned int timeout) +{ + return time_is_before_jiffies(timestamp + msecs_to_jiffies(timeout)); +} + +#define batadv_atomic_dec_not_zero(v) atomic_add_unless((v), -1, 0) + +/* Returns the smallest signed integer in two's complement with the sizeof x */ +#define batadv_smallest_signed_int(x) (1u << (7u + 8u * (sizeof(x) - 1u))) + +/* Checks if a sequence number x is a predecessor/successor of y. + * they handle overflows/underflows and can correctly check for a + * predecessor/successor unless the variable sequence number has grown by + * more then 2**(bitwidth(x)-1)-1. + * This means that for a uint8_t with the maximum value 255, it would think: + * - when adding nothing - it is neither a predecessor nor a successor + * - before adding more than 127 to the starting value - it is a predecessor, + * - when adding 128 - it is neither a predecessor nor a successor, + * - after adding more than 127 to the starting value - it is a successor + */ +#define batadv_seq_before(x, y) ({typeof(x) _d1 = (x); \ + typeof(y) _d2 = (y); \ + typeof(x) _dummy = (_d1 - _d2); \ + (void) (&_d1 == &_d2); \ + _dummy > batadv_smallest_signed_int(_dummy); }) +#define batadv_seq_after(x, y) batadv_seq_before(y, x) + +/* Stop preemption on local cpu while incrementing the counter */ +static inline void batadv_add_counter(struct batadv_priv *bat_priv, size_t idx, + size_t count) +{ + int cpu = get_cpu(); + per_cpu_ptr(bat_priv->bat_counters, cpu)[idx] += count; + put_cpu(); +} + +#define batadv_inc_counter(b, i) batadv_add_counter(b, i, 1) + +/* Sum and return the cpu-local counters for index 'idx' */ +static inline uint64_t batadv_sum_counter(struct batadv_priv *bat_priv, + size_t idx) +{ + uint64_t *counters, sum = 0; + int cpu; + + for_each_possible_cpu(cpu) { + counters = per_cpu_ptr(bat_priv->bat_counters, cpu); + sum += counters[idx]; + } + + return sum; +} + +#endif /* _NET_BATMAN_ADV_MAIN_H_ */ diff --combined net/batman-adv/originator.c index ac9bdf8,8c32cf1..8c32cf1 --- a/net/batman-adv/originator.c +++ b/net/batman-adv/originator.c @@@ -18,6 -18,7 +18,7 @@@ */
#include "main.h" + #include "distributed-arp-table.h" #include "originator.h" #include "hash.h" #include "translation-table.h" @@@ -220,9 -221,9 +221,9 @@@ struct batadv_orig_node *batadv_get_ori atomic_set(&orig_node->refcount, 2);
orig_node->tt_initialised = false; - orig_node->tt_poss_change = false; orig_node->bat_priv = bat_priv; memcpy(orig_node->orig, addr, ETH_ALEN); + batadv_dat_init_orig_node_addr(orig_node); orig_node->router = NULL; orig_node->tt_crc = 0; atomic_set(&orig_node->last_ttvn, 0); @@@ -415,23 -416,10 +416,10 @@@ int batadv_orig_seq_print_text(struct s int last_seen_msecs; unsigned long last_seen_jiffies; uint32_t i; - int ret = 0;
- primary_if = batadv_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 != BATADV_IF_ACTIVE) { - ret = seq_printf(seq, - "BATMAN mesh %s disabled - primary interface not active\n", - net_dev->name); + primary_if = batadv_seq_print_text_primary_if_get(seq); + if (!primary_if) goto out; - }
seq_printf(seq, "[B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s)]\n", BATADV_SOURCE_VERSION, primary_if->net_dev->name, @@@ -485,7 -473,7 +473,7 @@@ next out: if (primary_if) batadv_hardif_free_ref(primary_if); - return ret; + return 0; }
static int batadv_orig_node_add_if(struct batadv_orig_node *orig_node, diff --combined net/batman-adv/packet.h index 2d23a14,aa3e63a..aa3e63a --- a/net/batman-adv/packet.h +++ b/net/batman-adv/packet.h @@@ -23,14 -23,29 +23,29 @@@ #define BATADV_ETH_P_BATMAN 0x4305 /* unofficial/not registered Ethertype */
enum batadv_packettype { - BATADV_IV_OGM = 0x01, - BATADV_ICMP = 0x02, - BATADV_UNICAST = 0x03, - BATADV_BCAST = 0x04, - BATADV_VIS = 0x05, - BATADV_UNICAST_FRAG = 0x06, - BATADV_TT_QUERY = 0x07, - BATADV_ROAM_ADV = 0x08, + BATADV_IV_OGM = 0x01, + BATADV_ICMP = 0x02, + BATADV_UNICAST = 0x03, + BATADV_BCAST = 0x04, + BATADV_VIS = 0x05, + BATADV_UNICAST_FRAG = 0x06, + BATADV_TT_QUERY = 0x07, + BATADV_ROAM_ADV = 0x08, + BATADV_UNICAST_4ADDR = 0x09, + }; + + /** + * enum batadv_subtype - packet subtype for unicast4addr + * @BATADV_P_DATA: user payload + * @BATADV_P_DAT_DHT_GET: DHT request message + * @BATADV_P_DAT_DHT_PUT: DHT store message + * @BATADV_P_DAT_CACHE_REPLY: ARP reply generated by DAT + */ + enum batadv_subtype { + BATADV_P_DATA = 0x01, + BATADV_P_DAT_DHT_GET = 0x02, + BATADV_P_DAT_DHT_PUT = 0x03, + BATADV_P_DAT_CACHE_REPLY = 0x04, };
/* this file is included by batctl which needs these defines */ @@@ -161,6 -176,18 +176,18 @@@ struct batadv_unicast_packet uint8_t dest[ETH_ALEN]; } __packed;
+ /** + * struct batadv_unicast_4addr_packet - extended unicast packet + * @u: common unicast packet header + * @src: address of the source + * @subtype: packet subtype + */ + struct batadv_unicast_4addr_packet { + struct batadv_unicast_packet u; + uint8_t src[ETH_ALEN]; + uint8_t subtype; + } __packed; + struct batadv_unicast_frag_packet { struct batadv_header header; uint8_t ttvn; /* destination translation table version number */ diff --combined net/batman-adv/routing.c index 376b4cc,5da62df..5da62df --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@@ -28,6 -28,7 +28,7 @@@ #include "vis.h" #include "unicast.h" #include "bridge_loop_avoidance.h" + #include "distributed-arp-table.h"
static int batadv_route_unicast_packet(struct sk_buff *skb, struct batadv_hard_iface *recv_if); @@@ -284,7 -285,6 +285,6 @@@ static int batadv_recv_my_icmp_packet(s { struct batadv_hard_iface *primary_if = NULL; struct batadv_orig_node *orig_node = NULL; - struct batadv_neigh_node *router = NULL; struct batadv_icmp_packet_rr *icmp_packet; int ret = NET_RX_DROP;
@@@ -306,10 -306,6 +306,6 @@@ if (!orig_node) goto out;
- router = batadv_orig_node_get_router(orig_node); - if (!router) - goto out; - /* create a copy of the skb, if needed, to modify it. */ if (skb_cow(skb, ETH_HLEN) < 0) goto out; @@@ -321,14 -317,12 +317,12 @@@ icmp_packet->msg_type = BATADV_ECHO_REPLY; icmp_packet->header.ttl = BATADV_TTL;
- batadv_send_skb_packet(skb, router->if_incoming, router->addr); - ret = NET_RX_SUCCESS; + if (batadv_send_skb_to_orig(skb, orig_node, NULL)) + ret = NET_RX_SUCCESS;
out: if (primary_if) batadv_hardif_free_ref(primary_if); - if (router) - batadv_neigh_node_free_ref(router); if (orig_node) batadv_orig_node_free_ref(orig_node); return ret; @@@ -339,7 -333,6 +333,6 @@@ static int batadv_recv_icmp_ttl_exceede { struct batadv_hard_iface *primary_if = NULL; struct batadv_orig_node *orig_node = NULL; - struct batadv_neigh_node *router = NULL; struct batadv_icmp_packet *icmp_packet; int ret = NET_RX_DROP;
@@@ -361,10 -354,6 +354,6 @@@ if (!orig_node) goto out;
- router = batadv_orig_node_get_router(orig_node); - if (!router) - goto out; - /* create a copy of the skb, if needed, to modify it. */ if (skb_cow(skb, ETH_HLEN) < 0) goto out; @@@ -376,14 -365,12 +365,12 @@@ icmp_packet->msg_type = BATADV_TTL_EXCEEDED; icmp_packet->header.ttl = BATADV_TTL;
- batadv_send_skb_packet(skb, router->if_incoming, router->addr); - ret = NET_RX_SUCCESS; + if (batadv_send_skb_to_orig(skb, orig_node, NULL)) + ret = NET_RX_SUCCESS;
out: if (primary_if) batadv_hardif_free_ref(primary_if); - if (router) - batadv_neigh_node_free_ref(router); if (orig_node) batadv_orig_node_free_ref(orig_node); return ret; @@@ -397,7 -384,6 +384,6 @@@ int batadv_recv_icmp_packet(struct sk_b struct batadv_icmp_packet_rr *icmp_packet; struct ethhdr *ethhdr; struct batadv_orig_node *orig_node = NULL; - struct batadv_neigh_node *router = NULL; int hdr_size = sizeof(struct batadv_icmp_packet); int ret = NET_RX_DROP;
@@@ -446,10 -432,6 +432,6 @@@ if (!orig_node) goto out;
- router = batadv_orig_node_get_router(orig_node); - if (!router) - goto out; - /* create a copy of the skb, if needed, to modify it. */ if (skb_cow(skb, ETH_HLEN) < 0) goto out; @@@ -460,12 -442,10 +442,10 @@@ icmp_packet->header.ttl--;
/* route it */ - batadv_send_skb_packet(skb, router->if_incoming, router->addr); - ret = NET_RX_SUCCESS; + if (batadv_send_skb_to_orig(skb, orig_node, recv_if)) + ret = NET_RX_SUCCESS;
out: - if (router) - batadv_neigh_node_free_ref(router); if (orig_node) batadv_orig_node_free_ref(orig_node); return ret; @@@ -549,25 -529,18 +529,18 @@@ batadv_find_ifalter_router(struct batad if (tmp_neigh_node->if_incoming == recv_if) continue;
- if (!atomic_inc_not_zero(&tmp_neigh_node->refcount)) + if (router && tmp_neigh_node->tq_avg <= router->tq_avg) continue;
- /* if we don't have a router yet - * or this one is better, choose it. - */ - if ((!router) || - (tmp_neigh_node->tq_avg > router->tq_avg)) { - /* decrement refcount of - * previously selected router - */ - if (router) - batadv_neigh_node_free_ref(router); + if (!atomic_inc_not_zero(&tmp_neigh_node->refcount)) + continue;
- router = tmp_neigh_node; - atomic_inc_not_zero(&router->refcount); - } + /* decrement refcount of previously selected router */ + if (router) + batadv_neigh_node_free_ref(router);
- batadv_neigh_node_free_ref(tmp_neigh_node); + /* we found a better router (or at least one valid router) */ + router = tmp_neigh_node; }
/* use the first candidate if nothing was found. */ @@@ -687,21 -660,8 +660,8 @@@ int batadv_recv_roam_adv(struct sk_buf struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); struct batadv_roam_adv_packet *roam_adv_packet; struct batadv_orig_node *orig_node; - struct ethhdr *ethhdr; - - /* drop packet if it has not necessary minimum size */ - if (unlikely(!pskb_may_pull(skb, - sizeof(struct batadv_roam_adv_packet)))) - goto out;
- ethhdr = (struct ethhdr *)skb_mac_header(skb); - - /* packet with unicast indication but broadcast recipient */ - if (is_broadcast_ether_addr(ethhdr->h_dest)) - goto out; - - /* packet with broadcast sender address */ - if (is_broadcast_ether_addr(ethhdr->h_source)) + if (batadv_check_unicast_packet(skb, sizeof(*roam_adv_packet)) < 0) goto out;
batadv_inc_counter(bat_priv, BATADV_CNT_TT_ROAM_ADV_RX); @@@ -730,12 -690,6 +690,6 @@@ BATADV_TT_CLIENT_ROAM, atomic_read(&orig_node->last_ttvn) + 1);
- /* Roaming phase starts: I have new information but the ttvn has not - * been incremented yet. This flag will make me check all the incoming - * packets for the correct destination. - */ - bat_priv->tt.poss_change = true; - batadv_orig_node_free_ref(orig_node); out: /* returning NET_RX_DROP will make the caller function kfree the skb */ @@@ -907,8 -861,8 +861,8 @@@ static int batadv_route_unicast_packet( skb->len + ETH_HLEN);
/* route it */ - batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); - ret = NET_RX_SUCCESS; + if (batadv_send_skb_to_orig(skb, orig_node, recv_if)) + ret = NET_RX_SUCCESS;
out: if (neigh_node) @@@ -918,6 -872,46 +872,46 @@@ return ret; }
+ /** + * batadv_reroute_unicast_packet - update the unicast header for re-routing + * @bat_priv: the bat priv with all the soft interface information + * @unicast_packet: the unicast header to be updated + * @dst_addr: the payload destination + * @msg: the message to print explaining the reason of the requested re-route + * + * Search the global translation table for dst_addr and update the unicast + * header with the new corresponding information (originator address where the + * destination client currently is and its known TTVN) + * + * Returns true if the packet header has been updated, false otherwise + */ + static bool + batadv_reroute_unicast_packet(struct batadv_priv *bat_priv, + struct batadv_unicast_packet *unicast_packet, + uint8_t *dst_addr) + { + struct batadv_orig_node *orig_node; + bool ret = false; + + orig_node = batadv_transtable_search(bat_priv, NULL, dst_addr); + if (!orig_node) + goto out; + + if (batadv_compare_eth(orig_node->orig, unicast_packet->dest)) + goto out; + + /* update the packet header */ + memcpy(unicast_packet->dest, orig_node->orig, ETH_ALEN); + unicast_packet->ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn); + + ret = true; + out: + if (orig_node) + batadv_orig_node_free_ref(orig_node); + + return ret; + } + static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv, struct sk_buff *skb) { uint8_t curr_ttvn; @@@ -925,73 -919,99 +919,99 @@@ struct ethhdr *ethhdr; struct batadv_hard_iface *primary_if; struct batadv_unicast_packet *unicast_packet; - bool tt_poss_change; int is_old_ttvn;
- /* I could need to modify it */ - if (skb_cow(skb, sizeof(struct batadv_unicast_packet)) < 0) + /* check if there is enough data before accessing it */ + if (pskb_may_pull(skb, sizeof(*unicast_packet) + ETH_HLEN) < 0) + return 0; + + /* create a copy of the skb (in case of for re-routing) to modify it. */ + if (skb_cow(skb, sizeof(*unicast_packet)) < 0) return 0;
unicast_packet = (struct batadv_unicast_packet *)skb->data; + ethhdr = (struct ethhdr *)(skb->data + sizeof(*unicast_packet));
- if (batadv_is_my_mac(unicast_packet->dest)) { - tt_poss_change = bat_priv->tt.poss_change; - curr_ttvn = (uint8_t)atomic_read(&bat_priv->tt.vn); - } else { + /* check if the destination client was served by this node and it is now + * roaming. In this case, it means that the node has got a ROAM_ADV + * message and that it knows the new destination in the mesh to re-route + * the packet to + */ + if (batadv_tt_local_client_is_roaming(bat_priv, ethhdr->h_dest)) { + if (batadv_reroute_unicast_packet(bat_priv, unicast_packet, + ethhdr->h_dest)) + net_ratelimited_function(batadv_dbg, BATADV_DBG_TT, + bat_priv, + "Rerouting unicast packet to %pM (dst=%pM): Local Roaming\n", + unicast_packet->dest, + ethhdr->h_dest); + /* at this point the mesh destination should have been + * substituted with the originator address found in the global + * table. If not, let the packet go untouched anyway because + * there is nothing the node can do + */ + return 1; + } + + /* retrieve the TTVN known by this node for the packet destination. This + * value is used later to check if the node which sent (or re-routed + * last time) the packet had an updated information or not + */ + curr_ttvn = (uint8_t)atomic_read(&bat_priv->tt.vn); + if (!batadv_is_my_mac(unicast_packet->dest)) { orig_node = batadv_orig_hash_find(bat_priv, unicast_packet->dest); - + /* if it is not possible to find the orig_node representing the + * destination, the packet can immediately be dropped as it will + * not be possible to deliver it + */ if (!orig_node) return 0;
curr_ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn); batadv_orig_node_free_ref(orig_node); }
- /* Check whether I have to reroute the packet */ + /* check if the TTVN contained in the packet is fresher than what the + * node knows + */ is_old_ttvn = batadv_seq_before(unicast_packet->ttvn, curr_ttvn); - if (is_old_ttvn || tt_poss_change) { - /* check if there is enough data before accessing it */ - if (pskb_may_pull(skb, sizeof(struct batadv_unicast_packet) + - ETH_HLEN) < 0) - return 0; + if (!is_old_ttvn) + return 1;
- ethhdr = (struct ethhdr *)(skb->data + sizeof(*unicast_packet)); + /* the packet was forged based on outdated network information. Its + * destination can possibly be updated and forwarded towards the new + * target host + */ + if (batadv_reroute_unicast_packet(bat_priv, unicast_packet, + ethhdr->h_dest)) { + net_ratelimited_function(batadv_dbg, BATADV_DBG_TT, bat_priv, + "Rerouting unicast packet to %pM (dst=%pM): TTVN mismatch old_ttvn=%u new_ttvn=%u\n", + unicast_packet->dest, ethhdr->h_dest, + unicast_packet->ttvn, curr_ttvn); + return 1; + }
- /* we don't have an updated route for this client, so we should - * not try to reroute the packet!! - */ - if (batadv_tt_global_client_is_roaming(bat_priv, - ethhdr->h_dest)) - return 1; + /* the packet has not been re-routed: either the destination is + * currently served by this node or there is no destination at all and + * it is possible to drop the packet + */ + if (!batadv_is_my_client(bat_priv, ethhdr->h_dest)) + return 0;
- orig_node = batadv_transtable_search(bat_priv, NULL, - ethhdr->h_dest); - - if (!orig_node) { - if (!batadv_is_my_client(bat_priv, ethhdr->h_dest)) - return 0; - primary_if = batadv_primary_if_get_selected(bat_priv); - if (!primary_if) - return 0; - memcpy(unicast_packet->dest, - primary_if->net_dev->dev_addr, ETH_ALEN); - batadv_hardif_free_ref(primary_if); - } else { - memcpy(unicast_packet->dest, orig_node->orig, - ETH_ALEN); - curr_ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn); - batadv_orig_node_free_ref(orig_node); - } + /* update the header in order to let the packet be delivered to this + * node's soft interface + */ + primary_if = batadv_primary_if_get_selected(bat_priv); + if (!primary_if) + return 0;
- batadv_dbg(BATADV_DBG_ROUTES, bat_priv, - "TTVN mismatch (old_ttvn %u new_ttvn %u)! Rerouting unicast packet (for %pM) to %pM\n", - unicast_packet->ttvn, curr_ttvn, ethhdr->h_dest, - unicast_packet->dest); + memcpy(unicast_packet->dest, primary_if->net_dev->dev_addr, ETH_ALEN); + + batadv_hardif_free_ref(primary_if); + + unicast_packet->ttvn = curr_ttvn;
- unicast_packet->ttvn = curr_ttvn; - } return 1; }
@@@ -1000,7 -1020,19 +1020,19 @@@ int batadv_recv_unicast_packet(struct s { struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); struct batadv_unicast_packet *unicast_packet; + struct batadv_unicast_4addr_packet *unicast_4addr_packet; + uint8_t *orig_addr; + struct batadv_orig_node *orig_node = NULL; int hdr_size = sizeof(*unicast_packet); + bool is4addr; + + unicast_packet = (struct batadv_unicast_packet *)skb->data; + unicast_4addr_packet = (struct batadv_unicast_4addr_packet *)skb->data; + + is4addr = unicast_packet->header.packet_type == BATADV_UNICAST_4ADDR; + /* the caller function should have already pulled 2 bytes */ + if (is4addr) + hdr_size = sizeof(*unicast_4addr_packet);
if (batadv_check_unicast_packet(skb, hdr_size) < 0) return NET_RX_DROP; @@@ -1008,12 -1040,28 +1040,28 @@@ if (!batadv_check_unicast_ttvn(bat_priv, skb)) return NET_RX_DROP;
- unicast_packet = (struct batadv_unicast_packet *)skb->data; - /* packet for me */ if (batadv_is_my_mac(unicast_packet->dest)) { + if (is4addr) { + batadv_dat_inc_counter(bat_priv, + unicast_4addr_packet->subtype); + orig_addr = unicast_4addr_packet->src; + orig_node = batadv_orig_hash_find(bat_priv, orig_addr); + } + + if (batadv_dat_snoop_incoming_arp_request(bat_priv, skb, + hdr_size)) + goto rx_success; + if (batadv_dat_snoop_incoming_arp_reply(bat_priv, skb, + hdr_size)) + goto rx_success; + batadv_interface_rx(recv_if->soft_iface, skb, recv_if, hdr_size, - NULL); + orig_node); + + rx_success: + if (orig_node) + batadv_orig_node_free_ref(orig_node);
return NET_RX_SUCCESS; } @@@ -1050,8 -1098,17 +1098,17 @@@ int batadv_recv_ucast_frag_packet(struc if (!new_skb) return NET_RX_SUCCESS;
+ if (batadv_dat_snoop_incoming_arp_request(bat_priv, new_skb, + hdr_size)) + goto rx_success; + if (batadv_dat_snoop_incoming_arp_reply(bat_priv, new_skb, + hdr_size)) + goto rx_success; + batadv_interface_rx(recv_if->soft_iface, new_skb, recv_if, sizeof(struct batadv_unicast_packet), NULL); + + rx_success: return NET_RX_SUCCESS; }
@@@ -1143,9 -1200,16 +1200,16 @@@ int batadv_recv_bcast_packet(struct sk_ if (batadv_bla_is_backbone_gw(skb, orig_node, hdr_size)) goto out;
+ if (batadv_dat_snoop_incoming_arp_request(bat_priv, skb, hdr_size)) + goto rx_success; + if (batadv_dat_snoop_incoming_arp_reply(bat_priv, skb, hdr_size)) + goto rx_success; + /* broadcast for me */ batadv_interface_rx(recv_if->soft_iface, skb, recv_if, hdr_size, orig_node); + + rx_success: ret = NET_RX_SUCCESS; goto out;
diff --combined net/batman-adv/send.c index 570a8bc,c7f7023..c7f7023 --- a/net/batman-adv/send.c +++ b/net/batman-adv/send.c @@@ -18,6 -18,7 +18,7 @@@ */
#include "main.h" + #include "distributed-arp-table.h" #include "send.h" #include "routing.h" #include "translation-table.h" @@@ -77,6 -78,39 +78,39 @@@ send_skb_err return NET_XMIT_DROP; }
+ /** + * batadv_send_skb_to_orig - Lookup next-hop and transmit skb. + * @skb: Packet to be transmitted. + * @orig_node: Final destination of the packet. + * @recv_if: Interface used when receiving the packet (can be NULL). + * + * Looks up the best next-hop towards the passed originator and passes the + * skb on for preparation of MAC header. If the packet originated from this + * host, NULL can be passed as recv_if and no interface alternating is + * attempted. + * + * Returns TRUE on success; FALSE otherwise. + */ + bool batadv_send_skb_to_orig(struct sk_buff *skb, + struct batadv_orig_node *orig_node, + struct batadv_hard_iface *recv_if) + { + struct batadv_priv *bat_priv = orig_node->bat_priv; + struct batadv_neigh_node *neigh_node; + + /* batadv_find_router() increases neigh_nodes refcount if found. */ + neigh_node = batadv_find_router(bat_priv, orig_node, recv_if); + if (!neigh_node) + return false; + + /* route it */ + batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); + + batadv_neigh_node_free_ref(neigh_node); + + return true; + } + void batadv_schedule_bat_ogm(struct batadv_hard_iface *hard_iface) { struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); @@@ -209,6 -243,9 +243,9 @@@ static void batadv_send_outstanding_bca if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING) goto out;
+ if (batadv_dat_drop_broadcast_packet(bat_priv, forw_packet)) + goto out; + /* rebroadcast packet */ rcu_read_lock(); list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { diff --combined net/batman-adv/send.h index 643329b,0078dec..0078dec --- a/net/batman-adv/send.h +++ b/net/batman-adv/send.h @@@ -23,6 -23,9 +23,9 @@@ int batadv_send_skb_packet(struct sk_buff *skb, struct batadv_hard_iface *hard_iface, const uint8_t *dst_addr); + bool batadv_send_skb_to_orig(struct sk_buff *skb, + struct batadv_orig_node *orig_node, + struct batadv_hard_iface *recv_if); void batadv_schedule_bat_ogm(struct batadv_hard_iface *hard_iface); int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv, const struct sk_buff *skb, diff --combined net/batman-adv/soft-interface.c index b9a28d2,2d1f895..2d1f895 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@@ -20,6 -20,7 +20,7 @@@ #include "main.h" #include "soft-interface.h" #include "hard-interface.h" + #include "distributed-arp-table.h" #include "routing.h" #include "send.h" #include "debugfs.h" @@@ -146,13 -147,16 +147,16 @@@ static int batadv_interface_tx(struct s struct batadv_bcast_packet *bcast_packet; struct vlan_ethhdr *vhdr; __be16 ethertype = __constant_htons(BATADV_ETH_P_BATMAN); - static const uint8_t stp_addr[ETH_ALEN] = {0x01, 0x80, 0xC2, 0x00, 0x00, - 0x00}; + static const uint8_t stp_addr[ETH_ALEN] = {0x01, 0x80, 0xC2, 0x00, + 0x00, 0x00}; + static const uint8_t ectp_addr[ETH_ALEN] = {0xCF, 0x00, 0x00, 0x00, + 0x00, 0x00}; unsigned int header_len = 0; int data_len = skb->len, ret; short vid __maybe_unused = -1; bool do_bcast = false; uint32_t seqno; + unsigned long brd_delay = 1;
if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE) goto dropped; @@@ -180,10 -184,16 +184,16 @@@
/* don't accept stp packets. STP does not help in meshes. * better use the bridge loop avoidance ... + * + * The same goes for ECTP sent at least by some Cisco Switches, + * it might confuse the mesh when used with bridge loop avoidance. */ if (batadv_compare_eth(ethhdr->h_dest, stp_addr)) goto dropped;
+ if (batadv_compare_eth(ethhdr->h_dest, ectp_addr)) + goto dropped; + if (is_multicast_ether_addr(ethhdr->h_dest)) { do_bcast = true;
@@@ -216,6 -226,13 +226,13 @@@ if (!primary_if) goto dropped;
+ /* in case of ARP request, we do not immediately broadcasti the + * packet, instead we first wait for DAT to try to retrieve the + * correct ARP entry + */ + if (batadv_dat_snoop_outgoing_arp_request(bat_priv, skb)) + brd_delay = msecs_to_jiffies(ARP_REQ_DELAY); + if (batadv_skb_head_push(skb, sizeof(*bcast_packet)) < 0) goto dropped;
@@@ -237,7 -254,7 +254,7 @@@ seqno = atomic_inc_return(&bat_priv->bcast_seqno); bcast_packet->seqno = htonl(seqno);
- batadv_add_bcast_packet_to_list(bat_priv, skb, 1); + batadv_add_bcast_packet_to_list(bat_priv, skb, brd_delay);
/* a copy is stored in the bcast list, therefore removing * the original skb. @@@ -252,7 -269,12 +269,12 @@@ goto dropped; }
- ret = batadv_unicast_send_skb(skb, bat_priv); + if (batadv_dat_snoop_outgoing_arp_request(bat_priv, skb)) + goto dropped; + + batadv_dat_snoop_outgoing_arp_reply(bat_priv, skb); + + ret = batadv_unicast_send_skb(bat_priv, skb); if (ret != 0) goto dropped_freed; } @@@ -347,7 -369,51 +369,51 @@@ out return; }
+ /* batman-adv network devices have devices nesting below it and are a special + * "super class" of normal network devices; split their locks off into a + * separate class since they always nest. + */ + static struct lock_class_key batadv_netdev_xmit_lock_key; + static struct lock_class_key batadv_netdev_addr_lock_key; + + /** + * batadv_set_lockdep_class_one - Set lockdep class for a single tx queue + * @dev: device which owns the tx queue + * @txq: tx queue to modify + * @_unused: always NULL + */ + static void batadv_set_lockdep_class_one(struct net_device *dev, + struct netdev_queue *txq, + void *_unused) + { + lockdep_set_class(&txq->_xmit_lock, &batadv_netdev_xmit_lock_key); + } + + /** + * batadv_set_lockdep_class - Set txq and addr_list lockdep class + * @dev: network device to modify + */ + static void batadv_set_lockdep_class(struct net_device *dev) + { + lockdep_set_class(&dev->addr_list_lock, &batadv_netdev_addr_lock_key); + netdev_for_each_tx_queue(dev, batadv_set_lockdep_class_one, NULL); + } + + /** + * batadv_softif_init - Late stage initialization of soft interface + * @dev: registered network device to modify + * + * Returns error code on failures + */ + static int batadv_softif_init(struct net_device *dev) + { + batadv_set_lockdep_class(dev); + + return 0; + } + static const struct net_device_ops batadv_netdev_ops = { + .ndo_init = batadv_softif_init, .ndo_open = batadv_interface_open, .ndo_stop = batadv_interface_release, .ndo_get_stats = batadv_interface_stats, @@@ -414,6 -480,9 +480,9 @@@ struct net_device *batadv_softif_create atomic_set(&bat_priv->aggregated_ogms, 1); atomic_set(&bat_priv->bonding, 0); atomic_set(&bat_priv->bridge_loop_avoidance, 0); + #ifdef CONFIG_BATMAN_ADV_DAT + atomic_set(&bat_priv->distributed_arp_table, 1); + #endif atomic_set(&bat_priv->ap_isolation, 0); atomic_set(&bat_priv->vis_mode, BATADV_VIS_TYPE_CLIENT_UPDATE); atomic_set(&bat_priv->gw_mode, BATADV_GW_MODE_OFF); @@@ -436,7 -505,6 +505,6 @@@ #endif bat_priv->tt.last_changeset = NULL; bat_priv->tt.last_changeset_len = 0; - bat_priv->tt.poss_change = false;
bat_priv->primary_if = NULL; bat_priv->num_ifaces = 0; @@@ -556,6 -624,13 +624,13 @@@ static const struct { "tt_response_rx" }, { "tt_roam_adv_tx" }, { "tt_roam_adv_rx" }, + #ifdef CONFIG_BATMAN_ADV_DAT + { "dat_get_tx" }, + { "dat_get_rx" }, + { "dat_put_tx" }, + { "dat_put_rx" }, + { "dat_cached_reply_tx" }, + #endif };
static void batadv_get_strings(struct net_device *dev, uint32_t stringset, diff --combined net/batman-adv/sysfs.c index 66518c7,cc56ed0..cc56ed0 --- a/net/batman-adv/sysfs.c +++ b/net/batman-adv/sysfs.c @@@ -20,6 -20,7 +20,7 @@@ #include "main.h" #include "sysfs.h" #include "translation-table.h" + #include "distributed-arp-table.h" #include "originator.h" #include "hard-interface.h" #include "gateway_common.h" @@@ -122,7 -123,7 +123,7 @@@ ssize_t batadv_show_##_name(struct kobj batadv_store_##_name)
- #define BATADV_ATTR_HIF_STORE_UINT(_name, _min, _max, _post_func) \ + #define BATADV_ATTR_HIF_STORE_UINT(_name, _var, _min, _max, _post_func) \ ssize_t batadv_store_##_name(struct kobject *kobj, \ struct attribute *attr, char *buff, \ size_t count) \ @@@ -137,13 -138,13 +138,13 @@@ \ length = __batadv_store_uint_attr(buff, count, _min, _max, \ _post_func, attr, \ - &hard_iface->_name, net_dev); \ + &hard_iface->_var, net_dev); \ \ batadv_hardif_free_ref(hard_iface); \ return length; \ }
- #define BATADV_ATTR_HIF_SHOW_UINT(_name) \ + #define BATADV_ATTR_HIF_SHOW_UINT(_name, _var) \ ssize_t batadv_show_##_name(struct kobject *kobj, \ struct attribute *attr, char *buff) \ { \ @@@ -155,7 -156,7 +156,7 @@@ if (!hard_iface) \ return 0; \ \ - length = sprintf(buff, "%i\n", atomic_read(&hard_iface->_name));\ + length = sprintf(buff, "%i\n", atomic_read(&hard_iface->_var)); \ \ batadv_hardif_free_ref(hard_iface); \ return length; \ @@@ -164,9 -165,10 +165,10 @@@ /* Use this, if you are going to set [name] in hard_iface to an * unsigned integer value */ - #define BATADV_ATTR_HIF_UINT(_name, _mode, _min, _max, _post_func) \ - static BATADV_ATTR_HIF_STORE_UINT(_name, _min, _max, _post_func)\ - static BATADV_ATTR_HIF_SHOW_UINT(_name) \ + #define BATADV_ATTR_HIF_UINT(_name, _var, _mode, _min, _max, _post_func)\ + static BATADV_ATTR_HIF_STORE_UINT(_name, _var, _min, \ + _max, _post_func) \ + static BATADV_ATTR_HIF_SHOW_UINT(_name, _var) \ static BATADV_ATTR(_name, _mode, batadv_show_##_name, \ batadv_store_##_name)
@@@ -469,6 -471,10 +471,10 @@@ BATADV_ATTR_SIF_BOOL(bonding, S_IRUGO #ifdef CONFIG_BATMAN_ADV_BLA BATADV_ATTR_SIF_BOOL(bridge_loop_avoidance, S_IRUGO | S_IWUSR, NULL); #endif + #ifdef CONFIG_BATMAN_ADV_DAT + BATADV_ATTR_SIF_BOOL(distributed_arp_table, S_IRUGO | S_IWUSR, + batadv_dat_switch); + #endif BATADV_ATTR_SIF_BOOL(fragmentation, S_IRUGO | S_IWUSR, batadv_update_min_mtu); BATADV_ATTR_SIF_BOOL(ap_isolation, S_IRUGO | S_IWUSR, NULL); static BATADV_ATTR(vis_mode, S_IRUGO | S_IWUSR, batadv_show_vis_mode, @@@ -494,6 -500,9 +500,9 @@@ static struct batadv_attribute *batadv_ #ifdef CONFIG_BATMAN_ADV_BLA &batadv_attr_bridge_loop_avoidance, #endif + #ifdef CONFIG_BATMAN_ADV_DAT + &batadv_attr_distributed_arp_table, + #endif &batadv_attr_fragmentation, &batadv_attr_ap_isolation, &batadv_attr_vis_mode, @@@ -730,7 -739,7 +739,7 @@@ int batadv_throw_uevent(struct batadv_p enum batadv_uev_action action, const char *data) { int ret = -ENOMEM; - struct batadv_hard_iface *primary_if = NULL; + struct batadv_hard_iface *primary_if; struct kobject *bat_kobj; char *uevent_env[4] = { NULL, NULL, NULL, NULL };
diff --combined net/batman-adv/translation-table.c index 112edd3,6fecb94..6fecb94 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@@ -238,92 -238,133 +238,133 @@@ static int batadv_tt_local_init(struct return 0; }
+ static void batadv_tt_global_free(struct batadv_priv *bat_priv, + struct batadv_tt_global_entry *tt_global, + const char *message) + { + batadv_dbg(BATADV_DBG_TT, bat_priv, + "Deleting global tt entry %pM: %s\n", + tt_global->common.addr, message); + + batadv_hash_remove(bat_priv->tt.global_hash, batadv_compare_tt, + batadv_choose_orig, tt_global->common.addr); + batadv_tt_global_entry_free_ref(tt_global); + + } + void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr, int ifindex) { struct batadv_priv *bat_priv = netdev_priv(soft_iface); - struct batadv_tt_local_entry *tt_local_entry = NULL; - struct batadv_tt_global_entry *tt_global_entry = NULL; + struct batadv_tt_local_entry *tt_local; + struct batadv_tt_global_entry *tt_global; struct hlist_head *head; struct hlist_node *node; struct batadv_tt_orig_list_entry *orig_entry; int hash_added; + bool roamed_back = false;
- tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr); + tt_local = batadv_tt_local_hash_find(bat_priv, addr); + tt_global = batadv_tt_global_hash_find(bat_priv, addr);
- if (tt_local_entry) { - tt_local_entry->last_seen = jiffies; - /* possibly unset the BATADV_TT_CLIENT_PENDING flag */ - tt_local_entry->common.flags &= ~BATADV_TT_CLIENT_PENDING; - goto out; + if (tt_local) { + tt_local->last_seen = jiffies; + if (tt_local->common.flags & BATADV_TT_CLIENT_PENDING) { + batadv_dbg(BATADV_DBG_TT, bat_priv, + "Re-adding pending client %pM\n", addr); + /* whatever the reason why the PENDING flag was set, + * this is a client which was enqueued to be removed in + * this orig_interval. Since it popped up again, the + * flag can be reset like it was never enqueued + */ + tt_local->common.flags &= ~BATADV_TT_CLIENT_PENDING; + goto add_event; + } + + if (tt_local->common.flags & BATADV_TT_CLIENT_ROAM) { + batadv_dbg(BATADV_DBG_TT, bat_priv, + "Roaming client %pM came back to its original location\n", + addr); + /* the ROAM flag is set because this client roamed away + * and the node got a roaming_advertisement message. Now + * that the client popped up again at its original + * location such flag can be unset + */ + tt_local->common.flags &= ~BATADV_TT_CLIENT_ROAM; + roamed_back = true; + goto check_roaming; + } + goto check_roaming; }
- tt_local_entry = kmalloc(sizeof(*tt_local_entry), GFP_ATOMIC); - if (!tt_local_entry) + tt_local = kmalloc(sizeof(*tt_local), GFP_ATOMIC); + if (!tt_local) goto out;
batadv_dbg(BATADV_DBG_TT, bat_priv, "Creating new local tt entry: %pM (ttvn: %d)\n", addr, (uint8_t)atomic_read(&bat_priv->tt.vn));
- memcpy(tt_local_entry->common.addr, addr, ETH_ALEN); - tt_local_entry->common.flags = BATADV_NO_FLAGS; + memcpy(tt_local->common.addr, addr, ETH_ALEN); + tt_local->common.flags = BATADV_NO_FLAGS; if (batadv_is_wifi_iface(ifindex)) - tt_local_entry->common.flags |= BATADV_TT_CLIENT_WIFI; - atomic_set(&tt_local_entry->common.refcount, 2); - tt_local_entry->last_seen = jiffies; - tt_local_entry->common.added_at = tt_local_entry->last_seen; + tt_local->common.flags |= BATADV_TT_CLIENT_WIFI; + atomic_set(&tt_local->common.refcount, 2); + tt_local->last_seen = jiffies; + tt_local->common.added_at = tt_local->last_seen;
/* the batman interface mac address should never be purged */ if (batadv_compare_eth(addr, soft_iface->dev_addr)) - tt_local_entry->common.flags |= BATADV_TT_CLIENT_NOPURGE; + tt_local->common.flags |= BATADV_TT_CLIENT_NOPURGE;
/* The local entry has to be marked as NEW to avoid to send it in * a full table response going out before the next ttvn increment * (consistency check) */ - tt_local_entry->common.flags |= BATADV_TT_CLIENT_NEW; + tt_local->common.flags |= BATADV_TT_CLIENT_NEW;
hash_added = batadv_hash_add(bat_priv->tt.local_hash, batadv_compare_tt, - batadv_choose_orig, - &tt_local_entry->common, - &tt_local_entry->common.hash_entry); + batadv_choose_orig, &tt_local->common, + &tt_local->common.hash_entry);
if (unlikely(hash_added != 0)) { /* remove the reference for the hash */ - batadv_tt_local_entry_free_ref(tt_local_entry); + batadv_tt_local_entry_free_ref(tt_local); goto out; }
- batadv_tt_local_event(bat_priv, addr, tt_local_entry->common.flags); - - /* remove address from global hash if present */ - tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr); + add_event: + batadv_tt_local_event(bat_priv, addr, tt_local->common.flags);
+ check_roaming: /* Check whether it is a roaming! */ - if (tt_global_entry) { + if (tt_global) { /* These node are probably going to update their tt table */ - head = &tt_global_entry->orig_list; + head = &tt_global->orig_list; rcu_read_lock(); hlist_for_each_entry_rcu(orig_entry, node, head, list) { - orig_entry->orig_node->tt_poss_change = true; - - batadv_send_roam_adv(bat_priv, - tt_global_entry->common.addr, + batadv_send_roam_adv(bat_priv, tt_global->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 |= BATADV_TT_CLIENT_ROAM; - tt_global_entry->roam_at = jiffies; + if (roamed_back) { + batadv_tt_global_free(bat_priv, tt_global, + "Roaming canceled"); + tt_global = NULL; + } else { + /* The global entry has to be marked as ROAMING and + * has to be kept for consistency purpose + */ + tt_global->common.flags |= BATADV_TT_CLIENT_ROAM; + tt_global->roam_at = jiffies; + } } + out: - if (tt_local_entry) - batadv_tt_local_entry_free_ref(tt_local_entry); - if (tt_global_entry) - batadv_tt_global_entry_free_ref(tt_global_entry); + if (tt_local) + batadv_tt_local_entry_free_ref(tt_local); + if (tt_global) + batadv_tt_global_entry_free_ref(tt_global); }
static void batadv_tt_realloc_packet_buff(unsigned char **packet_buff, @@@ -434,22 -475,10 +475,10 @@@ int batadv_tt_local_seq_print_text(stru struct hlist_node *node; struct hlist_head *head; uint32_t i; - int ret = 0; - - primary_if = batadv_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 != BATADV_IF_ACTIVE) { - ret = seq_printf(seq, - "BATMAN mesh %s disabled - primary interface not active\n", - net_dev->name); + primary_if = batadv_seq_print_text_primary_if_get(seq); + if (!primary_if) goto out; - }
seq_printf(seq, "Locally retrieved addresses (from %s) announced via TT (TTVN: %u):\n", @@@ -479,7 -508,7 +508,7 @@@ out: if (primary_if) batadv_hardif_free_ref(primary_if); - return ret; + return 0; }
static void @@@ -501,24 -530,57 +530,57 @@@ batadv_tt_local_set_pending(struct bata tt_local_entry->common.addr, message); }
- void batadv_tt_local_remove(struct batadv_priv *bat_priv, const uint8_t *addr, - const char *message, bool roaming) + /** + * batadv_tt_local_remove - logically remove an entry from the local table + * @bat_priv: the bat priv with all the soft interface information + * @addr: the MAC address of the client to remove + * @message: message to append to the log on deletion + * @roaming: true if the deletion is due to a roaming event + * + * Returns the flags assigned to the local entry before being deleted + */ + uint16_t batadv_tt_local_remove(struct batadv_priv *bat_priv, + const uint8_t *addr, const char *message, + bool roaming) { - struct batadv_tt_local_entry *tt_local_entry = NULL; - uint16_t flags; + struct batadv_tt_local_entry *tt_local_entry; + uint16_t flags, curr_flags = BATADV_NO_FLAGS;
tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr); if (!tt_local_entry) goto out;
+ curr_flags = tt_local_entry->common.flags; + flags = BATADV_TT_CLIENT_DEL; - if (roaming) + /* if this global entry addition is due to a roaming, the node has to + * mark the local entry as "roamed" in order to correctly reroute + * packets later + */ + if (roaming) { flags |= BATADV_TT_CLIENT_ROAM; + /* mark the local client as ROAMed */ + tt_local_entry->common.flags |= BATADV_TT_CLIENT_ROAM; + } + + if (!(tt_local_entry->common.flags & BATADV_TT_CLIENT_NEW)) { + batadv_tt_local_set_pending(bat_priv, tt_local_entry, flags, + message); + goto out; + } + /* if this client has been added right now, it is possible to + * immediately purge it + */ + batadv_tt_local_event(bat_priv, tt_local_entry->common.addr, + curr_flags | BATADV_TT_CLIENT_DEL); + hlist_del_rcu(&tt_local_entry->common.hash_entry); + batadv_tt_local_entry_free_ref(tt_local_entry);
out: if (tt_local_entry) batadv_tt_local_entry_free_ref(tt_local_entry); + + return curr_flags; }
static void batadv_tt_local_purge_list(struct batadv_priv *bat_priv, @@@ -721,12 -783,23 +783,23 @@@ int batadv_tt_global_add(struct batadv_ const unsigned char *tt_addr, uint8_t flags, uint8_t ttvn) { - struct batadv_tt_global_entry *tt_global_entry = NULL; + struct batadv_tt_global_entry *tt_global_entry; + struct batadv_tt_local_entry *tt_local_entry; int ret = 0; int hash_added; struct batadv_tt_common_entry *common; + uint16_t local_flags;
tt_global_entry = batadv_tt_global_hash_find(bat_priv, tt_addr); + tt_local_entry = batadv_tt_local_hash_find(bat_priv, tt_addr); + + /* if the node already has a local client for this entry, it has to wait + * for a roaming advertisement instead of manually messing up the global + * table + */ + if ((flags & BATADV_TT_CLIENT_TEMP) && tt_local_entry && + !(tt_local_entry->common.flags & BATADV_TT_CLIENT_NEW)) + goto out;
if (!tt_global_entry) { tt_global_entry = kzalloc(sizeof(*tt_global_entry), GFP_ATOMIC); @@@ -738,6 -811,12 +811,12 @@@
common->flags = flags; tt_global_entry->roam_at = 0; + /* node must store current time in case of roaming. This is + * needed to purge this entry out on timeout (if nobody claims + * it) + */ + if (flags & BATADV_TT_CLIENT_ROAM) + tt_global_entry->roam_at = jiffies; atomic_set(&common->refcount, 2); common->added_at = jiffies;
@@@ -755,19 -834,39 +834,39 @@@ goto out_remove; } } else { + common = &tt_global_entry->common; /* If there is already a global entry, we can use this one for * our processing. - * But if we are trying to add a temporary client we can exit - * directly because the temporary information should never - * override any already known client state (whatever it is) + * But if we are trying to add a temporary client then here are + * two options at this point: + * 1) the global client is not a temporary client: the global + * client has to be left as it is, temporary information + * should never override any already known client state + * 2) the global client is a temporary client: purge the + * originator list and add the new one orig_entry */ - if (flags & BATADV_TT_CLIENT_TEMP) - goto out; + if (flags & BATADV_TT_CLIENT_TEMP) { + if (!(common->flags & BATADV_TT_CLIENT_TEMP)) + goto out; + if (batadv_tt_global_entry_has_orig(tt_global_entry, + orig_node)) + goto out_remove; + batadv_tt_global_del_orig_list(tt_global_entry); + goto add_orig_entry; + }
/* if the client was temporary added before receiving the first * OGM announcing it, we have to clear the TEMP flag */ - tt_global_entry->common.flags &= ~BATADV_TT_CLIENT_TEMP; + common->flags &= ~BATADV_TT_CLIENT_TEMP; + + /* if this is a normal add, possibly unset the ROAM flag. This + * flag could have been set before because the client was + * roaming, but now that the node got the ADD event, the flag + * can be unset + */ + if (!(flags & BATADV_TT_CLIENT_ROAM)) + common->flags &= ~BATADV_TT_CLIENT_ROAM;
/* If there is the BATADV_TT_CLIENT_ROAM flag set, there is only * one originator left in the list and we previously received a @@@ -776,33 -875,81 +875,81 @@@ * We should first delete the old originator before adding the * new one. */ - if (tt_global_entry->common.flags & BATADV_TT_CLIENT_ROAM) { + if (common->flags & BATADV_TT_CLIENT_ROAM) { batadv_tt_global_del_orig_list(tt_global_entry); - tt_global_entry->common.flags &= ~BATADV_TT_CLIENT_ROAM; + common->flags &= ~BATADV_TT_CLIENT_ROAM; tt_global_entry->roam_at = 0; } } + add_orig_entry: /* add the new orig_entry (if needed) or update it */ batadv_tt_global_orig_entry_add(tt_global_entry, orig_node, ttvn);
batadv_dbg(BATADV_DBG_TT, bat_priv, "Creating new global tt entry: %pM (via %pM)\n", - tt_global_entry->common.addr, orig_node->orig); + common->addr, orig_node->orig); + ret = 1;
out_remove: + /* remove address from local hash if present */ - batadv_tt_local_remove(bat_priv, tt_global_entry->common.addr, - "global tt received", - flags & BATADV_TT_CLIENT_ROAM); - ret = 1; + local_flags = batadv_tt_local_remove(bat_priv, tt_addr, + "global tt received", + !!(flags & BATADV_TT_CLIENT_ROAM)); + tt_global_entry->common.flags |= local_flags & BATADV_TT_CLIENT_WIFI; + + if (!(flags & BATADV_TT_CLIENT_ROAM)) + /* this is a normal global add. Therefore the client is not in a + * roaming state anymore. + */ + tt_global_entry->common.flags &= ~BATADV_TT_CLIENT_ROAM; + out: if (tt_global_entry) batadv_tt_global_entry_free_ref(tt_global_entry); + if (tt_local_entry) + batadv_tt_local_entry_free_ref(tt_local_entry); return ret; }
- /* print all orig nodes who announce the address for this global entry. - * it is assumed that the caller holds rcu_read_lock(); + /* batadv_transtable_best_orig - Get best originator list entry from tt entry + * @tt_global_entry: global translation table entry to be analyzed + * + * This functon assumes the caller holds rcu_read_lock(). + * Returns best originator list entry or NULL on errors. + */ + static struct batadv_tt_orig_list_entry * + batadv_transtable_best_orig(struct batadv_tt_global_entry *tt_global_entry) + { + struct batadv_neigh_node *router = NULL; + struct hlist_head *head; + struct hlist_node *node; + struct batadv_tt_orig_list_entry *orig_entry, *best_entry = NULL; + int best_tq = 0; + + head = &tt_global_entry->orig_list; + hlist_for_each_entry_rcu(orig_entry, node, head, list) { + router = batadv_orig_node_get_router(orig_entry->orig_node); + if (!router) + continue; + + if (router->tq_avg > best_tq) { + best_entry = orig_entry; + best_tq = router->tq_avg; + } + + batadv_neigh_node_free_ref(router); + } + + return best_entry; + } + + /* batadv_tt_global_print_entry - print all orig nodes who announce the address + * for this global entry + * @tt_global_entry: global translation table entry to be printed + * @seq: debugfs table seq_file struct + * + * This functon assumes the caller holds rcu_read_lock(). */ static void batadv_tt_global_print_entry(struct batadv_tt_global_entry *tt_global_entry, @@@ -810,21 -957,37 +957,37 @@@ { struct hlist_head *head; struct hlist_node *node; - struct batadv_tt_orig_list_entry *orig_entry; + struct batadv_tt_orig_list_entry *orig_entry, *best_entry; struct batadv_tt_common_entry *tt_common_entry; uint16_t flags; uint8_t last_ttvn;
tt_common_entry = &tt_global_entry->common; + flags = tt_common_entry->flags; + + best_entry = batadv_transtable_best_orig(tt_global_entry); + if (best_entry) { + last_ttvn = atomic_read(&best_entry->orig_node->last_ttvn); + seq_printf(seq, " %c %pM (%3u) via %pM (%3u) [%c%c%c]\n", + '*', tt_global_entry->common.addr, + best_entry->ttvn, best_entry->orig_node->orig, + last_ttvn, + (flags & BATADV_TT_CLIENT_ROAM ? 'R' : '.'), + (flags & BATADV_TT_CLIENT_WIFI ? 'W' : '.'), + (flags & BATADV_TT_CLIENT_TEMP ? 'T' : '.')); + }
head = &tt_global_entry->orig_list;
hlist_for_each_entry_rcu(orig_entry, node, head, list) { - flags = tt_common_entry->flags; + if (best_entry == orig_entry) + continue; + last_ttvn = atomic_read(&orig_entry->orig_node->last_ttvn); - seq_printf(seq, " * %pM (%3u) via %pM (%3u) [%c%c%c]\n", - tt_global_entry->common.addr, orig_entry->ttvn, - orig_entry->orig_node->orig, last_ttvn, + seq_printf(seq, " %c %pM (%3u) via %pM (%3u) [%c%c%c]\n", + '+', tt_global_entry->common.addr, + orig_entry->ttvn, orig_entry->orig_node->orig, + last_ttvn, (flags & BATADV_TT_CLIENT_ROAM ? 'R' : '.'), (flags & BATADV_TT_CLIENT_WIFI ? 'W' : '.'), (flags & BATADV_TT_CLIENT_TEMP ? 'T' : '.')); @@@ -842,22 -1005,10 +1005,10 @@@ int batadv_tt_global_seq_print_text(str struct hlist_node *node; struct hlist_head *head; uint32_t i; - int ret = 0; - - primary_if = batadv_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 != BATADV_IF_ACTIVE) { - ret = seq_printf(seq, - "BATMAN mesh %s disabled - primary interface not active\n", - net_dev->name); + primary_if = batadv_seq_print_text_primary_if_get(seq); + if (!primary_if) goto out; - }
seq_printf(seq, "Globally announced TT entries received via the mesh %s\n", @@@ -881,7 -1032,7 +1032,7 @@@ out: if (primary_if) batadv_hardif_free_ref(primary_if); - return ret; + return 0; }
/* deletes the orig list of a tt_global_entry */ @@@ -927,21 -1078,6 +1078,6 @@@ batadv_tt_global_del_orig_entry(struct spin_unlock_bh(&tt_global_entry->list_lock); }
- static void - batadv_tt_global_del_struct(struct batadv_priv *bat_priv, - struct batadv_tt_global_entry *tt_global_entry, - const char *message) - { - batadv_dbg(BATADV_DBG_TT, bat_priv, - "Deleting global tt entry %pM: %s\n", - tt_global_entry->common.addr, message); - - batadv_hash_remove(bat_priv->tt.global_hash, batadv_compare_tt, - batadv_choose_orig, tt_global_entry->common.addr); - batadv_tt_global_entry_free_ref(tt_global_entry); - - } - /* 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 BATADV_TT_CLIENT_ROAM flag and the * timer, otherwise we simply remove the originator scheduled for deletion. @@@ -990,7 -1126,7 +1126,7 @@@ static void batadv_tt_global_del(struc const unsigned char *addr, const char *message, bool roaming) { - struct batadv_tt_global_entry *tt_global_entry = NULL; + struct batadv_tt_global_entry *tt_global_entry; struct batadv_tt_local_entry *local_entry = NULL;
tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr); @@@ -1002,8 -1138,8 +1138,8 @@@ orig_node, message);
if (hlist_empty(&tt_global_entry->orig_list)) - batadv_tt_global_del_struct(bat_priv, tt_global_entry, - message); + batadv_tt_global_free(bat_priv, tt_global_entry, + message);
goto out; } @@@ -1026,7 -1162,7 +1162,7 @@@ if (local_entry) { /* local entry exists, case 2: client roamed to us. */ batadv_tt_global_del_orig_list(tt_global_entry); - batadv_tt_global_del_struct(bat_priv, tt_global_entry, message); + batadv_tt_global_free(bat_priv, tt_global_entry, message); } else /* no local entry exists, case 1: check for roaming */ batadv_tt_global_del_roaming(bat_priv, tt_global_entry, @@@ -1197,15 -1333,12 +1333,12 @@@ struct batadv_orig_node *batadv_transta struct batadv_tt_local_entry *tt_local_entry = NULL; struct batadv_tt_global_entry *tt_global_entry = NULL; struct batadv_orig_node *orig_node = NULL; - struct batadv_neigh_node *router = NULL; - struct hlist_head *head; - struct hlist_node *node; - struct batadv_tt_orig_list_entry *orig_entry; - int best_tq; + struct batadv_tt_orig_list_entry *best_entry;
if (src && atomic_read(&bat_priv->ap_isolation)) { tt_local_entry = batadv_tt_local_hash_find(bat_priv, src); - if (!tt_local_entry) + if (!tt_local_entry || + (tt_local_entry->common.flags & BATADV_TT_CLIENT_PENDING)) goto out; }
@@@ -1220,25 -1353,15 +1353,15 @@@ _batadv_is_ap_isolated(tt_local_entry, tt_global_entry)) goto out;
- best_tq = 0; - rcu_read_lock(); - head = &tt_global_entry->orig_list; - hlist_for_each_entry_rcu(orig_entry, node, head, list) { - router = batadv_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; - } - batadv_neigh_node_free_ref(router); - } + best_entry = batadv_transtable_best_orig(tt_global_entry); /* found anything? */ + if (best_entry) + orig_node = best_entry->orig_node; if (orig_node && !atomic_inc_not_zero(&orig_node->refcount)) orig_node = NULL; rcu_read_unlock(); + out: if (tt_global_entry) batadv_tt_global_entry_free_ref(tt_global_entry); @@@ -1520,7 -1643,6 +1643,6 @@@ static int batadv_send_tt_request(struc { struct sk_buff *skb = NULL; struct batadv_tt_query_packet *tt_request; - struct batadv_neigh_node *neigh_node = NULL; struct batadv_hard_iface *primary_if; struct batadv_tt_req_node *tt_req_node = NULL; int ret = 1; @@@ -1558,23 -1680,15 +1680,15 @@@ if (full_table) tt_request->flags |= BATADV_TT_FULL_TABLE;
- neigh_node = batadv_orig_node_get_router(dst_orig_node); - if (!neigh_node) - goto out; - - batadv_dbg(BATADV_DBG_TT, bat_priv, - "Sending TT_REQUEST to %pM via %pM [%c]\n", - dst_orig_node->orig, neigh_node->addr, - (full_table ? 'F' : '.')); + batadv_dbg(BATADV_DBG_TT, bat_priv, "Sending TT_REQUEST to %pM [%c]\n", + dst_orig_node->orig, (full_table ? 'F' : '.'));
batadv_inc_counter(bat_priv, BATADV_CNT_TT_REQUEST_TX);
- batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); - ret = 0; + if (batadv_send_skb_to_orig(skb, dst_orig_node, NULL)) + ret = 0;
out: - if (neigh_node) - batadv_neigh_node_free_ref(neigh_node); if (primary_if) batadv_hardif_free_ref(primary_if); if (ret) @@@ -1592,9 -1706,8 +1706,8 @@@ static boo batadv_send_other_tt_response(struct batadv_priv *bat_priv, struct batadv_tt_query_packet *tt_request) { - struct batadv_orig_node *req_dst_orig_node = NULL; + struct batadv_orig_node *req_dst_orig_node; struct batadv_orig_node *res_dst_orig_node = NULL; - struct batadv_neigh_node *neigh_node = NULL; struct batadv_hard_iface *primary_if = NULL; uint8_t orig_ttvn, req_ttvn, ttvn; int ret = false; @@@ -1620,10 -1733,6 +1733,6 @@@ if (!res_dst_orig_node) goto out;
- neigh_node = batadv_orig_node_get_router(res_dst_orig_node); - if (!neigh_node) - goto out; - primary_if = batadv_primary_if_get_selected(bat_priv); if (!primary_if) goto out; @@@ -1695,14 -1804,13 +1804,13 @@@ tt_response->flags |= BATADV_TT_FULL_TABLE;
batadv_dbg(BATADV_DBG_TT, bat_priv, - "Sending TT_RESPONSE %pM via %pM for %pM (ttvn: %u)\n", - res_dst_orig_node->orig, neigh_node->addr, - req_dst_orig_node->orig, req_ttvn); + "Sending TT_RESPONSE %pM for %pM (ttvn: %u)\n", + res_dst_orig_node->orig, req_dst_orig_node->orig, req_ttvn);
batadv_inc_counter(bat_priv, BATADV_CNT_TT_RESPONSE_TX);
- batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); - ret = true; + if (batadv_send_skb_to_orig(skb, res_dst_orig_node, NULL)) + ret = true; goto out;
unlock: @@@ -1713,8 -1821,6 +1821,6 @@@ out batadv_orig_node_free_ref(res_dst_orig_node); if (req_dst_orig_node) batadv_orig_node_free_ref(req_dst_orig_node); - if (neigh_node) - batadv_neigh_node_free_ref(neigh_node); if (primary_if) batadv_hardif_free_ref(primary_if); if (!ret) @@@ -1727,8 -1833,7 +1833,7 @@@ static boo batadv_send_my_tt_response(struct batadv_priv *bat_priv, struct batadv_tt_query_packet *tt_request) { - struct batadv_orig_node *orig_node = NULL; - struct batadv_neigh_node *neigh_node = NULL; + struct batadv_orig_node *orig_node; struct batadv_hard_iface *primary_if = NULL; uint8_t my_ttvn, req_ttvn, ttvn; int ret = false; @@@ -1753,10 -1858,6 +1858,6 @@@ if (!orig_node) goto out;
- neigh_node = batadv_orig_node_get_router(orig_node); - if (!neigh_node) - goto out; - primary_if = batadv_primary_if_get_selected(bat_priv); if (!primary_if) goto out; @@@ -1820,14 -1921,14 +1921,14 @@@ tt_response->flags |= BATADV_TT_FULL_TABLE;
batadv_dbg(BATADV_DBG_TT, bat_priv, - "Sending TT_RESPONSE to %pM via %pM [%c]\n", - orig_node->orig, neigh_node->addr, + "Sending TT_RESPONSE to %pM [%c]\n", + orig_node->orig, (tt_response->flags & BATADV_TT_FULL_TABLE ? 'F' : '.'));
batadv_inc_counter(bat_priv, BATADV_CNT_TT_RESPONSE_TX);
- batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); - ret = true; + if (batadv_send_skb_to_orig(skb, orig_node, NULL)) + ret = true; goto out;
unlock: @@@ -1835,8 -1936,6 +1936,6 @@@ out: if (orig_node) batadv_orig_node_free_ref(orig_node); - if (neigh_node) - batadv_neigh_node_free_ref(neigh_node); if (primary_if) batadv_hardif_free_ref(primary_if); if (!ret) @@@ -1893,7 -1992,7 +1992,7 @@@ static void _batadv_tt_update_changes(s static void batadv_tt_fill_gtable(struct batadv_priv *bat_priv, struct batadv_tt_query_packet *tt_response) { - struct batadv_orig_node *orig_node = NULL; + struct batadv_orig_node *orig_node;
orig_node = batadv_orig_hash_find(bat_priv, tt_response->src); if (!orig_node) @@@ -1935,7 -2034,7 +2034,7 @@@ static void batadv_tt_update_changes(st
bool batadv_is_my_client(struct batadv_priv *bat_priv, const uint8_t *addr) { - struct batadv_tt_local_entry *tt_local_entry = NULL; + struct batadv_tt_local_entry *tt_local_entry; bool ret = false;
tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr); @@@ -1995,10 -2094,6 +2094,6 @@@ void batadv_handle_tt_response(struct b
/* Recalculate the CRC for this orig_node and store it */ orig_node->tt_crc = batadv_tt_global_crc(bat_priv, orig_node); - /* Roaming phase is over: tables are in sync again. I can - * unset the flag - */ - orig_node->tt_poss_change = false; out: if (orig_node) batadv_orig_node_free_ref(orig_node); @@@ -2104,7 -2199,6 +2199,6 @@@ unlock static void batadv_send_roam_adv(struct batadv_priv *bat_priv, uint8_t *client, struct batadv_orig_node *orig_node) { - struct batadv_neigh_node *neigh_node = NULL; struct sk_buff *skb = NULL; struct batadv_roam_adv_packet *roam_adv_packet; int ret = 1; @@@ -2137,23 -2231,17 +2231,17 @@@ memcpy(roam_adv_packet->dst, orig_node->orig, ETH_ALEN); memcpy(roam_adv_packet->client, client, ETH_ALEN);
- neigh_node = batadv_orig_node_get_router(orig_node); - if (!neigh_node) - goto out; - batadv_dbg(BATADV_DBG_TT, bat_priv, - "Sending ROAMING_ADV to %pM (client %pM) via %pM\n", - orig_node->orig, client, neigh_node->addr); + "Sending ROAMING_ADV to %pM (client %pM)\n", + orig_node->orig, client);
batadv_inc_counter(bat_priv, BATADV_CNT_TT_ROAM_ADV_TX);
- batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); - ret = 0; + if (batadv_send_skb_to_orig(skb, orig_node, NULL)) + ret = 0;
out: - if (neigh_node) - batadv_neigh_node_free_ref(neigh_node); - if (ret) + if (ret && skb) kfree_skb(skb); return; } @@@ -2289,7 -2377,6 +2377,6 @@@ static int batadv_tt_commit_changes(str batadv_dbg(BATADV_DBG_TT, bat_priv, "Local changes committed, updating to ttvn %u\n", (uint8_t)atomic_read(&bat_priv->tt.vn)); - bat_priv->tt.poss_change = false;
/* reset the sending counter */ atomic_set(&bat_priv->tt.ogm_append_cnt, BATADV_TT_OGM_APPEND_MAX); @@@ -2401,11 -2488,6 +2488,6 @@@ void batadv_tt_update_orig(struct batad */ if (orig_node->tt_crc != tt_crc) goto request_table; - - /* Roaming phase is over: tables are in sync again. I can - * unset the flag - */ - orig_node->tt_poss_change = false; } else { /* if we missed more than one change or our tables are not * in sync anymore -> request fresh tt data @@@ -2438,12 -2520,38 +2520,38 @@@ bool batadv_tt_global_client_is_roaming if (!tt_global_entry) goto out;
- ret = tt_global_entry->common.flags & BATADV_TT_CLIENT_ROAM; + ret = !!(tt_global_entry->common.flags & BATADV_TT_CLIENT_ROAM); batadv_tt_global_entry_free_ref(tt_global_entry); out: return ret; }
+ /** + * batadv_tt_local_client_is_roaming - tells whether the client is roaming + * @bat_priv: the bat priv with all the soft interface information + * @addr: the MAC address of the local client to query + * + * Returns true if the local client is known to be roaming (it is not served by + * this node anymore) or not. If yes, the client is still present in the table + * to keep the latter consistent with the node TTVN + */ + bool batadv_tt_local_client_is_roaming(struct batadv_priv *bat_priv, + uint8_t *addr) + { + struct batadv_tt_local_entry *tt_local_entry; + bool ret = false; + + tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr); + if (!tt_local_entry) + goto out; + + ret = !!(tt_local_entry->common.flags & BATADV_TT_CLIENT_ROAM); + batadv_tt_local_entry_free_ref(tt_local_entry); + out: + return ret; + + } + bool batadv_tt_add_temporary_global_entry(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, const unsigned char *addr) diff --combined net/batman-adv/translation-table.h index 811fffd,46d4451..46d4451 --- a/net/batman-adv/translation-table.h +++ b/net/batman-adv/translation-table.h @@@ -24,9 -24,9 +24,9 @@@ int batadv_tt_len(int changes_num) int batadv_tt_init(struct batadv_priv *bat_priv); void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr, int ifindex); - void batadv_tt_local_remove(struct batadv_priv *bat_priv, - const uint8_t *addr, const char *message, - bool roaming); + uint16_t batadv_tt_local_remove(struct batadv_priv *bat_priv, + const uint8_t *addr, const char *message, + bool roaming); int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset); void batadv_tt_global_add_orig(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, @@@ -59,6 -59,8 +59,8 @@@ int batadv_tt_append_diff(struct batadv int packet_min_len); bool batadv_tt_global_client_is_roaming(struct batadv_priv *bat_priv, uint8_t *addr); + bool batadv_tt_local_client_is_roaming(struct batadv_priv *bat_priv, + uint8_t *addr); bool batadv_tt_add_temporary_global_entry(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, const unsigned char *addr); diff --combined net/batman-adv/types.h index ac1e07a,7b3d0d7..7b3d0d7 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@@ -28,20 -28,41 +28,41 @@@ (ETH_HLEN + max(sizeof(struct batadv_unicast_packet), \ sizeof(struct batadv_bcast_packet)))
+ #ifdef CONFIG_BATMAN_ADV_DAT + + /* batadv_dat_addr_t is the type used for all DHT addresses. If it is changed, + * BATADV_DAT_ADDR_MAX is changed as well. + * + * *Please be careful: batadv_dat_addr_t must be UNSIGNED* + */ + #define batadv_dat_addr_t uint16_t + + #endif /* CONFIG_BATMAN_ADV_DAT */ + + /** + * struct batadv_hard_iface_bat_iv - per hard interface B.A.T.M.A.N. IV data + * @ogm_buff: buffer holding the OGM packet + * @ogm_buff_len: length of the OGM packet buffer + * @ogm_seqno: OGM sequence number - used to identify each OGM + */ + struct batadv_hard_iface_bat_iv { + unsigned char *ogm_buff; + int ogm_buff_len; + atomic_t ogm_seqno; + }; + struct batadv_hard_iface { struct list_head list; int16_t if_num; char if_status; struct net_device *net_dev; - atomic_t seqno; atomic_t frag_seqno; - unsigned char *packet_buff; - int packet_len; struct kobject *hardif_obj; atomic_t refcount; struct packet_type batman_adv_ptype; struct net_device *soft_iface; struct rcu_head rcu; + struct batadv_hard_iface_bat_iv bat_iv; };
/** @@@ -63,6 -84,9 +84,9 @@@ struct batadv_orig_node uint8_t orig[ETH_ALEN]; uint8_t primary_addr[ETH_ALEN]; struct batadv_neigh_node __rcu *router; /* rcu protected pointer */ + #ifdef CONFIG_BATMAN_ADV_DAT + batadv_dat_addr_t dat_addr; + #endif unsigned long *bcast_own; uint8_t *bcast_own_sum; unsigned long last_seen; @@@ -77,13 -101,6 +101,6 @@@ spinlock_t tt_buff_lock; /* protects tt_buff */ atomic_t tt_size; bool tt_initialised; - /* The tt_poss_change flag is used to detect an ongoing roaming phase. - * If true, then I sent a Roaming_adv to this orig_node and I have to - * inspect every packet directed to it to check whether it is still - * the true destination or not. This flag will be reset to false as - * soon as I receive a new TTVN from this orig_node - */ - bool tt_poss_change; uint32_t last_real_seqno; uint8_t last_ttl; DECLARE_BITMAP(bcast_bits, BATADV_TQ_LOCAL_WINDOW_SIZE); @@@ -162,6 -179,13 +179,13 @@@ enum batadv_counters BATADV_CNT_TT_RESPONSE_RX, BATADV_CNT_TT_ROAM_ADV_TX, BATADV_CNT_TT_ROAM_ADV_RX, + #ifdef CONFIG_BATMAN_ADV_DAT + BATADV_CNT_DAT_GET_TX, + BATADV_CNT_DAT_GET_RX, + BATADV_CNT_DAT_PUT_TX, + BATADV_CNT_DAT_PUT_RX, + BATADV_CNT_DAT_CACHED_REPLY_TX, + #endif BATADV_CNT_NUM, };
@@@ -181,7 -205,6 +205,6 @@@ struct batadv_priv_tt atomic_t vn; atomic_t ogm_append_cnt; atomic_t local_changes; - bool poss_change; struct list_head changes_list; struct batadv_hashtable *local_hash; struct batadv_hashtable *global_hash; @@@ -228,6 -251,20 +251,20 @@@ struct batadv_priv_vis struct batadv_vis_info *my_info; };
+ /** + * struct batadv_priv_dat - per mesh interface DAT private data + * @addr: node DAT address + * @hash: hashtable representing the local ARP cache + * @work: work queue callback item for cache purging + */ + #ifdef CONFIG_BATMAN_ADV_DAT + struct batadv_priv_dat { + batadv_dat_addr_t addr; + struct batadv_hashtable *hash; + struct delayed_work work; + }; + #endif + struct batadv_priv { atomic_t mesh_state; struct net_device_stats stats; @@@ -237,6 -274,9 +274,9 @@@ atomic_t fragmentation; /* boolean */ atomic_t ap_isolation; /* boolean */ atomic_t bridge_loop_avoidance; /* boolean */ + #ifdef CONFIG_BATMAN_ADV_DAT + atomic_t distributed_arp_table; /* boolean */ + #endif atomic_t vis_mode; /* VIS_TYPE_* */ atomic_t gw_mode; /* GW_MODE_* */ atomic_t gw_sel_class; /* uint */ @@@ -255,7 -295,7 +295,7 @@@ struct hlist_head forw_bcast_list; struct batadv_hashtable *orig_hash; spinlock_t forw_bat_list_lock; /* protects forw_bat_list */ - spinlock_t forw_bcast_list_lock; /* protects */ + spinlock_t forw_bcast_list_lock; /* protects forw_bcast_list */ struct delayed_work orig_work; struct batadv_hard_iface __rcu *primary_if; /* rcu protected pointer */ struct batadv_algo_ops *bat_algo_ops; @@@ -265,6 -305,9 +305,9 @@@ struct batadv_priv_gw gw; struct batadv_priv_tt tt; struct batadv_priv_vis vis; + #ifdef CONFIG_BATMAN_ADV_DAT + struct batadv_priv_dat dat; + #endif };
struct batadv_socket_client { @@@ -318,6 -361,7 +361,7 @@@ struct batadv_backbone_gw struct hlist_node hash_entry; struct batadv_priv *bat_priv; unsigned long lasttime; /* last time we heard of this backbone gw */ + atomic_t wait_periods; atomic_t request_sent; atomic_t refcount; struct rcu_head rcu; @@@ -437,4 -481,36 +481,36 @@@ struct batadv_algo_ops void (*bat_ogm_emit)(struct batadv_forw_packet *forw_packet); };
+ /** + * struct batadv_dat_entry - it is a single entry of batman-adv ARP backend. It + * is used to stored ARP entries needed for the global DAT cache + * @ip: the IPv4 corresponding to this DAT/ARP entry + * @mac_addr: the MAC address associated to the stored IPv4 + * @last_update: time in jiffies when this entry was refreshed last time + * @hash_entry: hlist node for batadv_priv_dat::hash + * @refcount: number of contexts the object is used + * @rcu: struct used for freeing in an RCU-safe manner + */ + struct batadv_dat_entry { + __be32 ip; + uint8_t mac_addr[ETH_ALEN]; + unsigned long last_update; + struct hlist_node hash_entry; + atomic_t refcount; + struct rcu_head rcu; + }; + + /** + * struct batadv_dat_candidate - candidate destination for DAT operations + * @type: the type of the selected candidate. It can one of the following: + * - BATADV_DAT_CANDIDATE_NOT_FOUND + * - BATADV_DAT_CANDIDATE_ORIG + * @orig_node: if type is BATADV_DAT_CANDIDATE_ORIG this field points to the + * corresponding originator node structure + */ + struct batadv_dat_candidate { + int type; + struct batadv_orig_node *orig_node; + }; + #endif /* _NET_BATMAN_ADV_TYPES_H_ */ diff --combined net/batman-adv/unicast.c index f397232,9416136..9416136 --- a/net/batman-adv/unicast.c +++ b/net/batman-adv/unicast.c @@@ -291,14 -291,117 +291,117 @@@ out return ret; }
- int batadv_unicast_send_skb(struct sk_buff *skb, struct batadv_priv *bat_priv) + /** + * batadv_unicast_push_and_fill_skb - extends the buffer and initializes the + * common fields for unicast packets + * @skb: packet + * @hdr_size: amount of bytes to push at the beginning of the skb + * @orig_node: the destination node + * + * Returns false if the buffer extension was not possible or true otherwise + */ + static bool batadv_unicast_push_and_fill_skb(struct sk_buff *skb, int hdr_size, + struct batadv_orig_node *orig_node) + { + struct batadv_unicast_packet *unicast_packet; + uint8_t ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn); + + if (batadv_skb_head_push(skb, hdr_size) < 0) + return false; + + unicast_packet = (struct batadv_unicast_packet *)skb->data; + unicast_packet->header.version = BATADV_COMPAT_VERSION; + /* batman packet type: unicast */ + unicast_packet->header.packet_type = BATADV_UNICAST; + /* set unicast ttl */ + unicast_packet->header.ttl = BATADV_TTL; + /* copy the destination for faster routing */ + memcpy(unicast_packet->dest, orig_node->orig, ETH_ALEN); + /* set the destination tt version number */ + unicast_packet->ttvn = ttvn; + + return true; + } + + /** + * batadv_unicast_prepare_skb - encapsulate an skb with a unicast header + * @skb: the skb containing the payload to encapsulate + * @orig_node: the destination node + * + * Returns false if the payload could not be encapsulated or true otherwise + */ + static bool batadv_unicast_prepare_skb(struct sk_buff *skb, + struct batadv_orig_node *orig_node) + { + size_t uni_size = sizeof(struct batadv_unicast_packet); + return batadv_unicast_push_and_fill_skb(skb, uni_size, orig_node); + } + + /** + * batadv_unicast_4addr_prepare_skb - encapsulate an skb with a unicast4addr + * header + * @bat_priv: the bat priv with all the soft interface information + * @skb: the skb containing the payload to encapsulate + * @orig_node: the destination node + * @packet_subtype: the batman 4addr packet subtype to use + * + * Returns false if the payload could not be encapsulated or true otherwise + */ + bool batadv_unicast_4addr_prepare_skb(struct batadv_priv *bat_priv, + struct sk_buff *skb, + struct batadv_orig_node *orig, + int packet_subtype) + { + struct batadv_hard_iface *primary_if; + struct batadv_unicast_4addr_packet *unicast_4addr_packet; + bool ret = false; + + primary_if = batadv_primary_if_get_selected(bat_priv); + if (!primary_if) + goto out; + + /* pull the header space and fill the unicast_packet substructure. + * We can do that because the first member of the unicast_4addr_packet + * is of type struct unicast_packet + */ + if (!batadv_unicast_push_and_fill_skb(skb, + sizeof(*unicast_4addr_packet), + orig)) + goto out; + + unicast_4addr_packet = (struct batadv_unicast_4addr_packet *)skb->data; + unicast_4addr_packet->u.header.packet_type = BATADV_UNICAST_4ADDR; + memcpy(unicast_4addr_packet->src, primary_if->net_dev->dev_addr, + ETH_ALEN); + unicast_4addr_packet->subtype = packet_subtype; + + ret = true; + out: + if (primary_if) + batadv_hardif_free_ref(primary_if); + return ret; + } + + /** + * batadv_unicast_generic_send_skb - send an skb as unicast + * @bat_priv: the bat priv with all the soft interface information + * @skb: payload to send + * @packet_type: the batman unicast packet type to use + * @packet_subtype: the batman packet subtype. It is ignored if packet_type is + * not BATADV_UNICAT_4ADDR + * + * Returns 1 in case of error or 0 otherwise + */ + int batadv_unicast_generic_send_skb(struct batadv_priv *bat_priv, + struct sk_buff *skb, int packet_type, + int packet_subtype) { struct ethhdr *ethhdr = (struct ethhdr *)skb->data; struct batadv_unicast_packet *unicast_packet; struct batadv_orig_node *orig_node; struct batadv_neigh_node *neigh_node; int data_len = skb->len; - int ret = 1; + int ret = NET_RX_DROP; unsigned int dev_mtu;
/* get routing information */ @@@ -324,21 -427,23 +427,23 @@@ find_router if (!neigh_node) goto out;
- if (batadv_skb_head_push(skb, sizeof(*unicast_packet)) < 0) + switch (packet_type) { + case BATADV_UNICAST: + batadv_unicast_prepare_skb(skb, orig_node); + break; + case BATADV_UNICAST_4ADDR: + batadv_unicast_4addr_prepare_skb(bat_priv, skb, orig_node, + packet_subtype); + break; + default: + /* this function supports UNICAST and UNICAST_4ADDR only. It + * should never be invoked with any other packet type + */ goto out; + }
unicast_packet = (struct batadv_unicast_packet *)skb->data;
- unicast_packet->header.version = BATADV_COMPAT_VERSION; - /* batman packet type: unicast */ - unicast_packet->header.packet_type = BATADV_UNICAST; - /* set unicast ttl */ - unicast_packet->header.ttl = BATADV_TTL; - /* copy the destination for faster routing */ - memcpy(unicast_packet->dest, orig_node->orig, ETH_ALEN); - /* set the destination tt version number */ - unicast_packet->ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn); - /* inform the destination node that we are still missing a correct route * for this client. The destination will receive this packet and will * try to reroute it because the ttvn contained in the header is less @@@ -348,7 -453,9 +453,9 @@@ unicast_packet->ttvn = unicast_packet->ttvn - 1;
dev_mtu = neigh_node->if_incoming->net_dev->mtu; - if (atomic_read(&bat_priv->fragmentation) && + /* fragmentation mechanism only works for UNICAST (now) */ + if (packet_type == BATADV_UNICAST && + atomic_read(&bat_priv->fragmentation) && data_len + sizeof(*unicast_packet) > dev_mtu) { /* send frag skb decreases ttl */ unicast_packet->header.ttl++; @@@ -358,16 -465,15 +465,15 @@@ goto out; }
- batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); - ret = 0; - goto out; + if (batadv_send_skb_to_orig(skb, orig_node, NULL)) + ret = 0;
out: if (neigh_node) batadv_neigh_node_free_ref(neigh_node); if (orig_node) batadv_orig_node_free_ref(orig_node); - if (ret == 1) + if (ret == NET_RX_DROP) kfree_skb(skb); return ret; } diff --combined net/batman-adv/unicast.h index 1c46e2e,61abba5..61abba5 --- a/net/batman-adv/unicast.h +++ b/net/batman-adv/unicast.h @@@ -29,10 -29,44 +29,44 @@@ int batadv_frag_reassemble_skb(struct s struct batadv_priv *bat_priv, struct sk_buff **new_skb); void batadv_frag_list_free(struct list_head *head); int batadv_frag_send_skb(struct sk_buff *skb, struct batadv_priv *bat_priv, struct batadv_hard_iface *hard_iface, const uint8_t dstaddr[]); + bool batadv_unicast_4addr_prepare_skb(struct batadv_priv *bat_priv, + struct sk_buff *skb, + struct batadv_orig_node *orig_node, + int packet_subtype); + int batadv_unicast_generic_send_skb(struct batadv_priv *bat_priv, + struct sk_buff *skb, int packet_type, + int packet_subtype); + + + /** + * batadv_unicast_send_skb - send the skb encapsulated in a unicast packet + * @bat_priv: the bat priv with all the soft interface information + * @skb: the payload to send + */ + static inline int batadv_unicast_send_skb(struct batadv_priv *bat_priv, + struct sk_buff *skb) + { + return batadv_unicast_generic_send_skb(bat_priv, skb, BATADV_UNICAST, + 0); + } + + /** + * batadv_unicast_send_skb - send the skb encapsulated in a unicast4addr packet + * @bat_priv: the bat priv with all the soft interface information + * @skb: the payload to send + * @packet_subtype: the batman 4addr packet subtype to use + */ + static inline int batadv_unicast_4addr_send_skb(struct batadv_priv *bat_priv, + struct sk_buff *skb, + int packet_subtype) + { + return batadv_unicast_generic_send_skb(bat_priv, skb, + BATADV_UNICAST_4ADDR, + packet_subtype); + }
static inline int batadv_frag_can_reassemble(const struct sk_buff *skb, int mtu) { diff --combined net/batman-adv/vis.c index 5abd145,79589a3..79589a3 --- a/net/batman-adv/vis.c +++ b/net/batman-adv/vis.c @@@ -698,15 -698,12 +698,12 @@@ static void batadv_purge_vis_packets(st static void batadv_broadcast_vis_packet(struct batadv_priv *bat_priv, struct batadv_vis_info *info) { - struct batadv_neigh_node *router; struct batadv_hashtable *hash = bat_priv->orig_hash; struct hlist_node *node; struct hlist_head *head; struct batadv_orig_node *orig_node; struct batadv_vis_packet *packet; struct sk_buff *skb; - struct batadv_hard_iface *hard_iface; - uint8_t dstaddr[ETH_ALEN]; uint32_t i;
@@@ -722,30 -719,20 +719,20 @@@ if (!(orig_node->flags & BATADV_VIS_SERVER)) continue;
- router = batadv_orig_node_get_router(orig_node); - if (!router) - continue; - /* don't send it if we already received the packet from * this node. */ if (batadv_recv_list_is_in(bat_priv, &info->recv_list, - orig_node->orig)) { - batadv_neigh_node_free_ref(router); + orig_node->orig)) continue; - }
memcpy(packet->target_orig, orig_node->orig, ETH_ALEN); - hard_iface = router->if_incoming; - memcpy(dstaddr, router->addr, ETH_ALEN); - - batadv_neigh_node_free_ref(router); - skb = skb_clone(info->skb_packet, GFP_ATOMIC); - if (skb) - batadv_send_skb_packet(skb, hard_iface, - dstaddr); + if (!skb) + continue;
+ if (!batadv_send_skb_to_orig(skb, orig_node, NULL)) + kfree_skb(skb); } rcu_read_unlock(); } @@@ -755,7 -742,6 +742,6 @@@ static void batadv_unicast_vis_packet(s struct batadv_vis_info *info) { struct batadv_orig_node *orig_node; - struct batadv_neigh_node *router = NULL; struct sk_buff *skb; struct batadv_vis_packet *packet;
@@@ -765,17 -751,14 +751,14 @@@ if (!orig_node) goto out;
- router = batadv_orig_node_get_router(orig_node); - if (!router) + skb = skb_clone(info->skb_packet, GFP_ATOMIC); + if (!skb) goto out;
- skb = skb_clone(info->skb_packet, GFP_ATOMIC); - if (skb) - batadv_send_skb_packet(skb, router->if_incoming, router->addr); + if (!batadv_send_skb_to_orig(skb, orig_node, NULL)) + kfree_skb(skb);
out: - if (router) - batadv_neigh_node_free_ref(router); if (orig_node) batadv_orig_node_free_ref(orig_node); }