diff options
Diffstat (limited to 'net/batman-adv/routing.c')
| -rw-r--r-- | net/batman-adv/routing.c | 1970 |
1 files changed, 829 insertions, 1141 deletions
diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index 8828eddd3f7..35141534938 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -1,5 +1,4 @@ -/* - * Copyright (C) 2007-2010 B.A.T.M.A.N. contributors: +/* Copyright (C) 2007-2014 B.A.T.M.A.N. contributors: * * Marek Lindner, Simon Wunderlich * @@ -13,336 +12,116 @@ * 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 - * + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include "main.h" #include "routing.h" #include "send.h" -#include "hash.h" #include "soft-interface.h" #include "hard-interface.h" #include "icmp_socket.h" #include "translation-table.h" #include "originator.h" -#include "types.h" -#include "ring_buffer.h" -#include "vis.h" -#include "aggregation.h" -#include "gateway_common.h" -#include "gateway_client.h" -#include "unicast.h" - -void slide_own_bcast_window(struct batman_if *batman_if) -{ - struct bat_priv *bat_priv = netdev_priv(batman_if->soft_iface); - struct hashtable_t *hash = bat_priv->orig_hash; - struct hlist_node *walk; - struct hlist_head *head; - struct element_t *bucket; - struct orig_node *orig_node; - unsigned long *word; - int i; - size_t word_index; - - spin_lock_bh(&bat_priv->orig_hash_lock); - - for (i = 0; i < hash->size; i++) { - head = &hash->table[i]; - - hlist_for_each_entry(bucket, walk, head, hlist) { - orig_node = bucket->data; - word_index = batman_if->if_num * NUM_WORDS; - word = &(orig_node->bcast_own[word_index]); - - bit_get_packet(bat_priv, word, 1, 0); - orig_node->bcast_own_sum[batman_if->if_num] = - bit_packet_count(word); - } - } - - spin_unlock_bh(&bat_priv->orig_hash_lock); -} - -static void update_HNA(struct bat_priv *bat_priv, struct orig_node *orig_node, - unsigned char *hna_buff, int hna_buff_len) -{ - if ((hna_buff_len != orig_node->hna_buff_len) || - ((hna_buff_len > 0) && - (orig_node->hna_buff_len > 0) && - (memcmp(orig_node->hna_buff, hna_buff, hna_buff_len) != 0))) { - - if (orig_node->hna_buff_len > 0) - hna_global_del_orig(bat_priv, orig_node, - "originator changed hna"); - - if ((hna_buff_len > 0) && (hna_buff)) - hna_global_add_orig(bat_priv, orig_node, - hna_buff, hna_buff_len); - } -} - -static void update_route(struct bat_priv *bat_priv, - struct orig_node *orig_node, - struct neigh_node *neigh_node, - unsigned char *hna_buff, int hna_buff_len) -{ - /* route deleted */ - if ((orig_node->router) && (!neigh_node)) { - - bat_dbg(DBG_ROUTES, bat_priv, "Deleting route towards: %pM\n", - orig_node->orig); - hna_global_del_orig(bat_priv, orig_node, - "originator timed out"); - - /* route added */ - } else if ((!orig_node->router) && (neigh_node)) { - - bat_dbg(DBG_ROUTES, bat_priv, - "Adding route towards: %pM (via %pM)\n", - orig_node->orig, neigh_node->addr); - hna_global_add_orig(bat_priv, orig_node, - hna_buff, hna_buff_len); - - /* route changed */ - } else { - bat_dbg(DBG_ROUTES, bat_priv, - "Changing route towards: %pM " - "(now via %pM - was via %pM)\n", - orig_node->orig, neigh_node->addr, - orig_node->router->addr); - } - - orig_node->router = neigh_node; -} - - -void update_routes(struct bat_priv *bat_priv, struct orig_node *orig_node, - struct neigh_node *neigh_node, unsigned char *hna_buff, - int hna_buff_len) +#include "bridge_loop_avoidance.h" +#include "distributed-arp-table.h" +#include "network-coding.h" +#include "fragmentation.h" + +#include <linux/if_vlan.h> + +static int batadv_route_unicast_packet(struct sk_buff *skb, + struct batadv_hard_iface *recv_if); + +/** + * _batadv_update_route - set the router for this originator + * @bat_priv: the bat priv with all the soft interface information + * @orig_node: orig node which is to be configured + * @recv_if: the receive interface for which this route is set + * @neigh_node: neighbor which should be the next router + * + * This function does not perform any error checks + */ +static void _batadv_update_route(struct batadv_priv *bat_priv, + struct batadv_orig_node *orig_node, + struct batadv_hard_iface *recv_if, + struct batadv_neigh_node *neigh_node) { + struct batadv_orig_ifinfo *orig_ifinfo; + struct batadv_neigh_node *curr_router; - if (!orig_node) + orig_ifinfo = batadv_orig_ifinfo_get(orig_node, recv_if); + if (!orig_ifinfo) return; - if (orig_node->router != neigh_node) - update_route(bat_priv, orig_node, neigh_node, - hna_buff, hna_buff_len); - /* may be just HNA changed */ - else - update_HNA(bat_priv, orig_node, hna_buff, hna_buff_len); -} - -static int is_bidirectional_neigh(struct orig_node *orig_node, - struct orig_node *orig_neigh_node, - struct batman_packet *batman_packet, - struct batman_if *if_incoming) -{ - struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface); - struct neigh_node *neigh_node = NULL, *tmp_neigh_node = NULL; - unsigned char total_count; - - if (orig_node == orig_neigh_node) { - list_for_each_entry(tmp_neigh_node, - &orig_node->neigh_list, - list) { - - if (compare_orig(tmp_neigh_node->addr, - orig_neigh_node->orig) && - (tmp_neigh_node->if_incoming == if_incoming)) - neigh_node = tmp_neigh_node; - } - - if (!neigh_node) - neigh_node = create_neighbor(orig_node, - orig_neigh_node, - orig_neigh_node->orig, - if_incoming); - /* create_neighbor failed, return 0 */ - if (!neigh_node) - return 0; - - neigh_node->last_valid = jiffies; - } else { - /* find packet count of corresponding one hop neighbor */ - list_for_each_entry(tmp_neigh_node, - &orig_neigh_node->neigh_list, list) { - - if (compare_orig(tmp_neigh_node->addr, - orig_neigh_node->orig) && - (tmp_neigh_node->if_incoming == if_incoming)) - neigh_node = tmp_neigh_node; - } + rcu_read_lock(); + curr_router = rcu_dereference(orig_ifinfo->router); + if (curr_router && !atomic_inc_not_zero(&curr_router->refcount)) + curr_router = NULL; + rcu_read_unlock(); - if (!neigh_node) - neigh_node = create_neighbor(orig_neigh_node, - orig_neigh_node, - orig_neigh_node->orig, - if_incoming); - /* create_neighbor failed, return 0 */ - if (!neigh_node) - return 0; + /* route deleted */ + if ((curr_router) && (!neigh_node)) { + batadv_dbg(BATADV_DBG_ROUTES, bat_priv, + "Deleting route towards: %pM\n", orig_node->orig); + batadv_tt_global_del_orig(bat_priv, orig_node, -1, + "Deleted route towards originator"); + + /* route added */ + } else if ((!curr_router) && (neigh_node)) { + batadv_dbg(BATADV_DBG_ROUTES, bat_priv, + "Adding route towards: %pM (via %pM)\n", + orig_node->orig, neigh_node->addr); + /* route changed */ + } else if (neigh_node && curr_router) { + batadv_dbg(BATADV_DBG_ROUTES, bat_priv, + "Changing route towards: %pM (now via %pM - was via %pM)\n", + orig_node->orig, neigh_node->addr, + curr_router->addr); } - orig_node->last_valid = jiffies; + if (curr_router) + batadv_neigh_node_free_ref(curr_router); - /* pay attention to not get a value bigger than 100 % */ - total_count = (orig_neigh_node->bcast_own_sum[if_incoming->if_num] > - neigh_node->real_packet_count ? - neigh_node->real_packet_count : - orig_neigh_node->bcast_own_sum[if_incoming->if_num]); + /* increase refcount of new best neighbor */ + if (neigh_node && !atomic_inc_not_zero(&neigh_node->refcount)) + neigh_node = NULL; - /* if we have too few packets (too less data) we set tq_own to zero */ - /* if we receive too few packets it is not considered bidirectional */ - if ((total_count < TQ_LOCAL_BIDRECT_SEND_MINIMUM) || - (neigh_node->real_packet_count < TQ_LOCAL_BIDRECT_RECV_MINIMUM)) - orig_neigh_node->tq_own = 0; - else - /* neigh_node->real_packet_count is never zero as we - * only purge old information when getting new - * information */ - orig_neigh_node->tq_own = (TQ_MAX_VALUE * total_count) / - neigh_node->real_packet_count; - - /* - * 1 - ((1-x) ** 3), normalized to TQ_MAX_VALUE this does - * affect the nearly-symmetric links only a little, but - * punishes asymmetric links more. This will give a value - * between 0 and TQ_MAX_VALUE - */ - orig_neigh_node->tq_asym_penalty = - TQ_MAX_VALUE - - (TQ_MAX_VALUE * - (TQ_LOCAL_WINDOW_SIZE - neigh_node->real_packet_count) * - (TQ_LOCAL_WINDOW_SIZE - neigh_node->real_packet_count) * - (TQ_LOCAL_WINDOW_SIZE - neigh_node->real_packet_count)) / - (TQ_LOCAL_WINDOW_SIZE * - TQ_LOCAL_WINDOW_SIZE * - TQ_LOCAL_WINDOW_SIZE); - - batman_packet->tq = ((batman_packet->tq * - orig_neigh_node->tq_own * - orig_neigh_node->tq_asym_penalty) / - (TQ_MAX_VALUE * TQ_MAX_VALUE)); - - bat_dbg(DBG_BATMAN, bat_priv, - "bidirectional: " - "orig = %-15pM neigh = %-15pM => own_bcast = %2i, " - "real recv = %2i, local tq: %3i, asym_penalty: %3i, " - "total tq: %3i\n", - orig_node->orig, orig_neigh_node->orig, total_count, - neigh_node->real_packet_count, orig_neigh_node->tq_own, - orig_neigh_node->tq_asym_penalty, batman_packet->tq); - - /* if link has the minimum required transmission quality - * consider it bidirectional */ - if (batman_packet->tq >= TQ_TOTAL_BIDRECT_LIMIT) - return 1; + spin_lock_bh(&orig_node->neigh_list_lock); + rcu_assign_pointer(orig_ifinfo->router, neigh_node); + spin_unlock_bh(&orig_node->neigh_list_lock); + batadv_orig_ifinfo_free_ref(orig_ifinfo); - return 0; + /* decrease refcount of previous best neighbor */ + if (curr_router) + batadv_neigh_node_free_ref(curr_router); } -static void update_orig(struct bat_priv *bat_priv, - struct orig_node *orig_node, - struct ethhdr *ethhdr, - struct batman_packet *batman_packet, - struct batman_if *if_incoming, - unsigned char *hna_buff, int hna_buff_len, - char is_duplicate) +/** + * batadv_update_route - set the router for this originator + * @bat_priv: the bat priv with all the soft interface information + * @orig_node: orig node which is to be configured + * @recv_if: the receive interface for which this route is set + * @neigh_node: neighbor which should be the next router + */ +void batadv_update_route(struct batadv_priv *bat_priv, + struct batadv_orig_node *orig_node, + struct batadv_hard_iface *recv_if, + struct batadv_neigh_node *neigh_node) { - struct neigh_node *neigh_node = NULL, *tmp_neigh_node = NULL; - int tmp_hna_buff_len; - - bat_dbg(DBG_BATMAN, bat_priv, "update_originator(): " - "Searching and updating originator entry of received packet\n"); - - list_for_each_entry(tmp_neigh_node, &orig_node->neigh_list, list) { - if (compare_orig(tmp_neigh_node->addr, ethhdr->h_source) && - (tmp_neigh_node->if_incoming == if_incoming)) { - neigh_node = tmp_neigh_node; - continue; - } - - if (is_duplicate) - continue; - - ring_buffer_set(tmp_neigh_node->tq_recv, - &tmp_neigh_node->tq_index, 0); - tmp_neigh_node->tq_avg = - ring_buffer_avg(tmp_neigh_node->tq_recv); - } - - if (!neigh_node) { - struct orig_node *orig_tmp; + struct batadv_neigh_node *router = NULL; - orig_tmp = get_orig_node(bat_priv, ethhdr->h_source); - if (!orig_tmp) - return; - - neigh_node = create_neighbor(orig_node, orig_tmp, - ethhdr->h_source, if_incoming); - if (!neigh_node) - return; - } else - bat_dbg(DBG_BATMAN, bat_priv, - "Updating existing last-hop neighbor of originator\n"); - - orig_node->flags = batman_packet->flags; - neigh_node->last_valid = jiffies; + if (!orig_node) + goto out; - ring_buffer_set(neigh_node->tq_recv, - &neigh_node->tq_index, - batman_packet->tq); - neigh_node->tq_avg = ring_buffer_avg(neigh_node->tq_recv); + router = batadv_orig_router_get(orig_node, recv_if); - if (!is_duplicate) { - orig_node->last_ttl = batman_packet->ttl; - neigh_node->last_ttl = batman_packet->ttl; - } + if (router != neigh_node) + _batadv_update_route(bat_priv, orig_node, recv_if, neigh_node); - tmp_hna_buff_len = (hna_buff_len > batman_packet->num_hna * ETH_ALEN ? - batman_packet->num_hna * ETH_ALEN : hna_buff_len); - - /* if this neighbor already is our next hop there is nothing - * to change */ - if (orig_node->router == neigh_node) - goto update_hna; - - /* if this neighbor does not offer a better TQ we won't consider it */ - if ((orig_node->router) && - (orig_node->router->tq_avg > neigh_node->tq_avg)) - goto update_hna; - - /* if the TQ is the same and the link not more symetric we - * won't consider it either */ - if ((orig_node->router) && - ((neigh_node->tq_avg == orig_node->router->tq_avg) && - (orig_node->router->orig_node->bcast_own_sum[if_incoming->if_num] - >= neigh_node->orig_node->bcast_own_sum[if_incoming->if_num]))) - goto update_hna; - - update_routes(bat_priv, orig_node, neigh_node, - hna_buff, tmp_hna_buff_len); - goto update_gw; - -update_hna: - update_routes(bat_priv, orig_node, orig_node->router, - hna_buff, tmp_hna_buff_len); - -update_gw: - if (orig_node->gw_flags != batman_packet->gw_flags) - gw_node_update(bat_priv, orig_node, batman_packet->gw_flags); - - orig_node->gw_flags = batman_packet->gw_flags; - - /* restart gateway selection if fast or late switching was enabled */ - if ((orig_node->gw_flags) && - (atomic_read(&bat_priv->gw_mode) == GW_MODE_CLIENT) && - (atomic_read(&bat_priv->gw_sel_class) > 2)) - gw_check_election(bat_priv, orig_node); +out: + if (router) + batadv_neigh_node_free_ref(router); } /* checks whether the host restarted and is in the protection time. @@ -350,1048 +129,957 @@ update_gw: * 0 if the packet is to be accepted * 1 if the packet is to be ignored. */ -static int window_protected(struct bat_priv *bat_priv, - int32_t seq_num_diff, +int batadv_window_protected(struct batadv_priv *bat_priv, int32_t seq_num_diff, unsigned long *last_reset) { - if ((seq_num_diff <= -TQ_LOCAL_WINDOW_SIZE) - || (seq_num_diff >= EXPECTED_SEQNO_RANGE)) { - if (time_after(jiffies, *last_reset + - msecs_to_jiffies(RESET_PROTECTION_MS))) { - - *last_reset = jiffies; - bat_dbg(DBG_BATMAN, bat_priv, - "old packet received, start protection\n"); - - return 0; - } else + if (seq_num_diff <= -BATADV_TQ_LOCAL_WINDOW_SIZE || + seq_num_diff >= BATADV_EXPECTED_SEQNO_RANGE) { + if (!batadv_has_timed_out(*last_reset, + BATADV_RESET_PROTECTION_MS)) return 1; - } - return 0; -} - -/* processes a batman packet for all interfaces, adjusts the sequence number and - * finds out whether it is a duplicate. - * returns: - * 1 the packet is a duplicate - * 0 the packet has not yet been received - * -1 the packet is old and has been received while the seqno window - * was protected. Caller should drop it. - */ -static char count_real_packets(struct ethhdr *ethhdr, - struct batman_packet *batman_packet, - struct batman_if *if_incoming) -{ - struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface); - struct orig_node *orig_node; - struct neigh_node *tmp_neigh_node; - char is_duplicate = 0; - int32_t seq_diff; - int need_update = 0; - int set_mark; - - orig_node = get_orig_node(bat_priv, batman_packet->orig); - if (!orig_node) - return 0; - - seq_diff = batman_packet->seqno - orig_node->last_real_seqno; - - /* signalize caller that the packet is to be dropped. */ - if (window_protected(bat_priv, seq_diff, - &orig_node->batman_seqno_reset)) - return -1; - - list_for_each_entry(tmp_neigh_node, &orig_node->neigh_list, list) { - - is_duplicate |= get_bit_status(tmp_neigh_node->real_bits, - orig_node->last_real_seqno, - batman_packet->seqno); - - if (compare_orig(tmp_neigh_node->addr, ethhdr->h_source) && - (tmp_neigh_node->if_incoming == if_incoming)) - set_mark = 1; - else - set_mark = 0; - - /* if the window moved, set the update flag. */ - need_update |= bit_get_packet(bat_priv, - tmp_neigh_node->real_bits, - seq_diff, set_mark); - - tmp_neigh_node->real_packet_count = - bit_packet_count(tmp_neigh_node->real_bits); - } - - if (need_update) { - bat_dbg(DBG_BATMAN, bat_priv, - "updating last_seqno: old %d, new %d\n", - orig_node->last_real_seqno, batman_packet->seqno); - orig_node->last_real_seqno = batman_packet->seqno; - } - - return is_duplicate; -} - -/* copy primary address for bonding */ -static void mark_bonding_address(struct bat_priv *bat_priv, - struct orig_node *orig_node, - struct orig_node *orig_neigh_node, - struct batman_packet *batman_packet) - -{ - if (batman_packet->flags & PRIMARIES_FIRST_HOP) - memcpy(orig_neigh_node->primary_addr, - orig_node->orig, ETH_ALEN); - - return; -} - -/* mark possible bond.candidates in the neighbor list */ -void update_bonding_candidates(struct bat_priv *bat_priv, - struct orig_node *orig_node) -{ - int candidates; - int interference_candidate; - int best_tq; - struct neigh_node *tmp_neigh_node, *tmp_neigh_node2; - struct neigh_node *first_candidate, *last_candidate; - - /* update the candidates for this originator */ - if (!orig_node->router) { - orig_node->bond.candidates = 0; - return; - } - - best_tq = orig_node->router->tq_avg; - - /* update bond.candidates */ - - candidates = 0; - - /* mark other nodes which also received "PRIMARIES FIRST HOP" packets - * as "bonding partner" */ - - /* first, zero the list */ - list_for_each_entry(tmp_neigh_node, &orig_node->neigh_list, list) { - tmp_neigh_node->next_bond_candidate = NULL; - } - - first_candidate = NULL; - last_candidate = NULL; - list_for_each_entry(tmp_neigh_node, &orig_node->neigh_list, list) { - - /* only consider if it has the same primary address ... */ - if (memcmp(orig_node->orig, - tmp_neigh_node->orig_node->primary_addr, - ETH_ALEN) != 0) - continue; - - /* ... and is good enough to be considered */ - if (tmp_neigh_node->tq_avg < best_tq - BONDING_TQ_THRESHOLD) - continue; - - /* check if we have another candidate with the same - * mac address or interface. If we do, we won't - * select this candidate because of possible interference. */ - - interference_candidate = 0; - list_for_each_entry(tmp_neigh_node2, - &orig_node->neigh_list, list) { - - if (tmp_neigh_node2 == tmp_neigh_node) - continue; - - /* we only care if the other candidate is even - * considered as candidate. */ - if (!tmp_neigh_node2->next_bond_candidate) - continue; - - if ((tmp_neigh_node->if_incoming == - tmp_neigh_node2->if_incoming) - || (memcmp(tmp_neigh_node->addr, - tmp_neigh_node2->addr, ETH_ALEN) == 0)) { - - interference_candidate = 1; - break; - } - } - /* don't care further if it is an interference candidate */ - if (interference_candidate) - continue; - - if (!first_candidate) { - first_candidate = tmp_neigh_node; - tmp_neigh_node->next_bond_candidate = first_candidate; - } else - tmp_neigh_node->next_bond_candidate = last_candidate; - - last_candidate = tmp_neigh_node; - - candidates++; - } - - if (candidates > 0) { - first_candidate->next_bond_candidate = last_candidate; - orig_node->bond.selected = first_candidate; - } - - orig_node->bond.candidates = candidates; -} - -void receive_bat_packet(struct ethhdr *ethhdr, - struct batman_packet *batman_packet, - unsigned char *hna_buff, int hna_buff_len, - struct batman_if *if_incoming) -{ - struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface); - struct batman_if *batman_if; - struct orig_node *orig_neigh_node, *orig_node; - char has_directlink_flag; - char is_my_addr = 0, is_my_orig = 0, is_my_oldorig = 0; - char is_broadcast = 0, is_bidirectional, is_single_hop_neigh; - char is_duplicate; - uint32_t if_incoming_seqno; - - /* Silently drop when the batman packet is actually not a - * correct packet. - * - * This might happen if a packet is padded (e.g. Ethernet has a - * minimum frame length of 64 byte) and the aggregation interprets - * it as an additional length. - * - * TODO: A more sane solution would be to have a bit in the - * batman_packet to detect whether the packet is the last - * packet in an aggregation. Here we expect that the padding - * is always zero (or not 0x01) - */ - if (batman_packet->packet_type != BAT_PACKET) - return; - - /* could be changed by schedule_own_packet() */ - if_incoming_seqno = atomic_read(&if_incoming->seqno); - - has_directlink_flag = (batman_packet->flags & DIRECTLINK ? 1 : 0); - - is_single_hop_neigh = (compare_orig(ethhdr->h_source, - batman_packet->orig) ? 1 : 0); - - bat_dbg(DBG_BATMAN, bat_priv, - "Received BATMAN packet via NB: %pM, IF: %s [%pM] " - "(from OG: %pM, via prev OG: %pM, seqno %d, tq %d, " - "TTL %d, V %d, IDF %d)\n", - ethhdr->h_source, if_incoming->net_dev->name, - if_incoming->net_dev->dev_addr, batman_packet->orig, - batman_packet->prev_sender, batman_packet->seqno, - batman_packet->tq, batman_packet->ttl, batman_packet->version, - has_directlink_flag); - - rcu_read_lock(); - list_for_each_entry_rcu(batman_if, &if_list, list) { - if (batman_if->if_status != IF_ACTIVE) - continue; - - if (batman_if->soft_iface != if_incoming->soft_iface) - continue; - - if (compare_orig(ethhdr->h_source, - batman_if->net_dev->dev_addr)) - is_my_addr = 1; - - if (compare_orig(batman_packet->orig, - batman_if->net_dev->dev_addr)) - is_my_orig = 1; - - if (compare_orig(batman_packet->prev_sender, - batman_if->net_dev->dev_addr)) - is_my_oldorig = 1; - - if (compare_orig(ethhdr->h_source, broadcast_addr)) - is_broadcast = 1; - } - rcu_read_unlock(); - - if (batman_packet->version != COMPAT_VERSION) { - bat_dbg(DBG_BATMAN, bat_priv, - "Drop packet: incompatible batman version (%i)\n", - batman_packet->version); - return; - } - - if (is_my_addr) { - bat_dbg(DBG_BATMAN, bat_priv, - "Drop packet: received my own broadcast (sender: %pM" - ")\n", - ethhdr->h_source); - return; - } - - if (is_broadcast) { - bat_dbg(DBG_BATMAN, bat_priv, "Drop packet: " - "ignoring all packets with broadcast source addr (sender: %pM" - ")\n", ethhdr->h_source); - return; - } - - if (is_my_orig) { - unsigned long *word; - int offset; - - orig_neigh_node = get_orig_node(bat_priv, ethhdr->h_source); - - if (!orig_neigh_node) - return; - - /* neighbor has to indicate direct link and it has to - * come via the corresponding interface */ - /* if received seqno equals last send seqno save new - * seqno for bidirectional check */ - if (has_directlink_flag && - compare_orig(if_incoming->net_dev->dev_addr, - batman_packet->orig) && - (batman_packet->seqno - if_incoming_seqno + 2 == 0)) { - offset = if_incoming->if_num * NUM_WORDS; - word = &(orig_neigh_node->bcast_own[offset]); - bit_mark(word, 0); - orig_neigh_node->bcast_own_sum[if_incoming->if_num] = - bit_packet_count(word); - } - - bat_dbg(DBG_BATMAN, bat_priv, "Drop packet: " - "originator packet from myself (via neighbor)\n"); - return; - } - - if (is_my_oldorig) { - bat_dbg(DBG_BATMAN, bat_priv, - "Drop packet: ignoring all rebroadcast echos (sender: " - "%pM)\n", ethhdr->h_source); - return; - } - - orig_node = get_orig_node(bat_priv, batman_packet->orig); - if (!orig_node) - return; - - is_duplicate = count_real_packets(ethhdr, batman_packet, if_incoming); - - if (is_duplicate == -1) { - bat_dbg(DBG_BATMAN, bat_priv, - "Drop packet: packet within seqno protection time " - "(sender: %pM)\n", ethhdr->h_source); - return; - } - - if (batman_packet->tq == 0) { - bat_dbg(DBG_BATMAN, bat_priv, - "Drop packet: originator packet with tq equal 0\n"); - return; - } - - /* avoid temporary routing loops */ - if ((orig_node->router) && - (orig_node->router->orig_node->router) && - (compare_orig(orig_node->router->addr, - batman_packet->prev_sender)) && - !(compare_orig(batman_packet->orig, batman_packet->prev_sender)) && - (compare_orig(orig_node->router->addr, - orig_node->router->orig_node->router->addr))) { - bat_dbg(DBG_BATMAN, bat_priv, - "Drop packet: ignoring all rebroadcast packets that " - "may make me loop (sender: %pM)\n", ethhdr->h_source); - return; + *last_reset = jiffies; + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "old packet received, start protection\n"); } - /* if sender is a direct neighbor the sender mac equals - * originator mac */ - orig_neigh_node = (is_single_hop_neigh ? - orig_node : - get_orig_node(bat_priv, ethhdr->h_source)); - if (!orig_neigh_node) - return; - - /* drop packet if sender is not a direct neighbor and if we - * don't route towards it */ - if (!is_single_hop_neigh && (!orig_neigh_node->router)) { - bat_dbg(DBG_BATMAN, bat_priv, - "Drop packet: OGM via unknown neighbor!\n"); - return; - } - - is_bidirectional = is_bidirectional_neigh(orig_node, orig_neigh_node, - batman_packet, if_incoming); - - /* update ranking if it is not a duplicate or has the same - * seqno and similar ttl as the non-duplicate */ - if (is_bidirectional && - (!is_duplicate || - ((orig_node->last_real_seqno == batman_packet->seqno) && - (orig_node->last_ttl - 3 <= batman_packet->ttl)))) - update_orig(bat_priv, orig_node, ethhdr, batman_packet, - if_incoming, hna_buff, hna_buff_len, is_duplicate); - - mark_bonding_address(bat_priv, orig_node, - orig_neigh_node, batman_packet); - update_bonding_candidates(bat_priv, orig_node); - - /* is single hop (direct) neighbor */ - if (is_single_hop_neigh) { - - /* mark direct link on incoming interface */ - schedule_forward_packet(orig_node, ethhdr, batman_packet, - 1, hna_buff_len, if_incoming); - - bat_dbg(DBG_BATMAN, bat_priv, "Forwarding packet: " - "rebroadcast neighbor packet with direct link flag\n"); - return; - } - - /* multihop originator */ - if (!is_bidirectional) { - bat_dbg(DBG_BATMAN, bat_priv, - "Drop packet: not received via bidirectional link\n"); - return; - } - - if (is_duplicate) { - bat_dbg(DBG_BATMAN, bat_priv, - "Drop packet: duplicate packet received\n"); - return; - } - - bat_dbg(DBG_BATMAN, bat_priv, - "Forwarding packet: rebroadcast originator packet\n"); - schedule_forward_packet(orig_node, ethhdr, batman_packet, - 0, hna_buff_len, if_incoming); + return 0; } -int recv_bat_packet(struct sk_buff *skb, struct batman_if *batman_if) +bool batadv_check_management_packet(struct sk_buff *skb, + struct batadv_hard_iface *hard_iface, + int header_len) { - struct bat_priv *bat_priv = netdev_priv(batman_if->soft_iface); struct ethhdr *ethhdr; /* drop packet if it has not necessary minimum size */ - if (unlikely(!pskb_may_pull(skb, sizeof(struct batman_packet)))) - return NET_RX_DROP; + if (unlikely(!pskb_may_pull(skb, header_len))) + return false; - ethhdr = (struct ethhdr *)skb_mac_header(skb); + ethhdr = eth_hdr(skb); /* packet with broadcast indication but unicast recipient */ if (!is_broadcast_ether_addr(ethhdr->h_dest)) - return NET_RX_DROP; + return false; /* packet with broadcast sender address */ if (is_broadcast_ether_addr(ethhdr->h_source)) - return NET_RX_DROP; + return false; /* create a copy of the skb, if needed, to modify it. */ if (skb_cow(skb, 0) < 0) - return NET_RX_DROP; + return false; /* keep skb linear */ if (skb_linearize(skb) < 0) - return NET_RX_DROP; - - ethhdr = (struct ethhdr *)skb_mac_header(skb); - - spin_lock_bh(&bat_priv->orig_hash_lock); - receive_aggr_bat_packet(ethhdr, - skb->data, - skb_headlen(skb), - batman_if); - spin_unlock_bh(&bat_priv->orig_hash_lock); + return false; - kfree_skb(skb); - return NET_RX_SUCCESS; + return true; } -static int recv_my_icmp_packet(struct bat_priv *bat_priv, - struct sk_buff *skb, size_t icmp_len) +/** + * batadv_recv_my_icmp_packet - receive an icmp packet locally + * @bat_priv: the bat priv with all the soft interface information + * @skb: icmp packet to process + * + * Returns NET_RX_SUCCESS if the packet has been consumed or NET_RX_DROP + * otherwise. + */ +static int batadv_recv_my_icmp_packet(struct batadv_priv *bat_priv, + struct sk_buff *skb) { - struct orig_node *orig_node; - struct icmp_packet_rr *icmp_packet; - struct ethhdr *ethhdr; - struct batman_if *batman_if; - int ret; - uint8_t dstaddr[ETH_ALEN]; - - icmp_packet = (struct icmp_packet_rr *)skb->data; - ethhdr = (struct ethhdr *)skb_mac_header(skb); - - /* add data to device queue */ - if (icmp_packet->msg_type != ECHO_REQUEST) { - bat_socket_receive_packet(icmp_packet, icmp_len); - return NET_RX_DROP; - } - - if (!bat_priv->primary_if) - return NET_RX_DROP; - - /* answer echo request (ping) */ - /* get routing information */ - spin_lock_bh(&bat_priv->orig_hash_lock); - orig_node = ((struct orig_node *)hash_find(bat_priv->orig_hash, - compare_orig, choose_orig, - icmp_packet->orig)); - ret = NET_RX_DROP; - - if ((orig_node) && (orig_node->router)) { + struct batadv_hard_iface *primary_if = NULL; + struct batadv_orig_node *orig_node = NULL; + struct batadv_icmp_header *icmph; + int res, ret = NET_RX_DROP; + + icmph = (struct batadv_icmp_header *)skb->data; + + switch (icmph->msg_type) { + case BATADV_ECHO_REPLY: + case BATADV_DESTINATION_UNREACHABLE: + case BATADV_TTL_EXCEEDED: + /* receive the packet */ + if (skb_linearize(skb) < 0) + break; + + batadv_socket_receive_packet(icmph, skb->len); + break; + case BATADV_ECHO_REQUEST: + /* answer echo request (ping) */ + primary_if = batadv_primary_if_get_selected(bat_priv); + if (!primary_if) + goto out; - /* don't lock while sending the packets ... we therefore - * copy the required data before sending */ - batman_if = orig_node->router->if_incoming; - memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); - spin_unlock_bh(&bat_priv->orig_hash_lock); + /* get routing information */ + orig_node = batadv_orig_hash_find(bat_priv, icmph->orig); + if (!orig_node) + goto out; /* create a copy of the skb, if needed, to modify it. */ - if (skb_cow(skb, sizeof(struct ethhdr)) < 0) - return NET_RX_DROP; + if (skb_cow(skb, ETH_HLEN) < 0) + goto out; - icmp_packet = (struct icmp_packet_rr *)skb->data; - ethhdr = (struct ethhdr *)skb_mac_header(skb); + icmph = (struct batadv_icmp_header *)skb->data; - memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN); - memcpy(icmp_packet->orig, - bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN); - icmp_packet->msg_type = ECHO_REPLY; - icmp_packet->ttl = TTL; + ether_addr_copy(icmph->dst, icmph->orig); + ether_addr_copy(icmph->orig, primary_if->net_dev->dev_addr); + icmph->msg_type = BATADV_ECHO_REPLY; + icmph->ttl = BATADV_TTL; - send_skb_packet(skb, batman_if, dstaddr); - ret = NET_RX_SUCCESS; - - } else - spin_unlock_bh(&bat_priv->orig_hash_lock); + res = batadv_send_skb_to_orig(skb, orig_node, NULL); + if (res != NET_XMIT_DROP) + ret = NET_RX_SUCCESS; + break; + default: + /* drop unknown type */ + goto out; + } +out: + if (primary_if) + batadv_hardif_free_ref(primary_if); + if (orig_node) + batadv_orig_node_free_ref(orig_node); return ret; } -static int recv_icmp_ttl_exceeded(struct bat_priv *bat_priv, - struct sk_buff *skb, size_t icmp_len) +static int batadv_recv_icmp_ttl_exceeded(struct batadv_priv *bat_priv, + struct sk_buff *skb) { - struct orig_node *orig_node; - struct icmp_packet *icmp_packet; - struct ethhdr *ethhdr; - struct batman_if *batman_if; - int ret; - uint8_t dstaddr[ETH_ALEN]; + struct batadv_hard_iface *primary_if = NULL; + struct batadv_orig_node *orig_node = NULL; + struct batadv_icmp_packet *icmp_packet; + int ret = NET_RX_DROP; - icmp_packet = (struct icmp_packet *)skb->data; - ethhdr = (struct ethhdr *)skb_mac_header(skb); + icmp_packet = (struct batadv_icmp_packet *)skb->data; /* send TTL exceeded if packet is an echo request (traceroute) */ - if (icmp_packet->msg_type != ECHO_REQUEST) { - pr_debug("Warning - can't forward icmp packet from %pM to " - "%pM: ttl exceeded\n", icmp_packet->orig, - icmp_packet->dst); - return NET_RX_DROP; + if (icmp_packet->msg_type != BATADV_ECHO_REQUEST) { + pr_debug("Warning - can't forward icmp packet from %pM to %pM: ttl exceeded\n", + icmp_packet->orig, icmp_packet->dst); + goto out; } - if (!bat_priv->primary_if) - return NET_RX_DROP; + primary_if = batadv_primary_if_get_selected(bat_priv); + if (!primary_if) + goto out; /* get routing information */ - spin_lock_bh(&bat_priv->orig_hash_lock); - orig_node = ((struct orig_node *) - hash_find(bat_priv->orig_hash, compare_orig, choose_orig, - icmp_packet->orig)); - ret = NET_RX_DROP; - - if ((orig_node) && (orig_node->router)) { - - /* don't lock while sending the packets ... we therefore - * copy the required data before sending */ - batman_if = orig_node->router->if_incoming; - memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); - spin_unlock_bh(&bat_priv->orig_hash_lock); + orig_node = batadv_orig_hash_find(bat_priv, icmp_packet->orig); + if (!orig_node) + goto out; - /* create a copy of the skb, if needed, to modify it. */ - if (skb_cow(skb, sizeof(struct ethhdr)) < 0) - return NET_RX_DROP; + /* create a copy of the skb, if needed, to modify it. */ + if (skb_cow(skb, ETH_HLEN) < 0) + goto out; - icmp_packet = (struct icmp_packet *) skb->data; - ethhdr = (struct ethhdr *)skb_mac_header(skb); + icmp_packet = (struct batadv_icmp_packet *)skb->data; - memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN); - memcpy(icmp_packet->orig, - bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN); - icmp_packet->msg_type = TTL_EXCEEDED; - icmp_packet->ttl = TTL; + ether_addr_copy(icmp_packet->dst, icmp_packet->orig); + ether_addr_copy(icmp_packet->orig, primary_if->net_dev->dev_addr); + icmp_packet->msg_type = BATADV_TTL_EXCEEDED; + icmp_packet->ttl = BATADV_TTL; - send_skb_packet(skb, batman_if, dstaddr); + if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP) ret = NET_RX_SUCCESS; - } else - spin_unlock_bh(&bat_priv->orig_hash_lock); - +out: + if (primary_if) + batadv_hardif_free_ref(primary_if); + if (orig_node) + batadv_orig_node_free_ref(orig_node); return ret; } -int recv_icmp_packet(struct sk_buff *skb, struct batman_if *recv_if) +int batadv_recv_icmp_packet(struct sk_buff *skb, + struct batadv_hard_iface *recv_if) { - struct bat_priv *bat_priv = netdev_priv(recv_if->soft_iface); - struct icmp_packet_rr *icmp_packet; + struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); + struct batadv_icmp_header *icmph; + struct batadv_icmp_packet_rr *icmp_packet_rr; struct ethhdr *ethhdr; - struct orig_node *orig_node; - struct batman_if *batman_if; - int hdr_size = sizeof(struct icmp_packet); - int ret; - uint8_t dstaddr[ETH_ALEN]; - - /** - * we truncate all incoming icmp packets if they don't match our size - */ - if (skb->len >= sizeof(struct icmp_packet_rr)) - hdr_size = sizeof(struct icmp_packet_rr); + struct batadv_orig_node *orig_node = NULL; + int hdr_size = sizeof(struct batadv_icmp_header); + int ret = NET_RX_DROP; /* drop packet if it has not necessary minimum size */ if (unlikely(!pskb_may_pull(skb, hdr_size))) - return NET_RX_DROP; + goto out; - ethhdr = (struct ethhdr *)skb_mac_header(skb); + ethhdr = eth_hdr(skb); /* packet with unicast indication but broadcast recipient */ if (is_broadcast_ether_addr(ethhdr->h_dest)) - return NET_RX_DROP; + goto out; /* packet with broadcast sender address */ if (is_broadcast_ether_addr(ethhdr->h_source)) - return NET_RX_DROP; + goto out; /* not for me */ - if (!is_my_mac(ethhdr->h_dest)) - return NET_RX_DROP; + if (!batadv_is_my_mac(bat_priv, ethhdr->h_dest)) + goto out; - icmp_packet = (struct icmp_packet_rr *)skb->data; + icmph = (struct batadv_icmp_header *)skb->data; /* add record route information if not full */ - if ((hdr_size == sizeof(struct icmp_packet_rr)) && - (icmp_packet->rr_cur < BAT_RR_LEN)) { - memcpy(&(icmp_packet->rr[icmp_packet->rr_cur]), - ethhdr->h_dest, ETH_ALEN); - icmp_packet->rr_cur++; + if ((icmph->msg_type == BATADV_ECHO_REPLY || + icmph->msg_type == BATADV_ECHO_REQUEST) && + (skb->len >= sizeof(struct batadv_icmp_packet_rr))) { + if (skb_linearize(skb) < 0) + goto out; + + /* create a copy of the skb, if needed, to modify it. */ + if (skb_cow(skb, ETH_HLEN) < 0) + goto out; + + icmph = (struct batadv_icmp_header *)skb->data; + icmp_packet_rr = (struct batadv_icmp_packet_rr *)icmph; + if (icmp_packet_rr->rr_cur >= BATADV_RR_LEN) + goto out; + + ether_addr_copy(icmp_packet_rr->rr[icmp_packet_rr->rr_cur], + ethhdr->h_dest); + icmp_packet_rr->rr_cur++; } /* packet for me */ - if (is_my_mac(icmp_packet->dst)) - return recv_my_icmp_packet(bat_priv, skb, hdr_size); + if (batadv_is_my_mac(bat_priv, icmph->dst)) + return batadv_recv_my_icmp_packet(bat_priv, skb); /* TTL exceeded */ - if (icmp_packet->ttl < 2) - return recv_icmp_ttl_exceeded(bat_priv, skb, hdr_size); - - ret = NET_RX_DROP; + if (icmph->ttl < 2) + return batadv_recv_icmp_ttl_exceeded(bat_priv, skb); /* get routing information */ - spin_lock_bh(&bat_priv->orig_hash_lock); - orig_node = ((struct orig_node *) - hash_find(bat_priv->orig_hash, compare_orig, choose_orig, - icmp_packet->dst)); - - if ((orig_node) && (orig_node->router)) { - - /* don't lock while sending the packets ... we therefore - * copy the required data before sending */ - batman_if = orig_node->router->if_incoming; - memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); - spin_unlock_bh(&bat_priv->orig_hash_lock); + orig_node = batadv_orig_hash_find(bat_priv, icmph->dst); + if (!orig_node) + goto out; - /* create a copy of the skb, if needed, to modify it. */ - if (skb_cow(skb, sizeof(struct ethhdr)) < 0) - return NET_RX_DROP; + /* create a copy of the skb, if needed, to modify it. */ + if (skb_cow(skb, ETH_HLEN) < 0) + goto out; - icmp_packet = (struct icmp_packet_rr *)skb->data; - ethhdr = (struct ethhdr *)skb_mac_header(skb); + icmph = (struct batadv_icmp_header *)skb->data; - /* decrement ttl */ - icmp_packet->ttl--; + /* decrement ttl */ + icmph->ttl--; - /* route it */ - send_skb_packet(skb, batman_if, dstaddr); + /* route it */ + if (batadv_send_skb_to_orig(skb, orig_node, recv_if) != NET_XMIT_DROP) ret = NET_RX_SUCCESS; - } else - spin_unlock_bh(&bat_priv->orig_hash_lock); - +out: + if (orig_node) + batadv_orig_node_free_ref(orig_node); return ret; } -/* find a suitable router for this originator, and use - * bonding if possible. */ -struct neigh_node *find_router(struct bat_priv *bat_priv, - struct orig_node *orig_node, - struct batman_if *recv_if) +/** + * batadv_check_unicast_packet - Check for malformed unicast packets + * @bat_priv: the bat priv with all the soft interface information + * @skb: packet to check + * @hdr_size: size of header to pull + * + * Check for short header and bad addresses in given packet. Returns negative + * value when check fails and 0 otherwise. The negative value depends on the + * reason: -ENODATA for bad header, -EBADR for broadcast destination or source, + * and -EREMOTE for non-local (other host) destination. + */ +static int batadv_check_unicast_packet(struct batadv_priv *bat_priv, + struct sk_buff *skb, int hdr_size) { - struct orig_node *primary_orig_node; - struct orig_node *router_orig; - struct neigh_node *router, *first_candidate, *best_router; - static uint8_t zero_mac[ETH_ALEN] = {0, 0, 0, 0, 0, 0}; - int bonding_enabled; + struct ethhdr *ethhdr; - if (!orig_node) - return NULL; + /* drop packet if it has not necessary minimum size */ + if (unlikely(!pskb_may_pull(skb, hdr_size))) + return -ENODATA; - if (!orig_node->router) - return NULL; + ethhdr = eth_hdr(skb); - /* without bonding, the first node should - * always choose the default router. */ + /* packet with unicast indication but broadcast recipient */ + if (is_broadcast_ether_addr(ethhdr->h_dest)) + return -EBADR; - bonding_enabled = atomic_read(&bat_priv->bonding); + /* packet with broadcast sender address */ + if (is_broadcast_ether_addr(ethhdr->h_source)) + return -EBADR; - if ((!recv_if) && (!bonding_enabled)) - return orig_node->router; + /* not for me */ + if (!batadv_is_my_mac(bat_priv, ethhdr->h_dest)) + return -EREMOTE; - router_orig = orig_node->router->orig_node; + return 0; +} - /* if we have something in the primary_addr, we can search - * for a potential bonding candidate. */ - if (memcmp(router_orig->primary_addr, zero_mac, ETH_ALEN) == 0) - return orig_node->router; +/** + * batadv_find_router - find a suitable router for this originator + * @bat_priv: the bat priv with all the soft interface information + * @orig_node: the destination node + * @recv_if: pointer to interface this packet was received on + * + * Returns the router which should be used for this orig_node on + * this interface, or NULL if not available. + */ +struct batadv_neigh_node * +batadv_find_router(struct batadv_priv *bat_priv, + struct batadv_orig_node *orig_node, + struct batadv_hard_iface *recv_if) +{ + struct batadv_algo_ops *bao = bat_priv->bat_algo_ops; + struct batadv_neigh_node *first_candidate_router = NULL; + struct batadv_neigh_node *next_candidate_router = NULL; + struct batadv_neigh_node *router, *cand_router = NULL; + struct batadv_neigh_node *last_cand_router = NULL; + struct batadv_orig_ifinfo *cand, *first_candidate = NULL; + struct batadv_orig_ifinfo *next_candidate = NULL; + struct batadv_orig_ifinfo *last_candidate; + bool last_candidate_found = false; - /* find the orig_node which has the primary interface. might - * even be the same as our router_orig in many cases */ + if (!orig_node) + return NULL; - if (memcmp(router_orig->primary_addr, - router_orig->orig, ETH_ALEN) == 0) { - primary_orig_node = router_orig; - } else { - primary_orig_node = hash_find(bat_priv->orig_hash, compare_orig, - choose_orig, - router_orig->primary_addr); + router = batadv_orig_router_get(orig_node, recv_if); - if (!primary_orig_node) - return orig_node->router; - } + /* only consider bonding for recv_if == BATADV_IF_DEFAULT (first hop) + * and if activated. + */ + if (recv_if == BATADV_IF_DEFAULT || !atomic_read(&bat_priv->bonding) || + !router) + return router; + + /* bonding: loop through the list of possible routers found + * for the various outgoing interfaces and find a candidate after + * the last chosen bonding candidate (next_candidate). If no such + * router is found, use the first candidate found (the previously + * chosen bonding candidate might have been the last one in the list). + * If this can't be found either, return the previously choosen + * router - obviously there are no other candidates. + */ + rcu_read_lock(); + last_candidate = orig_node->last_bonding_candidate; + if (last_candidate) + last_cand_router = rcu_dereference(last_candidate->router); - /* with less than 2 candidates, we can't do any - * bonding and prefer the original router. */ + hlist_for_each_entry_rcu(cand, &orig_node->ifinfo_list, list) { + /* acquire some structures and references ... */ + if (!atomic_inc_not_zero(&cand->refcount)) + continue; - if (primary_orig_node->bond.candidates < 2) - return orig_node->router; + cand_router = rcu_dereference(cand->router); + if (!cand_router) + goto next; + if (!atomic_inc_not_zero(&cand_router->refcount)) { + cand_router = NULL; + goto next; + } - /* all nodes between should choose a candidate which - * is is not on the interface where the packet came - * in. */ - first_candidate = primary_orig_node->bond.selected; - router = first_candidate; + /* alternative candidate should be good enough to be + * considered + */ + if (!bao->bat_neigh_is_equiv_or_better(cand_router, + cand->if_outgoing, + router, recv_if)) + goto next; - if (bonding_enabled) { - /* in the bonding case, send the packets in a round - * robin fashion over the remaining interfaces. */ - do { - /* recv_if == NULL on the first node. */ - if (router->if_incoming != recv_if) - break; + /* don't use the same router twice */ + if (last_cand_router == cand_router) + goto next; - router = router->next_bond_candidate; - } while (router != first_candidate); + /* mark the first possible candidate */ + if (!first_candidate) { + atomic_inc(&cand_router->refcount); + atomic_inc(&cand->refcount); + first_candidate = cand; + first_candidate_router = cand_router; + } - primary_orig_node->bond.selected = router->next_bond_candidate; + /* check if the loop has already passed the previously selected + * candidate ... this function should select the next candidate + * AFTER the previously used bonding candidate. + */ + if (!last_candidate || last_candidate_found) { + next_candidate = cand; + next_candidate_router = cand_router; + break; + } - } else { - /* if bonding is disabled, use the best of the - * remaining candidates which are not using - * this interface. */ - best_router = first_candidate; + if (last_candidate == cand) + last_candidate_found = true; +next: + /* free references */ + if (cand_router) { + batadv_neigh_node_free_ref(cand_router); + cand_router = NULL; + } + batadv_orig_ifinfo_free_ref(cand); + } + rcu_read_unlock(); - do { - /* recv_if == NULL on the first node. */ - if ((router->if_incoming != recv_if) && - (router->tq_avg > best_router->tq_avg)) - best_router = router; + /* last_bonding_candidate is reset below, remove the old reference. */ + if (orig_node->last_bonding_candidate) + batadv_orig_ifinfo_free_ref(orig_node->last_bonding_candidate); - router = router->next_bond_candidate; - } while (router != first_candidate); + /* After finding candidates, handle the three cases: + * 1) there is a next candidate, use that + * 2) there is no next candidate, use the first of the list + * 3) there is no candidate at all, return the default router + */ + if (next_candidate) { + batadv_neigh_node_free_ref(router); - router = best_router; + /* remove references to first candidate, we don't need it. */ + if (first_candidate) { + batadv_neigh_node_free_ref(first_candidate_router); + batadv_orig_ifinfo_free_ref(first_candidate); + } + router = next_candidate_router; + orig_node->last_bonding_candidate = next_candidate; + } else if (first_candidate) { + batadv_neigh_node_free_ref(router); + + /* refcounting has already been done in the loop above. */ + router = first_candidate_router; + orig_node->last_bonding_candidate = first_candidate; + } else { + orig_node->last_bonding_candidate = NULL; } return router; } -static int check_unicast_packet(struct sk_buff *skb, int hdr_size) +static int batadv_route_unicast_packet(struct sk_buff *skb, + struct batadv_hard_iface *recv_if) { - struct ethhdr *ethhdr; + struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); + struct batadv_orig_node *orig_node = NULL; + struct batadv_unicast_packet *unicast_packet; + struct ethhdr *ethhdr = eth_hdr(skb); + int res, hdr_len, ret = NET_RX_DROP; - /* drop packet if it has not necessary minimum size */ - if (unlikely(!pskb_may_pull(skb, hdr_size))) - return -1; + unicast_packet = (struct batadv_unicast_packet *)skb->data; - ethhdr = (struct ethhdr *)skb_mac_header(skb); + /* TTL exceeded */ + if (unicast_packet->ttl < 2) { + pr_debug("Warning - can't forward unicast packet from %pM to %pM: ttl exceeded\n", + ethhdr->h_source, unicast_packet->dest); + goto out; + } - /* packet with unicast indication but broadcast recipient */ - if (is_broadcast_ether_addr(ethhdr->h_dest)) - return -1; + /* get routing information */ + orig_node = batadv_orig_hash_find(bat_priv, unicast_packet->dest); - /* packet with broadcast sender address */ - if (is_broadcast_ether_addr(ethhdr->h_source)) - return -1; + if (!orig_node) + goto out; - /* not for me */ - if (!is_my_mac(ethhdr->h_dest)) - return -1; + /* create a copy of the skb, if needed, to modify it. */ + if (skb_cow(skb, ETH_HLEN) < 0) + goto out; - return 0; -} + /* decrement ttl */ + unicast_packet = (struct batadv_unicast_packet *)skb->data; + unicast_packet->ttl--; -int route_unicast_packet(struct sk_buff *skb, struct batman_if *recv_if, - int hdr_size) -{ - struct bat_priv *bat_priv = netdev_priv(recv_if->soft_iface); - struct orig_node *orig_node; - struct neigh_node *router; - struct batman_if *batman_if; - uint8_t dstaddr[ETH_ALEN]; - struct unicast_packet *unicast_packet; - struct ethhdr *ethhdr = (struct ethhdr *)skb_mac_header(skb); - int ret; - struct sk_buff *new_skb; - - unicast_packet = (struct unicast_packet *)skb->data; + switch (unicast_packet->packet_type) { + case BATADV_UNICAST_4ADDR: + hdr_len = sizeof(struct batadv_unicast_4addr_packet); + break; + case BATADV_UNICAST: + hdr_len = sizeof(struct batadv_unicast_packet); + break; + default: + /* other packet types not supported - yet */ + hdr_len = -1; + break; + } - /* TTL exceeded */ - if (unicast_packet->ttl < 2) { - pr_debug("Warning - can't forward unicast packet from %pM to " - "%pM: ttl exceeded\n", ethhdr->h_source, - unicast_packet->dest); - return NET_RX_DROP; + if (hdr_len > 0) + batadv_skb_set_priority(skb, hdr_len); + + res = batadv_send_skb_to_orig(skb, orig_node, recv_if); + + /* translate transmit result into receive result */ + if (res == NET_XMIT_SUCCESS) { + /* skb was transmitted and consumed */ + batadv_inc_counter(bat_priv, BATADV_CNT_FORWARD); + batadv_add_counter(bat_priv, BATADV_CNT_FORWARD_BYTES, + skb->len + ETH_HLEN); + + ret = NET_RX_SUCCESS; + } else if (res == NET_XMIT_POLICED) { + /* skb was buffered and consumed */ + ret = NET_RX_SUCCESS; } - /* get routing information */ - spin_lock_bh(&bat_priv->orig_hash_lock); - orig_node = ((struct orig_node *) - hash_find(bat_priv->orig_hash, compare_orig, choose_orig, - unicast_packet->dest)); +out: + if (orig_node) + batadv_orig_node_free_ref(orig_node); + return ret; +} - router = find_router(bat_priv, orig_node, recv_if); +/** + * 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 + * @vid: VLAN identifier + * + * Search the 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, unsigned short vid) +{ + struct batadv_orig_node *orig_node = NULL; + struct batadv_hard_iface *primary_if = NULL; + bool ret = false; + uint8_t *orig_addr, orig_ttvn; + + if (batadv_is_my_client(bat_priv, dst_addr, vid)) { + primary_if = batadv_primary_if_get_selected(bat_priv); + if (!primary_if) + goto out; + orig_addr = primary_if->net_dev->dev_addr; + orig_ttvn = (uint8_t)atomic_read(&bat_priv->tt.vn); + } else { + orig_node = batadv_transtable_search(bat_priv, NULL, dst_addr, + vid); + if (!orig_node) + goto out; - if (!router) { - spin_unlock_bh(&bat_priv->orig_hash_lock); - return NET_RX_DROP; + if (batadv_compare_eth(orig_node->orig, unicast_packet->dest)) + goto out; + + orig_addr = orig_node->orig; + orig_ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn); } - /* don't lock while sending the packets ... we therefore - * copy the required data before sending */ + /* update the packet header */ + ether_addr_copy(unicast_packet->dest, orig_addr); + unicast_packet->ttvn = orig_ttvn; - batman_if = router->if_incoming; - memcpy(dstaddr, router->addr, ETH_ALEN); + ret = true; +out: + if (primary_if) + batadv_hardif_free_ref(primary_if); + if (orig_node) + batadv_orig_node_free_ref(orig_node); - spin_unlock_bh(&bat_priv->orig_hash_lock); + return ret; +} - /* create a copy of the skb, if needed, to modify it. */ - if (skb_cow(skb, sizeof(struct ethhdr)) < 0) - return NET_RX_DROP; +static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv, + struct sk_buff *skb, int hdr_len) { + struct batadv_unicast_packet *unicast_packet; + struct batadv_hard_iface *primary_if; + struct batadv_orig_node *orig_node; + uint8_t curr_ttvn, old_ttvn; + struct ethhdr *ethhdr; + unsigned short vid; + int is_old_ttvn; + + /* check if there is enough data before accessing it */ + if (!pskb_may_pull(skb, hdr_len + ETH_HLEN)) + return 0; - unicast_packet = (struct unicast_packet *)skb->data; + /* 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; - if (unicast_packet->packet_type == BAT_UNICAST && - atomic_read(&bat_priv->fragmentation) && - skb->len > batman_if->net_dev->mtu) - return frag_send_skb(skb, bat_priv, batman_if, - dstaddr); + unicast_packet = (struct batadv_unicast_packet *)skb->data; + vid = batadv_get_vid(skb, hdr_len); + ethhdr = (struct ethhdr *)(skb->data + hdr_len); - if (unicast_packet->packet_type == BAT_UNICAST_FRAG && - 2 * skb->len - hdr_size <= batman_if->net_dev->mtu) { + /* 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, vid)) { + if (batadv_reroute_unicast_packet(bat_priv, unicast_packet, + ethhdr->h_dest, vid)) + 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; + } - ret = frag_reassemble_skb(skb, bat_priv, &new_skb); + /* 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(bat_priv, 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; - if (ret == NET_RX_DROP) - return NET_RX_DROP; + curr_ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn); + batadv_orig_node_free_ref(orig_node); + } - /* packet was buffered for late merge */ - if (!new_skb) - return NET_RX_SUCCESS; + /* 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) + return 1; - skb = new_skb; - unicast_packet = (struct unicast_packet *)skb->data; + old_ttvn = unicast_packet->ttvn; + /* 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, vid)) { + 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, + old_ttvn, curr_ttvn); + return 1; } - /* decrement ttl */ - unicast_packet->ttl--; + /* 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, vid)) + return 0; - /* route it */ - send_skb_packet(skb, batman_if, dstaddr); + /* 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; + + ether_addr_copy(unicast_packet->dest, primary_if->net_dev->dev_addr); + + batadv_hardif_free_ref(primary_if); - return NET_RX_SUCCESS; + unicast_packet->ttvn = curr_ttvn; + + return 1; } -int recv_unicast_packet(struct sk_buff *skb, struct batman_if *recv_if) +/** + * batadv_recv_unhandled_unicast_packet - receive and process packets which + * are in the unicast number space but not yet known to the implementation + * @skb: unicast tvlv packet to process + * @recv_if: pointer to interface this packet was received on + * + * Returns NET_RX_SUCCESS if the packet has been consumed or NET_RX_DROP + * otherwise. + */ +int batadv_recv_unhandled_unicast_packet(struct sk_buff *skb, + struct batadv_hard_iface *recv_if) { - struct unicast_packet *unicast_packet; - int hdr_size = sizeof(struct unicast_packet); + struct batadv_unicast_packet *unicast_packet; + struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); + int check, hdr_size = sizeof(*unicast_packet); - if (check_unicast_packet(skb, hdr_size) < 0) + check = batadv_check_unicast_packet(bat_priv, skb, hdr_size); + if (check < 0) return NET_RX_DROP; - unicast_packet = (struct unicast_packet *)skb->data; + /* we don't know about this type, drop it. */ + unicast_packet = (struct batadv_unicast_packet *)skb->data; + if (batadv_is_my_mac(bat_priv, unicast_packet->dest)) + return NET_RX_DROP; + + return batadv_route_unicast_packet(skb, recv_if); +} + +int batadv_recv_unicast_packet(struct sk_buff *skb, + struct batadv_hard_iface *recv_if) +{ + 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 check, 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->packet_type == BATADV_UNICAST_4ADDR; + /* the caller function should have already pulled 2 bytes */ + if (is4addr) + hdr_size = sizeof(*unicast_4addr_packet); + + /* function returns -EREMOTE for promiscuous packets */ + check = batadv_check_unicast_packet(bat_priv, skb, hdr_size); + + /* Even though the packet is not for us, we might save it to use for + * decoding a later received coded packet + */ + if (check == -EREMOTE) + batadv_nc_skb_store_sniffed_unicast(bat_priv, skb); + + if (check < 0) + return NET_RX_DROP; + if (!batadv_check_unicast_ttvn(bat_priv, skb, hdr_size)) + return NET_RX_DROP; /* packet for me */ - if (is_my_mac(unicast_packet->dest)) { - interface_rx(recv_if->soft_iface, skb, recv_if, hdr_size); + if (batadv_is_my_mac(bat_priv, 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, + orig_node); + +rx_success: + if (orig_node) + batadv_orig_node_free_ref(orig_node); + return NET_RX_SUCCESS; } - return route_unicast_packet(skb, recv_if, hdr_size); + return batadv_route_unicast_packet(skb, recv_if); } -int recv_ucast_frag_packet(struct sk_buff *skb, struct batman_if *recv_if) +/** + * batadv_recv_unicast_tvlv - receive and process unicast tvlv packets + * @skb: unicast tvlv packet to process + * @recv_if: pointer to interface this packet was received on + * @dst_addr: the payload destination + * + * Returns NET_RX_SUCCESS if the packet has been consumed or NET_RX_DROP + * otherwise. + */ +int batadv_recv_unicast_tvlv(struct sk_buff *skb, + struct batadv_hard_iface *recv_if) { - struct bat_priv *bat_priv = netdev_priv(recv_if->soft_iface); - struct unicast_frag_packet *unicast_packet; - int hdr_size = sizeof(struct unicast_frag_packet); - struct sk_buff *new_skb = NULL; - int ret; + struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); + struct batadv_unicast_tvlv_packet *unicast_tvlv_packet; + unsigned char *tvlv_buff; + uint16_t tvlv_buff_len; + int hdr_size = sizeof(*unicast_tvlv_packet); + int ret = NET_RX_DROP; + + if (batadv_check_unicast_packet(bat_priv, skb, hdr_size) < 0) + return NET_RX_DROP; - if (check_unicast_packet(skb, hdr_size) < 0) + /* the header is likely to be modified while forwarding */ + if (skb_cow(skb, hdr_size) < 0) return NET_RX_DROP; - unicast_packet = (struct unicast_frag_packet *)skb->data; + /* packet needs to be linearized to access the tvlv content */ + if (skb_linearize(skb) < 0) + return NET_RX_DROP; - /* packet for me */ - if (is_my_mac(unicast_packet->dest)) { + unicast_tvlv_packet = (struct batadv_unicast_tvlv_packet *)skb->data; - ret = frag_reassemble_skb(skb, bat_priv, &new_skb); + tvlv_buff = (unsigned char *)(skb->data + hdr_size); + tvlv_buff_len = ntohs(unicast_tvlv_packet->tvlv_len); - if (ret == NET_RX_DROP) - return NET_RX_DROP; + if (tvlv_buff_len > skb->len - hdr_size) + return NET_RX_DROP; - /* packet was buffered for late merge */ - if (!new_skb) - return NET_RX_SUCCESS; + ret = batadv_tvlv_containers_process(bat_priv, false, NULL, + unicast_tvlv_packet->src, + unicast_tvlv_packet->dst, + tvlv_buff, tvlv_buff_len); - interface_rx(recv_if->soft_iface, new_skb, recv_if, - sizeof(struct unicast_packet)); - return NET_RX_SUCCESS; - } + if (ret != NET_RX_SUCCESS) + ret = batadv_route_unicast_packet(skb, recv_if); + else + consume_skb(skb); - return route_unicast_packet(skb, recv_if, hdr_size); + return ret; } +/** + * batadv_recv_frag_packet - process received fragment + * @skb: the received fragment + * @recv_if: interface that the skb is received on + * + * This function does one of the three following things: 1) Forward fragment, if + * the assembled packet will exceed our MTU; 2) Buffer fragment, if we till + * lack further fragments; 3) Merge fragments, if we have all needed parts. + * + * Return NET_RX_DROP if the skb is not consumed, NET_RX_SUCCESS otherwise. + */ +int batadv_recv_frag_packet(struct sk_buff *skb, + struct batadv_hard_iface *recv_if) +{ + struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); + struct batadv_orig_node *orig_node_src = NULL; + struct batadv_frag_packet *frag_packet; + int ret = NET_RX_DROP; + + if (batadv_check_unicast_packet(bat_priv, skb, + sizeof(*frag_packet)) < 0) + goto out; + + frag_packet = (struct batadv_frag_packet *)skb->data; + orig_node_src = batadv_orig_hash_find(bat_priv, frag_packet->orig); + if (!orig_node_src) + goto out; + + /* Route the fragment if it is not for us and too big to be merged. */ + if (!batadv_is_my_mac(bat_priv, frag_packet->dest) && + batadv_frag_skb_fwd(skb, recv_if, orig_node_src)) { + ret = NET_RX_SUCCESS; + goto out; + } + + batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_RX); + batadv_add_counter(bat_priv, BATADV_CNT_FRAG_RX_BYTES, skb->len); + + /* Add fragment to buffer and merge if possible. */ + if (!batadv_frag_skb_buffer(&skb, orig_node_src)) + goto out; + + /* Deliver merged packet to the appropriate handler, if it was + * merged + */ + if (skb) + batadv_batman_skb_recv(skb, recv_if->net_dev, + &recv_if->batman_adv_ptype, NULL); + + ret = NET_RX_SUCCESS; + +out: + if (orig_node_src) + batadv_orig_node_free_ref(orig_node_src); -int recv_bcast_packet(struct sk_buff *skb, struct batman_if *recv_if) + return ret; +} + +int batadv_recv_bcast_packet(struct sk_buff *skb, + struct batadv_hard_iface *recv_if) { - struct bat_priv *bat_priv = netdev_priv(recv_if->soft_iface); - struct orig_node *orig_node; - struct bcast_packet *bcast_packet; + struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); + struct batadv_orig_node *orig_node = NULL; + struct batadv_bcast_packet *bcast_packet; struct ethhdr *ethhdr; - int hdr_size = sizeof(struct bcast_packet); + int hdr_size = sizeof(*bcast_packet); + int ret = NET_RX_DROP; int32_t seq_diff; + uint32_t seqno; /* drop packet if it has not necessary minimum size */ if (unlikely(!pskb_may_pull(skb, hdr_size))) - return NET_RX_DROP; + goto out; - ethhdr = (struct ethhdr *)skb_mac_header(skb); + ethhdr = eth_hdr(skb); /* packet with broadcast indication but unicast recipient */ if (!is_broadcast_ether_addr(ethhdr->h_dest)) - return NET_RX_DROP; + goto out; /* packet with broadcast sender address */ if (is_broadcast_ether_addr(ethhdr->h_source)) - return NET_RX_DROP; + goto out; /* ignore broadcasts sent by myself */ - if (is_my_mac(ethhdr->h_source)) - return NET_RX_DROP; + if (batadv_is_my_mac(bat_priv, ethhdr->h_source)) + goto out; - bcast_packet = (struct bcast_packet *)skb->data; + bcast_packet = (struct batadv_bcast_packet *)skb->data; /* ignore broadcasts originated by myself */ - if (is_my_mac(bcast_packet->orig)) - return NET_RX_DROP; + if (batadv_is_my_mac(bat_priv, bcast_packet->orig)) + goto out; if (bcast_packet->ttl < 2) - return NET_RX_DROP; + goto out; - spin_lock_bh(&bat_priv->orig_hash_lock); - orig_node = ((struct orig_node *) - hash_find(bat_priv->orig_hash, compare_orig, choose_orig, - bcast_packet->orig)); + orig_node = batadv_orig_hash_find(bat_priv, bcast_packet->orig); - if (!orig_node) { - spin_unlock_bh(&bat_priv->orig_hash_lock); - return NET_RX_DROP; - } + if (!orig_node) + goto out; + + spin_lock_bh(&orig_node->bcast_seqno_lock); + seqno = ntohl(bcast_packet->seqno); /* check whether the packet is a duplicate */ - if (get_bit_status(orig_node->bcast_bits, - orig_node->last_bcast_seqno, - ntohl(bcast_packet->seqno))) { - spin_unlock_bh(&bat_priv->orig_hash_lock); - return NET_RX_DROP; - } + if (batadv_test_bit(orig_node->bcast_bits, orig_node->last_bcast_seqno, + seqno)) + goto spin_unlock; - seq_diff = ntohl(bcast_packet->seqno) - orig_node->last_bcast_seqno; + seq_diff = seqno - orig_node->last_bcast_seqno; /* check whether the packet is old and the host just restarted. */ - if (window_protected(bat_priv, seq_diff, - &orig_node->bcast_seqno_reset)) { - spin_unlock_bh(&bat_priv->orig_hash_lock); - return NET_RX_DROP; - } + if (batadv_window_protected(bat_priv, seq_diff, + &orig_node->bcast_seqno_reset)) + goto spin_unlock; /* mark broadcast in flood history, update window position - * if required. */ - if (bit_get_packet(bat_priv, orig_node->bcast_bits, seq_diff, 1)) - orig_node->last_bcast_seqno = ntohl(bcast_packet->seqno); - - spin_unlock_bh(&bat_priv->orig_hash_lock); - /* rebroadcast packet */ - add_bcast_packet_to_list(bat_priv, skb); - - /* broadcast for me */ - interface_rx(recv_if->soft_iface, skb, recv_if, hdr_size); - - return NET_RX_SUCCESS; -} - -int recv_vis_packet(struct sk_buff *skb, struct batman_if *recv_if) -{ - struct vis_packet *vis_packet; - struct ethhdr *ethhdr; - struct bat_priv *bat_priv = netdev_priv(recv_if->soft_iface); - int hdr_size = sizeof(struct vis_packet); - - /* keep skb linear */ - if (skb_linearize(skb) < 0) - return NET_RX_DROP; - - if (unlikely(!pskb_may_pull(skb, hdr_size))) - return NET_RX_DROP; - - vis_packet = (struct vis_packet *)skb->data; - ethhdr = (struct ethhdr *)skb_mac_header(skb); + * if required. + */ + if (batadv_bit_get_packet(bat_priv, orig_node->bcast_bits, seq_diff, 1)) + orig_node->last_bcast_seqno = seqno; - /* not for me */ - if (!is_my_mac(ethhdr->h_dest)) - return NET_RX_DROP; + spin_unlock_bh(&orig_node->bcast_seqno_lock); - /* ignore own packets */ - if (is_my_mac(vis_packet->vis_orig)) - return NET_RX_DROP; + /* check whether this has been sent by another originator before */ + if (batadv_bla_check_bcast_duplist(bat_priv, skb)) + goto out; - if (is_my_mac(vis_packet->sender_orig)) - return NET_RX_DROP; + batadv_skb_set_priority(skb, sizeof(struct batadv_bcast_packet)); - switch (vis_packet->vis_type) { - case VIS_TYPE_SERVER_SYNC: - receive_server_sync_packet(bat_priv, vis_packet, - skb_headlen(skb)); - break; + /* rebroadcast packet */ + batadv_add_bcast_packet_to_list(bat_priv, skb, 1); - case VIS_TYPE_CLIENT_UPDATE: - receive_client_update_packet(bat_priv, vis_packet, - skb_headlen(skb)); - break; + /* don't hand the broadcast up if it is from an originator + * from the same backbone. + */ + if (batadv_bla_is_backbone_gw(skb, orig_node, hdr_size)) + goto out; - default: /* ignore unknown packet */ - break; - } + 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; - /* We take a copy of the data in the packet, so we should - always free the skbuf. */ - return NET_RX_DROP; + /* 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; + +spin_unlock: + spin_unlock_bh(&orig_node->bcast_seqno_lock); +out: + if (orig_node) + batadv_orig_node_free_ref(orig_node); + return ret; } |
