162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright (C) B.A.T.M.A.N. contributors: 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Marek Lindner, Simon Wunderlich 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include "routing.h" 862306a36Sopenharmony_ci#include "main.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/atomic.h> 1162306a36Sopenharmony_ci#include <linux/byteorder/generic.h> 1262306a36Sopenharmony_ci#include <linux/compiler.h> 1362306a36Sopenharmony_ci#include <linux/errno.h> 1462306a36Sopenharmony_ci#include <linux/etherdevice.h> 1562306a36Sopenharmony_ci#include <linux/if_ether.h> 1662306a36Sopenharmony_ci#include <linux/jiffies.h> 1762306a36Sopenharmony_ci#include <linux/kref.h> 1862306a36Sopenharmony_ci#include <linux/netdevice.h> 1962306a36Sopenharmony_ci#include <linux/printk.h> 2062306a36Sopenharmony_ci#include <linux/rculist.h> 2162306a36Sopenharmony_ci#include <linux/rcupdate.h> 2262306a36Sopenharmony_ci#include <linux/skbuff.h> 2362306a36Sopenharmony_ci#include <linux/spinlock.h> 2462306a36Sopenharmony_ci#include <linux/stddef.h> 2562306a36Sopenharmony_ci#include <uapi/linux/batadv_packet.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include "bitarray.h" 2862306a36Sopenharmony_ci#include "bridge_loop_avoidance.h" 2962306a36Sopenharmony_ci#include "distributed-arp-table.h" 3062306a36Sopenharmony_ci#include "fragmentation.h" 3162306a36Sopenharmony_ci#include "hard-interface.h" 3262306a36Sopenharmony_ci#include "log.h" 3362306a36Sopenharmony_ci#include "network-coding.h" 3462306a36Sopenharmony_ci#include "originator.h" 3562306a36Sopenharmony_ci#include "send.h" 3662306a36Sopenharmony_ci#include "soft-interface.h" 3762306a36Sopenharmony_ci#include "tp_meter.h" 3862306a36Sopenharmony_ci#include "translation-table.h" 3962306a36Sopenharmony_ci#include "tvlv.h" 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic int batadv_route_unicast_packet(struct sk_buff *skb, 4262306a36Sopenharmony_ci struct batadv_hard_iface *recv_if); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/** 4562306a36Sopenharmony_ci * _batadv_update_route() - set the router for this originator 4662306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 4762306a36Sopenharmony_ci * @orig_node: orig node which is to be configured 4862306a36Sopenharmony_ci * @recv_if: the receive interface for which this route is set 4962306a36Sopenharmony_ci * @neigh_node: neighbor which should be the next router 5062306a36Sopenharmony_ci * 5162306a36Sopenharmony_ci * This function does not perform any error checks 5262306a36Sopenharmony_ci */ 5362306a36Sopenharmony_cistatic void _batadv_update_route(struct batadv_priv *bat_priv, 5462306a36Sopenharmony_ci struct batadv_orig_node *orig_node, 5562306a36Sopenharmony_ci struct batadv_hard_iface *recv_if, 5662306a36Sopenharmony_ci struct batadv_neigh_node *neigh_node) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci struct batadv_orig_ifinfo *orig_ifinfo; 5962306a36Sopenharmony_ci struct batadv_neigh_node *curr_router; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci orig_ifinfo = batadv_orig_ifinfo_get(orig_node, recv_if); 6262306a36Sopenharmony_ci if (!orig_ifinfo) 6362306a36Sopenharmony_ci return; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci spin_lock_bh(&orig_node->neigh_list_lock); 6662306a36Sopenharmony_ci /* curr_router used earlier may not be the current orig_ifinfo->router 6762306a36Sopenharmony_ci * anymore because it was dereferenced outside of the neigh_list_lock 6862306a36Sopenharmony_ci * protected region. After the new best neighbor has replace the current 6962306a36Sopenharmony_ci * best neighbor the reference counter needs to decrease. Consequently, 7062306a36Sopenharmony_ci * the code needs to ensure the curr_router variable contains a pointer 7162306a36Sopenharmony_ci * to the replaced best neighbor. 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci /* increase refcount of new best neighbor */ 7562306a36Sopenharmony_ci if (neigh_node) 7662306a36Sopenharmony_ci kref_get(&neigh_node->refcount); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci curr_router = rcu_replace_pointer(orig_ifinfo->router, neigh_node, 7962306a36Sopenharmony_ci true); 8062306a36Sopenharmony_ci spin_unlock_bh(&orig_node->neigh_list_lock); 8162306a36Sopenharmony_ci batadv_orig_ifinfo_put(orig_ifinfo); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* route deleted */ 8462306a36Sopenharmony_ci if (curr_router && !neigh_node) { 8562306a36Sopenharmony_ci batadv_dbg(BATADV_DBG_ROUTES, bat_priv, 8662306a36Sopenharmony_ci "Deleting route towards: %pM\n", orig_node->orig); 8762306a36Sopenharmony_ci batadv_tt_global_del_orig(bat_priv, orig_node, -1, 8862306a36Sopenharmony_ci "Deleted route towards originator"); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci /* route added */ 9162306a36Sopenharmony_ci } else if (!curr_router && neigh_node) { 9262306a36Sopenharmony_ci batadv_dbg(BATADV_DBG_ROUTES, bat_priv, 9362306a36Sopenharmony_ci "Adding route towards: %pM (via %pM)\n", 9462306a36Sopenharmony_ci orig_node->orig, neigh_node->addr); 9562306a36Sopenharmony_ci /* route changed */ 9662306a36Sopenharmony_ci } else if (neigh_node && curr_router) { 9762306a36Sopenharmony_ci batadv_dbg(BATADV_DBG_ROUTES, bat_priv, 9862306a36Sopenharmony_ci "Changing route towards: %pM (now via %pM - was via %pM)\n", 9962306a36Sopenharmony_ci orig_node->orig, neigh_node->addr, 10062306a36Sopenharmony_ci curr_router->addr); 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci /* decrease refcount of previous best neighbor */ 10462306a36Sopenharmony_ci batadv_neigh_node_put(curr_router); 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci/** 10862306a36Sopenharmony_ci * batadv_update_route() - set the router for this originator 10962306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 11062306a36Sopenharmony_ci * @orig_node: orig node which is to be configured 11162306a36Sopenharmony_ci * @recv_if: the receive interface for which this route is set 11262306a36Sopenharmony_ci * @neigh_node: neighbor which should be the next router 11362306a36Sopenharmony_ci */ 11462306a36Sopenharmony_civoid batadv_update_route(struct batadv_priv *bat_priv, 11562306a36Sopenharmony_ci struct batadv_orig_node *orig_node, 11662306a36Sopenharmony_ci struct batadv_hard_iface *recv_if, 11762306a36Sopenharmony_ci struct batadv_neigh_node *neigh_node) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci struct batadv_neigh_node *router = NULL; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci if (!orig_node) 12262306a36Sopenharmony_ci goto out; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci router = batadv_orig_router_get(orig_node, recv_if); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (router != neigh_node) 12762306a36Sopenharmony_ci _batadv_update_route(bat_priv, orig_node, recv_if, neigh_node); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ciout: 13062306a36Sopenharmony_ci batadv_neigh_node_put(router); 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci/** 13462306a36Sopenharmony_ci * batadv_window_protected() - checks whether the host restarted and is in the 13562306a36Sopenharmony_ci * protection time. 13662306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 13762306a36Sopenharmony_ci * @seq_num_diff: difference between the current/received sequence number and 13862306a36Sopenharmony_ci * the last sequence number 13962306a36Sopenharmony_ci * @seq_old_max_diff: maximum age of sequence number not considered as restart 14062306a36Sopenharmony_ci * @last_reset: jiffies timestamp of the last reset, will be updated when reset 14162306a36Sopenharmony_ci * is detected 14262306a36Sopenharmony_ci * @protection_started: is set to true if the protection window was started, 14362306a36Sopenharmony_ci * doesn't change otherwise. 14462306a36Sopenharmony_ci * 14562306a36Sopenharmony_ci * Return: 14662306a36Sopenharmony_ci * false if the packet is to be accepted. 14762306a36Sopenharmony_ci * true if the packet is to be ignored. 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_cibool batadv_window_protected(struct batadv_priv *bat_priv, s32 seq_num_diff, 15062306a36Sopenharmony_ci s32 seq_old_max_diff, unsigned long *last_reset, 15162306a36Sopenharmony_ci bool *protection_started) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci if (seq_num_diff <= -seq_old_max_diff || 15462306a36Sopenharmony_ci seq_num_diff >= BATADV_EXPECTED_SEQNO_RANGE) { 15562306a36Sopenharmony_ci if (!batadv_has_timed_out(*last_reset, 15662306a36Sopenharmony_ci BATADV_RESET_PROTECTION_MS)) 15762306a36Sopenharmony_ci return true; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci *last_reset = jiffies; 16062306a36Sopenharmony_ci if (protection_started) 16162306a36Sopenharmony_ci *protection_started = true; 16262306a36Sopenharmony_ci batadv_dbg(BATADV_DBG_BATMAN, bat_priv, 16362306a36Sopenharmony_ci "old packet received, start protection\n"); 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci return false; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci/** 17062306a36Sopenharmony_ci * batadv_check_management_packet() - Check preconditions for management packets 17162306a36Sopenharmony_ci * @skb: incoming packet buffer 17262306a36Sopenharmony_ci * @hard_iface: incoming hard interface 17362306a36Sopenharmony_ci * @header_len: minimal header length of packet type 17462306a36Sopenharmony_ci * 17562306a36Sopenharmony_ci * Return: true when management preconditions are met, false otherwise 17662306a36Sopenharmony_ci */ 17762306a36Sopenharmony_cibool batadv_check_management_packet(struct sk_buff *skb, 17862306a36Sopenharmony_ci struct batadv_hard_iface *hard_iface, 17962306a36Sopenharmony_ci int header_len) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci struct ethhdr *ethhdr; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* drop packet if it has not necessary minimum size */ 18462306a36Sopenharmony_ci if (unlikely(!pskb_may_pull(skb, header_len))) 18562306a36Sopenharmony_ci return false; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci ethhdr = eth_hdr(skb); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* packet with broadcast indication but unicast recipient */ 19062306a36Sopenharmony_ci if (!is_broadcast_ether_addr(ethhdr->h_dest)) 19162306a36Sopenharmony_ci return false; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci /* packet with invalid sender address */ 19462306a36Sopenharmony_ci if (!is_valid_ether_addr(ethhdr->h_source)) 19562306a36Sopenharmony_ci return false; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* create a copy of the skb, if needed, to modify it. */ 19862306a36Sopenharmony_ci if (skb_cow(skb, 0) < 0) 19962306a36Sopenharmony_ci return false; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* keep skb linear */ 20262306a36Sopenharmony_ci if (skb_linearize(skb) < 0) 20362306a36Sopenharmony_ci return false; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci return true; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci/** 20962306a36Sopenharmony_ci * batadv_recv_my_icmp_packet() - receive an icmp packet locally 21062306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 21162306a36Sopenharmony_ci * @skb: icmp packet to process 21262306a36Sopenharmony_ci * 21362306a36Sopenharmony_ci * Return: NET_RX_SUCCESS if the packet has been consumed or NET_RX_DROP 21462306a36Sopenharmony_ci * otherwise. 21562306a36Sopenharmony_ci */ 21662306a36Sopenharmony_cistatic int batadv_recv_my_icmp_packet(struct batadv_priv *bat_priv, 21762306a36Sopenharmony_ci struct sk_buff *skb) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci struct batadv_hard_iface *primary_if = NULL; 22062306a36Sopenharmony_ci struct batadv_orig_node *orig_node = NULL; 22162306a36Sopenharmony_ci struct batadv_icmp_header *icmph; 22262306a36Sopenharmony_ci int res, ret = NET_RX_DROP; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci icmph = (struct batadv_icmp_header *)skb->data; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci switch (icmph->msg_type) { 22762306a36Sopenharmony_ci case BATADV_ECHO_REQUEST: 22862306a36Sopenharmony_ci /* answer echo request (ping) */ 22962306a36Sopenharmony_ci primary_if = batadv_primary_if_get_selected(bat_priv); 23062306a36Sopenharmony_ci if (!primary_if) 23162306a36Sopenharmony_ci goto out; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* get routing information */ 23462306a36Sopenharmony_ci orig_node = batadv_orig_hash_find(bat_priv, icmph->orig); 23562306a36Sopenharmony_ci if (!orig_node) 23662306a36Sopenharmony_ci goto out; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* create a copy of the skb, if needed, to modify it. */ 23962306a36Sopenharmony_ci if (skb_cow(skb, ETH_HLEN) < 0) 24062306a36Sopenharmony_ci goto out; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci icmph = (struct batadv_icmp_header *)skb->data; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci ether_addr_copy(icmph->dst, icmph->orig); 24562306a36Sopenharmony_ci ether_addr_copy(icmph->orig, primary_if->net_dev->dev_addr); 24662306a36Sopenharmony_ci icmph->msg_type = BATADV_ECHO_REPLY; 24762306a36Sopenharmony_ci icmph->ttl = BATADV_TTL; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci res = batadv_send_skb_to_orig(skb, orig_node, NULL); 25062306a36Sopenharmony_ci if (res == NET_XMIT_SUCCESS) 25162306a36Sopenharmony_ci ret = NET_RX_SUCCESS; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci /* skb was consumed */ 25462306a36Sopenharmony_ci skb = NULL; 25562306a36Sopenharmony_ci break; 25662306a36Sopenharmony_ci case BATADV_TP: 25762306a36Sopenharmony_ci if (!pskb_may_pull(skb, sizeof(struct batadv_icmp_tp_packet))) 25862306a36Sopenharmony_ci goto out; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci batadv_tp_meter_recv(bat_priv, skb); 26162306a36Sopenharmony_ci ret = NET_RX_SUCCESS; 26262306a36Sopenharmony_ci /* skb was consumed */ 26362306a36Sopenharmony_ci skb = NULL; 26462306a36Sopenharmony_ci goto out; 26562306a36Sopenharmony_ci default: 26662306a36Sopenharmony_ci /* drop unknown type */ 26762306a36Sopenharmony_ci goto out; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ciout: 27062306a36Sopenharmony_ci batadv_hardif_put(primary_if); 27162306a36Sopenharmony_ci batadv_orig_node_put(orig_node); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci kfree_skb(skb); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci return ret; 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic int batadv_recv_icmp_ttl_exceeded(struct batadv_priv *bat_priv, 27962306a36Sopenharmony_ci struct sk_buff *skb) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci struct batadv_hard_iface *primary_if = NULL; 28262306a36Sopenharmony_ci struct batadv_orig_node *orig_node = NULL; 28362306a36Sopenharmony_ci struct batadv_icmp_packet *icmp_packet; 28462306a36Sopenharmony_ci int res, ret = NET_RX_DROP; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci icmp_packet = (struct batadv_icmp_packet *)skb->data; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci /* send TTL exceeded if packet is an echo request (traceroute) */ 28962306a36Sopenharmony_ci if (icmp_packet->msg_type != BATADV_ECHO_REQUEST) { 29062306a36Sopenharmony_ci pr_debug("Warning - can't forward icmp packet from %pM to %pM: ttl exceeded\n", 29162306a36Sopenharmony_ci icmp_packet->orig, icmp_packet->dst); 29262306a36Sopenharmony_ci goto out; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci primary_if = batadv_primary_if_get_selected(bat_priv); 29662306a36Sopenharmony_ci if (!primary_if) 29762306a36Sopenharmony_ci goto out; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* get routing information */ 30062306a36Sopenharmony_ci orig_node = batadv_orig_hash_find(bat_priv, icmp_packet->orig); 30162306a36Sopenharmony_ci if (!orig_node) 30262306a36Sopenharmony_ci goto out; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci /* create a copy of the skb, if needed, to modify it. */ 30562306a36Sopenharmony_ci if (skb_cow(skb, ETH_HLEN) < 0) 30662306a36Sopenharmony_ci goto out; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci icmp_packet = (struct batadv_icmp_packet *)skb->data; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci ether_addr_copy(icmp_packet->dst, icmp_packet->orig); 31162306a36Sopenharmony_ci ether_addr_copy(icmp_packet->orig, primary_if->net_dev->dev_addr); 31262306a36Sopenharmony_ci icmp_packet->msg_type = BATADV_TTL_EXCEEDED; 31362306a36Sopenharmony_ci icmp_packet->ttl = BATADV_TTL; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci res = batadv_send_skb_to_orig(skb, orig_node, NULL); 31662306a36Sopenharmony_ci if (res == NET_RX_SUCCESS) 31762306a36Sopenharmony_ci ret = NET_XMIT_SUCCESS; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci /* skb was consumed */ 32062306a36Sopenharmony_ci skb = NULL; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ciout: 32362306a36Sopenharmony_ci batadv_hardif_put(primary_if); 32462306a36Sopenharmony_ci batadv_orig_node_put(orig_node); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci kfree_skb(skb); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci return ret; 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci/** 33262306a36Sopenharmony_ci * batadv_recv_icmp_packet() - Process incoming icmp packet 33362306a36Sopenharmony_ci * @skb: incoming packet buffer 33462306a36Sopenharmony_ci * @recv_if: incoming hard interface 33562306a36Sopenharmony_ci * 33662306a36Sopenharmony_ci * Return: NET_RX_SUCCESS on success or NET_RX_DROP in case of failure 33762306a36Sopenharmony_ci */ 33862306a36Sopenharmony_ciint batadv_recv_icmp_packet(struct sk_buff *skb, 33962306a36Sopenharmony_ci struct batadv_hard_iface *recv_if) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); 34262306a36Sopenharmony_ci struct batadv_icmp_header *icmph; 34362306a36Sopenharmony_ci struct batadv_icmp_packet_rr *icmp_packet_rr; 34462306a36Sopenharmony_ci struct ethhdr *ethhdr; 34562306a36Sopenharmony_ci struct batadv_orig_node *orig_node = NULL; 34662306a36Sopenharmony_ci int hdr_size = sizeof(struct batadv_icmp_header); 34762306a36Sopenharmony_ci int res, ret = NET_RX_DROP; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci /* drop packet if it has not necessary minimum size */ 35062306a36Sopenharmony_ci if (unlikely(!pskb_may_pull(skb, hdr_size))) 35162306a36Sopenharmony_ci goto free_skb; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci ethhdr = eth_hdr(skb); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci /* packet with unicast indication but non-unicast recipient */ 35662306a36Sopenharmony_ci if (!is_valid_ether_addr(ethhdr->h_dest)) 35762306a36Sopenharmony_ci goto free_skb; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci /* packet with broadcast/multicast sender address */ 36062306a36Sopenharmony_ci if (is_multicast_ether_addr(ethhdr->h_source)) 36162306a36Sopenharmony_ci goto free_skb; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci /* not for me */ 36462306a36Sopenharmony_ci if (!batadv_is_my_mac(bat_priv, ethhdr->h_dest)) 36562306a36Sopenharmony_ci goto free_skb; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci icmph = (struct batadv_icmp_header *)skb->data; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* add record route information if not full */ 37062306a36Sopenharmony_ci if ((icmph->msg_type == BATADV_ECHO_REPLY || 37162306a36Sopenharmony_ci icmph->msg_type == BATADV_ECHO_REQUEST) && 37262306a36Sopenharmony_ci skb->len >= sizeof(struct batadv_icmp_packet_rr)) { 37362306a36Sopenharmony_ci if (skb_linearize(skb) < 0) 37462306a36Sopenharmony_ci goto free_skb; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci /* create a copy of the skb, if needed, to modify it. */ 37762306a36Sopenharmony_ci if (skb_cow(skb, ETH_HLEN) < 0) 37862306a36Sopenharmony_ci goto free_skb; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci ethhdr = eth_hdr(skb); 38162306a36Sopenharmony_ci icmph = (struct batadv_icmp_header *)skb->data; 38262306a36Sopenharmony_ci icmp_packet_rr = (struct batadv_icmp_packet_rr *)icmph; 38362306a36Sopenharmony_ci if (icmp_packet_rr->rr_cur >= BATADV_RR_LEN) 38462306a36Sopenharmony_ci goto free_skb; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci ether_addr_copy(icmp_packet_rr->rr[icmp_packet_rr->rr_cur], 38762306a36Sopenharmony_ci ethhdr->h_dest); 38862306a36Sopenharmony_ci icmp_packet_rr->rr_cur++; 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci /* packet for me */ 39262306a36Sopenharmony_ci if (batadv_is_my_mac(bat_priv, icmph->dst)) 39362306a36Sopenharmony_ci return batadv_recv_my_icmp_packet(bat_priv, skb); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci /* TTL exceeded */ 39662306a36Sopenharmony_ci if (icmph->ttl < 2) 39762306a36Sopenharmony_ci return batadv_recv_icmp_ttl_exceeded(bat_priv, skb); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci /* get routing information */ 40062306a36Sopenharmony_ci orig_node = batadv_orig_hash_find(bat_priv, icmph->dst); 40162306a36Sopenharmony_ci if (!orig_node) 40262306a36Sopenharmony_ci goto free_skb; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci /* create a copy of the skb, if needed, to modify it. */ 40562306a36Sopenharmony_ci if (skb_cow(skb, ETH_HLEN) < 0) 40662306a36Sopenharmony_ci goto put_orig_node; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci icmph = (struct batadv_icmp_header *)skb->data; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci /* decrement ttl */ 41162306a36Sopenharmony_ci icmph->ttl--; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* route it */ 41462306a36Sopenharmony_ci res = batadv_send_skb_to_orig(skb, orig_node, recv_if); 41562306a36Sopenharmony_ci if (res == NET_XMIT_SUCCESS) 41662306a36Sopenharmony_ci ret = NET_RX_SUCCESS; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci /* skb was consumed */ 41962306a36Sopenharmony_ci skb = NULL; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ciput_orig_node: 42262306a36Sopenharmony_ci batadv_orig_node_put(orig_node); 42362306a36Sopenharmony_cifree_skb: 42462306a36Sopenharmony_ci kfree_skb(skb); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci return ret; 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci/** 43062306a36Sopenharmony_ci * batadv_check_unicast_packet() - Check for malformed unicast packets 43162306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 43262306a36Sopenharmony_ci * @skb: packet to check 43362306a36Sopenharmony_ci * @hdr_size: size of header to pull 43462306a36Sopenharmony_ci * 43562306a36Sopenharmony_ci * Checks for short header and bad addresses in the given packet. 43662306a36Sopenharmony_ci * 43762306a36Sopenharmony_ci * Return: negative value when check fails and 0 otherwise. The negative value 43862306a36Sopenharmony_ci * depends on the reason: -ENODATA for bad header, -EBADR for broadcast 43962306a36Sopenharmony_ci * destination or source, and -EREMOTE for non-local (other host) destination. 44062306a36Sopenharmony_ci */ 44162306a36Sopenharmony_cistatic int batadv_check_unicast_packet(struct batadv_priv *bat_priv, 44262306a36Sopenharmony_ci struct sk_buff *skb, int hdr_size) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci struct ethhdr *ethhdr; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci /* drop packet if it has not necessary minimum size */ 44762306a36Sopenharmony_ci if (unlikely(!pskb_may_pull(skb, hdr_size))) 44862306a36Sopenharmony_ci return -ENODATA; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci ethhdr = eth_hdr(skb); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci /* packet with unicast indication but non-unicast recipient */ 45362306a36Sopenharmony_ci if (!is_valid_ether_addr(ethhdr->h_dest)) 45462306a36Sopenharmony_ci return -EBADR; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci /* packet with broadcast/multicast sender address */ 45762306a36Sopenharmony_ci if (is_multicast_ether_addr(ethhdr->h_source)) 45862306a36Sopenharmony_ci return -EBADR; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci /* not for me */ 46162306a36Sopenharmony_ci if (!batadv_is_my_mac(bat_priv, ethhdr->h_dest)) 46262306a36Sopenharmony_ci return -EREMOTE; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci return 0; 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci/** 46862306a36Sopenharmony_ci * batadv_last_bonding_get() - Get last_bonding_candidate of orig_node 46962306a36Sopenharmony_ci * @orig_node: originator node whose last bonding candidate should be retrieved 47062306a36Sopenharmony_ci * 47162306a36Sopenharmony_ci * Return: last bonding candidate of router or NULL if not found 47262306a36Sopenharmony_ci * 47362306a36Sopenharmony_ci * The object is returned with refcounter increased by 1. 47462306a36Sopenharmony_ci */ 47562306a36Sopenharmony_cistatic struct batadv_orig_ifinfo * 47662306a36Sopenharmony_cibatadv_last_bonding_get(struct batadv_orig_node *orig_node) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci struct batadv_orig_ifinfo *last_bonding_candidate; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci spin_lock_bh(&orig_node->neigh_list_lock); 48162306a36Sopenharmony_ci last_bonding_candidate = orig_node->last_bonding_candidate; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci if (last_bonding_candidate) 48462306a36Sopenharmony_ci kref_get(&last_bonding_candidate->refcount); 48562306a36Sopenharmony_ci spin_unlock_bh(&orig_node->neigh_list_lock); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci return last_bonding_candidate; 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci/** 49162306a36Sopenharmony_ci * batadv_last_bonding_replace() - Replace last_bonding_candidate of orig_node 49262306a36Sopenharmony_ci * @orig_node: originator node whose bonding candidates should be replaced 49362306a36Sopenharmony_ci * @new_candidate: new bonding candidate or NULL 49462306a36Sopenharmony_ci */ 49562306a36Sopenharmony_cistatic void 49662306a36Sopenharmony_cibatadv_last_bonding_replace(struct batadv_orig_node *orig_node, 49762306a36Sopenharmony_ci struct batadv_orig_ifinfo *new_candidate) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci struct batadv_orig_ifinfo *old_candidate; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci spin_lock_bh(&orig_node->neigh_list_lock); 50262306a36Sopenharmony_ci old_candidate = orig_node->last_bonding_candidate; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (new_candidate) 50562306a36Sopenharmony_ci kref_get(&new_candidate->refcount); 50662306a36Sopenharmony_ci orig_node->last_bonding_candidate = new_candidate; 50762306a36Sopenharmony_ci spin_unlock_bh(&orig_node->neigh_list_lock); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci batadv_orig_ifinfo_put(old_candidate); 51062306a36Sopenharmony_ci} 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci/** 51362306a36Sopenharmony_ci * batadv_find_router() - find a suitable router for this originator 51462306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 51562306a36Sopenharmony_ci * @orig_node: the destination node 51662306a36Sopenharmony_ci * @recv_if: pointer to interface this packet was received on 51762306a36Sopenharmony_ci * 51862306a36Sopenharmony_ci * Return: the router which should be used for this orig_node on 51962306a36Sopenharmony_ci * this interface, or NULL if not available. 52062306a36Sopenharmony_ci */ 52162306a36Sopenharmony_cistruct batadv_neigh_node * 52262306a36Sopenharmony_cibatadv_find_router(struct batadv_priv *bat_priv, 52362306a36Sopenharmony_ci struct batadv_orig_node *orig_node, 52462306a36Sopenharmony_ci struct batadv_hard_iface *recv_if) 52562306a36Sopenharmony_ci{ 52662306a36Sopenharmony_ci struct batadv_algo_ops *bao = bat_priv->algo_ops; 52762306a36Sopenharmony_ci struct batadv_neigh_node *first_candidate_router = NULL; 52862306a36Sopenharmony_ci struct batadv_neigh_node *next_candidate_router = NULL; 52962306a36Sopenharmony_ci struct batadv_neigh_node *router, *cand_router = NULL; 53062306a36Sopenharmony_ci struct batadv_neigh_node *last_cand_router = NULL; 53162306a36Sopenharmony_ci struct batadv_orig_ifinfo *cand, *first_candidate = NULL; 53262306a36Sopenharmony_ci struct batadv_orig_ifinfo *next_candidate = NULL; 53362306a36Sopenharmony_ci struct batadv_orig_ifinfo *last_candidate; 53462306a36Sopenharmony_ci bool last_candidate_found = false; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci if (!orig_node) 53762306a36Sopenharmony_ci return NULL; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci router = batadv_orig_router_get(orig_node, recv_if); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci if (!router) 54262306a36Sopenharmony_ci return router; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci /* only consider bonding for recv_if == BATADV_IF_DEFAULT (first hop) 54562306a36Sopenharmony_ci * and if activated. 54662306a36Sopenharmony_ci */ 54762306a36Sopenharmony_ci if (!(recv_if == BATADV_IF_DEFAULT && atomic_read(&bat_priv->bonding))) 54862306a36Sopenharmony_ci return router; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci /* bonding: loop through the list of possible routers found 55162306a36Sopenharmony_ci * for the various outgoing interfaces and find a candidate after 55262306a36Sopenharmony_ci * the last chosen bonding candidate (next_candidate). If no such 55362306a36Sopenharmony_ci * router is found, use the first candidate found (the previously 55462306a36Sopenharmony_ci * chosen bonding candidate might have been the last one in the list). 55562306a36Sopenharmony_ci * If this can't be found either, return the previously chosen 55662306a36Sopenharmony_ci * router - obviously there are no other candidates. 55762306a36Sopenharmony_ci */ 55862306a36Sopenharmony_ci rcu_read_lock(); 55962306a36Sopenharmony_ci last_candidate = batadv_last_bonding_get(orig_node); 56062306a36Sopenharmony_ci if (last_candidate) 56162306a36Sopenharmony_ci last_cand_router = rcu_dereference(last_candidate->router); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci hlist_for_each_entry_rcu(cand, &orig_node->ifinfo_list, list) { 56462306a36Sopenharmony_ci /* acquire some structures and references ... */ 56562306a36Sopenharmony_ci if (!kref_get_unless_zero(&cand->refcount)) 56662306a36Sopenharmony_ci continue; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci cand_router = rcu_dereference(cand->router); 56962306a36Sopenharmony_ci if (!cand_router) 57062306a36Sopenharmony_ci goto next; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci if (!kref_get_unless_zero(&cand_router->refcount)) { 57362306a36Sopenharmony_ci cand_router = NULL; 57462306a36Sopenharmony_ci goto next; 57562306a36Sopenharmony_ci } 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci /* alternative candidate should be good enough to be 57862306a36Sopenharmony_ci * considered 57962306a36Sopenharmony_ci */ 58062306a36Sopenharmony_ci if (!bao->neigh.is_similar_or_better(cand_router, 58162306a36Sopenharmony_ci cand->if_outgoing, router, 58262306a36Sopenharmony_ci recv_if)) 58362306a36Sopenharmony_ci goto next; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci /* don't use the same router twice */ 58662306a36Sopenharmony_ci if (last_cand_router == cand_router) 58762306a36Sopenharmony_ci goto next; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci /* mark the first possible candidate */ 59062306a36Sopenharmony_ci if (!first_candidate) { 59162306a36Sopenharmony_ci kref_get(&cand_router->refcount); 59262306a36Sopenharmony_ci kref_get(&cand->refcount); 59362306a36Sopenharmony_ci first_candidate = cand; 59462306a36Sopenharmony_ci first_candidate_router = cand_router; 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci /* check if the loop has already passed the previously selected 59862306a36Sopenharmony_ci * candidate ... this function should select the next candidate 59962306a36Sopenharmony_ci * AFTER the previously used bonding candidate. 60062306a36Sopenharmony_ci */ 60162306a36Sopenharmony_ci if (!last_candidate || last_candidate_found) { 60262306a36Sopenharmony_ci next_candidate = cand; 60362306a36Sopenharmony_ci next_candidate_router = cand_router; 60462306a36Sopenharmony_ci break; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci if (last_candidate == cand) 60862306a36Sopenharmony_ci last_candidate_found = true; 60962306a36Sopenharmony_cinext: 61062306a36Sopenharmony_ci /* free references */ 61162306a36Sopenharmony_ci if (cand_router) { 61262306a36Sopenharmony_ci batadv_neigh_node_put(cand_router); 61362306a36Sopenharmony_ci cand_router = NULL; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci batadv_orig_ifinfo_put(cand); 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci rcu_read_unlock(); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci /* After finding candidates, handle the three cases: 62062306a36Sopenharmony_ci * 1) there is a next candidate, use that 62162306a36Sopenharmony_ci * 2) there is no next candidate, use the first of the list 62262306a36Sopenharmony_ci * 3) there is no candidate at all, return the default router 62362306a36Sopenharmony_ci */ 62462306a36Sopenharmony_ci if (next_candidate) { 62562306a36Sopenharmony_ci batadv_neigh_node_put(router); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci kref_get(&next_candidate_router->refcount); 62862306a36Sopenharmony_ci router = next_candidate_router; 62962306a36Sopenharmony_ci batadv_last_bonding_replace(orig_node, next_candidate); 63062306a36Sopenharmony_ci } else if (first_candidate) { 63162306a36Sopenharmony_ci batadv_neigh_node_put(router); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci kref_get(&first_candidate_router->refcount); 63462306a36Sopenharmony_ci router = first_candidate_router; 63562306a36Sopenharmony_ci batadv_last_bonding_replace(orig_node, first_candidate); 63662306a36Sopenharmony_ci } else { 63762306a36Sopenharmony_ci batadv_last_bonding_replace(orig_node, NULL); 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci /* cleanup of candidates */ 64162306a36Sopenharmony_ci if (first_candidate) { 64262306a36Sopenharmony_ci batadv_neigh_node_put(first_candidate_router); 64362306a36Sopenharmony_ci batadv_orig_ifinfo_put(first_candidate); 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci if (next_candidate) { 64762306a36Sopenharmony_ci batadv_neigh_node_put(next_candidate_router); 64862306a36Sopenharmony_ci batadv_orig_ifinfo_put(next_candidate); 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci batadv_orig_ifinfo_put(last_candidate); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci return router; 65462306a36Sopenharmony_ci} 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_cistatic int batadv_route_unicast_packet(struct sk_buff *skb, 65762306a36Sopenharmony_ci struct batadv_hard_iface *recv_if) 65862306a36Sopenharmony_ci{ 65962306a36Sopenharmony_ci struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); 66062306a36Sopenharmony_ci struct batadv_orig_node *orig_node = NULL; 66162306a36Sopenharmony_ci struct batadv_unicast_packet *unicast_packet; 66262306a36Sopenharmony_ci struct ethhdr *ethhdr = eth_hdr(skb); 66362306a36Sopenharmony_ci int res, hdr_len, ret = NET_RX_DROP; 66462306a36Sopenharmony_ci unsigned int len; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci unicast_packet = (struct batadv_unicast_packet *)skb->data; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci /* TTL exceeded */ 66962306a36Sopenharmony_ci if (unicast_packet->ttl < 2) { 67062306a36Sopenharmony_ci pr_debug("Warning - can't forward unicast packet from %pM to %pM: ttl exceeded\n", 67162306a36Sopenharmony_ci ethhdr->h_source, unicast_packet->dest); 67262306a36Sopenharmony_ci goto free_skb; 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci /* get routing information */ 67662306a36Sopenharmony_ci orig_node = batadv_orig_hash_find(bat_priv, unicast_packet->dest); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci if (!orig_node) 67962306a36Sopenharmony_ci goto free_skb; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci /* create a copy of the skb, if needed, to modify it. */ 68262306a36Sopenharmony_ci if (skb_cow(skb, ETH_HLEN) < 0) 68362306a36Sopenharmony_ci goto put_orig_node; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci /* decrement ttl */ 68662306a36Sopenharmony_ci unicast_packet = (struct batadv_unicast_packet *)skb->data; 68762306a36Sopenharmony_ci unicast_packet->ttl--; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci switch (unicast_packet->packet_type) { 69062306a36Sopenharmony_ci case BATADV_UNICAST_4ADDR: 69162306a36Sopenharmony_ci hdr_len = sizeof(struct batadv_unicast_4addr_packet); 69262306a36Sopenharmony_ci break; 69362306a36Sopenharmony_ci case BATADV_UNICAST: 69462306a36Sopenharmony_ci hdr_len = sizeof(struct batadv_unicast_packet); 69562306a36Sopenharmony_ci break; 69662306a36Sopenharmony_ci default: 69762306a36Sopenharmony_ci /* other packet types not supported - yet */ 69862306a36Sopenharmony_ci hdr_len = -1; 69962306a36Sopenharmony_ci break; 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci if (hdr_len > 0) 70362306a36Sopenharmony_ci batadv_skb_set_priority(skb, hdr_len); 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci len = skb->len; 70662306a36Sopenharmony_ci res = batadv_send_skb_to_orig(skb, orig_node, recv_if); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci /* translate transmit result into receive result */ 70962306a36Sopenharmony_ci if (res == NET_XMIT_SUCCESS) { 71062306a36Sopenharmony_ci ret = NET_RX_SUCCESS; 71162306a36Sopenharmony_ci /* skb was transmitted and consumed */ 71262306a36Sopenharmony_ci batadv_inc_counter(bat_priv, BATADV_CNT_FORWARD); 71362306a36Sopenharmony_ci batadv_add_counter(bat_priv, BATADV_CNT_FORWARD_BYTES, 71462306a36Sopenharmony_ci len + ETH_HLEN); 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci /* skb was consumed */ 71862306a36Sopenharmony_ci skb = NULL; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ciput_orig_node: 72162306a36Sopenharmony_ci batadv_orig_node_put(orig_node); 72262306a36Sopenharmony_cifree_skb: 72362306a36Sopenharmony_ci kfree_skb(skb); 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci return ret; 72662306a36Sopenharmony_ci} 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci/** 72962306a36Sopenharmony_ci * batadv_reroute_unicast_packet() - update the unicast header for re-routing 73062306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information 73162306a36Sopenharmony_ci * @skb: unicast packet to process 73262306a36Sopenharmony_ci * @unicast_packet: the unicast header to be updated 73362306a36Sopenharmony_ci * @dst_addr: the payload destination 73462306a36Sopenharmony_ci * @vid: VLAN identifier 73562306a36Sopenharmony_ci * 73662306a36Sopenharmony_ci * Search the translation table for dst_addr and update the unicast header with 73762306a36Sopenharmony_ci * the new corresponding information (originator address where the destination 73862306a36Sopenharmony_ci * client currently is and its known TTVN) 73962306a36Sopenharmony_ci * 74062306a36Sopenharmony_ci * Return: true if the packet header has been updated, false otherwise 74162306a36Sopenharmony_ci */ 74262306a36Sopenharmony_cistatic bool 74362306a36Sopenharmony_cibatadv_reroute_unicast_packet(struct batadv_priv *bat_priv, struct sk_buff *skb, 74462306a36Sopenharmony_ci struct batadv_unicast_packet *unicast_packet, 74562306a36Sopenharmony_ci u8 *dst_addr, unsigned short vid) 74662306a36Sopenharmony_ci{ 74762306a36Sopenharmony_ci struct batadv_orig_node *orig_node = NULL; 74862306a36Sopenharmony_ci struct batadv_hard_iface *primary_if = NULL; 74962306a36Sopenharmony_ci bool ret = false; 75062306a36Sopenharmony_ci const u8 *orig_addr; 75162306a36Sopenharmony_ci u8 orig_ttvn; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci if (batadv_is_my_client(bat_priv, dst_addr, vid)) { 75462306a36Sopenharmony_ci primary_if = batadv_primary_if_get_selected(bat_priv); 75562306a36Sopenharmony_ci if (!primary_if) 75662306a36Sopenharmony_ci goto out; 75762306a36Sopenharmony_ci orig_addr = primary_if->net_dev->dev_addr; 75862306a36Sopenharmony_ci orig_ttvn = (u8)atomic_read(&bat_priv->tt.vn); 75962306a36Sopenharmony_ci } else { 76062306a36Sopenharmony_ci orig_node = batadv_transtable_search(bat_priv, NULL, dst_addr, 76162306a36Sopenharmony_ci vid); 76262306a36Sopenharmony_ci if (!orig_node) 76362306a36Sopenharmony_ci goto out; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci if (batadv_compare_eth(orig_node->orig, unicast_packet->dest)) 76662306a36Sopenharmony_ci goto out; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci orig_addr = orig_node->orig; 76962306a36Sopenharmony_ci orig_ttvn = (u8)atomic_read(&orig_node->last_ttvn); 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci /* update the packet header */ 77362306a36Sopenharmony_ci skb_postpull_rcsum(skb, unicast_packet, sizeof(*unicast_packet)); 77462306a36Sopenharmony_ci ether_addr_copy(unicast_packet->dest, orig_addr); 77562306a36Sopenharmony_ci unicast_packet->ttvn = orig_ttvn; 77662306a36Sopenharmony_ci skb_postpush_rcsum(skb, unicast_packet, sizeof(*unicast_packet)); 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci ret = true; 77962306a36Sopenharmony_ciout: 78062306a36Sopenharmony_ci batadv_hardif_put(primary_if); 78162306a36Sopenharmony_ci batadv_orig_node_put(orig_node); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci return ret; 78462306a36Sopenharmony_ci} 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_cistatic bool batadv_check_unicast_ttvn(struct batadv_priv *bat_priv, 78762306a36Sopenharmony_ci struct sk_buff *skb, int hdr_len) 78862306a36Sopenharmony_ci{ 78962306a36Sopenharmony_ci struct batadv_unicast_packet *unicast_packet; 79062306a36Sopenharmony_ci struct batadv_hard_iface *primary_if; 79162306a36Sopenharmony_ci struct batadv_orig_node *orig_node; 79262306a36Sopenharmony_ci u8 curr_ttvn, old_ttvn; 79362306a36Sopenharmony_ci struct ethhdr *ethhdr; 79462306a36Sopenharmony_ci unsigned short vid; 79562306a36Sopenharmony_ci int is_old_ttvn; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci /* check if there is enough data before accessing it */ 79862306a36Sopenharmony_ci if (!pskb_may_pull(skb, hdr_len + ETH_HLEN)) 79962306a36Sopenharmony_ci return false; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci /* create a copy of the skb (in case of for re-routing) to modify it. */ 80262306a36Sopenharmony_ci if (skb_cow(skb, sizeof(*unicast_packet)) < 0) 80362306a36Sopenharmony_ci return false; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci unicast_packet = (struct batadv_unicast_packet *)skb->data; 80662306a36Sopenharmony_ci vid = batadv_get_vid(skb, hdr_len); 80762306a36Sopenharmony_ci ethhdr = (struct ethhdr *)(skb->data + hdr_len); 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci /* do not reroute multicast frames in a unicast header */ 81062306a36Sopenharmony_ci if (is_multicast_ether_addr(ethhdr->h_dest)) 81162306a36Sopenharmony_ci return true; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci /* check if the destination client was served by this node and it is now 81462306a36Sopenharmony_ci * roaming. In this case, it means that the node has got a ROAM_ADV 81562306a36Sopenharmony_ci * message and that it knows the new destination in the mesh to re-route 81662306a36Sopenharmony_ci * the packet to 81762306a36Sopenharmony_ci */ 81862306a36Sopenharmony_ci if (batadv_tt_local_client_is_roaming(bat_priv, ethhdr->h_dest, vid)) { 81962306a36Sopenharmony_ci if (batadv_reroute_unicast_packet(bat_priv, skb, unicast_packet, 82062306a36Sopenharmony_ci ethhdr->h_dest, vid)) 82162306a36Sopenharmony_ci batadv_dbg_ratelimited(BATADV_DBG_TT, 82262306a36Sopenharmony_ci bat_priv, 82362306a36Sopenharmony_ci "Rerouting unicast packet to %pM (dst=%pM): Local Roaming\n", 82462306a36Sopenharmony_ci unicast_packet->dest, 82562306a36Sopenharmony_ci ethhdr->h_dest); 82662306a36Sopenharmony_ci /* at this point the mesh destination should have been 82762306a36Sopenharmony_ci * substituted with the originator address found in the global 82862306a36Sopenharmony_ci * table. If not, let the packet go untouched anyway because 82962306a36Sopenharmony_ci * there is nothing the node can do 83062306a36Sopenharmony_ci */ 83162306a36Sopenharmony_ci return true; 83262306a36Sopenharmony_ci } 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci /* retrieve the TTVN known by this node for the packet destination. This 83562306a36Sopenharmony_ci * value is used later to check if the node which sent (or re-routed 83662306a36Sopenharmony_ci * last time) the packet had an updated information or not 83762306a36Sopenharmony_ci */ 83862306a36Sopenharmony_ci curr_ttvn = (u8)atomic_read(&bat_priv->tt.vn); 83962306a36Sopenharmony_ci if (!batadv_is_my_mac(bat_priv, unicast_packet->dest)) { 84062306a36Sopenharmony_ci orig_node = batadv_orig_hash_find(bat_priv, 84162306a36Sopenharmony_ci unicast_packet->dest); 84262306a36Sopenharmony_ci /* if it is not possible to find the orig_node representing the 84362306a36Sopenharmony_ci * destination, the packet can immediately be dropped as it will 84462306a36Sopenharmony_ci * not be possible to deliver it 84562306a36Sopenharmony_ci */ 84662306a36Sopenharmony_ci if (!orig_node) 84762306a36Sopenharmony_ci return false; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci curr_ttvn = (u8)atomic_read(&orig_node->last_ttvn); 85062306a36Sopenharmony_ci batadv_orig_node_put(orig_node); 85162306a36Sopenharmony_ci } 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci /* check if the TTVN contained in the packet is fresher than what the 85462306a36Sopenharmony_ci * node knows 85562306a36Sopenharmony_ci */ 85662306a36Sopenharmony_ci is_old_ttvn = batadv_seq_before(unicast_packet->ttvn, curr_ttvn); 85762306a36Sopenharmony_ci if (!is_old_ttvn) 85862306a36Sopenharmony_ci return true; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci old_ttvn = unicast_packet->ttvn; 86162306a36Sopenharmony_ci /* the packet was forged based on outdated network information. Its 86262306a36Sopenharmony_ci * destination can possibly be updated and forwarded towards the new 86362306a36Sopenharmony_ci * target host 86462306a36Sopenharmony_ci */ 86562306a36Sopenharmony_ci if (batadv_reroute_unicast_packet(bat_priv, skb, unicast_packet, 86662306a36Sopenharmony_ci ethhdr->h_dest, vid)) { 86762306a36Sopenharmony_ci batadv_dbg_ratelimited(BATADV_DBG_TT, bat_priv, 86862306a36Sopenharmony_ci "Rerouting unicast packet to %pM (dst=%pM): TTVN mismatch old_ttvn=%u new_ttvn=%u\n", 86962306a36Sopenharmony_ci unicast_packet->dest, ethhdr->h_dest, 87062306a36Sopenharmony_ci old_ttvn, curr_ttvn); 87162306a36Sopenharmony_ci return true; 87262306a36Sopenharmony_ci } 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci /* the packet has not been re-routed: either the destination is 87562306a36Sopenharmony_ci * currently served by this node or there is no destination at all and 87662306a36Sopenharmony_ci * it is possible to drop the packet 87762306a36Sopenharmony_ci */ 87862306a36Sopenharmony_ci if (!batadv_is_my_client(bat_priv, ethhdr->h_dest, vid)) 87962306a36Sopenharmony_ci return false; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci /* update the header in order to let the packet be delivered to this 88262306a36Sopenharmony_ci * node's soft interface 88362306a36Sopenharmony_ci */ 88462306a36Sopenharmony_ci primary_if = batadv_primary_if_get_selected(bat_priv); 88562306a36Sopenharmony_ci if (!primary_if) 88662306a36Sopenharmony_ci return false; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci /* update the packet header */ 88962306a36Sopenharmony_ci skb_postpull_rcsum(skb, unicast_packet, sizeof(*unicast_packet)); 89062306a36Sopenharmony_ci ether_addr_copy(unicast_packet->dest, primary_if->net_dev->dev_addr); 89162306a36Sopenharmony_ci unicast_packet->ttvn = curr_ttvn; 89262306a36Sopenharmony_ci skb_postpush_rcsum(skb, unicast_packet, sizeof(*unicast_packet)); 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci batadv_hardif_put(primary_if); 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci return true; 89762306a36Sopenharmony_ci} 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci/** 90062306a36Sopenharmony_ci * batadv_recv_unhandled_unicast_packet() - receive and process packets which 90162306a36Sopenharmony_ci * are in the unicast number space but not yet known to the implementation 90262306a36Sopenharmony_ci * @skb: unicast tvlv packet to process 90362306a36Sopenharmony_ci * @recv_if: pointer to interface this packet was received on 90462306a36Sopenharmony_ci * 90562306a36Sopenharmony_ci * Return: NET_RX_SUCCESS if the packet has been consumed or NET_RX_DROP 90662306a36Sopenharmony_ci * otherwise. 90762306a36Sopenharmony_ci */ 90862306a36Sopenharmony_ciint batadv_recv_unhandled_unicast_packet(struct sk_buff *skb, 90962306a36Sopenharmony_ci struct batadv_hard_iface *recv_if) 91062306a36Sopenharmony_ci{ 91162306a36Sopenharmony_ci struct batadv_unicast_packet *unicast_packet; 91262306a36Sopenharmony_ci struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); 91362306a36Sopenharmony_ci int check, hdr_size = sizeof(*unicast_packet); 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci check = batadv_check_unicast_packet(bat_priv, skb, hdr_size); 91662306a36Sopenharmony_ci if (check < 0) 91762306a36Sopenharmony_ci goto free_skb; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci /* we don't know about this type, drop it. */ 92062306a36Sopenharmony_ci unicast_packet = (struct batadv_unicast_packet *)skb->data; 92162306a36Sopenharmony_ci if (batadv_is_my_mac(bat_priv, unicast_packet->dest)) 92262306a36Sopenharmony_ci goto free_skb; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci return batadv_route_unicast_packet(skb, recv_if); 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_cifree_skb: 92762306a36Sopenharmony_ci kfree_skb(skb); 92862306a36Sopenharmony_ci return NET_RX_DROP; 92962306a36Sopenharmony_ci} 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci/** 93262306a36Sopenharmony_ci * batadv_recv_unicast_packet() - Process incoming unicast packet 93362306a36Sopenharmony_ci * @skb: incoming packet buffer 93462306a36Sopenharmony_ci * @recv_if: incoming hard interface 93562306a36Sopenharmony_ci * 93662306a36Sopenharmony_ci * Return: NET_RX_SUCCESS on success or NET_RX_DROP in case of failure 93762306a36Sopenharmony_ci */ 93862306a36Sopenharmony_ciint batadv_recv_unicast_packet(struct sk_buff *skb, 93962306a36Sopenharmony_ci struct batadv_hard_iface *recv_if) 94062306a36Sopenharmony_ci{ 94162306a36Sopenharmony_ci struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); 94262306a36Sopenharmony_ci struct batadv_unicast_packet *unicast_packet; 94362306a36Sopenharmony_ci struct batadv_unicast_4addr_packet *unicast_4addr_packet; 94462306a36Sopenharmony_ci u8 *orig_addr, *orig_addr_gw; 94562306a36Sopenharmony_ci struct batadv_orig_node *orig_node = NULL, *orig_node_gw = NULL; 94662306a36Sopenharmony_ci int check, hdr_size = sizeof(*unicast_packet); 94762306a36Sopenharmony_ci enum batadv_subtype subtype; 94862306a36Sopenharmony_ci int ret = NET_RX_DROP; 94962306a36Sopenharmony_ci bool is4addr, is_gw; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci unicast_packet = (struct batadv_unicast_packet *)skb->data; 95262306a36Sopenharmony_ci is4addr = unicast_packet->packet_type == BATADV_UNICAST_4ADDR; 95362306a36Sopenharmony_ci /* the caller function should have already pulled 2 bytes */ 95462306a36Sopenharmony_ci if (is4addr) 95562306a36Sopenharmony_ci hdr_size = sizeof(*unicast_4addr_packet); 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci /* function returns -EREMOTE for promiscuous packets */ 95862306a36Sopenharmony_ci check = batadv_check_unicast_packet(bat_priv, skb, hdr_size); 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci /* Even though the packet is not for us, we might save it to use for 96162306a36Sopenharmony_ci * decoding a later received coded packet 96262306a36Sopenharmony_ci */ 96362306a36Sopenharmony_ci if (check == -EREMOTE) 96462306a36Sopenharmony_ci batadv_nc_skb_store_sniffed_unicast(bat_priv, skb); 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci if (check < 0) 96762306a36Sopenharmony_ci goto free_skb; 96862306a36Sopenharmony_ci if (!batadv_check_unicast_ttvn(bat_priv, skb, hdr_size)) 96962306a36Sopenharmony_ci goto free_skb; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci unicast_packet = (struct batadv_unicast_packet *)skb->data; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci /* packet for me */ 97462306a36Sopenharmony_ci if (batadv_is_my_mac(bat_priv, unicast_packet->dest)) { 97562306a36Sopenharmony_ci /* If this is a unicast packet from another backgone gw, 97662306a36Sopenharmony_ci * drop it. 97762306a36Sopenharmony_ci */ 97862306a36Sopenharmony_ci orig_addr_gw = eth_hdr(skb)->h_source; 97962306a36Sopenharmony_ci orig_node_gw = batadv_orig_hash_find(bat_priv, orig_addr_gw); 98062306a36Sopenharmony_ci if (orig_node_gw) { 98162306a36Sopenharmony_ci is_gw = batadv_bla_is_backbone_gw(skb, orig_node_gw, 98262306a36Sopenharmony_ci hdr_size); 98362306a36Sopenharmony_ci batadv_orig_node_put(orig_node_gw); 98462306a36Sopenharmony_ci if (is_gw) { 98562306a36Sopenharmony_ci batadv_dbg(BATADV_DBG_BLA, bat_priv, 98662306a36Sopenharmony_ci "%s(): Dropped unicast pkt received from another backbone gw %pM.\n", 98762306a36Sopenharmony_ci __func__, orig_addr_gw); 98862306a36Sopenharmony_ci goto free_skb; 98962306a36Sopenharmony_ci } 99062306a36Sopenharmony_ci } 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci if (is4addr) { 99362306a36Sopenharmony_ci unicast_4addr_packet = 99462306a36Sopenharmony_ci (struct batadv_unicast_4addr_packet *)skb->data; 99562306a36Sopenharmony_ci subtype = unicast_4addr_packet->subtype; 99662306a36Sopenharmony_ci batadv_dat_inc_counter(bat_priv, subtype); 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci /* Only payload data should be considered for speedy 99962306a36Sopenharmony_ci * join. For example, DAT also uses unicast 4addr 100062306a36Sopenharmony_ci * types, but those packets should not be considered 100162306a36Sopenharmony_ci * for speedy join, since the clients do not actually 100262306a36Sopenharmony_ci * reside at the sending originator. 100362306a36Sopenharmony_ci */ 100462306a36Sopenharmony_ci if (subtype == BATADV_P_DATA) { 100562306a36Sopenharmony_ci orig_addr = unicast_4addr_packet->src; 100662306a36Sopenharmony_ci orig_node = batadv_orig_hash_find(bat_priv, 100762306a36Sopenharmony_ci orig_addr); 100862306a36Sopenharmony_ci } 100962306a36Sopenharmony_ci } 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci if (batadv_dat_snoop_incoming_arp_request(bat_priv, skb, 101262306a36Sopenharmony_ci hdr_size)) 101362306a36Sopenharmony_ci goto rx_success; 101462306a36Sopenharmony_ci if (batadv_dat_snoop_incoming_arp_reply(bat_priv, skb, 101562306a36Sopenharmony_ci hdr_size)) 101662306a36Sopenharmony_ci goto rx_success; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci batadv_dat_snoop_incoming_dhcp_ack(bat_priv, skb, hdr_size); 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci batadv_interface_rx(recv_if->soft_iface, skb, hdr_size, 102162306a36Sopenharmony_ci orig_node); 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_cirx_success: 102462306a36Sopenharmony_ci batadv_orig_node_put(orig_node); 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci return NET_RX_SUCCESS; 102762306a36Sopenharmony_ci } 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci ret = batadv_route_unicast_packet(skb, recv_if); 103062306a36Sopenharmony_ci /* skb was consumed */ 103162306a36Sopenharmony_ci skb = NULL; 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_cifree_skb: 103462306a36Sopenharmony_ci kfree_skb(skb); 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci return ret; 103762306a36Sopenharmony_ci} 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci/** 104062306a36Sopenharmony_ci * batadv_recv_unicast_tvlv() - receive and process unicast tvlv packets 104162306a36Sopenharmony_ci * @skb: unicast tvlv packet to process 104262306a36Sopenharmony_ci * @recv_if: pointer to interface this packet was received on 104362306a36Sopenharmony_ci * 104462306a36Sopenharmony_ci * Return: NET_RX_SUCCESS if the packet has been consumed or NET_RX_DROP 104562306a36Sopenharmony_ci * otherwise. 104662306a36Sopenharmony_ci */ 104762306a36Sopenharmony_ciint batadv_recv_unicast_tvlv(struct sk_buff *skb, 104862306a36Sopenharmony_ci struct batadv_hard_iface *recv_if) 104962306a36Sopenharmony_ci{ 105062306a36Sopenharmony_ci struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); 105162306a36Sopenharmony_ci struct batadv_unicast_tvlv_packet *unicast_tvlv_packet; 105262306a36Sopenharmony_ci unsigned char *tvlv_buff; 105362306a36Sopenharmony_ci u16 tvlv_buff_len; 105462306a36Sopenharmony_ci int hdr_size = sizeof(*unicast_tvlv_packet); 105562306a36Sopenharmony_ci int ret = NET_RX_DROP; 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci if (batadv_check_unicast_packet(bat_priv, skb, hdr_size) < 0) 105862306a36Sopenharmony_ci goto free_skb; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci /* the header is likely to be modified while forwarding */ 106162306a36Sopenharmony_ci if (skb_cow(skb, hdr_size) < 0) 106262306a36Sopenharmony_ci goto free_skb; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci /* packet needs to be linearized to access the tvlv content */ 106562306a36Sopenharmony_ci if (skb_linearize(skb) < 0) 106662306a36Sopenharmony_ci goto free_skb; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci unicast_tvlv_packet = (struct batadv_unicast_tvlv_packet *)skb->data; 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci tvlv_buff = (unsigned char *)(skb->data + hdr_size); 107162306a36Sopenharmony_ci tvlv_buff_len = ntohs(unicast_tvlv_packet->tvlv_len); 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci if (tvlv_buff_len > skb->len - hdr_size) 107462306a36Sopenharmony_ci goto free_skb; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci ret = batadv_tvlv_containers_process(bat_priv, BATADV_UNICAST_TVLV, 107762306a36Sopenharmony_ci NULL, skb, tvlv_buff, 107862306a36Sopenharmony_ci tvlv_buff_len); 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci if (ret != NET_RX_SUCCESS) { 108162306a36Sopenharmony_ci ret = batadv_route_unicast_packet(skb, recv_if); 108262306a36Sopenharmony_ci /* skb was consumed */ 108362306a36Sopenharmony_ci skb = NULL; 108462306a36Sopenharmony_ci } 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_cifree_skb: 108762306a36Sopenharmony_ci kfree_skb(skb); 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci return ret; 109062306a36Sopenharmony_ci} 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci/** 109362306a36Sopenharmony_ci * batadv_recv_frag_packet() - process received fragment 109462306a36Sopenharmony_ci * @skb: the received fragment 109562306a36Sopenharmony_ci * @recv_if: interface that the skb is received on 109662306a36Sopenharmony_ci * 109762306a36Sopenharmony_ci * This function does one of the three following things: 1) Forward fragment, if 109862306a36Sopenharmony_ci * the assembled packet will exceed our MTU; 2) Buffer fragment, if we still 109962306a36Sopenharmony_ci * lack further fragments; 3) Merge fragments, if we have all needed parts. 110062306a36Sopenharmony_ci * 110162306a36Sopenharmony_ci * Return: NET_RX_DROP if the skb is not consumed, NET_RX_SUCCESS otherwise. 110262306a36Sopenharmony_ci */ 110362306a36Sopenharmony_ciint batadv_recv_frag_packet(struct sk_buff *skb, 110462306a36Sopenharmony_ci struct batadv_hard_iface *recv_if) 110562306a36Sopenharmony_ci{ 110662306a36Sopenharmony_ci struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); 110762306a36Sopenharmony_ci struct batadv_orig_node *orig_node_src = NULL; 110862306a36Sopenharmony_ci struct batadv_frag_packet *frag_packet; 110962306a36Sopenharmony_ci int ret = NET_RX_DROP; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci if (batadv_check_unicast_packet(bat_priv, skb, 111262306a36Sopenharmony_ci sizeof(*frag_packet)) < 0) 111362306a36Sopenharmony_ci goto free_skb; 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci frag_packet = (struct batadv_frag_packet *)skb->data; 111662306a36Sopenharmony_ci orig_node_src = batadv_orig_hash_find(bat_priv, frag_packet->orig); 111762306a36Sopenharmony_ci if (!orig_node_src) 111862306a36Sopenharmony_ci goto free_skb; 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci skb->priority = frag_packet->priority + 256; 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci /* Route the fragment if it is not for us and too big to be merged. */ 112362306a36Sopenharmony_ci if (!batadv_is_my_mac(bat_priv, frag_packet->dest) && 112462306a36Sopenharmony_ci batadv_frag_skb_fwd(skb, recv_if, orig_node_src)) { 112562306a36Sopenharmony_ci /* skb was consumed */ 112662306a36Sopenharmony_ci skb = NULL; 112762306a36Sopenharmony_ci ret = NET_RX_SUCCESS; 112862306a36Sopenharmony_ci goto put_orig_node; 112962306a36Sopenharmony_ci } 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_RX); 113262306a36Sopenharmony_ci batadv_add_counter(bat_priv, BATADV_CNT_FRAG_RX_BYTES, skb->len); 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci /* Add fragment to buffer and merge if possible. */ 113562306a36Sopenharmony_ci if (!batadv_frag_skb_buffer(&skb, orig_node_src)) 113662306a36Sopenharmony_ci goto put_orig_node; 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci /* Deliver merged packet to the appropriate handler, if it was 113962306a36Sopenharmony_ci * merged 114062306a36Sopenharmony_ci */ 114162306a36Sopenharmony_ci if (skb) { 114262306a36Sopenharmony_ci batadv_batman_skb_recv(skb, recv_if->net_dev, 114362306a36Sopenharmony_ci &recv_if->batman_adv_ptype, NULL); 114462306a36Sopenharmony_ci /* skb was consumed */ 114562306a36Sopenharmony_ci skb = NULL; 114662306a36Sopenharmony_ci } 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci ret = NET_RX_SUCCESS; 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ciput_orig_node: 115162306a36Sopenharmony_ci batadv_orig_node_put(orig_node_src); 115262306a36Sopenharmony_cifree_skb: 115362306a36Sopenharmony_ci kfree_skb(skb); 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci return ret; 115662306a36Sopenharmony_ci} 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci/** 115962306a36Sopenharmony_ci * batadv_recv_bcast_packet() - Process incoming broadcast packet 116062306a36Sopenharmony_ci * @skb: incoming packet buffer 116162306a36Sopenharmony_ci * @recv_if: incoming hard interface 116262306a36Sopenharmony_ci * 116362306a36Sopenharmony_ci * Return: NET_RX_SUCCESS on success or NET_RX_DROP in case of failure 116462306a36Sopenharmony_ci */ 116562306a36Sopenharmony_ciint batadv_recv_bcast_packet(struct sk_buff *skb, 116662306a36Sopenharmony_ci struct batadv_hard_iface *recv_if) 116762306a36Sopenharmony_ci{ 116862306a36Sopenharmony_ci struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); 116962306a36Sopenharmony_ci struct batadv_orig_node *orig_node = NULL; 117062306a36Sopenharmony_ci struct batadv_bcast_packet *bcast_packet; 117162306a36Sopenharmony_ci struct ethhdr *ethhdr; 117262306a36Sopenharmony_ci int hdr_size = sizeof(*bcast_packet); 117362306a36Sopenharmony_ci s32 seq_diff; 117462306a36Sopenharmony_ci u32 seqno; 117562306a36Sopenharmony_ci int ret; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci /* drop packet if it has not necessary minimum size */ 117862306a36Sopenharmony_ci if (unlikely(!pskb_may_pull(skb, hdr_size))) 117962306a36Sopenharmony_ci goto free_skb; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci ethhdr = eth_hdr(skb); 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci /* packet with broadcast indication but unicast recipient */ 118462306a36Sopenharmony_ci if (!is_broadcast_ether_addr(ethhdr->h_dest)) 118562306a36Sopenharmony_ci goto free_skb; 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci /* packet with broadcast/multicast sender address */ 118862306a36Sopenharmony_ci if (is_multicast_ether_addr(ethhdr->h_source)) 118962306a36Sopenharmony_ci goto free_skb; 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci /* ignore broadcasts sent by myself */ 119262306a36Sopenharmony_ci if (batadv_is_my_mac(bat_priv, ethhdr->h_source)) 119362306a36Sopenharmony_ci goto free_skb; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci bcast_packet = (struct batadv_bcast_packet *)skb->data; 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci /* ignore broadcasts originated by myself */ 119862306a36Sopenharmony_ci if (batadv_is_my_mac(bat_priv, bcast_packet->orig)) 119962306a36Sopenharmony_ci goto free_skb; 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci if (bcast_packet->ttl-- < 2) 120262306a36Sopenharmony_ci goto free_skb; 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci orig_node = batadv_orig_hash_find(bat_priv, bcast_packet->orig); 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci if (!orig_node) 120762306a36Sopenharmony_ci goto free_skb; 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci spin_lock_bh(&orig_node->bcast_seqno_lock); 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci seqno = ntohl(bcast_packet->seqno); 121262306a36Sopenharmony_ci /* check whether the packet is a duplicate */ 121362306a36Sopenharmony_ci if (batadv_test_bit(orig_node->bcast_bits, orig_node->last_bcast_seqno, 121462306a36Sopenharmony_ci seqno)) 121562306a36Sopenharmony_ci goto spin_unlock; 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci seq_diff = seqno - orig_node->last_bcast_seqno; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci /* check whether the packet is old and the host just restarted. */ 122062306a36Sopenharmony_ci if (batadv_window_protected(bat_priv, seq_diff, 122162306a36Sopenharmony_ci BATADV_BCAST_MAX_AGE, 122262306a36Sopenharmony_ci &orig_node->bcast_seqno_reset, NULL)) 122362306a36Sopenharmony_ci goto spin_unlock; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci /* mark broadcast in flood history, update window position 122662306a36Sopenharmony_ci * if required. 122762306a36Sopenharmony_ci */ 122862306a36Sopenharmony_ci if (batadv_bit_get_packet(bat_priv, orig_node->bcast_bits, seq_diff, 1)) 122962306a36Sopenharmony_ci orig_node->last_bcast_seqno = seqno; 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci spin_unlock_bh(&orig_node->bcast_seqno_lock); 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci /* check whether this has been sent by another originator before */ 123462306a36Sopenharmony_ci if (batadv_bla_check_bcast_duplist(bat_priv, skb)) 123562306a36Sopenharmony_ci goto free_skb; 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci batadv_skb_set_priority(skb, sizeof(struct batadv_bcast_packet)); 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci /* rebroadcast packet */ 124062306a36Sopenharmony_ci ret = batadv_forw_bcast_packet(bat_priv, skb, 0, false); 124162306a36Sopenharmony_ci if (ret == NETDEV_TX_BUSY) 124262306a36Sopenharmony_ci goto free_skb; 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci /* don't hand the broadcast up if it is from an originator 124562306a36Sopenharmony_ci * from the same backbone. 124662306a36Sopenharmony_ci */ 124762306a36Sopenharmony_ci if (batadv_bla_is_backbone_gw(skb, orig_node, hdr_size)) 124862306a36Sopenharmony_ci goto free_skb; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci if (batadv_dat_snoop_incoming_arp_request(bat_priv, skb, hdr_size)) 125162306a36Sopenharmony_ci goto rx_success; 125262306a36Sopenharmony_ci if (batadv_dat_snoop_incoming_arp_reply(bat_priv, skb, hdr_size)) 125362306a36Sopenharmony_ci goto rx_success; 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci batadv_dat_snoop_incoming_dhcp_ack(bat_priv, skb, hdr_size); 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci /* broadcast for me */ 125862306a36Sopenharmony_ci batadv_interface_rx(recv_if->soft_iface, skb, hdr_size, orig_node); 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_cirx_success: 126162306a36Sopenharmony_ci ret = NET_RX_SUCCESS; 126262306a36Sopenharmony_ci goto out; 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_cispin_unlock: 126562306a36Sopenharmony_ci spin_unlock_bh(&orig_node->bcast_seqno_lock); 126662306a36Sopenharmony_cifree_skb: 126762306a36Sopenharmony_ci kfree_skb(skb); 126862306a36Sopenharmony_ci ret = NET_RX_DROP; 126962306a36Sopenharmony_ciout: 127062306a36Sopenharmony_ci batadv_orig_node_put(orig_node); 127162306a36Sopenharmony_ci return ret; 127262306a36Sopenharmony_ci} 1273